diff options
Diffstat (limited to 'lib/mlibc/options/ansi/generic/stdio-stubs.cpp')
-rw-r--r-- | lib/mlibc/options/ansi/generic/stdio-stubs.cpp | 1270 |
1 files changed, 1270 insertions, 0 deletions
diff --git a/lib/mlibc/options/ansi/generic/stdio-stubs.cpp b/lib/mlibc/options/ansi/generic/stdio-stubs.cpp new file mode 100644 index 0000000..479a655 --- /dev/null +++ b/lib/mlibc/options/ansi/generic/stdio-stubs.cpp @@ -0,0 +1,1270 @@ +#include <ctype.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <stdlib.h> +#include <wchar.h> +#include <ctype.h> +#include <limits.h> + +#include <abi-bits/fcntl.h> + +#include <bits/ensure.h> + +#include <mlibc/lock.hpp> +#include <mlibc/allocator.hpp> +#include <mlibc/debug.hpp> +#include <mlibc/file-io.hpp> +#include <mlibc/ansi-sysdeps.hpp> +#include <frg/mutex.hpp> +#include <frg/expected.hpp> +#include <frg/printf.hpp> + +template<typename F> +struct PrintfAgent { + PrintfAgent(F *formatter, frg::va_struct *vsp) + : _formatter{formatter}, _vsp{vsp} { } + + frg::expected<frg::format_error> operator() (char c) { + _formatter->append(c); + return {}; + } + frg::expected<frg::format_error> operator() (const char *c, size_t n) { + _formatter->append(c, n); + return {}; + } + + frg::expected<frg::format_error> operator() (char t, frg::format_options opts, + frg::printf_size_mod szmod) { + switch(t) { + case 'c': + if (szmod == frg::printf_size_mod::long_size) { + char c_buf[sizeof(wchar_t)]; + auto c = static_cast<wchar_t>(va_arg(_vsp->args, wint_t)); + mbstate_t shift_state = {}; + if (wcrtomb(c_buf, c, &shift_state) == size_t(-1)) + return frg::format_error::agent_error; + _formatter->append(c_buf); + break; + } + frg::do_printf_chars(*_formatter, t, opts, szmod, _vsp); + break; + case 'p': case 's': + frg::do_printf_chars(*_formatter, t, opts, szmod, _vsp); + break; + case 'd': case 'i': case 'o': case 'x': case 'X': case 'u': + frg::do_printf_ints(*_formatter, t, opts, szmod, _vsp); + break; + case 'f': case 'F': case 'g': case 'G': case 'e': case 'E': + frg::do_printf_floats(*_formatter, t, opts, szmod, _vsp); + break; + case 'm': + __ensure(!opts.fill_zeros); + __ensure(!opts.left_justify); + __ensure(!opts.alt_conversion); + __ensure(opts.minimum_width == 0); + __ensure(szmod == frg::printf_size_mod::default_size); + __ensure(!opts.precision); + _formatter->append(strerror(errno)); + break; + case 'n': { + __ensure(szmod == frg::printf_size_mod::default_size); + auto p = va_arg(_vsp->args, int *); + *p = _formatter->count; + break; + } + default: + mlibc::infoLogger() << "\e[31mmlibc: Unknown printf terminator '" + << t << "'\e[39m" << frg::endlog; + __ensure(!"Illegal printf terminator"); + } + + return {}; + } + +private: + F *_formatter; + frg::va_struct *_vsp; +}; + +struct StreamPrinter { + StreamPrinter(FILE *stream) + : stream(stream), count(0) { } + + void append(char c) { + fwrite_unlocked(&c, 1, 1, stream); + count++; + } + + void append(const char *str) { + fwrite_unlocked(str, strlen(str), 1, stream); + count += strlen(str); + } + + void append(const char *str, size_t n) { + fwrite_unlocked(str, n, 1, stream); + count += n; + } + + FILE *stream; + size_t count; +}; + +struct BufferPrinter { + BufferPrinter(char *buffer) + : buffer(buffer), count(0) { } + + void append(char c) { + buffer[count] = c; + count++; + } + + void append(const char *str) { + // TODO: use strcat + for(size_t i = 0; str[i]; i++) { + buffer[count] = str[i]; + count++; + } + } + + void append(const char *str, size_t n) { + // TODO: use strcat + for(size_t i = 0; i < n; i++) { + buffer[count] = str[i]; + count++; + } + } + + char *buffer; + size_t count; +}; + +struct LimitedPrinter { + LimitedPrinter(char *buffer, size_t limit) + : buffer(buffer), limit(limit), count(0) { } + + void append(char c) { + if(count < limit) + buffer[count] = c; + count++; + } + + void append(const char *str) { + // TODO: use strcat + for(size_t i = 0; str[i]; i++) + append(str[i]); + } + + void append(const char *str, size_t n) { + // TODO: use strcat + for(size_t i = 0; i < n; i++) + append(str[i]); + } + + char *buffer; + size_t limit; + size_t count; +}; + +struct ResizePrinter { + ResizePrinter() + : buffer(nullptr), limit(0), count(0) { } + + void expand() { + if(count == limit) { + auto new_limit = frg::max(2 * limit, size_t(16)); + auto new_buffer = reinterpret_cast<char *>(malloc(new_limit)); + __ensure(new_buffer); + memcpy(new_buffer, buffer, count); + free(buffer); + buffer = new_buffer; + limit = new_limit; + } + __ensure(count < limit); + } + + void append(char c) { + expand(); + buffer[count] = c; + count++; + } + + void append(const char *str) { + for(size_t i = 0; str[i]; i++) + append(str[i]); + } + + void append(const char *str, size_t n) { + for(size_t i = 0; i < n; i++) + append(str[i]); + } + + char *buffer; + size_t limit; + size_t count; +}; + +int remove(const char *filename) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_rmdir, -1); + if(int e = mlibc::sys_rmdir(filename); e) { + if (e == ENOTDIR) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_unlinkat, -1); + if(e = mlibc::sys_unlinkat(AT_FDCWD, filename, 0); e) { + errno = e; + return -1; + } + + return 0; + } + return -1; + } + + return 0; +} + +int rename(const char *path, const char *new_path) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_rename, -1); + if(int e = mlibc::sys_rename(path, new_path); e) { + errno = e; + return -1; + } + return 0; +} + +int renameat(int olddirfd, const char *old_path, int newdirfd, const char *new_path) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_renameat, -1); + if(int e = mlibc::sys_renameat(olddirfd, old_path, newdirfd, new_path); e) { + errno = e; + return -1; + } + return 0; +} + +FILE *tmpfile(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +char *tmpnam(char *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +// fflush() is provided by the POSIX sublibrary +// fopen() is provided by the POSIX sublibrary +FILE *freopen(const char *__restrict path, const char *__restrict mode, FILE *__restrict f) { + auto file = static_cast<mlibc::abstract_file *>(f); + frg::unique_lock lock(file->_lock); + + if(file->reopen(path, mode) == -1) { + errno = EINVAL; + return nullptr; + } + + return f; +} + +void setbuf(FILE *__restrict stream, char *__restrict buffer) { + setvbuf(stream, buffer, buffer ? _IOFBF : _IONBF, BUFSIZ); +} +// setvbuf() is provided by the POSIX sublibrary + +void setlinebuf(FILE *stream) { + setvbuf(stream, NULL, _IOLBF, 0); +} + +void setbuffer(FILE *f, char *buf, size_t size) { + setvbuf(f, buf, buf ? _IOFBF : _IONBF, size); +} + +int fprintf(FILE *__restrict stream, const char *__restrict format, ...) { + va_list args; + va_start(args, format); + int result = vfprintf(stream, format, args); + va_end(args); + return result; +} + +int fscanf(FILE *__restrict stream, const char *__restrict format, ...) { + va_list args; + va_start(args, format); + int result = vfscanf(stream, format, args); + va_end(args); + return result; +} + +int printf(const char *__restrict format, ...) { + va_list args; + va_start(args, format); + int result = vfprintf(stdout, format, args); + va_end(args); + return result; +} + +namespace { + enum { + SCANF_TYPE_CHAR, + SCANF_TYPE_SHORT, + SCANF_TYPE_INTMAX, + SCANF_TYPE_L, + SCANF_TYPE_LL, + SCANF_TYPE_PTRDIFF, + SCANF_TYPE_SIZE_T, + SCANF_TYPE_INT + }; +} + +static void store_int(void *dest, unsigned int size, unsigned long long i) { + switch (size) { + case SCANF_TYPE_CHAR: + *(char *)dest = i; + break; + case SCANF_TYPE_SHORT: + *(short *)dest = i; + break; + case SCANF_TYPE_INTMAX: + *(intmax_t *)dest = i; + break; + case SCANF_TYPE_L: + *(long *)dest = i; + break; + case SCANF_TYPE_LL: + *(long long *)dest = i; + break; + case SCANF_TYPE_PTRDIFF: + *(ptrdiff_t *)dest = i; + break; + case SCANF_TYPE_SIZE_T: + *(size_t *)dest = i; + break; + /* fallthrough */ + case SCANF_TYPE_INT: + default: + *(int *)dest = i; + break; + } +} + +template<typename H> +static int do_scanf(H &handler, const char *fmt, __builtin_va_list args) { + int match_count = 0; + for (; *fmt; fmt++) { + + if (isspace(*fmt)) { + while (isspace(fmt[1])) fmt++; + while (isspace(handler.look_ahead())) + handler.consume(); + continue; + } + + if (*fmt != '%' || fmt[1] == '%') { + if (*fmt == '%') + fmt++; + char c = handler.consume(); + if (c != *fmt) + break; + continue; + } + + void *dest = NULL; + /* %n$ format */ + if (isdigit(*fmt) && fmt[1] == '$') { + /* TODO: dest = get_arg_at_pos(args, *fmt -'0'); */ + fmt += 3; + } else { + if (fmt[1] != '*') { + dest = va_arg(args, void*); + } + fmt++; + } + + int width = 0; + if (*fmt == '*') { + fmt++; + } else if (*fmt == '\'') { + /* TODO: numeric seperators locale stuff */ + mlibc::infoLogger() << "do_scanf: \' not implemented!" << frg::endlog; + fmt++; + continue; + } else if (*fmt == 'm') { + /* TODO: allocate buffer for them */ + mlibc::infoLogger() << "do_scanf: m not implemented!" << frg::endlog; + fmt++; + continue; + } else if (*fmt >= '0' && *fmt <= '9') { + /* read in width specifier */ + width = 0; + while (*fmt >= '0' && *fmt <= '9') { + width = width * 10 + (*fmt - '0'); + fmt++; + continue; + } + } + + /* type modifiers */ + unsigned int type = SCANF_TYPE_INT; + unsigned int base = 10; + switch (*fmt) { + case 'h': { + if (fmt[1] == 'h') { + type = SCANF_TYPE_CHAR; + fmt += 2; + break; + } + type = SCANF_TYPE_SHORT; + fmt++; + break; + } + case 'j': { + type = SCANF_TYPE_INTMAX; + fmt++; + break; + } + case 'l': { + if (fmt[1] == 'l') { + type = SCANF_TYPE_LL; + fmt += 2; + break; + } + type = SCANF_TYPE_L; + fmt++; + break; + } + case 'L': { + type = SCANF_TYPE_LL; + fmt++; + break; + } + case 'q': { + type = SCANF_TYPE_LL; + fmt++; + break; + } + case 't': { + type = SCANF_TYPE_PTRDIFF; + fmt++; + break; + } + case 'z': { + type = SCANF_TYPE_SIZE_T; + fmt++; + break; + } + } + + // Leading whitespace is skipped for most conversions except these. + if (*fmt != 'c' && *fmt != '[' && *fmt != 'n') { + while (isspace(handler.look_ahead())) + handler.consume(); + } + + switch (*fmt) { + case 'd': + case 'u': + base = 10; + [[fallthrough]]; + case 'i': { + bool is_negative = false; + unsigned long long res = 0; + + if((*fmt == 'i' || *fmt == 'd') && handler.look_ahead() == '-') { + handler.consume(); + is_negative = true; + } + + if(*fmt == 'i' && handler.look_ahead() == '0') { + handler.consume(); + if(handler.look_ahead() == 'x') { + handler.consume(); + base = 16; + } else { + base = 8; + } + } + + char c = handler.look_ahead(); + switch (base) { + case 10: + if(!isdigit(c)) + return match_count; + while (c >= '0' && c <= '9') { + handler.consume(); + res = res * 10 + (c - '0'); + c = handler.look_ahead(); + } + break; + case 16: + if (c == '0') { + handler.consume(); + c = handler.look_ahead(); + if (c == 'x') { + handler.consume(); + c = handler.look_ahead(); + } + } + while (true) { + if (c >= '0' && c <= '9') { + handler.consume(); + res = res * 16 + (c - '0'); + } else if (c >= 'a' && c <= 'f') { + handler.consume(); + res = res * 16 + (c - 'a' + 10); + } else if (c >= 'A' && c <= 'F') { + handler.consume(); + res = res * 16 + (c - 'A' + 10); + } else { + break; + } + c = handler.look_ahead(); + } + break; + case 8: + while (c >= '0' && c <= '7') { + handler.consume(); + res = res * 8 + (c - '0'); + c = handler.look_ahead(); + } + break; + } + if (dest) { + if(is_negative) + store_int(dest, type, -res); + else + store_int(dest, type, res); + } + break; + } + case 'o': { + unsigned long long res = 0; + char c = handler.look_ahead(); + while (c >= '0' && c <= '7') { + handler.consume(); + res = res * 8 + (c - '0'); + c = handler.look_ahead(); + } + if (dest) + store_int(dest, type, res); + break; + } + case 'x': + case 'X': { + unsigned long long res = 0; + char c = handler.look_ahead(); + if (c == '0') { + handler.consume(); + c = handler.look_ahead(); + if (c == 'x') { + handler.consume(); + c = handler.look_ahead(); + } + } + while (true) { + if (c >= '0' && c <= '9') { + handler.consume(); + res = res * 16 + (c - '0'); + } else if (c >= 'a' && c <= 'f') { + handler.consume(); + res = res * 16 + (c - 'a' + 10); + } else if (c >= 'A' && c <= 'F') { + handler.consume(); + res = res * 16 + (c - 'A' + 10); + } else { + break; + } + c = handler.look_ahead(); + } + if (dest) + store_int(dest, type, res); + break; + } + case 's': { + char *typed_dest = (char *)dest; + char c = handler.look_ahead(); + int count = 0; + while (c && !isspace(c)) { + handler.consume(); + if (typed_dest) + typed_dest[count] = c; + c = handler.look_ahead(); + count++; + if (width && count >= width) + break; + } + if (typed_dest) + typed_dest[count] = '\0'; + break; + } + case 'c': { + char *typed_dest = (char *)dest; + char c = handler.look_ahead(); + int count = 0; + if (!width) + width = 1; + while (c && count < width) { + handler.consume(); + if (typed_dest) + typed_dest[count] = c; + c = handler.look_ahead(); + count++; + } + break; + } + case '[': { + fmt++; + int invert = 0; + if (*fmt == '^') { + invert = 1; + fmt++; + } + + char scanset[257]; + memset(&scanset[0], invert, sizeof(char) * 257); + scanset[0] = '\0'; + + if (*fmt == '-') { + fmt++; + scanset[1+'-'] = 1 - invert; + } else if (*fmt == ']') { + fmt++; + scanset[1+']'] = 1 - invert; + } + + for (; *fmt != ']'; fmt++) { + if (!*fmt) return EOF; + if (*fmt == '-' && *fmt != ']') { + fmt++; + for (char c = *(fmt - 2); c < *fmt; c++) + scanset[1 + c] = 1 - invert; + } + scanset[1 + *fmt] = 1 - invert; + } + + char *typed_dest = (char *)dest; + int count = 0; + char c = handler.look_ahead(); + while (c && (!width || count < width)) { + handler.consume(); + if (!scanset[1 + c]) + break; + if (typed_dest) + typed_dest[count] = c; + c = handler.look_ahead(); + count++; + } + if (typed_dest) + typed_dest[count] = '\0'; + break; + } + case 'p': { + unsigned long long res = 0; + char c = handler.look_ahead(); + if (c == '0') { + handler.consume(); + c = handler.look_ahead(); + if (c == 'x') { + handler.consume(); + c = handler.look_ahead(); + } + } + while (true) { + if (c >= '0' && c <= '9') { + handler.consume(); + res = res * 16 + (c - '0'); + } else if (c >= 'a' && c <= 'f') { + handler.consume(); + res = res * 16 + (c - 'a'); + } else if (c >= 'A' && c <= 'F') { + handler.consume(); + res = res * 16 + (c - 'A'); + } else { + break; + } + c = handler.look_ahead(); + } + void **typed_dest = (void **)dest; + *typed_dest = (void *)(uintptr_t)res; + break; + } + case 'n': { + int *typed_dest = (int *)dest; + if (typed_dest) + *typed_dest = handler.num_consumed; + continue; + } + } + if (dest) match_count++; + } + return match_count; +} + +int scanf(const char *__restrict format, ...) { + va_list args; + va_start(args, format); + int result = vfscanf(stdin, format, args); + va_end(args); + return result; +} + +int snprintf(char *__restrict buffer, size_t max_size, const char *__restrict format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf(buffer, max_size, format, args); + va_end(args); + return result; +} + +int sprintf(char *__restrict buffer, const char *__restrict format, ...) { + va_list args; + va_start(args, format); + int result = vsprintf(buffer, format, args); + va_end(args); + return result; +} + +int sscanf(const char *__restrict buffer, const char *__restrict format, ...) { + va_list args; + va_start(args, format); + + int result = vsscanf(buffer, format, args); + + va_end(args); + return result; +} + +int vfprintf(FILE *__restrict stream, const char *__restrict format, __builtin_va_list args) { + frg::va_struct vs; + frg::arg arg_list[NL_ARGMAX + 1]; + vs.arg_list = arg_list; + va_copy(vs.args, args); + auto file = static_cast<mlibc::abstract_file *>(stream); + frg::unique_lock lock(file->_lock); + StreamPrinter p{stream}; +// mlibc::infoLogger() << "printf(" << format << ")" << frg::endlog; + auto res = frg::printf_format(PrintfAgent{&p, &vs}, format, &vs); + if (!res) + return -static_cast<int>(res.error()); + + return p.count; +} + +int vfscanf(FILE *__restrict stream, const char *__restrict format, __builtin_va_list args) { + auto file = static_cast<mlibc::abstract_file *>(stream); + frg::unique_lock lock(file->_lock); + + struct { + char look_ahead() { + char c; + size_t actual_size; + file->read(&c, 1, &actual_size); + if (actual_size) + file->unget(c); + return actual_size ? c : 0; + } + + char consume() { + char c; + size_t actual_size; + file->read(&c, 1, &actual_size); + if (actual_size) + num_consumed++; + return actual_size ? c : 0; + } + + mlibc::abstract_file *file; + int num_consumed; + } handler = {file, 0}; + + return do_scanf(handler, format, args); +} + +int vprintf(const char *__restrict format, __builtin_va_list args){ + return vfprintf(stdout, format, args); +} + +int vscanf(const char *__restrict, __builtin_va_list) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int vsnprintf(char *__restrict buffer, size_t max_size, + const char *__restrict format, __builtin_va_list args) { + frg::va_struct vs; + frg::arg arg_list[NL_ARGMAX + 1]; + vs.arg_list = arg_list; + va_copy(vs.args, args); + LimitedPrinter p{buffer, max_size ? max_size - 1 : 0}; +// mlibc::infoLogger() << "printf(" << format << ")" << frg::endlog; + auto res = frg::printf_format(PrintfAgent{&p, &vs}, format, &vs); + if (!res) + return -static_cast<int>(res.error()); + if (max_size) + p.buffer[frg::min(max_size - 1, p.count)] = 0; + return p.count; +} + +int vsprintf(char *__restrict buffer, const char *__restrict format, __builtin_va_list args) { + frg::va_struct vs; + frg::arg arg_list[NL_ARGMAX + 1]; + vs.arg_list = arg_list; + va_copy(vs.args, args); + BufferPrinter p(buffer); +// mlibc::infoLogger() << "printf(" << format << ")" << frg::endlog; + auto res = frg::printf_format(PrintfAgent{&p, &vs}, format, &vs); + if (!res) + return -static_cast<int>(res.error()); + p.buffer[p.count] = 0; + return p.count; +} + +int vsscanf(const char *__restrict buffer, const char *__restrict format, __builtin_va_list args) { + struct { + char look_ahead() { + return *buffer; + } + + char consume() { + num_consumed++; + return *buffer++; + } + + const char *buffer; + int num_consumed; + } handler = {buffer, 0}; + + int result = do_scanf(handler, format, args); + + return result; +} + +int fwprintf(FILE *__restrict, const wchar_t *__restrict, ...) MLIBC_STUB_BODY +int fwscanf(FILE *__restrict, const wchar_t *__restrict, ...) MLIBC_STUB_BODY +int vfwprintf(FILE *__restrict, const wchar_t *__restrict, __builtin_va_list) MLIBC_STUB_BODY +int vfwscanf(FILE *__restrict, const wchar_t *__restrict, __builtin_va_list) MLIBC_STUB_BODY + +int swprintf(wchar_t *__restrict, size_t, const wchar_t *__restrict, ...) MLIBC_STUB_BODY +int swscanf(wchar_t *__restrict, size_t, const wchar_t *__restrict, ...) MLIBC_STUB_BODY +int vswprintf(wchar_t *__restrict, size_t, const wchar_t *__restrict, __builtin_va_list) MLIBC_STUB_BODY +int vswscanf(wchar_t *__restrict, size_t, const wchar_t *__restrict, __builtin_va_list) MLIBC_STUB_BODY + +int wprintf(const wchar_t *__restrict, ...) MLIBC_STUB_BODY +int wscanf(const wchar_t *__restrict, ...) MLIBC_STUB_BODY +int vwprintf(const wchar_t *__restrict, __builtin_va_list) MLIBC_STUB_BODY +int vwscanf(const wchar_t *__restrict, __builtin_va_list) MLIBC_STUB_BODY + +int fgetc(FILE *stream) { + char c; + auto bytes_read = fread(&c, 1, 1, stream); + if(bytes_read != 1) + return EOF; + return c; +} + +char *fgets(char *__restrict buffer, size_t max_size, FILE *__restrict stream) { + auto file = static_cast<mlibc::abstract_file *>(stream); + frg::unique_lock lock(file->_lock); + return fgets_unlocked(buffer, max_size, stream); +} + +int fputc_unlocked(int c, FILE *stream) { + char d = c; + if(fwrite_unlocked(&d, 1, 1, stream) != 1) + return EOF; + return 1; +} + +int fputc(int c, FILE *stream) { + auto file = static_cast<mlibc::abstract_file *>(stream); + frg::unique_lock lock(file->_lock); + return fputc_unlocked(c, stream); +} + +int fputs_unlocked(const char *__restrict string, FILE *__restrict stream) { + if(fwrite_unlocked(string, strlen(string), 1, stream) != 1) + return EOF; + return 1; +} + +int fputs(const char *__restrict string, FILE *__restrict stream) { + auto file = static_cast<mlibc::abstract_file *>(stream); + frg::unique_lock lock(file->_lock); + return fputs_unlocked(string, stream); +} + +int getc_unlocked(FILE *stream) { + return fgetc_unlocked(stream); +} + +int getc(FILE *stream) { + return fgetc(stream); +} + +int getchar_unlocked(void) { + return fgetc_unlocked(stdin); +} + +int getchar(void) { + return fgetc(stdin); +} + +char *gets(char *s){ + return fgets(s, SIZE_MAX, stdin); +} + +int putc_unlocked(int c, FILE *stream) { + char d = c; + if(fwrite_unlocked(&d, 1, 1, stream) != 1) + return EOF; + return c; +} + +int putc(int c, FILE *stream) { + auto file = static_cast<mlibc::abstract_file *>(stream); + frg::unique_lock lock(file->_lock); + return putc_unlocked(c, stream); +} + +int putchar_unlocked(int c) { + return putc_unlocked(c, stdout); +} + +int putchar(int c) { + auto file = static_cast<mlibc::abstract_file *>(stdout); + frg::unique_lock lock(file->_lock); + return putchar_unlocked(c); +} + +int puts(const char *string) { + auto file = static_cast<mlibc::abstract_file *>(stdout); + frg::unique_lock lock(file->_lock); + + size_t progress = 0; + size_t len = strlen(string); + while(progress < len) { + size_t chunk; + if(file->write(string + progress, + len - progress, &chunk)) { + return EOF; + }else if(!chunk) { + return EOF; + } + + progress += chunk; + } + + size_t unused; + if (!file->write("\n", 1, &unused)) { + return EOF; + } + + return 1; +} + +wint_t fgetwc(FILE *) MLIBC_STUB_BODY +wchar_t *fgetws(wchar_t *__restrict, int, FILE *__restrict) MLIBC_STUB_BODY +wint_t fputwc(wchar_t, FILE *) MLIBC_STUB_BODY +int fputws(const wchar_t *__restrict, FILE *__restrict) MLIBC_STUB_BODY +int fwide(FILE *, int) MLIBC_STUB_BODY +wint_t getwc(FILE *) MLIBC_STUB_BODY +wint_t getwchar(void) MLIBC_STUB_BODY +wint_t putwc(wchar_t, FILE *) MLIBC_STUB_BODY +wint_t putwchar(wchar_t) MLIBC_STUB_BODY +wint_t ungetwc(wint_t, FILE *) MLIBC_STUB_BODY + +size_t fread(void *buffer, size_t size, size_t count, FILE *file_base) { + auto file = static_cast<mlibc::abstract_file *>(file_base); + frg::unique_lock lock(file->_lock); + return fread_unlocked(buffer, size, count, file_base); +} + +size_t fwrite(const void *buffer, size_t size , size_t count, FILE *file_base) { + auto file = static_cast<mlibc::abstract_file *>(file_base); + frg::unique_lock lock(file->_lock); + return fwrite_unlocked(buffer, size, count, file_base); +} + +int fgetpos(FILE *__restrict, fpos_t *__restrict) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +// fseek() is provided by the POSIX sublibrary +int fsetpos(FILE *, const fpos_t *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} +// ftell() is provided by the POSIX sublibrary + +void clearerr(FILE *file_base) { + file_base->__status_bits = 0; +} + +int feof(FILE *file_base) { + return file_base->__status_bits & __MLIBC_EOF_BIT; +} + +int ferror(FILE *file_base) { + return file_base->__status_bits & __MLIBC_ERROR_BIT; +} + +void perror(const char *string) { + int error = errno; + if (string && *string) { + fprintf(stderr, "%s: ", string); + } + fprintf(stderr, "%s\n", strerror(error)); +} + +// POSIX extensions. + +ssize_t getline(char **line, size_t *n, FILE *stream) { + return getdelim(line, n, '\n', stream); +} + +ssize_t getdelim(char **line, size_t *n, int delim, FILE *stream) { + // Otherwise, we cannot store the buffer / size. + if(!line || !n) { + errno = EINVAL; + return -1; + } + + char *buffer = *line; + /* set the starting capacity to 512 if buffer = NULL */ + size_t capacity = (!buffer) ? 512 : *n; + size_t nwritten = 0; + + auto file = static_cast<mlibc::abstract_file *>(stream); + frg::unique_lock lock(file->_lock); + + // Avoid allocating if we've already hit the end + auto c = fgetc_unlocked(stream); + if (c == EOF || ferror(stream)) { + return -1; + } else { + file->unget(c); + } + + while (true) { + // Fill the buffer + while (buffer && capacity > 0 && nwritten < capacity - 1) { + auto c = fgetc_unlocked(stream); + if (ferror(stream)) { + return -1; + } else if (c == EOF) { + buffer[nwritten] = 0; + return nwritten; + } + + buffer[nwritten++] = c; + + if (c == delim) { + buffer[nwritten] = 0; + return nwritten; + } + } + + // Double the size of the buffer (but make sure it's at least 1024) + capacity = (capacity >= 1024) ? capacity * 2 : 1024; + buffer = reinterpret_cast<char *>(getAllocator().reallocate(*line, capacity)); + if (!buffer) { + errno = ENOMEM; + return -1; + } + + *line = buffer; + *n = capacity; + } +} + +// GLIBC extensions. + +int asprintf(char **out, const char *format, ...) { + va_list args; + va_start(args, format); + int result = vasprintf(out, format, args); + va_end(args); + return result; +} + +int vasprintf(char **out, const char *format, __builtin_va_list args) { + frg::va_struct vs; + frg::arg arg_list[NL_ARGMAX + 1]; + vs.arg_list = arg_list; + va_copy(vs.args, args); + ResizePrinter p; +// mlibc::infoLogger() << "printf(" << format << ")" << frg::endlog; + auto res = frg::printf_format(PrintfAgent{&p, &vs}, format, &vs); + if (!res) + return -static_cast<int>(res.error()); + p.expand(); + p.buffer[p.count] = 0; + *out = p.buffer; + return p.count; +} + +// Linux unlocked I/O extensions. + +void flockfile(FILE *file_base) { + static_cast<mlibc::abstract_file *>(file_base)->_lock.lock(); +} + +void funlockfile(FILE *file_base) { + static_cast<mlibc::abstract_file *>(file_base)->_lock.unlock(); +} + +int ftrylockfile(FILE *file_base) { + static_cast<mlibc::abstract_file *>(file_base)->_lock.try_lock(); + return 0; +} + +void clearerr_unlocked(FILE *file_base) { + file_base->__status_bits = 0; +} + +int feof_unlocked(FILE *file_base) { + return file_base->__status_bits & __MLIBC_EOF_BIT; +} + +int ferror_unlocked(FILE *file_base) { + return file_base->__status_bits & __MLIBC_ERROR_BIT; +} + +int fgetc_unlocked(FILE *stream) { + unsigned char d; + if(fread_unlocked(&d, 1, 1, stream) != 1) + return EOF; + return (int)d; +} + +size_t fread_unlocked(void *buffer, size_t size, size_t count, FILE *file_base) { + auto file = static_cast<mlibc::abstract_file *>(file_base); + if(!size || !count) + return 0; + + // Distinguish two cases here: If the object size is one, we perform byte-wise reads. + // Otherwise, we try to read each object individually. + if(size == 1) { + size_t progress = 0; + while(progress < count) { + size_t chunk; + if(int e = file->read((char *)buffer + progress, + count - progress, &chunk)) { + errno = e; + return 0; + }else if(!chunk) { + // TODO: Handle eof. + break; + } + + progress += chunk; + } + + return progress; + }else{ + for(size_t i = 0; i < count; i++) { + size_t progress = 0; + while(progress < size) { + size_t chunk; + if(int e = file->read((char *)buffer + i * size + progress, + size - progress, &chunk)) { + errno = e; + return 0; + }else if(!chunk) { + // TODO: Handle eof. + break; + } + + progress += chunk; + } + + if(progress < size) + return i; + } + + return count; + } +} + +size_t fwrite_unlocked(const void *buffer, size_t size, size_t count, FILE *file_base) { + auto file = static_cast<mlibc::abstract_file *>(file_base); + if(!size || !count) + return 0; + + // Distinguish two cases here: If the object size is one, we perform byte-wise writes. + // Otherwise, we try to write each object individually. + if(size == 1) { + size_t progress = 0; + while(progress < count) { + size_t chunk; + if(file->write((const char *)buffer + progress, + count - progress, &chunk)) { + // TODO: Handle I/O errors. + mlibc::infoLogger() << "mlibc: fwrite() I/O errors are not handled" + << frg::endlog; + break; + }else if(!chunk) { + // TODO: Handle eof. + break; + } + + progress += chunk; + } + + return progress; + }else{ + for(size_t i = 0; i < count; i++) { + size_t progress = 0; + while(progress < size) { + size_t chunk; + if(file->write((const char *)buffer + i * size + progress, + size - progress, &chunk)) { + // TODO: Handle I/O errors. + mlibc::infoLogger() << "mlibc: fwrite() I/O errors are not handled" + << frg::endlog; + break; + }else if(!chunk) { + // TODO: Handle eof. + break; + } + + progress += chunk; + } + + if(progress < size) + return i; + } + + return count; + } +} + +char *fgets_unlocked(char *__restrict buffer, int max_size, FILE *stream) { + __ensure(max_size > 0); + for(int i = 0; ; i++) { + if(i == max_size - 1) { + buffer[i] = 0; + return buffer; + } + + auto c = fgetc_unlocked(stream); + + // If fgetc() fails, there is either an EOF or an I/O error. + if(c == EOF) { + if(i) { + buffer[i] = 0; + return buffer; + } else { + // In this case, the buffer is not changed. + return nullptr; + } + } else { + buffer[i] = c; + } + + if(c == '\n') { + buffer[i + 1] = 0; + return buffer; + } + } +} |