Files
kaguya 9a9b91c940 user: implement mlibc as the libc, finally.
It's finally done..

Signed-off-by: kaguya <vpshinomiya@protonmail.com>
2026-05-02 03:31:49 -04:00

1501 lines
34 KiB
C++

#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[MB_LEN_MAX];
auto c = static_cast<wchar_t>(va_arg(_vsp->args, wint_t));
mbstate_t shift_state = {};
size_t res = wcrtomb(c_buf, c, &shift_state);
if (res == size_t(-1))
return frg::format_error::agent_error;
_formatter->append(c_buf, res);
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 'b': case 'B': 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': {
switch(szmod) {
case frg::printf_size_mod::default_size: {
auto p = va_arg(_vsp->args, int *);
*p = _formatter->count;
} break;
case frg::printf_size_mod::char_size: {
auto p = va_arg(_vsp->args, signed char *);
*p = static_cast<signed char>(_formatter->count);
} break;
case frg::printf_size_mod::short_size: {
auto p = va_arg(_vsp->args, short *);
*p = static_cast<short>(_formatter->count);
} break;
case frg::printf_size_mod::long_size: {
auto p = va_arg(_vsp->args, long *);
*p = static_cast<long>(_formatter->count);
} break;
case frg::printf_size_mod::longlong_size: {
auto p = va_arg(_vsp->args, long long *);
*p = static_cast<long long>(_formatter->count);
} break;
case frg::printf_size_mod::longdouble_size:
__ensure(!"Illegal size for %n printf modifier");
break;
case frg::printf_size_mod::native_size: {
auto p = va_arg(_vsp->args, ptrdiff_t *);
*p = static_cast<ptrdiff_t>(_formatter->count);
} break;
case frg::printf_size_mod::intmax_size: {
auto p = va_arg(_vsp->args, intmax_t *);
*p = static_cast<intmax_t>(_formatter->count);
} break;
}
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, nullptr, _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
};
} // namespace
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) {
#define NOMATCH_CHECK(cond) ({ if(cond) return match_count; }) // if cond is true, matching error
#define EOF_CHECK(cond) ({ if(cond) return match_count ? match_count : EOF; }) // if cond is true, no more data to read
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();
EOF_CHECK(c == '\0');
NOMATCH_CHECK(c != *fmt);
continue;
}
void *dest = nullptr;
/* %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++;
}
bool allocate_buf = false;
auto temp_dest = frg::string<MemoryAllocator>{getAllocator()};
auto temp_wdest = frg::basic_string<wchar_t, MemoryAllocator>{getAllocator()};
int count = 0;
const auto append_to_buffer = [&](char c) {
if(allocate_buf) {
temp_dest += c;
} else {
char *typed_dest = (char *)dest;
if(typed_dest)
typed_dest[count] = c;
}
count++;
};
const auto append_to_wbuffer = [&](wchar_t c) {
if(allocate_buf) {
temp_wdest += c;
} else {
wchar_t *typed_dest = (wchar_t *)dest;
if(typed_dest)
typed_dest[count] = c;
}
count++;
};
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') {
allocate_buf = true;
fmt++;
} 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;
char c = handler.look_ahead();
EOF_CHECK(c == '\0');
if(c == '-') {
handler.consume();
is_negative = true;
} else if(c == '+')
handler.consume();
if(*fmt == 'i' && handler.look_ahead() == '0') {
handler.consume();
c = handler.look_ahead();
if(tolower(c) == 'x') {
handler.consume();
base = 16;
} else if(tolower(c) == 'b') {
handler.consume();
base = 2;
} else {
base = 8;
}
}
c = handler.look_ahead();
int count = 0;
switch (base) {
case 10:
NOMATCH_CHECK(!isdigit(c));
while (c >= '0' && c <= '9') {
handler.consume();
res = res * 10 + (c - '0');
c = handler.look_ahead();
}
break;
case 16:
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;
}
count++;
c = handler.look_ahead();
}
NOMATCH_CHECK(count == 0);
break;
case 8:
while (c >= '0' && c <= '7') {
handler.consume();
res = res * 8 + (c - '0');
c = handler.look_ahead();
}
// no need for a match check, the starting 0 was already consumed
break;
case 2:
NOMATCH_CHECK(c != '0' && c != '1');
while (c == '0' || c == '1') {
handler.consume();
res = res * 2 + (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': {
bool is_negative = false;
unsigned long long res = 0;
char c = handler.look_ahead();
EOF_CHECK(c == '\0');
if(c == '-') {
handler.consume();
is_negative = true;
} else if(c == '+')
handler.consume();
c = handler.look_ahead();
NOMATCH_CHECK(!(c >= '0' && c <= '7'));
while (c >= '0' && c <= '7') {
handler.consume();
res = res * 8 + (c - '0');
c = handler.look_ahead();
}
if (dest) {
if(is_negative)
store_int(dest, type, -res);
else
store_int(dest, type, res);
}
break;
}
case 'x':
case 'X': {
bool is_negative = false;
unsigned long long res = 0;
char c = handler.look_ahead();
int count = 0;
EOF_CHECK(c == '\0');
if(c == '-') {
handler.consume();
is_negative = true;
} else if(c == '+')
handler.consume();
c = handler.look_ahead();
if (c == '0') {
handler.consume();
c = handler.look_ahead();
if (tolower(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;
}
count++;
c = handler.look_ahead();
}
NOMATCH_CHECK(count == 0);
if (dest) {
if(is_negative)
store_int(dest, type, -res);
else
store_int(dest, type, res);
}
break;
}
case 'b': {
bool is_negative = false;
unsigned long long res = 0;
char c = handler.look_ahead();
int count = 0;
EOF_CHECK(c == '\0');
if(c == '-') {
handler.consume();
is_negative = true;
} else if(c == '+')
handler.consume();
if (c == '0') {
handler.consume();
c = handler.look_ahead();
if (tolower(c) == 'b') {
handler.consume();
c = handler.look_ahead();
}
}
while (true) {
if (c == '0' || c == '1') {
handler.consume();
res = res * 2 + (c - '0');
} else {
break;
}
count++;
c = handler.look_ahead();
}
NOMATCH_CHECK(count == 0);
if (dest) {
if(is_negative)
store_int(dest, type, -res);
else
store_int(dest, type, res);
}
break;
}
case 's': {
char c = handler.look_ahead();
EOF_CHECK(c == '\0');
while (c && !isspace(c)) {
handler.consume();
if(type == SCANF_TYPE_L)
append_to_wbuffer(c);
else
append_to_buffer(c);
c = handler.look_ahead();
if (width && count >= width)
break;
}
NOMATCH_CHECK(count == 0);
if(type == SCANF_TYPE_L)
append_to_wbuffer(L'\0');
else
append_to_buffer('\0');
break;
}
case 'c': {
char c = handler.look_ahead();
EOF_CHECK(c == '\0');
if (!width)
width = 1;
while (c && count < width) {
handler.consume();
if(type == SCANF_TYPE_L)
append_to_wbuffer(c);
else
append_to_buffer(c);
c = handler.look_ahead();
}
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 c = handler.look_ahead();
EOF_CHECK(c == '\0');
while (c && (!width || count < width)) {
handler.consume();
if (!scanset[1 + c])
break;
if(type == SCANF_TYPE_L)
append_to_wbuffer(c);
else
append_to_buffer(c);
c = handler.look_ahead();
}
NOMATCH_CHECK(count == 0);
if(type == SCANF_TYPE_L)
append_to_wbuffer(L'\0');
else
append_to_buffer('\0');
break;
}
case 'p': {
unsigned long long res = 0;
char c = handler.look_ahead();
int count = 0;
EOF_CHECK(c == '\0');
if (c == '0') {
handler.consume();
c = handler.look_ahead();
if (tolower(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;
}
count++;
c = handler.look_ahead();
}
NOMATCH_CHECK(count == 0);
void **typed_dest = (void **)dest;
*typed_dest = (void *)(uintptr_t)res;
break;
}
case 'n': {
if(dest) {
switch(type) {
case SCANF_TYPE_CHAR:
*(signed char *)dest = (signed char)handler.num_consumed;
break;
case SCANF_TYPE_SHORT:
*(short *)dest = (short)handler.num_consumed;
break;
case SCANF_TYPE_INTMAX:
*(intmax_t *)dest = (intmax_t)handler.num_consumed;
break;
case SCANF_TYPE_L:
*(long *)dest = (long)handler.num_consumed;
break;
case SCANF_TYPE_LL:
*(long long *)dest = (long long)handler.num_consumed;
break;
case SCANF_TYPE_PTRDIFF:
*(ptrdiff_t *)dest = (ptrdiff_t)handler.num_consumed;
break;
case SCANF_TYPE_SIZE_T:
*(size_t *)dest = (size_t)handler.num_consumed;
break;
case SCANF_TYPE_INT:
*(int *)dest = (int)handler.num_consumed;
break;
}
}
continue;
}
}
if(allocate_buf && dest) {
if(type == SCANF_TYPE_L) {
wchar_t *temp = (wchar_t *)getAllocator().allocate((temp_wdest.size() + 1) * sizeof(wchar_t));
memcpy(temp, temp_wdest.data(), temp_wdest.size() * sizeof(wchar_t));
temp[temp_wdest.size()] = L'\0';
wchar_t **dest_ptr = (wchar_t **)dest;
*dest_ptr = temp;
} else {
char *temp = (char *)getAllocator().allocate(temp_dest.size() + 1);
memcpy(temp, temp_dest.data(), temp_dest.size());
temp[temp_dest.size()] = '\0';
char **dest_ptr = (char **)dest;
*dest_ptr = temp;
}
}
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, 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, 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, int 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) {
// fwrite with a length of 0 will return 0, so we need to explicitly allow
// zero length strings.
size_t length = strlen(string);
if (length != 0 && fwrite_unlocked(string, length, 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, INT_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 f, fpos_t *__restrict out) {
auto file = static_cast<mlibc::abstract_file *>(f);
off_t current_offset;
if(int e = file->tell(&current_offset); e) {
errno = e;
return -1;
}
*out = static_cast<fpos_t>(current_offset);
return 0;
}
// fseek() is provided by the POSIX sublibrary
int fsetpos(FILE *f, const fpos_t *pos) {
auto file = static_cast<mlibc::abstract_file *>(f);
frg::unique_lock lock(file->_lock);
if(int e = file->seek(*pos, SEEK_SET); e) {
errno = e;
return -1;
}
return 0;
}
// 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 *__restrict 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;
}
}
}