banan-os/userspace/libraries/LibC/stdio.cpp

901 lines
15 KiB
C++
Raw Normal View History

#include <BAN/Assert.h>
#include <BAN/Debug.h>
2024-08-01 01:30:00 +03:00
#include <BAN/Math.h>
#include <bits/printf.h>
2023-04-23 14:32:37 +03:00
#include <errno.h>
#include <fcntl.h>
#include <scanf_impl.h>
2023-04-05 23:58:40 +03:00
#include <stdarg.h>
#include <stdio.h>
2024-02-14 17:22:45 +02:00
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
2023-04-05 23:58:40 +03:00
#include <unistd.h>
struct FILE
{
int fd;
mode_t mode;
int buffer_type;
bool eof;
bool error;
2023-04-23 14:32:37 +03:00
int pid;
2024-02-14 17:22:45 +02:00
int unget_char;
unsigned char inline_buffer_storage[BUFSIZ];
unsigned char* buffer;
uint32_t buffer_size;
uint32_t buffer_index;
2023-04-23 14:32:37 +03:00
};
struct ScopeLock
{
ScopeLock(FILE* file)
: m_file(file)
{
flockfile(m_file);
}
~ScopeLock()
{
funlockfile(m_file);
}
FILE* m_file;
};
static FILE s_files[FOPEN_MAX];
2023-04-05 23:58:40 +03:00
2023-04-23 14:32:37 +03:00
FILE* stdin = &s_files[0];
FILE* stdout = &s_files[1];
FILE* stderr = &s_files[2];
FILE* stddbg = &s_files[3];
2023-04-23 14:32:37 +03:00
static void init_closed_file(FILE* file)
{
file->fd = -1;
file->mode = 0;
file->buffer_type = _IOLBF;
file->eof = false;
file->error = false;
file->pid = -1;
file->unget_char = EOF;
file->buffer = file->inline_buffer_storage;
file->buffer_size = BUFSIZ;
file->buffer_index = 0;
}
void _init_stdio()
{
for (size_t i = 0; i < FOPEN_MAX; i++)
init_closed_file(&s_files[i]);
s_files[STDIN_FILENO].fd = STDIN_FILENO;
s_files[STDIN_FILENO].mode = O_RDONLY;
s_files[STDOUT_FILENO].fd = STDOUT_FILENO;
s_files[STDOUT_FILENO].mode = O_WRONLY;
s_files[STDERR_FILENO].fd = STDERR_FILENO;
s_files[STDERR_FILENO].mode = O_WRONLY;
s_files[STDERR_FILENO].buffer_type = _IONBF;
s_files[STDDBG_FILENO].fd = STDDBG_FILENO;
s_files[STDDBG_FILENO].mode = O_WRONLY;
}
2023-04-23 14:32:37 +03:00
void clearerr(FILE* file)
{
ScopeLock _(file);
2023-04-23 14:32:37 +03:00
file->eof = false;
file->error = false;
}
char* ctermid(char* buffer)
{
static char s_buffer[L_ctermid];
char* target = buffer ? buffer : s_buffer;
syscall(SYS_TERMID, target);
return target;
}
int fclose(FILE* file)
{
ScopeLock _(file);
(void)fflush(file);
int ret = (close(file->fd) == -1) ? EOF : 0;
init_closed_file(file);
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;
2024-02-20 13:25:24 +02:00
if (strspn(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;
}
2023-04-23 14:32:37 +03:00
return 0;
}
FILE* fdopen(int fd, const char* mode_str)
{
mode_t mode = parse_mode_string(mode_str);
if (mode == 0)
{
errno = EINVAL;
return nullptr;
}
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++)
{
2024-08-01 01:30:00 +03:00
ScopeLock _(&s_files[i]);
if (s_files[i].fd != -1)
continue;
s_files[i].fd = fd;
s_files[i].mode = mode & O_ACCMODE;
ASSERT(s_files[i].buffer == s_files[i].inline_buffer_storage);
ASSERT(s_files[i].buffer_size == BUFSIZ);
return &s_files[i];
}
errno = EMFILE;
return nullptr;
}
2023-04-23 14:32:37 +03:00
int feof(FILE* file)
{
return file->eof;
}
int ferror(FILE* file)
{
return file->error;
}
int fflush(FILE* file)
{
if (file == nullptr)
{
for (int i = 0; i < FOPEN_MAX; i++)
if (s_files[i].fd != -1)
if (int ret = fflush(&s_files[i]); ret != 0)
return ret;
return 0;
}
ScopeLock _(file);
file->unget_char = EOF;
2023-04-23 14:32:37 +03:00
if (file->buffer_index == 0)
return 0;
if (syscall(SYS_WRITE, file->fd, file->buffer, file->buffer_index) < 0)
2023-04-23 14:32:37 +03:00
{
file->error = true;
return EOF;
}
2023-04-05 23:58:40 +03:00
2023-04-23 14:32:37 +03:00
file->buffer_index = 0;
return 0;
}
2023-04-05 23:58:40 +03:00
2023-04-23 14:32:37 +03:00
int fgetc(FILE* file)
2023-04-05 23:58:40 +03:00
{
ScopeLock _(file);
return getc_unlocked(file);
2023-04-05 23:58:40 +03:00
}
2023-04-23 14:32:37 +03:00
int fgetpos(FILE* file, fpos_t* pos)
2023-04-05 23:58:40 +03:00
{
ScopeLock _(file);
off_t offset = ftello(file);
if (offset == -1)
return -1;
*pos = offset;
2023-04-05 23:58:40 +03:00
return 0;
}
2023-04-23 14:32:37 +03:00
char* fgets(char* str, int size, FILE* file)
{
if (size == 0)
2023-04-23 14:32:37 +03:00
return nullptr;
ScopeLock _(file);
int i = 0;
for (; i < size - 1; i++)
{
int c = getc_unlocked(file);
if (c == EOF)
2023-04-23 14:32:37 +03:00
{
if (i == 0)
return nullptr;
break;
2023-04-23 14:32:37 +03:00
}
str[i] = c;
if (c == '\n')
2023-04-23 14:32:37 +03:00
{
i++;
break;
2023-04-23 14:32:37 +03:00
}
}
str[i] = '\0';
2023-04-23 14:32:37 +03:00
return str;
}
int fileno(FILE* fp)
{
if (fp == nullptr)
return EBADF;
return fp->fd;
}
2023-04-23 14:32:37 +03:00
void flockfile(FILE*)
2023-04-05 23:58:40 +03:00
{
// FIXME: when threads are implemented
}
2023-04-23 14:32:37 +03:00
FILE* fopen(const char* pathname, const char* mode_str)
{
mode_t mode = parse_mode_string(mode_str);
if (mode == 0)
2023-04-23 14:32:37 +03:00
{
errno = EINVAL;
return nullptr;
}
int fd = open(pathname, mode, 0666);
2023-04-23 14:32:37 +03:00
if (fd == -1)
return nullptr;
// FIXME: when threads are implemented
2023-04-23 14:32:37 +03:00
for (int i = 0; i < FOPEN_MAX; i++)
{
2024-08-01 01:30:00 +03:00
ScopeLock _(&s_files[i]);
if (s_files[i].fd != -1)
continue;
s_files[i].fd = fd;
s_files[i].mode = mode & O_ACCMODE;
ASSERT(s_files[i].buffer == s_files[i].inline_buffer_storage);
ASSERT(s_files[i].buffer_size == BUFSIZ);
return &s_files[i];
2023-04-23 14:32:37 +03:00
}
2023-04-23 14:32:37 +03:00
errno = EMFILE;
2023-04-05 23:58:40 +03:00
return nullptr;
}
2023-04-23 14:32:37 +03:00
int fprintf(FILE* file, const char* format, ...)
2023-04-05 23:58:40 +03:00
{
2023-04-23 14:32:37 +03:00
va_list arguments;
va_start(arguments, format);
int ret = vfprintf(file, format, arguments);
va_end(arguments);
return ret;
2023-04-05 23:58:40 +03:00
}
2023-04-23 14:32:37 +03:00
int fputc(int c, FILE* file)
2023-04-05 23:58:40 +03:00
{
ScopeLock _(file);
return putc_unlocked(c, file);
2023-04-05 23:58:40 +03:00
}
2023-04-23 14:32:37 +03:00
int fputs(const char* str, FILE* file)
2023-04-05 23:58:40 +03:00
{
ScopeLock _(file);
2023-04-23 14:32:37 +03:00
while (*str)
{
if (putc_unlocked(*str, file) == EOF)
2023-04-23 14:32:37 +03:00
return EOF;
str++;
}
2023-04-05 23:58:40 +03:00
return 0;
}
2023-04-23 14:32:37 +03:00
size_t fread(void* buffer, size_t size, size_t nitems, FILE* file)
2023-04-05 23:58:40 +03:00
{
ScopeLock _(file);
2023-04-25 13:22:33 +03:00
if (file->eof || nitems * size == 0)
return 0;
size_t target = size * nitems;
size_t nread = 0;
if (file->unget_char != EOF)
{
*static_cast<unsigned char*>(buffer) = file->unget_char;
file->unget_char = EOF;
nread++;
}
while (nread < target)
{
ssize_t ret = syscall(SYS_READ, file->fd, static_cast<unsigned char*>(buffer) + nread, target - nread);
if (ret < 0)
file->error = true;
else if (ret == 0)
file->eof = true;
if (ret <= 0)
return nread;
nread += ret;
}
return nread / size;
2023-04-23 14:32:37 +03:00
}
FILE* freopen(const char* pathname, const char* mode_str, FILE* file)
{
mode_t mode = parse_mode_string(mode_str);
if (mode == 0)
{
errno = EINVAL;
return nullptr;
}
ScopeLock _(file);
if (pathname)
{
2024-08-01 01:30:00 +03:00
fclose(file);
file->fd = open(pathname, mode, 0666);
file->mode = mode & O_ACCMODE;
if (file->fd == -1)
return nullptr;
}
else
{
if ((file->mode & mode) != mode)
{
2024-08-01 01:30:00 +03:00
fclose(file);
errno = EBADF;
return nullptr;
}
2024-08-01 01:30:00 +03:00
file->mode = mode & O_ACCMODE;
}
return file;
}
2023-04-23 14:32:37 +03:00
int fscanf(FILE* file, const char* format, ...)
{
va_list arguments;
va_start(arguments, format);
int ret = vfscanf(file, format, arguments);
va_end(arguments);
return ret;
}
2023-04-23 14:32:37 +03:00
int fseek(FILE* file, long offset, int whence)
{
return fseeko(file, offset, whence);
}
int fseeko(FILE* file, off_t offset, int whence)
{
ScopeLock _(file);
file->unget_char = EOF;
long ret = syscall(SYS_SEEK, file->fd, offset, whence);
if (ret < 0)
2023-04-23 14:32:37 +03:00
return -1;
file->eof = false;
2023-04-05 23:58:40 +03:00
return 0;
}
2023-04-23 14:32:37 +03:00
int fsetpos(FILE* file, const fpos_t* pos)
2023-04-05 23:58:40 +03:00
{
2023-04-23 14:32:37 +03:00
return fseek(file, *pos, SEEK_SET);
2023-04-05 23:58:40 +03:00
}
2023-04-23 14:32:37 +03:00
long ftell(FILE* file)
{
return ftello(file);
}
off_t ftello(FILE* file)
{
ScopeLock _(file);
long ret = syscall(SYS_TELL, file->fd);
if (ret < 0)
return -1;
return ret - (file->unget_char != EOF);
2023-04-23 14:32:37 +03:00
}
int ftrylockfile(FILE*)
{
// FIXME: when threads are implemented
return 0;
}
2023-04-23 14:32:37 +03:00
void funlockfile(FILE*)
{
// FIXME: when threads are implemented
}
2023-04-23 14:32:37 +03:00
size_t fwrite(const void* buffer, size_t size, size_t nitems, FILE* file)
{
ScopeLock _(file);
2023-04-23 14:32:37 +03:00
unsigned char* ubuffer = (unsigned char*)buffer;
for (size_t byte = 0; byte < nitems * size; byte++)
if (putc_unlocked(ubuffer[byte], file) == EOF)
2023-04-23 14:32:37 +03:00
return byte / size;
return nitems;
}
int getc(FILE* file)
{
ScopeLock _(file);
return getc_unlocked(file);
2023-04-23 14:32:37 +03:00
}
int getchar(void)
{
return getc(stdin);
}
int getc_unlocked(FILE* file)
{
if (file->eof)
return EOF;
2023-04-23 14:32:37 +03:00
if (file->unget_char != EOF)
{
int ch = file->unget_char;
file->unget_char = EOF;
return (unsigned char)ch;
}
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 getchar_unlocked(void)
{
return getc_unlocked(stdin);
}
2023-04-23 14:32:37 +03:00
char* gets(char* buffer)
{
if (stdin->eof)
return nullptr;
unsigned char* ubuffer = (unsigned char*)buffer;
2023-04-23 14:32:37 +03:00
int first = fgetc(stdin);
if (first == EOF)
return nullptr;
*ubuffer++ = first;
for (;;)
{
int c = fgetc(stdin);
if (c == '\n' || c == EOF)
{
*ubuffer++ = '\0';
return buffer;
}
*ubuffer++ = c;
}
}
2024-02-14 17:22:45 +02:00
int pclose(FILE* file)
{
if (file->pid == -1)
{
errno = EBADF;
return -1;
}
pid_t pid = file->pid;
(void)fclose(file);
int stat;
while (waitpid(pid, &stat, 0) != -1)
{
if (errno != EINTR)
{
stat = -1;
break;
}
}
return stat;
}
2023-04-05 23:58:40 +03:00
2023-04-23 14:32:37 +03:00
void perror(const char* string)
2023-04-05 23:58:40 +03:00
{
ScopeLock _(stderr);
2023-04-23 14:32:37 +03:00
if (string && *string)
{
fputs(string, stderr);
fputs(": ", stderr);
}
fputs(strerror(errno), stderr);
2023-04-25 13:22:33 +03:00
fputc('\n', stderr);
2023-04-23 14:32:37 +03:00
stderr->error = true;
}
2024-02-14 17:22:45 +02:00
FILE* popen(const char* command, const char* mode_str)
{
if ((mode_str[0] != 'r' && mode_str[0] != 'w') || mode_str[1] != '\0')
{
errno = EINVAL;
return nullptr;
}
bool read = (mode_str[0] == 'r');
int fds[2];
if (pipe(fds) == -1)
return nullptr;
pid_t pid = fork();
if (pid == 0)
{
if (read)
dup2(fds[1], STDOUT_FILENO);
else
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
close(fds[1]);
execl("/bin/Shell", "sh", "-c", command, nullptr);
exit(1);
}
if (pid == -1)
{
close(fds[0]);
close(fds[1]);
return nullptr;
}
close(read ? fds[1] : fds[0]);
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++)
{
2024-08-01 01:30:00 +03:00
ScopeLock _(&s_files[i]);
if (s_files[i].fd != -1)
continue;
s_files[i].fd = read ? fds[0] : fds[1];
s_files[i].mode = (unsigned)(read ? O_RDONLY : O_WRONLY);
s_files[i].pid = pid;
ASSERT(s_files[i].buffer == s_files[i].inline_buffer_storage);
ASSERT(s_files[i].buffer_size == BUFSIZ);
return &s_files[i];
2024-02-14 17:22:45 +02:00
}
errno = EMFILE;
return nullptr;
}
2023-04-05 23:58:40 +03:00
2023-04-23 14:32:37 +03:00
int printf(const char* format, ...)
{
va_list arguments;
va_start(arguments, format);
int ret = vfprintf(stdout, format, arguments);
va_end(arguments);
return ret;
2023-04-05 23:58:40 +03:00
}
2023-04-23 14:32:37 +03:00
int putc(int c, FILE* file)
2023-04-05 23:58:40 +03:00
{
2023-04-23 14:32:37 +03:00
return fputc(c, file);
2023-04-05 23:58:40 +03:00
}
2023-04-23 14:32:37 +03:00
int putchar(int c)
2023-04-05 23:58:40 +03:00
{
2023-04-23 14:32:37 +03:00
return putc(c, stdout);
2023-04-05 23:58:40 +03:00
}
int putc_unlocked(int c, FILE* file)
{
file->buffer[file->buffer_index++] = c;
2024-08-01 01:30:00 +03:00
if (file->buffer_type == _IONBF || (file->buffer_type == _IOLBF && c == '\n') || file->buffer_index >= file->buffer_size)
if (fflush(file) == EOF)
return EOF;
return (unsigned char)c;
}
2023-04-23 14:32:37 +03:00
int putchar_unlocked(int c)
{
return putc_unlocked(c, stdout);
}
2023-04-23 14:32:37 +03:00
int puts(const char* string)
2023-04-05 23:58:40 +03:00
{
ScopeLock _(stdout);
2023-04-23 14:32:37 +03:00
if (fputs(string, stdout) == EOF)
return EOF;
if (fputc('\n', stdout) == EOF)
return EOF;
return 0;
2023-04-05 23:58:40 +03:00
}
2023-12-14 10:58:50 +02:00
int remove(const char* path)
{
struct stat st;
if (stat(path, &st) == -1)
return -1;
if (S_ISDIR(st.st_mode))
return rmdir(path);
return unlink(path);
}
2023-04-23 14:32:37 +03:00
int rename(const char* old, const char* _new)
{
struct stat st;
if (lstat(old, &st) == -1)
return -1;
if (!S_ISREG(st.st_mode))
{
errno = ENOTSUP;
return -1;
}
if (unlink(_new) == -1 && errno != ENOENT)
return -1;
int old_fd = open(old, O_RDWR);
int new_fd = open(_new, O_RDWR | O_CREAT | O_EXCL, st.st_mode);
if (old_fd == -1 || new_fd == -1)
goto error;
for (;;)
{
char buffer[512];
ssize_t nread = read(old_fd, buffer, sizeof(buffer));
if (nread == -1)
{
unlink(_new);
goto error;
}
if (nread == 0)
break;
if (write(new_fd, buffer, nread) != nread)
{
unlink(_new);
goto error;
}
}
unlink(old);
return 0;
error:
if (old_fd != -1)
close(old_fd);
if (new_fd != -1)
close(new_fd);
return -1;
}
2023-04-23 14:32:37 +03:00
void rewind(FILE* file)
{
ScopeLock _(file);
file->error = false;
(void)fseek(file, 0L, SEEK_SET);
}
2023-04-23 14:32:37 +03:00
int scanf(const char* format, ...)
{
va_list arguments;
va_start(arguments, format);
int ret = vscanf(format, arguments);
va_end(arguments);
return ret;
}
2023-04-23 14:32:37 +03:00
2024-08-01 01:30:00 +03:00
void setbuf(FILE* file, char* buffer)
{
int type = buffer ? _IOFBF : _IONBF;
setvbuf(file, buffer, type, BUFSIZ);
}
2023-04-23 14:32:37 +03:00
2024-08-01 01:30:00 +03:00
int setvbuf(FILE* file, char* buffer, int type, size_t size)
{
if (file->fd == -1)
{
errno = EBADF;
return -1;
}
if (buffer == nullptr)
{
buffer = reinterpret_cast<char*>(file->inline_buffer_storage);
size = BAN::Math::min<size_t>(size, BUFSIZ);
}
file->buffer_type = type;
file->buffer_size = size;
file->buffer = reinterpret_cast<unsigned char*>(buffer);
return 0;
}
2023-04-23 14:32:37 +03:00
int snprintf(char* buffer, size_t max_size, const char* format, ...)
{
va_list arguments;
va_start(arguments, format);
int ret = vsnprintf(buffer, max_size, format, arguments);
va_end(arguments);
return ret;
}
2023-04-23 14:32:37 +03:00
int sprintf(char* buffer, const char* format, ...)
{
va_list arguments;
va_start(arguments, format);
int ret = vsprintf(buffer, format, arguments);
va_end(arguments);
return ret;
}
2023-04-23 14:32:37 +03:00
int sscanf(const char* s, const char* format, ...)
{
va_list arguments;
va_start(arguments, format);
int ret = vsscanf(s, format, arguments);
va_end(arguments);
return ret;
}
2023-04-23 14:32:37 +03:00
// TODO
char* tempnam(const char*, const char*);
// TODO
FILE* tmpfile(void);
char* tmpnam(char* storage)
{
static char s_storage[PATH_MAX];
if (storage == nullptr)
storage = s_storage;
for (int i = 0; i < TMP_MAX; i++)
{
sprintf(storage, "/tmp/tmp_%04x", rand());
struct stat st;
if (stat(storage, &st) == -1 && errno == ENOENT)
break;
}
return storage;
}
2023-04-23 14:32:37 +03:00
int ungetc(int c, FILE* stream)
{
if (c == EOF)
return EOF;
ScopeLock _(stream);
if (stream->unget_char != EOF)
return EOF;
stream->unget_char = c;
stream->eof = false;
return (unsigned char)c;
}
2023-04-23 14:32:37 +03:00
int vfprintf(FILE* file, const char* format, va_list arguments)
2023-04-05 23:58:40 +03:00
{
ScopeLock _(file);
return printf_impl(format, arguments, [](int c, void* file) { return putc_unlocked(c, static_cast<FILE*>(file)); }, file);
2023-04-05 23:58:40 +03:00
}
int vfscanf(FILE* file, const char* format, va_list arguments)
{
ScopeLock _(file);
return scanf_impl(format, arguments, [](void* file) { return getc_unlocked(static_cast<FILE*>(file)); }, file);
}
2023-04-23 14:32:37 +03:00
int vprintf(const char* format, va_list arguments)
2023-04-05 23:58:40 +03:00
{
2023-04-23 14:32:37 +03:00
return vfprintf(stdout, format, arguments);
2023-04-05 23:58:40 +03:00
}
2023-04-23 14:32:37 +03:00
int vscanf(const char* format, va_list arguments)
{
return vfscanf(stdin, format, arguments);
}
2023-04-23 14:32:37 +03:00
int vsnprintf(char* buffer, size_t max_size, const char* format, va_list arguments)
{
if (buffer == nullptr)
return printf_impl(format, arguments, [](int, void*) { return 0; }, nullptr);
struct print_info
{
char* buffer;
size_t remaining;
};
print_info info { buffer, max_size };
2023-05-15 22:02:33 +03:00
int ret = printf_impl(format, arguments,
[](int c, void* _info)
{
print_info* info = (print_info*)_info;
2023-05-15 22:02:33 +03:00
if (info->remaining == 1)
{
*info->buffer = '\0';
info->remaining = 0;
}
else if (info->remaining)
{
2023-05-15 22:02:33 +03:00
*info->buffer = c;
info->buffer++;
info->remaining--;
}
return 0;
}, &info
);
2023-05-15 22:02:33 +03:00
*info.buffer = '\0';
return ret;
}
2023-04-23 14:32:37 +03:00
int vsprintf(char* buffer, const char* format, va_list arguments)
{
if (buffer == nullptr)
return printf_impl(format, arguments, [](int, void*) { return 0; }, nullptr);
2023-05-15 22:02:33 +03:00
int ret = printf_impl(format, arguments,
[](int c, void* _buffer)
{
*(*(char**)_buffer)++ = c;
return 0;
}, &buffer
);
2023-05-15 22:02:33 +03:00
*buffer = '\0';
return ret;
}
2023-04-23 14:32:37 +03:00
int vsscanf(const char* s, const char* format, va_list arguments)
{
return scanf_impl(format, arguments,
[](void* data) -> int
{
char ret = **static_cast<char**>(data);
(*static_cast<char**>(data))++;
if (ret == '\0')
return -1;
return ret;
}, &s
);
}
FILE* tmpfile(void)
{
ASSERT_NOT_REACHED();
}