From cfb0ead2d910bb500ead17799fe265b646bde521 Mon Sep 17 00:00:00 2001 From: apio Date: Fri, 21 Jul 2023 22:56:03 +0200 Subject: [PATCH 1/7] libc: Flush all open streams on exit This does nothing for now, but prepares for buffering. --- libc/src/atexit.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libc/src/atexit.cpp b/libc/src/atexit.cpp index 94f86e08..74628b44 100644 --- a/libc/src/atexit.cpp +++ b/libc/src/atexit.cpp @@ -1,3 +1,4 @@ +#include #include typedef void (*atexit_func_t)(void); @@ -23,6 +24,8 @@ extern "C" { while (atexit_registered_funcs--) { atexit_funcs[atexit_registered_funcs](); } + fflush(NULL); + _Exit(status); } } -- 2.34.1 From a3ed950be8da30d4d7ad9ce85242ad5495a3a8aa Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 22 Jul 2023 00:02:12 +0200 Subject: [PATCH 2/7] libc: Basic write buffers --- apps/CMakeLists.txt | 1 + apps/buffer-test.cpp | 27 ++++++++ apps/su.cpp | 1 + libc/include/stdio.h | 18 +++++- libc/src/init.cpp | 11 +++- libc/src/stdio.cpp | 151 +++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 199 insertions(+), 10 deletions(-) create mode 100644 apps/buffer-test.cpp diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index caa2a70b..20d24f19 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -41,3 +41,4 @@ luna_app(pivot_root.cpp pivot_root) luna_app(cp.cpp cp) luna_app(kill.cpp kill) luna_app(gol.cpp gol) +luna_app(buffer-test.cpp buffer-test) diff --git a/apps/buffer-test.cpp b/apps/buffer-test.cpp new file mode 100644 index 00000000..ed64bfa3 --- /dev/null +++ b/apps/buffer-test.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +int main() +{ + fprintf(stderr, "Writing incomplete line to stdout (_IOLBF=%d)...\n", stdout->_mode); + fputs("hi!", stdout); + sleep(3); + putchar('\n'); + fprintf(stderr, "Incomplete line should have been written.\n"); + + FILE* f = fopen("/dev/console", "w+"); + assert(f); + assert(setvbuf(f, NULL, _IOFBF, 0) == 0); + + fprintf(stderr, "Writing long text to file (_IOFBF=%d)...\n", f->_mode); + + fputs("Hello world!\nHow are you doing!\nThis is a test for many lines of buffering.\n", f); + + sleep(3); + fflush(f); + + fprintf(stderr, "Long text should have been written.\n"); + + fclose(f); +} diff --git a/apps/su.cpp b/apps/su.cpp index f1602672..cefcecc7 100644 --- a/apps/su.cpp +++ b/apps/su.cpp @@ -31,6 +31,7 @@ char* getpass() tcsetpgrp(STDIN_FILENO, getpgid(0)); fputs("Password: ", stdout); + fflush(stdout); if (tcgetattr(STDIN_FILENO, &orig) < 0) { diff --git a/libc/include/stdio.h b/libc/include/stdio.h index bab534d0..0e706f52 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -10,11 +10,20 @@ #define __need_NULL #include +#define FOPEN_MAX 64 // Make sure this value matches FD_MAX in the kernel source. + typedef struct { int _fd; int _err; int _eof; + size_t _bufcap; + size_t _bufsize; + size_t _bufindex; + char* _buffer; + int _flags; + int _pending; + int _mode; } FILE; #define EOF -1 @@ -26,11 +35,15 @@ extern FILE* stderr; #define stdout stdout #define stderr stderr -#define BUFSIZ 1024 +#define BUFSIZ 4096 #define FILENAME_MAX \ 1024 // As Luna does not impose a limit on this, this is the recommended size for character arrays holding a file // name. +#define _IONBF 0 +#define _IOLBF 1 +#define _IOFBF 2 + #ifdef __cplusplus extern "C" { @@ -172,6 +185,9 @@ extern "C" /* Create a unique temporary file. */ FILE* tmpfile(void); + /* Change a file's buffering mode and internal buffer. */ + int setvbuf(FILE* stream, char* buf, int mode, size_t size); + #ifdef __cplusplus } #endif diff --git a/libc/src/init.cpp b/libc/src/init.cpp index a035db2a..e8fe79b9 100644 --- a/libc/src/init.cpp +++ b/libc/src/init.cpp @@ -4,6 +4,9 @@ extern char** environ; +extern "C" FILE* _fdopen_impl(int, const char*, int); +extern "C" void _init_stdio(); + extern "C" { void libc_init(int argc, char** argv, int envc, char** envp) @@ -11,8 +14,10 @@ extern "C" ignore(argc, argv, envc); environ = envp; - stdin = fdopen(STDIN_FILENO, "r"); - stdout = fdopen(STDOUT_FILENO, "w"); - stderr = fdopen(STDERR_FILENO, "w"); + _init_stdio(); + + stdin = _fdopen_impl(STDIN_FILENO, "r", _IOLBF); + stdout = _fdopen_impl(STDOUT_FILENO, "w", _IOLBF); + stderr = _fdopen_impl(STDERR_FILENO, "w", _IONBF); } } diff --git a/libc/src/stdio.cpp b/libc/src/stdio.cpp index 0ce0b5fc..3a01c05b 100644 --- a/libc/src/stdio.cpp +++ b/libc/src/stdio.cpp @@ -11,6 +11,10 @@ FILE* stdin = nullptr; FILE* stderr = nullptr; FILE* stdout = nullptr; +FILE* s_open_files[FOPEN_MAX]; + +#define FFLAG_FREE_AFTER_BUFFER_USE (1 << 0) + static const char* read_tmpdir() { const char* tmpdir = getenv("TMPDIR"); @@ -51,11 +55,71 @@ static int fdopen_check_compatible_mode(int fd, int new_flags) return 0; } +static int flush_write_buffer(FILE* stream) +{ + if (stream->_mode == _IONBF) return 0; + + ssize_t result = write(stream->_fd, stream->_buffer, stream->_bufsize); + + stream->_bufindex = 0; + stream->_bufsize = 0; + + return result < 0 ? EOF : 0; +} + +static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size) +{ + ssize_t total_written = 0; + + while (size > 0) + { + ssize_t nwritten; + if (stream->_mode != _IONBF) + { + if ((stream->_bufsize + size) > stream->_bufcap) + { + if (flush_write_buffer(stream) < 0) return -1; + } + + ssize_t size_remaining = stream->_bufcap - stream->_bufsize; + nwritten = size > size_remaining ? size_remaining : size; + memcpy(stream->_buffer + stream->_bufsize, data, nwritten); + + stream->_bufsize += nwritten; + + if (stream->_mode == _IOLBF && memchr(data, '\n', nwritten)) + { + if (flush_write_buffer(stream) < 0) return -1; + } + } + else + nwritten = write(stream->_fd, data, size > BUFSIZ ? BUFSIZ : size); + if (nwritten < 0) return nwritten; + size -= nwritten; + data += nwritten; + total_written += nwritten; + } + + return total_written; +} + extern "C" { - int fflush(FILE*) + void _init_stdio() { - // FIXME: Files are not buffered right now. + memset(&s_open_files, 0, sizeof(s_open_files)); + } + + int fflush(FILE* stream) + { + if (stream && stream->_mode != _IONBF) flush_write_buffer(stream); + else if (!stream) + { + for (int i = 0; i < FOPEN_MAX; i++) + { + if (s_open_files[i]) fflush(s_open_files[i]); + } + } return 0; } @@ -78,10 +142,19 @@ extern "C" f->_fd = fd; clearerr(f); + f->_pending = 0; + f->_flags = flags; + f->_mode = isatty(fd) ? _IOLBF : _IOFBF; + f->_bufsize = f->_bufindex = 0; + f->_buffer = nullptr; + setvbuf(f, NULL, f->_mode, 0); + + s_open_files[fd] = f; + return f; } - FILE* fdopen(int fd, const char* mode) + FILE* _fdopen_impl(int fd, const char* mode, int buffering_mode) { int flags; @@ -95,9 +168,23 @@ extern "C" f->_fd = fd; clearerr(f); + f->_pending = 0; + f->_flags = flags; + f->_mode = buffering_mode < 0 ? (isatty(fd) ? _IOLBF : _IOFBF) : buffering_mode; + f->_bufsize = f->_bufindex = 0; + f->_buffer = nullptr; + setvbuf(f, NULL, f->_mode, 0); + + s_open_files[fd] = f; + return f; } + FILE* fdopen(int fd, const char* mode) + { + return _fdopen_impl(fd, mode, -1); + } + FILE* freopen(const char* path, const char* mode, FILE* stream) { int flags; @@ -106,6 +193,10 @@ extern "C" close(stream->_fd); + s_open_files[stream->_fd] = nullptr; + + if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer); + if (!path) { fail("FIXME: freopen() called with path=nullptr"); } int fd = open(path, flags, 0666); @@ -114,13 +205,28 @@ extern "C" stream->_fd = fd; clearerr(stream); + stream->_pending = 0; + stream->_flags = flags; + stream->_mode = isatty(fd) ? _IOLBF : _IOFBF; + stream->_bufsize = stream->_bufindex = 0; + stream->_buffer = nullptr; + setvbuf(stream, NULL, stream->_mode, 0); + + s_open_files[fd] = stream; + return stream; } int fclose(FILE* stream) { + if (fflush(stream) < 0) return EOF; + if (close(stream->_fd) < 0) return EOF; + if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer); + + s_open_files[stream->_fd] = nullptr; + free(stream); return 0; @@ -155,7 +261,12 @@ extern "C" { if (size * nmemb == 0) return 0; - ssize_t nwrite = write(stream->_fd, buf, size * nmemb); + ssize_t nwrite = write_into_buffer(stream, (const u8*)buf, size * nmemb); + if (nwrite < 0) + { + stream->_err = 1; + return 0; + } return (size_t)nwrite / size; } @@ -211,7 +322,7 @@ extern "C" int fputc(int c, FILE* stream) { u8 value = (u8)c; - ssize_t rc = write(stream->_fd, &value, 1); + ssize_t rc = write_into_buffer(stream, &value, 1); if (rc <= 0) return EOF; return c; } @@ -228,7 +339,7 @@ extern "C" int fputs(const char* str, FILE* stream) { - ssize_t rc = write(stream->_fd, str, strlen(str)); + ssize_t rc = write_into_buffer(stream, (const u8*)str, strlen(str)); return (rc < 0) ? -1 : 0; } @@ -496,4 +607,32 @@ extern "C" { fail("FIXME: ungetc: not implemented"); } + + int setvbuf(FILE* stream, char* buf, int mode, size_t size) + { + int pending = 0; + if (mode < 0 || mode > _IOFBF) return errno = EINVAL, -1; + if (stream->_bufsize != 0 || stream->_bufindex != 0) return -1; // Buffer is already in use. + if (mode != _IONBF && buf == NULL) + { + size = BUFSIZ; + buf = (char*)calloc(size, 1); + if (!buf) return -1; + pending = FFLAG_FREE_AFTER_BUFFER_USE; + } + else + { + buf = NULL; + size = 0; + } + + if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer); + + stream->_buffer = buf; + stream->_bufcap = size; + stream->_mode = mode; + stream->_pending = pending; + + return 0; + } } -- 2.34.1 From d60ad184f15fca30a70a82db68636dc2c30305e1 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 22 Jul 2023 10:58:34 +0200 Subject: [PATCH 3/7] libc: A bit of nice refactoring --- libc/include/stdio.h | 23 +++++++------ libc/src/stdio.cpp | 81 +++++++++++++++++++++++--------------------- 2 files changed, 56 insertions(+), 48 deletions(-) diff --git a/libc/include/stdio.h b/libc/include/stdio.h index 0e706f52..f8f6961d 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -14,16 +14,19 @@ typedef struct { - int _fd; - int _err; - int _eof; - size_t _bufcap; - size_t _bufsize; - size_t _bufindex; - char* _buffer; - int _flags; - int _pending; - int _mode; + int _fd; // The underlying file descriptor. + int _err; // The error status flag. + int _eof; // The end-of-file status flag. + struct + { + size_t capacity; // The buffer's total capacity. + size_t size; // The buffer's used size. + size_t index; // The read index into the buffer. + char* buffer; // The memory used for the buffer. + int status; // The buffer status flags. + int mode; // The buffering mode. + } _buf; + int _flags; // The file access mode with which the file was opened. } FILE; #define EOF -1 diff --git a/libc/src/stdio.cpp b/libc/src/stdio.cpp index 3a01c05b..de32c539 100644 --- a/libc/src/stdio.cpp +++ b/libc/src/stdio.cpp @@ -13,7 +13,12 @@ FILE* stdout = nullptr; FILE* s_open_files[FOPEN_MAX]; -#define FFLAG_FREE_AFTER_BUFFER_USE (1 << 0) +enum FileStatusFlags +{ + BufferIsMalloced = (1 << 0), + LastRead = (1 << 1), + LastWrite = (1 << 2), +}; static const char* read_tmpdir() { @@ -57,12 +62,12 @@ static int fdopen_check_compatible_mode(int fd, int new_flags) static int flush_write_buffer(FILE* stream) { - if (stream->_mode == _IONBF) return 0; + if (stream->_buf.mode == _IONBF) return 0; - ssize_t result = write(stream->_fd, stream->_buffer, stream->_bufsize); + ssize_t result = write(stream->_fd, stream->_buf.buffer, stream->_buf.size); - stream->_bufindex = 0; - stream->_bufsize = 0; + stream->_buf.index = 0; + stream->_buf.size = 0; return result < 0 ? EOF : 0; } @@ -74,20 +79,20 @@ static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size) while (size > 0) { ssize_t nwritten; - if (stream->_mode != _IONBF) + if (stream->_buf.mode != _IONBF) { - if ((stream->_bufsize + size) > stream->_bufcap) + if ((stream->_buf.size + size) > stream->_buf.capacity) { if (flush_write_buffer(stream) < 0) return -1; } - ssize_t size_remaining = stream->_bufcap - stream->_bufsize; + ssize_t size_remaining = stream->_buf.capacity - stream->_buf.size; nwritten = size > size_remaining ? size_remaining : size; - memcpy(stream->_buffer + stream->_bufsize, data, nwritten); + memcpy(stream->_buf.buffer + stream->_buf.size, data, nwritten); - stream->_bufsize += nwritten; + stream->_buf.size += nwritten; - if (stream->_mode == _IOLBF && memchr(data, '\n', nwritten)) + if (stream->_buf.mode == _IOLBF && memchr(data, '\n', nwritten)) { if (flush_write_buffer(stream) < 0) return -1; } @@ -112,7 +117,7 @@ extern "C" int fflush(FILE* stream) { - if (stream && stream->_mode != _IONBF) flush_write_buffer(stream); + if (stream && stream->_buf.mode != _IONBF) flush_write_buffer(stream); else if (!stream) { for (int i = 0; i < FOPEN_MAX; i++) @@ -142,12 +147,12 @@ extern "C" f->_fd = fd; clearerr(f); - f->_pending = 0; f->_flags = flags; - f->_mode = isatty(fd) ? _IOLBF : _IOFBF; - f->_bufsize = f->_bufindex = 0; - f->_buffer = nullptr; - setvbuf(f, NULL, f->_mode, 0); + f->_buf.status = 0; + f->_buf.mode = isatty(fd) ? _IOLBF : _IOFBF; + f->_buf.size = f->_buf.index = 0; + f->_buf.buffer = nullptr; + setvbuf(f, NULL, f->_buf.mode, 0); s_open_files[fd] = f; @@ -168,12 +173,12 @@ extern "C" f->_fd = fd; clearerr(f); - f->_pending = 0; f->_flags = flags; - f->_mode = buffering_mode < 0 ? (isatty(fd) ? _IOLBF : _IOFBF) : buffering_mode; - f->_bufsize = f->_bufindex = 0; - f->_buffer = nullptr; - setvbuf(f, NULL, f->_mode, 0); + f->_buf.status = 0; + f->_buf.mode = buffering_mode < 0 ? (isatty(fd) ? _IOLBF : _IOFBF) : buffering_mode; + f->_buf.size = f->_buf.index = 0; + f->_buf.buffer = nullptr; + setvbuf(f, NULL, f->_buf.mode, 0); s_open_files[fd] = f; @@ -195,7 +200,7 @@ extern "C" s_open_files[stream->_fd] = nullptr; - if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer); + if (stream->_buf.buffer && (stream->_buf.status & FileStatusFlags::BufferIsMalloced)) free(stream->_buf.buffer); if (!path) { fail("FIXME: freopen() called with path=nullptr"); } @@ -205,12 +210,12 @@ extern "C" stream->_fd = fd; clearerr(stream); - stream->_pending = 0; stream->_flags = flags; - stream->_mode = isatty(fd) ? _IOLBF : _IOFBF; - stream->_bufsize = stream->_bufindex = 0; - stream->_buffer = nullptr; - setvbuf(stream, NULL, stream->_mode, 0); + stream->_buf.status = 0; + stream->_buf.mode = isatty(fd) ? _IOLBF : _IOFBF; + stream->_buf.size = stream->_buf.index = 0; + stream->_buf.buffer = nullptr; + setvbuf(stream, NULL, stream->_buf.mode, 0); s_open_files[fd] = stream; @@ -223,7 +228,7 @@ extern "C" if (close(stream->_fd) < 0) return EOF; - if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer); + if (stream->_buf.buffer && (stream->_buf.status & FileStatusFlags::BufferIsMalloced)) free(stream->_buf.buffer); s_open_files[stream->_fd] = nullptr; @@ -610,28 +615,28 @@ extern "C" int setvbuf(FILE* stream, char* buf, int mode, size_t size) { - int pending = 0; + int status = 0; if (mode < 0 || mode > _IOFBF) return errno = EINVAL, -1; - if (stream->_bufsize != 0 || stream->_bufindex != 0) return -1; // Buffer is already in use. + if (stream->_buf.size != 0 || stream->_buf.index != 0) return -1; // Buffer is already in use. if (mode != _IONBF && buf == NULL) { size = BUFSIZ; buf = (char*)calloc(size, 1); if (!buf) return -1; - pending = FFLAG_FREE_AFTER_BUFFER_USE; + status = FileStatusFlags::BufferIsMalloced; } - else + else if (mode == _IONBF) { buf = NULL; size = 0; } - if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer); + if (stream->_buf.buffer && (stream->_buf.status & FileStatusFlags::BufferIsMalloced)) free(stream->_buf.buffer); - stream->_buffer = buf; - stream->_bufcap = size; - stream->_mode = mode; - stream->_pending = pending; + stream->_buf.buffer = buf; + stream->_buf.capacity = size; + stream->_buf.mode = mode; + stream->_buf.status = status; return 0; } -- 2.34.1 From 420270ebd47b0c289a22ea23393fdebe32db8852 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 22 Jul 2023 11:17:51 +0200 Subject: [PATCH 4/7] libc: Implement read buffering =D --- apps/buffer-test.cpp | 4 +-- libc/src/stdio.cpp | 78 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/apps/buffer-test.cpp b/apps/buffer-test.cpp index ed64bfa3..09319c73 100644 --- a/apps/buffer-test.cpp +++ b/apps/buffer-test.cpp @@ -4,7 +4,7 @@ int main() { - fprintf(stderr, "Writing incomplete line to stdout (_IOLBF=%d)...\n", stdout->_mode); + fprintf(stderr, "Writing incomplete line to stdout (_IOLBF=%d)...\n", stdout->_buf.mode); fputs("hi!", stdout); sleep(3); putchar('\n'); @@ -14,7 +14,7 @@ int main() assert(f); assert(setvbuf(f, NULL, _IOFBF, 0) == 0); - fprintf(stderr, "Writing long text to file (_IOFBF=%d)...\n", f->_mode); + fprintf(stderr, "Writing long text to file (_IOFBF=%d)...\n", f->_buf.mode); fputs("Hello world!\nHow are you doing!\nThis is a test for many lines of buffering.\n", f); diff --git a/libc/src/stdio.cpp b/libc/src/stdio.cpp index de32c539..4f09faf1 100644 --- a/libc/src/stdio.cpp +++ b/libc/src/stdio.cpp @@ -69,9 +69,27 @@ static int flush_write_buffer(FILE* stream) stream->_buf.index = 0; stream->_buf.size = 0; + stream->_buf.status &= ~FileStatusFlags::LastWrite; + return result < 0 ? EOF : 0; } +static int flush_read_buffer(FILE* stream) +{ + if (stream->_buf.mode == _IONBF) return 0; + + // Reset the stream to its expected position. + ssize_t unread_bytes = stream->_buf.size - stream->_buf.index; + lseek(stream->_fd, -unread_bytes, SEEK_CUR); + + stream->_buf.index = 0; + stream->_buf.size = 0; + + stream->_buf.status &= ~FileStatusFlags::LastRead; + + return 0; +} + static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size) { ssize_t total_written = 0; @@ -81,6 +99,8 @@ static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size) ssize_t nwritten; if (stream->_buf.mode != _IONBF) { + if (stream->_buf.status & FileStatusFlags::LastRead) flush_read_buffer(stream); + if ((stream->_buf.size + size) > stream->_buf.capacity) { if (flush_write_buffer(stream) < 0) return -1; @@ -90,6 +110,8 @@ static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size) nwritten = size > size_remaining ? size_remaining : size; memcpy(stream->_buf.buffer + stream->_buf.size, data, nwritten); + stream->_buf.status |= FileStatusFlags::LastWrite; + stream->_buf.size += nwritten; if (stream->_buf.mode == _IOLBF && memchr(data, '\n', nwritten)) @@ -108,6 +130,51 @@ static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size) return total_written; } +static ssize_t read_data_into_buffer(FILE* stream) +{ + stream->_buf.index = 0; + ssize_t nread = read(stream->_fd, stream->_buf.buffer, stream->_buf.capacity); + if (nread >= 0) stream->_buf.size = nread; + stream->_buf.status |= FileStatusFlags::LastRead; + return nread; +} + +static ssize_t read_from_buffer(FILE* stream, u8* data, ssize_t size) +{ + ssize_t total_read = 0; + + while (size > 0) + { + ssize_t nread; + if (stream->_buf.mode != _IONBF) + { + if (stream->_buf.status & FileStatusFlags::LastWrite) flush_write_buffer(stream); + + if (stream->_buf.size == stream->_buf.index) + { + ssize_t rc; + if ((rc = read_data_into_buffer(stream)) < 0) return -1; + if (rc == 0) return total_read; + } + + ssize_t size_remaining = stream->_buf.size - stream->_buf.index; + nread = size > size_remaining ? size_remaining : size; + memcpy(data, stream->_buf.buffer + stream->_buf.index, nread); + + stream->_buf.index += nread; + } + else + nread = read(stream->_fd, data, size > BUFSIZ ? BUFSIZ : size); + if (nread < 0) return nread; + if (nread == 0) return total_read; + size -= nread; + data += nread; + total_read += nread; + } + + return total_read; +} + extern "C" { void _init_stdio() @@ -117,7 +184,12 @@ extern "C" int fflush(FILE* stream) { - if (stream && stream->_buf.mode != _IONBF) flush_write_buffer(stream); + if (stream && stream->_buf.mode != _IONBF) + { + if (stream->_buf.status & FileStatusFlags::LastWrite) flush_write_buffer(stream); + else if (stream->_buf.status & FileStatusFlags::LastRead) + flush_read_buffer(stream); + } else if (!stream) { for (int i = 0; i < FOPEN_MAX; i++) @@ -246,7 +318,7 @@ extern "C" { if (size * nmemb == 0) return 0; - ssize_t nread = read(stream->_fd, buf, size * nmemb); + ssize_t nread = read_from_buffer(stream, (u8*)buf, size * nmemb); if (nread < 0) { @@ -351,7 +423,7 @@ extern "C" int fgetc(FILE* stream) { u8 value; - ssize_t rc = read(stream->_fd, &value, 1); + ssize_t rc = read_from_buffer(stream, &value, 1); if (rc <= 0) return EOF; return value; } -- 2.34.1 From 19b4aa9f810d44f2e0cf73889a514b204913eaf0 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 22 Jul 2023 11:19:48 +0200 Subject: [PATCH 5/7] libc: Flush buffers before dealing with file positions --- libc/src/stdio.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libc/src/stdio.cpp b/libc/src/stdio.cpp index 4f09faf1..4e3440ff 100644 --- a/libc/src/stdio.cpp +++ b/libc/src/stdio.cpp @@ -350,6 +350,8 @@ extern "C" int fseek(FILE* stream, long offset, int whence) { + fflush(stream); + long result = lseek(stream->_fd, offset, whence); if (result < 0) return -1; @@ -361,11 +363,15 @@ extern "C" long ftell(FILE* stream) { + fflush(stream); + return lseek(stream->_fd, 0, SEEK_CUR); } void rewind(FILE* stream) { + fflush(stream); + lseek(stream->_fd, 0, SEEK_SET); clearerr(stream); @@ -383,6 +389,8 @@ extern "C" int fsetpos(FILE* stream, const fpos_t* pos) { + fflush(stream); + return fseek(stream, *pos, SEEK_SET); } -- 2.34.1 From 77022abafdc60fc965de0b9667f7272e9aaffb0a Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 22 Jul 2023 11:25:20 +0200 Subject: [PATCH 6/7] libc: Implement ungetc --- libc/src/stdio.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libc/src/stdio.cpp b/libc/src/stdio.cpp index 4e3440ff..6e84e267 100644 --- a/libc/src/stdio.cpp +++ b/libc/src/stdio.cpp @@ -688,9 +688,18 @@ extern "C" return f; } - int ungetc(int, FILE*) + int ungetc(int c, FILE* stream) { - fail("FIXME: ungetc: not implemented"); + if (stream->_buf.index == 0) + return EOF; // No data currently in the read buffer, or no data has been read from it. + + if (stream->_buf.mode == _IONBF) + return EOF; // FIXME: C doesn't state that ungetc() should only work on buffered streams. + + stream->_buf.index--; + stream->_buf.buffer[stream->_buf.index] = (char)c; + + return 0; } int setvbuf(FILE* stream, char* buf, int mode, size_t size) -- 2.34.1 From 085d2895e8f3d1b942cb0e1e91b15b4771c494c5 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 22 Jul 2023 11:34:05 +0200 Subject: [PATCH 7/7] libc: Implement setbuf(), setbuffer(), and setlinebuf() These are all simple wrappers around setvbuf(). --- libc/include/stdio.h | 11 +++++++++-- libc/src/stdio.cpp | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/libc/include/stdio.h b/libc/include/stdio.h index f8f6961d..4a955650 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -132,8 +132,6 @@ extern "C" /* Clear the error and end-of-file indicators in stream. */ void clearerr(FILE* stream); - void setbuf(FILE*, char*); - /* Write formatted output to a file. */ int fprintf(FILE* stream, const char* format, ...); @@ -191,6 +189,15 @@ extern "C" /* Change a file's buffering mode and internal buffer. */ int setvbuf(FILE* stream, char* buf, int mode, size_t size); + /* Change a file's internal buffer. */ + void setbuf(FILE* stream, char* buf); + + /* Change a file's internal buffer. */ + void setbuffer(FILE* stream, char* buf, size_t size); + + /* Change a file's buffering mode to line buffered. */ + void setlinebuf(FILE* stream); + #ifdef __cplusplus } #endif diff --git a/libc/src/stdio.cpp b/libc/src/stdio.cpp index 6e84e267..e48e2900 100644 --- a/libc/src/stdio.cpp +++ b/libc/src/stdio.cpp @@ -729,4 +729,19 @@ extern "C" return 0; } + + void setbuf(FILE* stream, char* buf) + { + setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ); + } + + void setbuffer(FILE* stream, char* buf, size_t size) + { + setvbuf(stream, buf, buf ? _IOFBF : _IONBF, size); + } + + void setlinebuf(FILE* stream) + { + setvbuf(stream, NULL, _IOLBF, 0); + } } -- 2.34.1