#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include template struct PrintfAgent { PrintfAgent(F *formatter, frg::va_struct *vsp) : _formatter{formatter}, _vsp{vsp} { } frg::expected operator() (char c) { _formatter->append(c); return {}; } frg::expected operator() (const char *c, size_t n) { _formatter->append(c, n); return {}; } frg::expected 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(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(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(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 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(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(res.error()); return p.count; } int vfscanf(FILE *__restrict stream, const char *__restrict format, __builtin_va_list args) { auto file = static_cast(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(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(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(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(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(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(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(stdout); frg::unique_lock lock(file->_lock); return putchar_unlocked(c); } int puts(const char *string) { auto file = static_cast(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(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(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(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(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(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(file_base)->_lock.lock(); } void funlockfile(FILE *file_base) { static_cast(file_base)->_lock.unlock(); } int ftrylockfile(FILE *file_base) { static_cast(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(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(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; } } }