Squash commits for public release
This commit is contained in:
17
libs/libc/stdio/_internal.h
Normal file
17
libs/libc/stdio/_internal.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _LIBC_STDIO__INTERNAL_H
|
||||
#define _LIBC_STDIO__INTERNAL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
typedef int (*_lookupch_callback)(void* callback_params);
|
||||
typedef int (*_getch_callback)(void* callback_params);
|
||||
typedef int (*_putch_callback)(char ch, char* buf_base, size_t* written, void* callback_params);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif // _LIBC_STDIO__INTERNAL_H
|
||||
349
libs/libc/stdio/printf.c
Normal file
349
libs/libc/stdio/printf.c
Normal file
@@ -0,0 +1,349 @@
|
||||
#include "_internal.h"
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/_structs.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const char* HEX_alphabet = "0123456789ABCDEF";
|
||||
static const char* hex_alphabet = "0123456789abcdef";
|
||||
|
||||
static int _printf_hex32_impl(unsigned int value, const char* alph, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
|
||||
{
|
||||
int nxt = 0;
|
||||
char tmp_buf[16];
|
||||
|
||||
while (value > 0) {
|
||||
tmp_buf[nxt++] = alph[(value % 16)];
|
||||
value /= 16;
|
||||
}
|
||||
|
||||
callback('0', base_buf, written, callback_params);
|
||||
callback('x', base_buf, written, callback_params);
|
||||
if (nxt == 0) {
|
||||
callback('0', base_buf, written, callback_params);
|
||||
}
|
||||
|
||||
while (nxt) {
|
||||
callback(tmp_buf[--nxt], base_buf, written, callback_params);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _printf_hex64_impl(unsigned long value, const char* alph, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
|
||||
{
|
||||
int nxt = 0;
|
||||
char tmp_buf[32];
|
||||
|
||||
while (value > 0) {
|
||||
tmp_buf[nxt++] = alph[(value % 16)];
|
||||
value /= 16;
|
||||
}
|
||||
|
||||
callback('0', base_buf, written, callback_params);
|
||||
callback('x', base_buf, written, callback_params);
|
||||
if (nxt == 0) {
|
||||
callback('0', base_buf, written, callback_params);
|
||||
}
|
||||
|
||||
while (nxt) {
|
||||
callback(tmp_buf[--nxt], base_buf, written, callback_params);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _printf_hex32(unsigned int value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
|
||||
{
|
||||
return _printf_hex32_impl(value, hex_alphabet, base_buf, written, callback, callback_params);
|
||||
}
|
||||
|
||||
static int _printf_HEX32(unsigned int value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
|
||||
{
|
||||
return _printf_hex32_impl(value, HEX_alphabet, base_buf, written, callback, callback_params);
|
||||
}
|
||||
|
||||
static int _printf_hex64(unsigned long value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
|
||||
{
|
||||
return _printf_hex64_impl(value, hex_alphabet, base_buf, written, callback, callback_params);
|
||||
}
|
||||
|
||||
static int _printf_HEX64(unsigned long value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
|
||||
{
|
||||
return _printf_hex64_impl(value, HEX_alphabet, base_buf, written, callback, callback_params);
|
||||
}
|
||||
|
||||
static int _printf_u32(unsigned int value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
|
||||
{
|
||||
int nxt = 0;
|
||||
char tmp_buf[16];
|
||||
|
||||
while (value > 0) {
|
||||
tmp_buf[nxt++] = (value % 10) + '0';
|
||||
value /= 10;
|
||||
}
|
||||
|
||||
if (nxt == 0) {
|
||||
callback('0', base_buf, written, callback_params);
|
||||
}
|
||||
|
||||
while (nxt) {
|
||||
callback(tmp_buf[--nxt], base_buf, written, callback_params);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _printf_u64(unsigned long value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
|
||||
{
|
||||
int nxt = 0;
|
||||
char tmp_buf[32];
|
||||
|
||||
while (value > 0) {
|
||||
tmp_buf[nxt++] = (value % 10) + '0';
|
||||
value /= 10;
|
||||
}
|
||||
|
||||
if (nxt == 0) {
|
||||
callback('0', base_buf, written, callback_params);
|
||||
}
|
||||
|
||||
while (nxt) {
|
||||
callback(tmp_buf[--nxt], base_buf, written, callback_params);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _printf_i32(int value, char* buf, size_t* written, _putch_callback callback, void* callback_params)
|
||||
{
|
||||
if (value < 0) {
|
||||
callback('-', buf, written, callback_params);
|
||||
value = -value;
|
||||
}
|
||||
return _printf_u32(value, buf, written, callback, callback_params);
|
||||
}
|
||||
|
||||
static int _printf_i64(long value, char* buf, size_t* written, _putch_callback callback, void* callback_params)
|
||||
{
|
||||
if (value < 0) {
|
||||
callback('-', buf, written, callback_params);
|
||||
value = -value;
|
||||
}
|
||||
return _printf_u64(value, buf, written, callback, callback_params);
|
||||
}
|
||||
|
||||
static int _printf_string(const char* value, char* buf, size_t* written, _putch_callback callback, void* callback_params)
|
||||
{
|
||||
if (!value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t len = strlen(value);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
callback(value[i], buf, written, callback_params);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t _printf_internal(char* buf, const char* format, _putch_callback callback, void* callback_params, va_list arg)
|
||||
{
|
||||
const char* p = format;
|
||||
size_t written = 0;
|
||||
while (*p) {
|
||||
int l_arg = 0;
|
||||
int h_arg = 0;
|
||||
if (*p == '%' && *(p + 1)) {
|
||||
// Reading arguments
|
||||
parse_args:
|
||||
p++;
|
||||
switch (*p) {
|
||||
case 'l':
|
||||
l_arg++;
|
||||
if (*(p + 1)) {
|
||||
goto parse_args;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
h_arg++;
|
||||
if (*(p + 1)) {
|
||||
goto parse_args;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Reading conversion specifiers
|
||||
switch (*p) {
|
||||
case 'i':
|
||||
case 'd':
|
||||
if (l_arg) {
|
||||
long value = va_arg(arg, long);
|
||||
_printf_i64(value, buf, &written, callback, callback_params);
|
||||
} else {
|
||||
int value = va_arg(arg, int);
|
||||
_printf_i32(value, buf, &written, callback, callback_params);
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
if (l_arg) {
|
||||
uint64_t value = va_arg(arg, uint64_t);
|
||||
_printf_u64(value, buf, &written, callback, callback_params);
|
||||
} else {
|
||||
uint32_t value = va_arg(arg, uint32_t);
|
||||
_printf_u32(value, buf, &written, callback, callback_params);
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
if (l_arg) {
|
||||
uint64_t value = va_arg(arg, uint64_t);
|
||||
_printf_hex64(value, buf, &written, callback, callback_params);
|
||||
} else {
|
||||
uint32_t value = va_arg(arg, uint32_t);
|
||||
_printf_hex32(value, buf, &written, callback, callback_params);
|
||||
}
|
||||
break;
|
||||
case 'X':
|
||||
if (l_arg) {
|
||||
uint64_t value = va_arg(arg, uint64_t);
|
||||
_printf_HEX64(value, buf, &written, callback, callback_params);
|
||||
} else {
|
||||
uint32_t value = va_arg(arg, uint32_t);
|
||||
_printf_HEX32(value, buf, &written, callback, callback_params);
|
||||
}
|
||||
break;
|
||||
case 'c': {
|
||||
char value = (char)va_arg(arg, int);
|
||||
callback(value, buf, &written, callback_params);
|
||||
} break;
|
||||
case 's': {
|
||||
const char* value = va_arg(arg, const char*);
|
||||
_printf_string(value, buf, &written, callback, callback_params);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
callback(*p, buf, &written, callback_params);
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
static int putch_callback_sized_buf(char ch, char* buf_base, size_t* written, void* callback_params)
|
||||
{
|
||||
if (!callback_params) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!written) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t n = *(size_t*)callback_params;
|
||||
size_t vw = *written;
|
||||
if (vw >= n) {
|
||||
return -1;
|
||||
}
|
||||
buf_base[vw++] = ch;
|
||||
*written = vw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vsnprintf(char* s, size_t n, const char* format, va_list arg)
|
||||
{
|
||||
if (!s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t wr = _printf_internal(s, format, putch_callback_sized_buf, &n, arg);
|
||||
if (wr == n) {
|
||||
s[n - 1] = '\0';
|
||||
} else {
|
||||
s[wr] = '\0';
|
||||
}
|
||||
return (int)wr;
|
||||
}
|
||||
|
||||
int snprintf(char* s, size_t n, const char* format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
int res = vsnprintf(s, n, format, arg);
|
||||
va_end(arg);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int putch_callback_buf(char ch, char* buf_base, size_t* written, void* callback_params)
|
||||
{
|
||||
if (!written) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t vw = *written;
|
||||
buf_base[vw++] = ch;
|
||||
*written = vw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vsprintf(char* s, const char* format, va_list arg)
|
||||
{
|
||||
if (!s) {
|
||||
return 0;
|
||||
}
|
||||
ssize_t wr = _printf_internal(s, format, putch_callback_buf, NULL, arg);
|
||||
s[wr] = '\0';
|
||||
return (int)wr;
|
||||
}
|
||||
|
||||
int sprintf(char* s, const char* format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
int res = vsprintf(s, format, arg);
|
||||
va_end(arg);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int putch_callback_stream(char c, char* buf_base, size_t* written, void* callback_params)
|
||||
{
|
||||
FILE* stream = (FILE*)callback_params;
|
||||
if (!stream) {
|
||||
return -1;
|
||||
}
|
||||
return fputc(c, stream);
|
||||
}
|
||||
|
||||
static int vfprintf(FILE* stream, const char* format, va_list arg)
|
||||
{
|
||||
return _printf_internal(NULL, format, putch_callback_stream, stream, arg);
|
||||
}
|
||||
|
||||
int fprintf(FILE* stream, const char* format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
int res = vfprintf(stream, format, arg);
|
||||
va_end(arg);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int vprintf(const char* format, va_list arg)
|
||||
{
|
||||
return vfprintf(stdout, format, arg);
|
||||
}
|
||||
|
||||
int printf(const char* format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
int ret = vprintf(format, arg);
|
||||
va_end(arg);
|
||||
return ret;
|
||||
}
|
||||
202
libs/libc/stdio/scanf.c
Normal file
202
libs/libc/stdio/scanf.c
Normal file
@@ -0,0 +1,202 @@
|
||||
#include "_internal.h"
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/_structs.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int _scanf_u32(_lookupch_callback lookupch, _getch_callback getch, void* callback_params, uint32_t* val)
|
||||
{
|
||||
int read = 0;
|
||||
uint32_t res = 0;
|
||||
while (isdigit(lookupch(callback_params))) {
|
||||
res *= 10;
|
||||
char ch = getch(callback_params);
|
||||
res += (ch - '0');
|
||||
read++;
|
||||
}
|
||||
*val = res;
|
||||
return read;
|
||||
}
|
||||
|
||||
static int _scanf_i32(_lookupch_callback lookupch, _getch_callback getch, void* callback_params, int* val)
|
||||
{
|
||||
int read = 0;
|
||||
bool negative = 0;
|
||||
if (lookupch(callback_params) == '-') {
|
||||
negative = true;
|
||||
getch(callback_params);
|
||||
read++;
|
||||
}
|
||||
|
||||
uint32_t tmp_u32;
|
||||
int read_imm = _scanf_u32(lookupch, getch, callback_params, &tmp_u32);
|
||||
if (!read_imm) {
|
||||
return 0;
|
||||
}
|
||||
read += read_imm;
|
||||
|
||||
int tmp_i32 = (int)tmp_u32;
|
||||
if (negative) {
|
||||
tmp_i32 = -tmp_i32;
|
||||
}
|
||||
*val = tmp_i32;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
static int _scanf_internal(const char* format, _lookupch_callback lookupch, _getch_callback getch, void* callback_params, va_list arg)
|
||||
{
|
||||
const char* p = format;
|
||||
int passed = 0;
|
||||
|
||||
while (*p) {
|
||||
int l_arg = 0;
|
||||
int h_arg = 0;
|
||||
while (isspace(*p)) {
|
||||
p++;
|
||||
}
|
||||
if (*p == '%' && *(p + 1)) {
|
||||
// Reading arguments
|
||||
parse_args:
|
||||
p++;
|
||||
switch (*p) {
|
||||
case 'l':
|
||||
l_arg++;
|
||||
if (*(p + 1)) {
|
||||
goto parse_args;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
h_arg++;
|
||||
if (*(p + 1)) {
|
||||
goto parse_args;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
while (isspace(lookupch(callback_params))) {
|
||||
getch(callback_params);
|
||||
passed++;
|
||||
}
|
||||
|
||||
switch (*p) {
|
||||
case 'i':
|
||||
case 'd':
|
||||
if (!l_arg) {
|
||||
int* value = va_arg(arg, int*);
|
||||
int read = _scanf_i32(lookupch, getch, callback_params, value);
|
||||
passed += read;
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
if (!l_arg) {
|
||||
uint32_t* value = va_arg(arg, uint32_t*);
|
||||
int read = _scanf_u32(lookupch, getch, callback_params, value);
|
||||
passed += read;
|
||||
}
|
||||
break;
|
||||
case 's': {
|
||||
int rv = 0;
|
||||
char* value = va_arg(arg, char*);
|
||||
while (!isspace(lookupch(callback_params))) {
|
||||
char ch = getch(callback_params);
|
||||
value[rv++] = ch;
|
||||
passed++;
|
||||
}
|
||||
value[rv] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
} else {
|
||||
while (isspace(lookupch(callback_params))) {
|
||||
getch(callback_params);
|
||||
passed++;
|
||||
}
|
||||
if (lookupch(callback_params) != *p) {
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
getch(callback_params);
|
||||
passed++;
|
||||
}
|
||||
}
|
||||
return passed;
|
||||
}
|
||||
|
||||
struct buffer_params {
|
||||
const char* buf;
|
||||
};
|
||||
typedef struct buffer_params buffer_params_t;
|
||||
|
||||
static int lookup_callback_buffer(void* callback_params)
|
||||
{
|
||||
buffer_params_t* bp = (buffer_params_t*)callback_params;
|
||||
if (!bp->buf) {
|
||||
return EOF;
|
||||
}
|
||||
return *bp->buf;
|
||||
}
|
||||
|
||||
static int getch_callback_buffer(void* callback_params)
|
||||
{
|
||||
buffer_params_t* bp = (buffer_params_t*)callback_params;
|
||||
if (!bp->buf) {
|
||||
return EOF;
|
||||
}
|
||||
char res = *bp->buf;
|
||||
bp->buf++;
|
||||
return res;
|
||||
}
|
||||
|
||||
int vsscanf(const char* buf, const char* format, va_list arg)
|
||||
{
|
||||
buffer_params_t bp = { .buf = buf };
|
||||
return _scanf_internal(format, lookup_callback_buffer, getch_callback_buffer, (void*)&bp, arg);
|
||||
}
|
||||
|
||||
static int lookup_callback_stream(void* callback_params)
|
||||
{
|
||||
FILE* file = (FILE*)callback_params;
|
||||
int res = fgetc(file);
|
||||
ungetc(res, file);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int getch_callback_stream(void* callback_params)
|
||||
{
|
||||
FILE* file = (FILE*)callback_params;
|
||||
int res = fgetc(file);
|
||||
return res;
|
||||
}
|
||||
|
||||
int vfscanf(FILE* stream, const char* format, va_list arg)
|
||||
{
|
||||
return _scanf_internal(format, lookup_callback_stream, getch_callback_stream, (void*)stream, arg);
|
||||
}
|
||||
|
||||
int sscanf(const char* buf, const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
int count = vsscanf(buf, format, ap);
|
||||
va_end(ap);
|
||||
return count;
|
||||
}
|
||||
|
||||
int scanf(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
int count = vfscanf(stdin, format, ap);
|
||||
va_end(ap);
|
||||
return count;
|
||||
}
|
||||
668
libs/libc/stdio/stdio.c
Normal file
668
libs/libc/stdio/stdio.c
Normal file
@@ -0,0 +1,668 @@
|
||||
#include <bits/errno.h>
|
||||
#include <bits/fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define _IO_MAGIC 0xFBAD0000 /* Magic number */
|
||||
#define _IO_MAGIC_MASK 0xFFFF0000
|
||||
#define _IO_USER_BUF 0x0001 /* Don't deallocate buffer on close. */
|
||||
#define _IO_UNBUFFERED 0x0002
|
||||
#define _IO_NO_READS 0x0004 /* Reading not allowed. */
|
||||
#define _IO_NO_WRITES 0x0008 /* Writing not allowed. */
|
||||
#define _IO_EOF_SEEN 0x0010
|
||||
#define _IO_ERR_SEEN 0x0020
|
||||
#define _IO_DELETE_DONT_CLOSE 0x0040 /* Don't call close(_fileno) on close. */
|
||||
#define _IO_LINKED 0x0080 /* In the list of all open files. */
|
||||
#define _IO_IN_BACKUP 0x0100
|
||||
#define _IO_LINE_BUF 0x0200
|
||||
#define _IO_TIED_PUT_GET 0x0400 /* Put and get pointer move in unison. */
|
||||
#define _IO_CURRENTLY_PUTTING 0x0800
|
||||
#define _IO_IS_APPENDING 0x1000
|
||||
#define _IO_IS_FILEBUF 0x2000
|
||||
/* 0x4000 No longer used, reserved for compat. */
|
||||
#define _IO_USER_LOCK 0x8000
|
||||
|
||||
struct __fbuf {
|
||||
char* base;
|
||||
char* ptr; /* current pointer */
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct __rwbuf {
|
||||
__fbuf_t rbuf;
|
||||
__fbuf_t wbuf;
|
||||
char* base;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct __file {
|
||||
int _flags; /* flags, below; this FILE is free if 0 */
|
||||
int _file; /* fileno, if Unix descriptor, else -1 */
|
||||
size_t _r; /* read space left */
|
||||
size_t _w; /* write space left */
|
||||
__rwbuf_t _bf; /* rw buffer */
|
||||
int _ungotc; /* ungot char. If spot is empty, it equals to UNGOTC_EMPTY */
|
||||
};
|
||||
|
||||
static FILE _stdstreams[3];
|
||||
FILE* stdin = &_stdstreams[0];
|
||||
FILE* stdout = &_stdstreams[1];
|
||||
FILE* stderr = &_stdstreams[2];
|
||||
|
||||
/* Static functions */
|
||||
static inline int _can_read(FILE* file);
|
||||
static inline int _can_write(FILE* file);
|
||||
static inline int _can_use_buffer(FILE* file);
|
||||
static int _parse_mode(const char* mode, mode_t* flags);
|
||||
|
||||
/* Buffer */
|
||||
static inline int _free_buf(FILE* stream);
|
||||
static size_t _do_system_write(const void* ptr, size_t size, FILE* stream);
|
||||
static int _resize_buf(FILE* stream, size_t size);
|
||||
static ssize_t _flush_wbuf(FILE* stream);
|
||||
static void _split_rwbuf(FILE* stream);
|
||||
static int _resize_buf(FILE* stream, size_t size);
|
||||
|
||||
/* Stream */
|
||||
static int _init_stream(FILE* file);
|
||||
static int _init_file_with_fd(FILE* file, int fd);
|
||||
static int _open_file(FILE* file, const char* path, const char* mode);
|
||||
static FILE* _fopen_internal(const char* path, const char* mode);
|
||||
|
||||
/* Read/write */
|
||||
static size_t _do_system_read(char* ptr, size_t size, FILE* stream);
|
||||
static size_t _do_system_write(const void* ptr, size_t size, FILE* stream);
|
||||
static size_t _fread_internal(char* ptr, size_t size, FILE* stream);
|
||||
static size_t _fwrite_internal(const void* ptr, size_t size, FILE* stream);
|
||||
|
||||
/* Public functions */
|
||||
|
||||
FILE* fopen(const char* path, const char* mode)
|
||||
{
|
||||
if (!path || !mode)
|
||||
return NULL;
|
||||
|
||||
return _fopen_internal(path, mode);
|
||||
}
|
||||
|
||||
int fclose(FILE* stream)
|
||||
{
|
||||
int res;
|
||||
|
||||
/* Flush & close the stream, and then free any allocated memory. */
|
||||
fflush(stream);
|
||||
res = close(stream->_file);
|
||||
|
||||
if (res == -EBADF || res == -EFAULT)
|
||||
return EOF;
|
||||
|
||||
_free_buf(stream);
|
||||
free(stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t fread(void* ptr, size_t size, size_t count, FILE* stream)
|
||||
{
|
||||
if (!ptr || !stream)
|
||||
return 0;
|
||||
|
||||
return _fread_internal(ptr, size * count, stream);
|
||||
}
|
||||
|
||||
size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream)
|
||||
{
|
||||
if (!ptr) {
|
||||
set_errno(EINVAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!stream) {
|
||||
set_errno(EINVAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _fwrite_internal(ptr, size * count, stream);
|
||||
}
|
||||
|
||||
static void _drop_rbuf(FILE* stream)
|
||||
{
|
||||
stream->_r = 0;
|
||||
stream->_ungotc = UNGOTC_EMPTY;
|
||||
}
|
||||
|
||||
int fseek(FILE* stream, uint32_t offset, int origin)
|
||||
{
|
||||
if (!stream) {
|
||||
set_errno(EINVAL);
|
||||
return 0;
|
||||
}
|
||||
fflush(stream);
|
||||
_drop_rbuf(stream);
|
||||
|
||||
return lseek(stream->_file, offset, origin);
|
||||
}
|
||||
|
||||
int fputc(int c, FILE* stream)
|
||||
{
|
||||
int res = fwrite(&c, 1, 1, stream);
|
||||
if (!res)
|
||||
return -errno;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int putc(int c, FILE* stream)
|
||||
{
|
||||
return fputc(c, stream);
|
||||
}
|
||||
|
||||
int putchar(int c)
|
||||
{
|
||||
return fputc(c, stdout);
|
||||
}
|
||||
|
||||
long ftell(FILE* stream)
|
||||
{
|
||||
if (!stream) {
|
||||
set_errno(EINVAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fflush(stream);
|
||||
_drop_rbuf(stream);
|
||||
|
||||
return lseek(stream->_file, 0, SEEK_CUR);
|
||||
}
|
||||
|
||||
int fputs(const char* s, FILE* stream)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
|
||||
int res = fwrite(s, len, 1, stream);
|
||||
|
||||
if (!res)
|
||||
return -errno;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int puts(const char* s)
|
||||
{
|
||||
return fputs(s, stdout);
|
||||
}
|
||||
|
||||
int fgetc(FILE* stream)
|
||||
{
|
||||
char c;
|
||||
|
||||
if (fread(&c, 1, 1, stream) != 1)
|
||||
return EOF;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int getc(FILE* stream)
|
||||
{
|
||||
return fgetc(stream);
|
||||
}
|
||||
|
||||
int getchar()
|
||||
{
|
||||
return fgetc(stdin);
|
||||
}
|
||||
|
||||
int ungetc(int c, FILE* stream)
|
||||
{
|
||||
if (c == EOF)
|
||||
return EOF;
|
||||
|
||||
if (!stream) {
|
||||
set_errno(EINVAL);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (stream->_ungotc != UNGOTC_EMPTY) {
|
||||
set_errno(EBUSY);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
stream->_flags &= ~_IO_EOF_SEEN;
|
||||
stream->_ungotc = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
char* fgets(char* s, int size, FILE* stream)
|
||||
{
|
||||
unsigned int rd = 0;
|
||||
char c;
|
||||
|
||||
if (!stream) {
|
||||
set_errno(EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We need to flush the stdout and stderr streams before reading. */
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
while (c != '\n' && rd < size) {
|
||||
if ((c = fgetc(stream)) < 0) {
|
||||
if (rd == 0) {
|
||||
return NULL;
|
||||
}
|
||||
// EOF should be set
|
||||
s[rd] = '\0';
|
||||
break;
|
||||
}
|
||||
s[rd++] = c;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
char* gets(char* str)
|
||||
{
|
||||
// Currently the max size of gets is 4096.
|
||||
return fgets(str, 4096, stdout);
|
||||
}
|
||||
|
||||
int setvbuf(FILE* stream, char* buf, int mode, size_t size)
|
||||
{
|
||||
if (!stream) {
|
||||
set_errno(EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mode != _IONBF && mode != _IOLBF && mode != _IOFBF) {
|
||||
set_errno(EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Clear the buffer type flags and reset it. */
|
||||
|
||||
stream->_flags &= ~(int)(_IO_UNBUFFERED | _IO_LINE_BUF);
|
||||
if (mode & _IOLBF)
|
||||
stream->_flags |= _IO_LINE_BUF;
|
||||
|
||||
if (mode & _IONBF)
|
||||
stream->_flags |= _IO_UNBUFFERED;
|
||||
|
||||
_flush_wbuf(stream);
|
||||
|
||||
if (!_can_use_buffer(stream))
|
||||
return _free_buf(stream);
|
||||
|
||||
if (!buf)
|
||||
return _resize_buf(stream, size);
|
||||
|
||||
stream->_bf.base = buf;
|
||||
stream->_bf.size = size;
|
||||
_split_rwbuf(stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setbuf(FILE* stream, char* buf)
|
||||
{
|
||||
setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
|
||||
}
|
||||
|
||||
void setlinebuf(FILE* stream)
|
||||
{
|
||||
setvbuf(stream, NULL, _IOLBF, 0);
|
||||
}
|
||||
|
||||
int fflush(FILE* stream)
|
||||
{
|
||||
if (!stream)
|
||||
return -EBADF;
|
||||
|
||||
return _flush_wbuf(stream);
|
||||
}
|
||||
|
||||
int feof(FILE* stream)
|
||||
{
|
||||
return stream->_flags & _IO_EOF_SEEN;
|
||||
}
|
||||
|
||||
int __stream_info(FILE* stream)
|
||||
{
|
||||
static const char* names[] = { "(STDIN) ", "(STDOUT) ", "(STDERR) " };
|
||||
char rwinfo[4] = "-/-";
|
||||
__fbuf_t *rbuf, *wbuf;
|
||||
const char* name;
|
||||
|
||||
if (!stream)
|
||||
return 1;
|
||||
|
||||
if (stream->_file >= 0 && stream->_file <= 2)
|
||||
name = names[stream->_file];
|
||||
|
||||
if (_can_read(stream)) {
|
||||
rwinfo[0] = 'r';
|
||||
rbuf = &stream->_bf.rbuf;
|
||||
}
|
||||
|
||||
if (_can_write(stream)) {
|
||||
rwinfo[2] = 'w';
|
||||
wbuf = &stream->_bf.wbuf;
|
||||
}
|
||||
|
||||
printf("__stream_info():\n");
|
||||
printf(" fd=%d %sflags=%s\n", stream->_file, name, rwinfo);
|
||||
printf(" ungotc=%s val=%x\n", stream->_ungotc == UNGOTC_EMPTY ? "False" : "True", stream->_ungotc);
|
||||
|
||||
if (_can_read(stream)) {
|
||||
printf(" read space left=%u\n", stream->_r);
|
||||
printf(" rbuf.base=%x rbuf.size=%u rbuf.ptr=%x\n", (size_t)rbuf->base,
|
||||
rbuf->size, (size_t)rbuf->ptr);
|
||||
}
|
||||
|
||||
if (_can_write(stream)) {
|
||||
printf(" write space left=%u\n", stream->_w);
|
||||
printf(" wbuf.base=%x wbuf.size=%u wbuf.ptr=%x\n", (size_t)wbuf->base,
|
||||
wbuf->size, (size_t)wbuf->ptr);
|
||||
}
|
||||
|
||||
printf(" rwbuf.base=%x rwbuf.size=%u\n", (size_t)stream->_bf.base,
|
||||
stream->_bf.size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _stdio_init()
|
||||
{
|
||||
_init_file_with_fd(stdin, STDIN);
|
||||
_init_file_with_fd(stdout, STDOUT);
|
||||
_init_file_with_fd(stderr, STDERR);
|
||||
setbuf(stderr, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _stdio_deinit()
|
||||
{
|
||||
// FIXME
|
||||
_flush_wbuf(stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Static functions */
|
||||
|
||||
static inline int _can_read(FILE* file)
|
||||
{
|
||||
return (file->_flags & _IO_NO_READS) == 0;
|
||||
}
|
||||
|
||||
static inline int _can_write(FILE* file)
|
||||
{
|
||||
return (file->_flags & _IO_NO_WRITES) == 0;
|
||||
}
|
||||
|
||||
static inline int _can_use_buffer(FILE* file)
|
||||
{
|
||||
return (file->_flags & _IO_UNBUFFERED) == 0;
|
||||
}
|
||||
|
||||
/* Because this checks the first and second character only, the possible
|
||||
combinations are: r, w, a, r+ and w+. */
|
||||
static int _parse_mode(const char* mode, mode_t* flags)
|
||||
{
|
||||
int has_plus, len;
|
||||
|
||||
if (!(len = strlen(mode)))
|
||||
return 0;
|
||||
|
||||
*flags = 0;
|
||||
if (len > 1 && mode[1] == '+')
|
||||
has_plus = 1;
|
||||
|
||||
switch (mode[0]) {
|
||||
case 'r':
|
||||
*flags = has_plus ? O_RDONLY : O_RDWR;
|
||||
return 0;
|
||||
|
||||
case 'w':
|
||||
*flags = has_plus ? (O_WRONLY | O_CREAT) : (O_RDWR | O_CREAT);
|
||||
return 0;
|
||||
|
||||
case 'a':
|
||||
*flags = O_APPEND | O_CREAT;
|
||||
return 0;
|
||||
|
||||
/* TODO: Add binary mode when the rest will support such option. */
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Buffer */
|
||||
|
||||
static inline int _free_buf(FILE* stream)
|
||||
{
|
||||
/* Don't free the buffer if the user provided one with setvbuf. */
|
||||
if (stream->_flags & _IO_USER_BUF)
|
||||
return 0;
|
||||
|
||||
if (stream->_bf.base)
|
||||
free(stream->_bf.base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _split_rwbuf(FILE* stream)
|
||||
{
|
||||
size_t rsize, wsize;
|
||||
|
||||
rsize = ((stream->_bf.size + 1) / 2) & (size_t)~0x03;
|
||||
wsize = (stream->_bf.size - rsize) & (size_t)~0x03;
|
||||
|
||||
/* TODO: Base on stream flags. */
|
||||
stream->_bf.rbuf.base = stream->_bf.base;
|
||||
stream->_bf.rbuf.ptr = stream->_bf.rbuf.base;
|
||||
stream->_bf.rbuf.size = rsize;
|
||||
|
||||
stream->_bf.wbuf.base = stream->_bf.base + stream->_bf.rbuf.size;
|
||||
stream->_bf.wbuf.ptr = stream->_bf.wbuf.base;
|
||||
stream->_bf.wbuf.size = wsize;
|
||||
|
||||
stream->_r = 0;
|
||||
stream->_w = stream->_bf.wbuf.size;
|
||||
}
|
||||
|
||||
static int _resize_buf(FILE* stream, size_t size)
|
||||
{
|
||||
_free_buf(stream);
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
stream->_bf.base = malloc(size);
|
||||
|
||||
if (!stream->_bf.base) {
|
||||
stream->_r = 0;
|
||||
stream->_w = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream->_bf.size = size;
|
||||
_split_rwbuf(stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t _flush_wbuf(FILE* stream)
|
||||
{
|
||||
size_t write_size, written;
|
||||
|
||||
write_size = stream->_bf.wbuf.size - stream->_w;
|
||||
written = _do_system_write(stream->_bf.wbuf.base, write_size, stream);
|
||||
|
||||
if (written != write_size)
|
||||
return -EFAULT;
|
||||
|
||||
stream->_w = stream->_bf.wbuf.size;
|
||||
stream->_bf.wbuf.ptr = stream->_bf.wbuf.base;
|
||||
return (ssize_t)write;
|
||||
}
|
||||
|
||||
/* Stream */
|
||||
|
||||
static int _init_stream(FILE* file)
|
||||
{
|
||||
file->_file = -1;
|
||||
file->_flags = _IO_MAGIC;
|
||||
file->_r = 0;
|
||||
file->_w = 0;
|
||||
file->_bf.base = NULL;
|
||||
file->_bf.size = 0;
|
||||
file->_ungotc = UNGOTC_EMPTY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _init_file_with_fd(FILE* file, int fd)
|
||||
{
|
||||
_init_stream(file);
|
||||
_resize_buf(file, BUFSIZ);
|
||||
file->_file = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _open_file(FILE* file, const char* path, const char* mode)
|
||||
{
|
||||
mode_t flags = 0;
|
||||
int err = _parse_mode(mode, &flags);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
int fd = open(path, flags);
|
||||
if (fd < 0)
|
||||
return errno;
|
||||
|
||||
file->_file = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static FILE* _fopen_internal(const char* path, const char* mode)
|
||||
{
|
||||
FILE* file = malloc(sizeof(FILE));
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
_init_stream(file);
|
||||
_resize_buf(file, BUFSIZ);
|
||||
_open_file(file, path, mode);
|
||||
return file;
|
||||
}
|
||||
|
||||
/* Read */
|
||||
|
||||
static size_t _do_system_read(char* ptr, size_t size, FILE* stream)
|
||||
{
|
||||
ssize_t read_size = read(stream->_file, ptr, size);
|
||||
return read_size < 0 ? 0 : (size_t)read_size;
|
||||
}
|
||||
|
||||
static size_t _do_system_write(const void* ptr, size_t size, FILE* stream)
|
||||
{
|
||||
ssize_t write_size = write(stream->_file, ptr, size);
|
||||
|
||||
if (write_size < 0)
|
||||
return 0;
|
||||
|
||||
return (size_t)write_size;
|
||||
}
|
||||
|
||||
static size_t _fread_internal(char* ptr, size_t size, FILE* stream)
|
||||
{
|
||||
size_t total_size, read_from_buf;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
if (!_can_use_buffer(stream)) {
|
||||
total_size = _do_system_read(ptr, size, stream);
|
||||
if (total_size < size) {
|
||||
stream->_flags |= _IO_EOF_SEEN;
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
total_size = 0;
|
||||
|
||||
/* If the ungot char buffer is not empty, push it onto the buffer first. */
|
||||
if (stream->_ungotc != UNGOTC_EMPTY) {
|
||||
ptr[0] = (char)stream->_ungotc;
|
||||
ptr++;
|
||||
size--;
|
||||
total_size++;
|
||||
stream->_ungotc = UNGOTC_EMPTY;
|
||||
}
|
||||
|
||||
/* First read any bytes still sitting in the read buffer. */
|
||||
if (stream->_r) {
|
||||
read_from_buf = min(stream->_r, size);
|
||||
memcpy(ptr, stream->_bf.rbuf.ptr, read_from_buf);
|
||||
ptr += read_from_buf;
|
||||
size -= read_from_buf;
|
||||
stream->_bf.rbuf.ptr += read_from_buf;
|
||||
stream->_r -= read_from_buf;
|
||||
total_size += read_from_buf;
|
||||
}
|
||||
|
||||
/* Read the remaining bytes that were not stored in the read buffer. */
|
||||
while (size > 0) {
|
||||
stream->_bf.rbuf.ptr = stream->_bf.rbuf.base;
|
||||
stream->_r = _do_system_read(
|
||||
stream->_bf.rbuf.ptr, stream->_bf.rbuf.size, stream);
|
||||
|
||||
if (!stream->_r) {
|
||||
if (size) {
|
||||
stream->_flags |= _IO_EOF_SEEN;
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
read_from_buf = min(stream->_r, size);
|
||||
memcpy(ptr, stream->_bf.rbuf.ptr, read_from_buf);
|
||||
ptr += read_from_buf;
|
||||
size -= read_from_buf;
|
||||
stream->_bf.rbuf.ptr += read_from_buf;
|
||||
stream->_r -= read_from_buf;
|
||||
total_size += read_from_buf;
|
||||
}
|
||||
|
||||
return total_size;
|
||||
}
|
||||
|
||||
static size_t _fwrite_internal(const void* ptr, size_t size, FILE* stream)
|
||||
{
|
||||
size_t total_size;
|
||||
|
||||
if (!_can_use_buffer(stream))
|
||||
return _do_system_write(ptr, size, stream);
|
||||
|
||||
total_size = 0;
|
||||
while (size > 0) {
|
||||
size_t write_size = min(stream->_w, size);
|
||||
memcpy(stream->_bf.wbuf.ptr, ptr, write_size);
|
||||
ptr += write_size;
|
||||
size -= write_size;
|
||||
stream->_bf.wbuf.ptr += write_size;
|
||||
stream->_w -= write_size;
|
||||
total_size += write_size;
|
||||
|
||||
if (!stream->_w)
|
||||
_flush_wbuf(stream);
|
||||
}
|
||||
|
||||
return total_size;
|
||||
}
|
||||
Reference in New Issue
Block a user