From ed873a652efd11dac78e26fcb5758df02dbfe22e Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Fri, 14 Jan 2022 17:16:04 +0100 Subject: [PATCH] stdio: fgetc(), fgets(), tests for fputc() and fputs() (DONE) --- CMakeLists.txt | 2 ++ STATUS | 20 +++++++++--------- src/libc/stdio/fgetc.c | 43 +++++++++++++++++++++++++++++++++++++++ src/libc/stdio/fgets.c | 11 ++++++++++ src/libc/stdio/fileutil.c | 43 +++++++++++++++++++++++++++++++++------ src/libc/stdio/fileutil.h | 2 +- src/libc/stdio/fputc.c | 6 ++++-- 7 files changed, 108 insertions(+), 19 deletions(-) create mode 100644 src/libc/stdio/fgetc.c create mode 100644 src/libc/stdio/fgets.c diff --git a/CMakeLists.txt b/CMakeLists.txt index de39356..494b786 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,7 +126,9 @@ set(SOURCES src/libc/stdio/ferror.c src/libc/stdio/feof.c src/libc/stdio/fflush.c + src/libc/stdio/fgetc.c src/libc/stdio/fgetpos.c + src/libc/stdio/fgets.c src/libc/stdio/fileutil.c src/libc/stdio/fopen.c src/libc/stdio/fprintf.c diff --git a/STATUS b/STATUS index d691127..e3603a0 100644 --- a/STATUS +++ b/STATUS @@ -119,16 +119,16 @@ TEST: Function/symbol/macro needs to be tested (EXT) dprintf TEST (EXT) vdprintf TEST - 7.19.7.1 fgetc TODO - 7.19.7.2 fgets TODO - 7.19.7.3 fputc TEST - 7.19.7.4 fputs TEST - 7.19.7.5 getc LDEPS(fgetc) - 7.19.7.6 getchar LDEPS(fgetc) - 7.19.7.7 gets LDEPS(fgets) - 7.19.7.8 putc TEST - 7.19.7.9 putchar TEST - 7.19.7.10 puts TEST + 7.19.7.1 fgetc - + 7.19.7.2 fgets - + 7.19.7.3 fputc - + 7.19.7.4 fputs - + 7.19.7.5 getc - + 7.19.7.6 getchar LDEPS(stdin) + 7.19.7.7 gets LDEPS(stdin) + 7.19.7.8 putc - + 7.19.7.9 putchar LDEPS(stdout) + 7.19.7.10 puts LDEPS(stdout) 7.19.7.11 ungetc - 7.19.8.1 fread - diff --git a/src/libc/stdio/fgetc.c b/src/libc/stdio/fgetc.c new file mode 100644 index 0000000..986c0a6 --- /dev/null +++ b/src/libc/stdio/fgetc.c @@ -0,0 +1,43 @@ +#include +#include "fileutil.h" + +int fgetc(FILE *fp) +{ + if(!fp->readable) { + fp->error = 1; + return EOF; + } + + /* For this function we inline __fp_fread2() and__fp_buffered_read() in + order to maintain decent performance */ + unsigned char c; + __fp_buffer_mode_read(fp); + + /* No buffered data available, non-buffered mode */ + if(!fp->buf) { + ssize_t rc = __fp_read(fp, &c, 1); + return (rc == 1) ? c : EOF; + } + + /* If there is no data, get some */ + if(fp->bufpos >= fp->bufread) { + ssize_t rc = __fp_read(fp, fp->buf, fp->bufsize); + if(rc <= 0) + return EOF; + fp->bufread = rc; + } + + /* Get a byte from the buffer */ + c = fp->buf[fp->bufpos++]; + fp->bufungetc -= (fp->bufungetc > 0); + + /* Rewind the buffer if at end, and clear _IONBF ungetc() buffers */ + if(fp->bufpos >= fp->bufread) { + fp->bufread = 0; + fp->bufpos = 0; + if(fp->bufmode == _IONBF) + __fp_remove_buffer(fp); + } + + return c; +} diff --git a/src/libc/stdio/fgets.c b/src/libc/stdio/fgets.c new file mode 100644 index 0000000..af51fce --- /dev/null +++ b/src/libc/stdio/fgets.c @@ -0,0 +1,11 @@ +#include +#include "fileutil.h" + +char *fgets(char * restrict s, int n, FILE * restrict fp) +{ + ssize_t read_size = __fp_fread2(fp, s, n - 1, '\n'); + if(read_size <= 0) + return NULL; + s[read_size] = 0; + return s; +} diff --git a/src/libc/stdio/fileutil.c b/src/libc/stdio/fileutil.c index 7d3873e..40a695d 100644 --- a/src/libc/stdio/fileutil.c +++ b/src/libc/stdio/fileutil.c @@ -1,5 +1,4 @@ #include "fileutil.h" -#include #include #include #include @@ -98,10 +97,26 @@ ssize_t __fp_fread2(FILE *fp, void *data, size_t request_size, int stop_char) while(read_size < request_size) { int remaining = request_size - read_size; - int chunk = __fp_buffered_read(fp, data+read_size, remaining); + int chunk = __fp_buffered_read(fp, data+read_size, remaining, + stop_char); /* Stream is not/no longer buffered, finish unbuffered */ - if(chunk < 0) { + if(chunk < 0 && stop_char >= 0) { + unsigned char c; + + /* When there is a stop char, we can only read one + byte at a time, which is reaaaally slow */ + for(int i = 0; i < remaining; i++) { + ssize_t rc = __fp_read(fp, &c, 1); + if(rc != 1) + break; + ((char *)data)[read_size++] = c; + if(c == stop_char) + break; + } + return read_size; + } + else if(chunk < 0) { ssize_t rc = __fp_read(fp, data+read_size, remaining); return read_size + (rc == EOF ? 0 : rc); } @@ -110,6 +125,11 @@ ssize_t __fp_fread2(FILE *fp, void *data, size_t request_size, int stop_char) if(read_size >= request_size) break; + /* If a stop char has been read, stop */ + if(stop_char >= 0 && read_size > 0 && + ((char *)data)[read_size-1] == stop_char) + break; + /* Get more data from the file descriptor into the buffer */ if(fp->buf) { ssize_t rc = __fp_read(fp, fp->buf, fp->bufsize); @@ -122,18 +142,29 @@ ssize_t __fp_fread2(FILE *fp, void *data, size_t request_size, int stop_char) return read_size; } -ssize_t __fp_buffered_read(FILE *fp, void *data, size_t request_size) +ssize_t __fp_buffered_read(FILE *fp, void *data, size_t request_size, + int stop_char) { if(!fp->buf || __fp_hasbuf_write(fp)) return -1; - int read_size = min((int)request_size, fp->bufread - fp->bufpos); + int read_size = request_size; + if(read_size > (int)(fp->bufread - fp->bufpos)) + read_size = fp->bufread - fp->bufpos; if(read_size <= 0) return 0; + if(stop_char >= 0) { + char *end = memchr(fp->buf+fp->bufpos, stop_char, read_size); + if(end != NULL) + read_size = end - (fp->buf + fp->bufpos) + 1; + } + memcpy(data, fp->buf + fp->bufpos, read_size); fp->bufpos += read_size; - fp->bufungetc = max(fp->bufungetc - read_size, 0); + fp->bufungetc = fp->bufungetc - read_size; + if(fp->bufungetc < 0) + fp->bufungetc = 0; /* Rewind the buffer if we read it fully */ if(fp->bufpos >= fp->bufread) { diff --git a/src/libc/stdio/fileutil.h b/src/libc/stdio/fileutil.h index ea39dcc..8627ee9 100644 --- a/src/libc/stdio/fileutil.h +++ b/src/libc/stdio/fileutil.h @@ -43,7 +43,7 @@ ssize_t __fp_fread2(FILE *fp, void *data, size_t request_size, int stop_char); Returns amount of data read; if >= 0 but < size, the buffer should be refilled. Returns -1 to indicate that unbuffered access should be used. Allows reading from temporary ungetc() buffers and cleans them. */ -ssize_t __fp_buffered_read(FILE *fp, void *data, size_t size); +ssize_t __fp_buffered_read(FILE *fp, void *data, size_t size, int stop_char); /* Reads data from a file descriptor; updates the fdpos and sets the error indicator. Returns 0 on success, EOF on error. */ diff --git a/src/libc/stdio/fputc.c b/src/libc/stdio/fputc.c index d11bf47..fbb7156 100644 --- a/src/libc/stdio/fputc.c +++ b/src/libc/stdio/fputc.c @@ -15,8 +15,10 @@ int fputc(int c_int, FILE *fp) return 0; } - if(!fp->buf) - return __fp_write(fp, &c, 1); + if(!fp->buf) { + ssize_t rc = __fp_write(fp, &c, 1); + return (rc == 1) ? c : EOF; + } __fp_buffer_mode_write(fp);