diff --git a/libc/include/stdio.h b/libc/include/stdio.h index 5a4ad7e..4a296f1 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -17,12 +17,7 @@ #define SEEK_END 1 #define SEEK_SET 2 -// Standard file pointers -#define STDIN_FILENO 0 -#define STDOUT_FILENO 1 -#define STDERR_FILENO 2 - -/* The ultimate FILE struct */ +/* The FILE struct */ typedef struct _iobuf { // 24B unsigned char* _ptr; // File buffer base unsigned int _size; // Size of buffer @@ -33,6 +28,7 @@ typedef struct _iobuf { // 24B char __reserved[6]; } FILE; +// Standard file pointers extern FILE* stdin; extern FILE* stdout; extern FILE* stderr; @@ -59,20 +55,23 @@ char* fgets(char* str, int n, FILE* stream); int fputs(const char* str, FILE* stream); int puts(const char* str); -/* Error handling */ +/* TODO: Error handling void perror(const char* s); int feof(FILE* stream); int ferror(FILE* stream); void clearerr(FILE* stream); +*/ /* Formatted I/O */ int printf(const char* fmt, ...); int vprintf(const char* fmt, va_list va); int fprintf(FILE* stream, const char* fmt, ...); int vfprintf(FILE* stream, const char* fmt, va_list va); -// INFO: (v)sprinf() Has been depricated and removed as it is unsafe (potencial buffer overflow). Please use (v)snprintf() +// NOTE: (v)sprinf() Has been depricated and removed as it is unsafe (potencial buffer overflow). Please use (v)snprintf() int snprintf(char* str, size_t size, const char* fmt, ...); int vsnprintf(char* str, size_t size, const char* fmt, va_list va); +int vpprintf(void (*outfn)(char c, void* arg), void* arg, const char* fmt, ...) +int vpprintf(void (*outfn)(char c, void* arg), void* arg, const char* fmt, va_list va) int scanf(const char* fmt, ...); int vscanf(const char* fmt, va_list va); diff --git a/libc/stdio/char.c b/libc/stdio/char.c new file mode 100644 index 0000000..0cff5ff --- /dev/null +++ b/libc/stdio/char.c @@ -0,0 +1,29 @@ +#include +#include + +int getchar(void) { return getc(stdin); } +int putchar(int c) { return putc(stdout); } + +int getc(FILE* stream) { + if (!stream) { return EOF; } + + unsigned char ch; + ssize_t res = read(stream->_file, &ch, 1); + if (res <= 0) { return EOF; } + + return (int)ch; +} + +int putc(int c, FILE* stream) { + if (!stream) { return EOF; } + + unsigned char ch = (unsigned char)c; + ssize_t res = write(stream->_file, &c, 1); + if (res <= 0) { return EOF; } + + return (int)ch; +} + +// TODO: +int ungetc(int c, FILE* stream) { return EOF; } + diff --git a/libc/stdio/file.c b/libc/stdio/file.c new file mode 100644 index 0000000..711a5eb --- /dev/null +++ b/libc/stdio/file.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +FILE* fopen(const char* path, const char* mode) { + FILE* fp = (FILE*)malloc(sizeof(FILE)); + if (!fp) { return NULL; } + + int flags = 0; + if (strcmp(mode, "r") == 0) { flags = O_RDONLY; } + else if (strcmp(mode, "w") == 0) { flags = O_WRONLY | O_CREAT | O_TRUNC; } + else if (strcmp(mode, "a") == 0) { flags = O_WRONLY | O_CREAT | O_APPEND; } + else if (strcmp(mode, "r+") == 0) { flags = O_RDWR; } + else if (strcmp(mode, "w+") == 0) { flags = O_RDWR | O_CREAT | O_TRUNC; } + else if (strcmp(mode, "a+") == 0) { flags = O_RDWR | O_CREAT | O_APPEND; } + else { // Unsuppored mode + free(file); + return NULL; + } + + int fd = open(path, flags); + if (fd == -1) { + free(FILE); + return NULL; + } + + fp->_file = fd; + fp->_ptr = NULL; + fp->_size = 0; + fp->_offset = 0; + fp->_flag = flags; + + return fp; +} + +int fclose(FILE* stream) { + if (!stream) { return -1; } + int ret = close(stream->_file); + free(stream); + return ret; +} + +size_t fread(void* ptr, size_t size, size_t count, FILE* stream) { + if (!stream || !ptr || size == 0 || count == 0) { return 0; } + + size_t total = size * count; + ssize_t read = read(stream->_file, ptr, total); + + if (read < 0) return 0; + + stream->offset += read; + return read / size; +} + +size_t fwrite(void* ptr, size_t size, size_t count, FILE* stream) { + if (!stream || !ptr || size == 0 || count == 0) { return 0; } + + size_t total = size * count; + ssize_t written = write(stream->_file, ptr, total); + + if (read < 0) return 0; + + stream->offset += written; + return written / size; +} + +// TODO +int fseek(FILE* stream, long offset, int whence); +long ftell(FILE* stream); +void rewind(FILE* stream); +int fflush(FILE* stream); + diff --git a/libc/stdio/line.c b/libc/stdio/line.c new file mode 100644 index 0000000..caf00a6 --- /dev/null +++ b/libc/stdio/line.c @@ -0,0 +1,32 @@ +#include +#include + +char* fgets(char* str, int n, FILE* stream) { + if (!str || !stream || n <= 1) { return NULL; } + + char* dest = str; + int ch; + + while (--n > 0 && (ch = getc(stream)) != EOF) { + *dest++ = ch; + if (ch == '\n') break; // Stop at newline + } + + *dest = '\0'; // Null terminate + + return (dest == str) ? NULL : str; +} + +int fputs(const char* str, FILE* stream) { + if (!str || !stream) { return EOF; } + + while (*str) { + if (putc(*str++, stream) == EOF) { return EOF; } + } + + return 0; +} + +inline char* gets(char* str, int n) { return fgets(str, n, stdin); } +inline int puts(const char* str) { return fputs(str, stdout); } + diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c new file mode 100644 index 0000000..aa72a35 --- /dev/null +++ b/libc/stdio/printf.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include + +/* Internal definitions */ +typedef struct { + int buflen; + int offset; + char* buf; +} printfEnv_t; + +/* Helpers */ +static void _dynamicPutc(char c, printfEnv_t* env) { + if (!env->buf) { return; } + + if (env->offset < env->buflen) { + env->buf[env->offset++] = c; + } else { + env->buflen *= 2; + env->buf = realloc(env->buf, env->buflen); + + // If there's not enough memory, we chatch the NULL pointer in the fwrite() call + if (!env->buf) { return; } + + env->buf[env->offset++] = c; + } +} + +static void _staticPutc(char c, printfEnv_t* env) { + if (!env->buf) { return; } + + if (env->offset < env->buflen) { + env->buf[env->offset] = c; + env->offset++; + } +} + +/* Impementation */ +int vpprintf(void (*outfn)(char c, void* arg), void* arg, const char* fmt, va_list va) { /* TODO */ } + +int vsnprintf(char* str, size_t size, const char* fmt, va_list va) { + printfEnv_t env; + env.buflen = size; + env.offset = 0; + env.buf = str; + + // Format the string + int ret = vpprintf(_staticPutc, &env, fmt, va); + + return ret; +} + +int vfprintf(FILE* stream, const char* fmt, va_list va) { + printfEnv_t env; + env.buflen = 128; + env.offset = 0; + env.buf = malloc(env.buflen); + if (!env.buf) { return -1; } + + // Format the string + int ret = vpprintf(_dynamicPutc, &env, fmt, va); + + // Write to stream + if (fwrite(env.buf, 1, ret, stream) != 1) { return -1; } + + free(env.buf); + return ret; +} + +int vprintf(const char* fmt, va_list va) { return vfprintf(stdout, fmt, va); } + +// Print using an custom putc function +int pprintf(void (*outfn)(char c), const char* fmt, ...) { + va_list va; + va_start(va, fmt); + int ret = vpprintf(outfn, fmt, va); + va_end(va); + return ret; +} + +// Print to a buffer +int snprintf(char* str, size_t size, const char* fmt, ...) { + va_list va; + va_start(va, fmt); + int ret = vsnprintf(str, size, fmt, va); + va_end(va); + return ret; +} + +// Print to a stream +int fprintf(FILE* stream, const char* fmt, ...) { + va_list va; + va_start(va, fmt); + int ret = vfprintf(stream, fmt, va); + va_end(va); + return ret; +} + +// Print to stdout +int printf(const char* fmt, ...) { + va_list va; + va_start(va, fmt); + int ret = vfprintf(stdout, fmt, va); + va_end(va); + return ret; +} + diff --git a/libc/stdio/scanf.c b/libc/stdio/scanf.c new file mode 100644 index 0000000..f7d8d8f --- /dev/null +++ b/libc/stdio/scanf.c @@ -0,0 +1,10 @@ +#include + +// TODO +int scanf(const char* fmt, ...); +int vscanf(const char* fmt, va_list va); +int fscanf(FILE* stream, const char* fmt, ...); +int vfscanf(FILE* stream, const char* fmt, va_list va); +int sscanf(const char* str, const char* fmt, ...); +int vsscanf(const char* str, const char* fmt, va_list va); +