9a9b91c940
It's finally done.. Signed-off-by: kaguya <vpshinomiya@protonmail.com>
1501 lines
34 KiB
C++
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(¤t_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;
|
|
}
|
|
}
|
|
}
|