#include "nolibc.h"
#ifndef _NOLIBC_STDIO_H
#define _NOLIBC_STDIO_H
#include "std.h"
#include "arch.h"
#include "errno.h"
#include "fcntl.h"
#include "types.h"
#include "sys.h"
#include "stdarg.h"
#include "stdlib.h"
#include "string.h"
#include "compiler.h"
static const char *strerror(int errnum);
#ifndef EOF
#define EOF (-1)
#endif
#define _IOFBF 0
#define _IOLBF 1
#define _IONBF 2
typedef struct FILE {
char dummy[1];
} FILE;
static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
static __attribute__((unused))
FILE *fdopen(int fd, const char *mode __attribute__((unused)))
{
if (fd < 0) {
SET_ERRNO(EBADF);
return NULL;
}
return (FILE*)(intptr_t)~fd;
}
static __attribute__((unused))
FILE *fopen(const char *pathname, const char *mode)
{
int flags, fd;
switch (*mode) {
case 'r':
flags = O_RDONLY;
break;
case 'w':
flags = O_WRONLY | O_CREAT | O_TRUNC;
break;
case 'a':
flags = O_WRONLY | O_CREAT | O_APPEND;
break;
default:
SET_ERRNO(EINVAL); return NULL;
}
if (mode[1] == '+')
flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
fd = open(pathname, flags, 0666);
return fdopen(fd, mode);
}
static __attribute__((unused))
int fileno(FILE *stream)
{
intptr_t i = (intptr_t)stream;
if (i >= 0) {
SET_ERRNO(EBADF);
return -1;
}
return ~i;
}
static __attribute__((unused))
int fflush(FILE *stream)
{
intptr_t i = (intptr_t)stream;
if (i > 0) {
SET_ERRNO(EBADF);
return -1;
}
return 0;
}
static __attribute__((unused))
int fclose(FILE *stream)
{
intptr_t i = (intptr_t)stream;
if (i >= 0) {
SET_ERRNO(EBADF);
return -1;
}
if (close(~i))
return EOF;
return 0;
}
#define getc(stream) fgetc(stream)
static __attribute__((unused))
int fgetc(FILE* stream)
{
unsigned char ch;
if (read(fileno(stream), &ch, 1) <= 0)
return EOF;
return ch;
}
static __attribute__((unused))
int getchar(void)
{
return fgetc(stdin);
}
#define putc(c, stream) fputc(c, stream)
static __attribute__((unused))
int fputc(int c, FILE* stream)
{
unsigned char ch = c;
if (write(fileno(stream), &ch, 1) <= 0)
return EOF;
return ch;
}
static __attribute__((unused))
int putchar(int c)
{
return fputc(c, stdout);
}
static __attribute__((unused))
int _fwrite(const void *buf, size_t size, FILE *stream)
{
ssize_t ret;
int fd = fileno(stream);
while (size) {
ret = write(fd, buf, size);
if (ret <= 0)
return EOF;
size -= ret;
buf += ret;
}
return 0;
}
static __attribute__((unused))
size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream)
{
size_t written;
for (written = 0; written < nmemb; written++) {
if (_fwrite(s, size, stream) != 0)
break;
s += size;
}
return written;
}
static __attribute__((unused))
int _fread(void *buf, size_t size, FILE *stream)
{
int fd = fileno(stream);
ssize_t ret;
while (size) {
ret = read(fd, buf, size);
if (ret <= 0)
return EOF;
size -= ret;
buf += ret;
}
return 0;
}
static __attribute__((unused))
size_t fread(void *s, size_t size, size_t nmemb, FILE *stream)
{
size_t nread;
for (nread = 0; nread < nmemb; nread++) {
if (_fread(s, size, stream) != 0)
break;
s += size;
}
return nread;
}
static __attribute__((unused))
int fputs(const char *s, FILE *stream)
{
return _fwrite(s, strlen(s), stream);
}
static __attribute__((unused))
int puts(const char *s)
{
if (fputs(s, stdout) == EOF)
return EOF;
return putchar('\n');
}
static __attribute__((unused))
char *fgets(char *s, int size, FILE *stream)
{
int ofs;
int c;
for (ofs = 0; ofs + 1 < size;) {
c = fgetc(stream);
if (c == EOF)
break;
s[ofs++] = c;
if (c == '\n')
break;
}
if (ofs < size)
s[ofs] = 0;
return ofs ? s : NULL;
}
static __attribute__((unused))
int fseek(FILE *stream, long offset, int whence)
{
int fd = fileno(stream);
off_t ret;
ret = lseek(fd, offset, whence);
if (ret >= 0)
return 0;
return -1;
}
typedef int (*__nolibc_printf_cb)(void *state, const char *buf, size_t size);
#define _NOLIBC_PF_FLAG(ch) (1u << ((ch) & 0x1f))
#define _NOLIBC_PF_FLAG_NZ(ch) ((ch) ? _NOLIBC_PF_FLAG(ch) : 0)
#define _NOLIBC_PF_FLAG8(cmp_1, cmp_2, cmp_3, cmp_4, cmp_5, cmp_6, cmp_7, cmp_8, ...) \
(_NOLIBC_PF_FLAG_NZ(cmp_1) | _NOLIBC_PF_FLAG_NZ(cmp_2) | \
_NOLIBC_PF_FLAG_NZ(cmp_3) | _NOLIBC_PF_FLAG_NZ(cmp_4) | \
_NOLIBC_PF_FLAG_NZ(cmp_5) | _NOLIBC_PF_FLAG_NZ(cmp_6) | \
_NOLIBC_PF_FLAG_NZ(cmp_7) | _NOLIBC_PF_FLAG_NZ(cmp_8))
#define _NOLIBC_PF_FLAGS_CONTAIN(flags, ...) \
((flags) & _NOLIBC_PF_FLAG8(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0))
#define _NOLIBC_PF_CHAR_IS_ONE_OF(ch, cmp_1, ...) \
((unsigned int)(ch) - (cmp_1 & 0xe0) > 0x1f ? 0 : \
_NOLIBC_PF_FLAGS_CONTAIN(_NOLIBC_PF_FLAG(ch), cmp_1, __VA_ARGS__))
static __attribute__((unused, format(printf, 3, 0)))
int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list args)
{
char ch;
unsigned long long v;
long long signed_v;
int written, width, precision, len;
unsigned int flags, ch_flag;
char outbuf[2 + 31 + 22 + 1];
char *out;
const char *outstr;
unsigned int sign_prefix;
int got_width;
written = 0;
while (1) {
outstr = fmt;
ch = *fmt++;
if (!ch)
break;
width = 0;
flags = 0;
if (ch != '%') {
while (*fmt && *fmt != '%')
fmt++;
len = fmt - outstr;
goto do_output;
}
while (1) {
ch = *fmt++;
ch_flag = _NOLIBC_PF_CHAR_IS_ONE_OF(ch, ' ', '#', '+', '-', '0');
if (!ch_flag)
break;
flags |= ch_flag;
}
for (got_width = 0;; ch = *fmt++) {
if (ch == '*') {
precision = va_arg(args, int);
ch = *fmt++;
} else {
for (precision = 0; ch >= '0' && ch <= '9'; ch = *fmt++)
precision = precision * 10 + (ch - '0');
}
if (got_width)
break;
width = precision;
if (ch != '.') {
precision = -1;
break;
}
got_width = 1;
}
if (width < 0) {
width = -width;
flags |= _NOLIBC_PF_FLAG('-');
}
if (ch == 'L')
ch = 'j';
ch_flag = _NOLIBC_PF_CHAR_IS_ONE_OF(ch, 'l', 't', 'z', 'j', 'q');
if (ch_flag != 0) {
if (ch == 'l' && fmt[0] == 'l') {
fmt++;
ch_flag = _NOLIBC_PF_FLAG('j');
}
flags |= ch_flag;
ch = *fmt++;
}
ch_flag = _NOLIBC_PF_FLAG(ch) | (flags & _NOLIBC_PF_FLAG('#')) >> 1;
if (((ch >= 'a' && ch <= 'z') || ch == 'X') &&
_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'c', 'd', 'i', 'u', 'o', 'x', 'p', 's')) {
if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag | (flags & ~_NOLIBC_PF_FLAG('p')),
'p', 's', 'l', 't', 'z')) {
v = va_arg(args, unsigned long);
signed_v = (long)v;
} else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, 'j', 'q')) {
v = va_arg(args, unsigned long long);
signed_v = v;
} else {
v = va_arg(args, unsigned int);
signed_v = (int)v;
}
if (ch == 'c') {
outbuf[0] = v;
len = 1;
outstr = outbuf;
goto do_output;
}
if (ch == 's') {
outstr = (const char *)(uintptr_t)v;
if (!outstr) {
outstr = "(null)";
len = precision < 0 || precision >= 6 ? 6 : 0;
goto do_output;
}
goto do_strlen_output;
}
sign_prefix = 0;
if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i')) {
if (signed_v < 0) {
sign_prefix = '-';
v = -(signed_v + 1);
v++;
} else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '+')) {
sign_prefix = '+';
} else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, ' ')) {
sign_prefix = ' ';
}
} else {
if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'o') &&
_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, '#' - 1))
sign_prefix = '0';
}
out = outbuf + 2 + 31;
if (v == 0) {
if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p')) {
outstr = "(nil)";
len = 5;
goto do_output;
}
if (!precision) {
len = 0;
goto prepend_sign;
}
out[0] = '0';
len = 1;
} else {
unsigned long long recip;
unsigned int base;
if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i', 'u')) {
base = 10;
recip = _NOLIBC_U64TOA_RECIP(10);
} else if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'o')) {
base = 8;
recip = _NOLIBC_U64TOA_RECIP(8);
} else {
base = 16;
recip = _NOLIBC_U64TOA_RECIP(16);
if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p', '#' - 1)) {
sign_prefix = '0' << 8 | 'x';
}
}
len = _nolibc_u64toa_base(v, out, base, recip);
}
if (precision < 0) {
if (!_NOLIBC_PF_FLAGS_CONTAIN(flags, '0'))
goto no_zero_padding;
if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '-'))
goto no_zero_padding;
precision = width;
if (sign_prefix) {
precision--;
if (sign_prefix >= 256)
precision--;
}
}
if (precision > 31)
precision = 31;
for (; len < precision; len++) {
_NOLIBC_OPTIMIZER_HIDE_VAR(len);
*--out = '0';
}
no_zero_padding:
if (sign_prefix - *out) {
prepend_sign:
for (; sign_prefix; sign_prefix >>= 8) {
_NOLIBC_OPTIMIZER_HIDE_VAR(len);
len++;
*--out = sign_prefix;
}
}
outstr = out;
goto do_output;
}
if (ch == 'm') {
#ifdef NOLIBC_IGNORE_ERRNO
outstr = "unknown error";
#else
outstr = strerror(errno);
#endif
goto do_strlen_output;
}
if (ch != '%') {
fmt = outstr + 1;
}
len = 1;
width = 0;
outstr = fmt - 1;
goto do_output;
do_strlen_output:
for (len = 0; precision < 0 || len < precision; len++)
if (!outstr[len])
break;
do_output:
written += len;
_NOLIBC_OPTIMIZER_HIDE_VAR(len);
width -= len;
flags = _NOLIBC_PF_FLAGS_CONTAIN(flags, '-');
if (flags && cb(state, outstr, len) != 0)
return -1;
while (width > 0) {
int pad_len = ((width - 1) & 15) + 1;
width -= pad_len;
written += pad_len;
if (cb(state, " ", pad_len) != 0)
return -1;
}
if (!flags && cb(state, outstr, len) != 0)
return -1;
}
if (cb(state, NULL, 0) != 0)
return -1;
return written;
}
static int __nolibc_fprintf_cb(void *stream, const char *buf, size_t size)
{
return _fwrite(buf, size, stream);
}
static __attribute__((unused, format(printf, 2, 0)))
int vfprintf(FILE *stream, const char *fmt, va_list args)
{
return __nolibc_printf(__nolibc_fprintf_cb, stream, fmt, args);
}
static __attribute__((unused, format(printf, 1, 0)))
int vprintf(const char *fmt, va_list args)
{
return vfprintf(stdout, fmt, args);
}
static __attribute__((unused, format(printf, 2, 3)))
int fprintf(FILE *stream, const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = vfprintf(stream, fmt, args);
va_end(args);
return ret;
}
static __attribute__((unused, format(printf, 1, 2)))
int printf(const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = vfprintf(stdout, fmt, args);
va_end(args);
return ret;
}
static __attribute__((unused, format(printf, 2, 0)))
int vdprintf(int fd, const char *fmt, va_list args)
{
FILE *stream;
stream = fdopen(fd, NULL);
if (!stream)
return -1;
return vfprintf(stream, fmt, args);
}
static __attribute__((unused, format(printf, 2, 3)))
int dprintf(int fd, const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = vdprintf(fd, fmt, args);
va_end(args);
return ret;
}
struct __nolibc_sprintf_cb_state {
char *buf;
size_t space;
};
static int __nolibc_sprintf_cb(void *v_state, const char *buf, size_t size)
{
struct __nolibc_sprintf_cb_state *state = v_state;
size_t space = state->space;
char *tgt;
if (size >= space) {
if (space <= 1)
return 0;
size = space - 1;
}
tgt = state->buf;
*tgt = '\0';
if (size) {
state->space = space - size;
state->buf = tgt + size;
memcpy(tgt, buf, size);
}
return 0;
}
static __attribute__((unused, format(printf, 3, 0)))
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
struct __nolibc_sprintf_cb_state state = { .buf = buf, .space = size };
return __nolibc_printf(__nolibc_sprintf_cb, &state, fmt, args);
}
static __attribute__((unused, format(printf, 3, 4)))
int snprintf(char *buf, size_t size, const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = vsnprintf(buf, size, fmt, args);
va_end(args);
return ret;
}
static __attribute__((unused, format(printf, 2, 0)))
int vsprintf(char *buf, const char *fmt, va_list args)
{
return vsnprintf(buf, SIZE_MAX, fmt, args);
}
static __attribute__((unused, format(printf, 2, 3)))
int sprintf(char *buf, const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = vsprintf(buf, fmt, args);
va_end(args);
return ret;
}
static __attribute__((unused, format(printf, 2, 0)))
int __nolibc_vasprintf(char **strp, const char *fmt, va_list args1, va_list args2)
{
int len1, len2;
char *buf;
len1 = vsnprintf(NULL, 0, fmt, args1);
if (len1 < 0)
return -1;
buf = malloc(len1 + 1);
if (!buf)
return -1;
len2 = vsnprintf(buf, len1 + 1, fmt, args2);
if (len2 < 0) {
free(buf);
return -1;
}
*strp = buf;
return len1;
}
static __attribute__((unused, format(printf, 2, 0)))
int vasprintf(char **strp, const char *fmt, va_list args)
{
va_list args2;
int ret;
va_copy(args2, args);
ret = __nolibc_vasprintf(strp, fmt, args, args2);
va_end(args2);
return ret;
}
static __attribute__((unused, format(printf, 2, 3)))
int asprintf(char **strp, const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = vasprintf(strp, fmt, args);
va_end(args);
return ret;
}
static __attribute__((unused))
int vsscanf(const char *str, const char *format, va_list args)
{
uintmax_t uval;
intmax_t ival;
int base;
char *endptr;
int matches;
int lpref;
matches = 0;
while (1) {
if (*format == '%') {
lpref = 0;
format++;
if (*format == 'l') {
lpref = 1;
format++;
if (*format == 'l') {
lpref = 2;
format++;
}
}
if (*format == '%') {
if ('%' != *str)
goto done;
str++;
format++;
continue;
} else if (*format == 'd') {
ival = strtoll(str, &endptr, 10);
if (lpref == 0)
*va_arg(args, int *) = ival;
else if (lpref == 1)
*va_arg(args, long *) = ival;
else if (lpref == 2)
*va_arg(args, long long *) = ival;
} else if (*format == 'u' || *format == 'x' || *format == 'X') {
base = *format == 'u' ? 10 : 16;
uval = strtoull(str, &endptr, base);
if (lpref == 0)
*va_arg(args, unsigned int *) = uval;
else if (lpref == 1)
*va_arg(args, unsigned long *) = uval;
else if (lpref == 2)
*va_arg(args, unsigned long long *) = uval;
} else if (*format == 'p') {
*va_arg(args, void **) = (void *)strtoul(str, &endptr, 16);
} else {
SET_ERRNO(EILSEQ);
goto done;
}
format++;
str = endptr;
matches++;
} else if (*format == '\0') {
goto done;
} else if (isspace(*format)) {
while (isspace(*format))
format++;
while (isspace(*str))
str++;
} else if (*format == *str) {
format++;
str++;
} else {
if (!matches)
matches = EOF;
goto done;
}
}
done:
return matches;
}
static __attribute__((unused, format(scanf, 2, 3)))
int sscanf(const char *str, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = vsscanf(str, format, args);
va_end(args);
return ret;
}
static __attribute__((unused))
void perror(const char *msg)
{
#ifdef NOLIBC_IGNORE_ERRNO
fprintf(stderr, "%s%sunknown error\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "");
#else
fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
#endif
}
static __attribute__((unused))
int setvbuf(FILE *stream __attribute__((unused)),
char *buf __attribute__((unused)),
int mode,
size_t size __attribute__((unused)))
{
switch (mode) {
case _IOFBF:
case _IOLBF:
case _IONBF:
break;
default:
return EOF;
}
return 0;
}
static __attribute__((unused))
int strerror_r(int errnum, char *buf, size_t buflen)
{
if (buflen < 18)
return ERANGE;
__builtin_memcpy(buf, "errno=", 6);
i64toa_r(errnum, buf + 6);
return 0;
}
static __attribute__((unused))
const char *strerror(int errnum)
{
static char buf[18];
char *b = buf;
_NOLIBC_OPTIMIZER_HIDE_VAR(b);
strerror_r(errnum, b, sizeof(buf));
return b;
}
#endif