aboutsummaryrefslogtreecommitdiff
path: root/lib/mlibc/options/ansi/generic/stdio-stubs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mlibc/options/ansi/generic/stdio-stubs.cpp')
-rw-r--r--lib/mlibc/options/ansi/generic/stdio-stubs.cpp1270
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;
+ }
+ }
+}