LibC: Implement freopen, rewind and fix bugs in code

Now everything will be properly locked once threads are implemented.
All functions "lock" the stream for the wanted operation
This commit is contained in:
Bananymous 2024-02-14 16:34:21 +02:00
parent 7eb5d220fd
commit de629291b9
1 changed files with 184 additions and 92 deletions

View File

@ -11,6 +11,7 @@
struct FILE struct FILE
{ {
int fd { -1 }; int fd { -1 };
mode_t mode { 0 };
bool eof { false }; bool eof { false };
bool error { false }; bool error { false };
@ -18,11 +19,27 @@ struct FILE
uint32_t buffer_index { 0 }; uint32_t buffer_index { 0 };
}; };
struct ScopeLock
{
ScopeLock(FILE* file)
: m_file(file)
{
flockfile(m_file);
}
~ScopeLock()
{
funlockfile(m_file);
}
FILE* m_file;
};
static FILE s_files[FOPEN_MAX] { static FILE s_files[FOPEN_MAX] {
{ .fd = STDIN_FILENO }, { .fd = STDIN_FILENO, .mode = O_RDONLY },
{ .fd = STDOUT_FILENO }, { .fd = STDOUT_FILENO, .mode = O_WRONLY },
{ .fd = STDERR_FILENO }, { .fd = STDERR_FILENO, .mode = O_WRONLY },
{ .fd = STDDBG_FILENO }, { .fd = STDDBG_FILENO, .mode = O_WRONLY },
}; };
FILE* stdin = &s_files[0]; FILE* stdin = &s_files[0];
@ -32,6 +49,7 @@ FILE* stddbg = &s_files[3];
void clearerr(FILE* file) void clearerr(FILE* file)
{ {
ScopeLock _(file);
file->eof = false; file->eof = false;
file->error = false; file->error = false;
} }
@ -46,22 +64,51 @@ char* ctermid(char* buffer)
int fclose(FILE* file) int fclose(FILE* file)
{ {
if (close(file->fd) == -1) ScopeLock _(file);
return EOF; (void)fflush(file);
int ret = (close(file->fd) == -1) ? EOF : 0;
file->fd = -1; file->fd = -1;
return ret;
}
static mode_t parse_mode_string(const char* mode_str)
{
size_t len = strlen(mode_str);
if (len == 0 || len > 3)
return 0;
if (len == 3 && mode_str[1] == mode_str[2])
return 0;
if (strcspn(mode_str + 1, "b+") != len - 1)
return 0;
bool plus = (mode_str[1] == '+' || mode_str[2] == '+');
switch (mode_str[0])
{
case 'r': return plus ? O_RDWR : O_RDONLY;
case 'w': return plus ? O_RDWR | O_CREAT | O_TRUNC : O_WRONLY | O_CREAT | O_TRUNC;
case 'a': return plus ? O_RDWR | O_CREAT | O_APPEND : O_WRONLY | O_CREAT | O_APPEND;
}
return 0; return 0;
} }
FILE* fdopen(int fd, const char* mode) FILE* fdopen(int fd, const char* mode_str)
{ {
// FIXME mode_t mode = parse_mode_string(mode_str);
(void)mode; if (mode == 0)
{
errno = EINVAL;
return nullptr;
}
mode &= ~O_TRUNC;
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++) for (int i = 0; i < FOPEN_MAX; i++)
{ {
if (s_files[i].fd == -1) if (s_files[i].fd == -1)
{ {
s_files[i] = { .fd = fd }; s_files[i] = {
.fd = fd,
.mode = mode & O_ACCMODE,
};
return &s_files[i]; return &s_files[i];
} }
} }
@ -91,6 +138,8 @@ int fflush(FILE* file)
return 0; return 0;
} }
ScopeLock _(file);
if (file->buffer_index == 0) if (file->buffer_index == 0)
return 0; return 0;
@ -106,29 +155,13 @@ int fflush(FILE* file)
int fgetc(FILE* file) int fgetc(FILE* file)
{ {
if (file->eof) ScopeLock _(file);
return EOF; return getc_unlocked(file);
unsigned char c;
long ret = syscall(SYS_READ, file->fd, &c, 1);
if (ret < 0)
{
file->error = true;
return EOF;
}
if (ret == 0)
{
file->eof = true;
return EOF;
}
return c;
} }
int fgetpos(FILE* file, fpos_t* pos) int fgetpos(FILE* file, fpos_t* pos)
{ {
ScopeLock _(file);
off_t offset = ftello(file); off_t offset = ftello(file);
if (offset == -1) if (offset == -1)
return -1; return -1;
@ -140,10 +173,11 @@ char* fgets(char* str, int size, FILE* file)
{ {
if (size == 0) if (size == 0)
return nullptr; return nullptr;
ScopeLock _(file);
int i = 0; int i = 0;
for (; i < size - 1; i++) for (; i < size - 1; i++)
{ {
char c = fgetc(file); int c = getc_unlocked(file);
if (c == EOF) if (c == EOF)
{ {
if (i == 0) if (i == 0)
@ -168,54 +202,33 @@ int fileno(FILE* fp)
return fp->fd; return fp->fd;
} }
// TODO void flockfile(FILE*)
void flockfile(FILE*);
FILE* fopen(const char* pathname, const char* mode)
{ {
uint8_t flags = 0; // FIXME: when threads are implemented
if (mode[0] == 'r') }
flags |= O_RDONLY;
else if (mode[0] == 'w') FILE* fopen(const char* pathname, const char* mode_str)
flags |= O_WRONLY | O_CREAT | O_TRUNC; {
else if (mode[0] == 'a') mode_t mode = parse_mode_string(mode_str);
flags |= O_WRONLY | O_CREAT | O_APPEND; if (mode == 0)
else
{ {
errno = EINVAL; errno = EINVAL;
return nullptr; return nullptr;
} }
if (mode[1] && mode[2] && mode[1] == mode[2]) int fd = open(pathname, mode, 0666);
{
errno = EINVAL;
return nullptr;
}
for (int i = 1; i <= 2; i++)
{
if (mode[i] == 0)
break;
else if (mode[i] == '+')
flags |= O_RDWR;
else if (mode[i] == 'b')
continue;
else
{
errno = EINVAL;
return nullptr;
}
}
int fd = open(pathname, flags);
if (fd == -1) if (fd == -1)
return nullptr; return nullptr;
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++) for (int i = 0; i < FOPEN_MAX; i++)
{ {
if (s_files[i].fd == -1) if (s_files[i].fd == -1)
{ {
s_files[i] = { .fd = fd }; s_files[i] = {
.fd = fd,
.mode = mode & O_ACCMODE
};
return &s_files[i]; return &s_files[i];
} }
} }
@ -235,18 +248,16 @@ int fprintf(FILE* file, const char* format, ...)
int fputc(int c, FILE* file) int fputc(int c, FILE* file)
{ {
file->buffer[file->buffer_index++] = c; ScopeLock _(file);
if (c == '\n' || file->buffer_index == sizeof(file->buffer)) return putc_unlocked(c, file);
if (fflush(file) == EOF)
return EOF;
return (unsigned char)c;
} }
int fputs(const char* str, FILE* file) int fputs(const char* str, FILE* file)
{ {
ScopeLock _(file);
while (*str) while (*str)
{ {
if (fputc(*str, file) == EOF) if (putc_unlocked(*str, file) == EOF)
return EOF; return EOF;
str++; str++;
} }
@ -255,6 +266,7 @@ int fputs(const char* str, FILE* file)
size_t fread(void* buffer, size_t size, size_t nitems, FILE* file) size_t fread(void* buffer, size_t size, size_t nitems, FILE* file)
{ {
ScopeLock _(file);
if (file->eof || nitems * size == 0) if (file->eof || nitems * size == 0)
return 0; return 0;
@ -279,8 +291,42 @@ size_t fread(void* buffer, size_t size, size_t nitems, FILE* file)
return nread / size; return nread / size;
} }
// TODO FILE* freopen(const char* pathname, const char* mode_str, FILE* file)
FILE* freopen(const char*, const char*, FILE*); {
mode_t mode = parse_mode_string(mode_str);
if (mode == 0)
{
errno = EINVAL;
return nullptr;
}
ScopeLock _(file);
(void)fflush(file);
if (pathname)
{
close(file->fd);
file->fd = open(pathname, mode, 0666);
file->mode = mode & O_ACCMODE;
if (file->fd == -1)
return nullptr;
}
else
{
mode &= O_ACCMODE;
if ((file->mode & mode) != mode)
{
close(file->fd);
file->fd = -1;
errno = EBADF;
return nullptr;
}
file->mode = mode;
}
return file;
}
int fscanf(FILE* file, const char* format, ...) int fscanf(FILE* file, const char* format, ...)
{ {
@ -298,10 +344,10 @@ int fseek(FILE* file, long offset, int whence)
int fseeko(FILE* file, off_t offset, int whence) int fseeko(FILE* file, off_t offset, int whence)
{ {
ScopeLock _(file);
long ret = syscall(SYS_SEEK, file->fd, offset, whence); long ret = syscall(SYS_SEEK, file->fd, offset, whence);
if (ret < 0) if (ret < 0)
return -1; return -1;
file->eof = false; file->eof = false;
return 0; return 0;
} }
@ -318,30 +364,38 @@ long ftell(FILE* file)
off_t ftello(FILE* file) off_t ftello(FILE* file)
{ {
ScopeLock _(file);
long ret = syscall(SYS_TELL, file->fd); long ret = syscall(SYS_TELL, file->fd);
if (ret < 0) if (ret < 0)
return -1; return -1;
return ret; return ret;
} }
// TODO int ftrylockfile(FILE*)
int ftrylockfile(FILE*); {
// FIXME: when threads are implemented
return 0;
}
// TODO void funlockfile(FILE*)
void funlockfile(FILE*); {
// FIXME: when threads are implemented
}
size_t fwrite(const void* buffer, size_t size, size_t nitems, FILE* file) size_t fwrite(const void* buffer, size_t size, size_t nitems, FILE* file)
{ {
ScopeLock _(file);
unsigned char* ubuffer = (unsigned char*)buffer; unsigned char* ubuffer = (unsigned char*)buffer;
for (size_t byte = 0; byte < nitems * size; byte++) for (size_t byte = 0; byte < nitems * size; byte++)
if (fputc(ubuffer[byte], file) == EOF) if (putc_unlocked(ubuffer[byte], file) == EOF)
return byte / size; return byte / size;
return nitems; return nitems;
} }
int getc(FILE* file) int getc(FILE* file)
{ {
return fgetc(file); ScopeLock _(file);
return getc_unlocked(file);
} }
int getchar(void) int getchar(void)
@ -349,11 +403,33 @@ int getchar(void)
return getc(stdin); return getc(stdin);
} }
// TODO int getc_unlocked(FILE* file)
int getc_unlocked(FILE*); {
if (file->eof)
return EOF;
// TODO unsigned char c;
int getchar_unlocked(void); long ret = syscall(SYS_READ, file->fd, &c, 1);
if (ret < 0)
{
file->error = true;
return EOF;
}
if (ret == 0)
{
file->eof = true;
return EOF;
}
return c;
}
int getchar_unlocked(void)
{
return getc_unlocked(stdin);
}
char* gets(char* buffer) char* gets(char* buffer)
{ {
@ -384,6 +460,7 @@ int pclose(FILE*);
void perror(const char* string) void perror(const char* string)
{ {
ScopeLock _(stderr);
if (string && *string) if (string && *string)
{ {
fputs(string, stderr); fputs(string, stderr);
@ -416,14 +493,23 @@ int putchar(int c)
return putc(c, stdout); return putc(c, stdout);
} }
// TODO int putc_unlocked(int c, FILE* file)
int putc_unlocked(int, FILE*); {
file->buffer[file->buffer_index++] = c;
if (c == '\n' || file->buffer_index == sizeof(file->buffer))
if (fflush(file) == EOF)
return EOF;
return (unsigned char)c;
}
// TODO int putchar_unlocked(int c)
int putchar_unlocked(int); {
return putc_unlocked(c, stdout);
}
int puts(const char* string) int puts(const char* string)
{ {
ScopeLock _(stdout);
if (fputs(string, stdout) == EOF) if (fputs(string, stdout) == EOF)
return EOF; return EOF;
if (fputc('\n', stdout) == EOF) if (fputc('\n', stdout) == EOF)
@ -444,8 +530,12 @@ int remove(const char* path)
// TODO // TODO
int rename(const char*, const char*); int rename(const char*, const char*);
// TODO void rewind(FILE* file)
void rewind(FILE*); {
ScopeLock _(file);
file->error = false;
(void)fseek(file, 0L, SEEK_SET);
}
int scanf(const char* format, ...) int scanf(const char* format, ...)
{ {
@ -503,12 +593,14 @@ int ungetc(int, FILE*);
int vfprintf(FILE* file, const char* format, va_list arguments) int vfprintf(FILE* file, const char* format, va_list arguments)
{ {
return printf_impl(format, arguments, [](int c, void* file) { return fputc(c, static_cast<FILE*>(file)); }, file); ScopeLock _(file);
return printf_impl(format, arguments, [](int c, void* file) { return putc_unlocked(c, static_cast<FILE*>(file)); }, file);
} }
int vfscanf(FILE* file, const char* format, va_list arguments) int vfscanf(FILE* file, const char* format, va_list arguments)
{ {
return scanf_impl(format, arguments, [](void* file) { return fgetc(static_cast<FILE*>(file)); }, file); ScopeLock _(file);
return scanf_impl(format, arguments, [](void* file) { return getc_unlocked(static_cast<FILE*>(file)); }, file);
} }
int vprintf(const char* format, va_list arguments) int vprintf(const char* format, va_list arguments)