diff --git a/CMakeLists.txt b/CMakeLists.txt index 6679ff6..a463886 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,10 +130,15 @@ set(SOURCES src/libc/stdio/fileutil.c src/libc/stdio/fopen.c src/libc/stdio/fprintf.c + src/libc/stdio/fread.c src/libc/stdio/freopen.c src/libc/stdio/fseek.c src/libc/stdio/fsetpos.c src/libc/stdio/ftell.c + src/libc/stdio/fwrite.c + src/libc/stdio/getc.c + src/libc/stdio/getchar.c + src/libc/stdio/gets.c src/libc/stdio/printf.c src/libc/stdio/printf/format_fixed.c src/libc/stdio/printf/format_fp.c @@ -141,6 +146,7 @@ set(SOURCES src/libc/stdio/printf/print.c src/libc/stdio/printf/util.c src/libc/stdio/putc.c + src/libc/stdio/putchar.c src/libc/stdio/puts.c src/libc/stdio/remove.c src/libc/stdio/rewind.c diff --git a/STATUS b/STATUS index d51da92..3e02f34 100644 --- a/STATUS +++ b/STATUS @@ -122,16 +122,16 @@ TEST: Function/symbol/macro needs to be tested 7.19.7.2 fgets TODO 7.19.7.3 fputc TODO 7.19.7.4 fputs TODO - 7.19.7.5 getc TODO - 7.19.7.6 getchar TODO - 7.19.7.7 gets TODO - 7.19.7.8 putc TODO - 7.19.7.9 putchar TODO - 7.19.7.10 puts TODO + 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 LDPES(fputc) + 7.19.7.9 putchar LDEPS(fputc) + 7.19.7.10 puts LDEPS(fputs) 7.19.7.11 ungetc TODO - 7.19.8.1 fread TODO - 7.19.8.2 fwrite TODO + 7.19.8.1 fread TEST + 7.19.8.2 fwrite TEST 7.19.9.1 fgetpos TEST 7.19.9.2 fseek TEST diff --git a/include/stdio.h b/include/stdio.h index 8ec7286..970ee04 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -175,7 +175,60 @@ extern int vasprintf(char ** __restrict__ __str, char const * __restrict__ __format, va_list __args); /* -** File positioning functions; +** Character input/output functions. +*/ + +/* Read a character from a stream. */ +extern int fgetc(FILE *__fp); + +/* Read at most n characters from a stream, stopping after a newline. */ +extern char *fgets(char * __restrict__ __s, int __n, + FILE * __restrict__ __fp); + +/* Write a character to a stream. */ +extern int fputc(int __c, FILE *__fp); + +/* Write a string to a stream (excluding the NUL nyte). */ +extern int fputs(char const * __restrict__ s, FILE * __restrict__ fp); + +extern int getc(FILE *__fp); +#define getc fgetc + +/* Get a character from stdin */ +extern int getchar(void); +#define getchar() fgetc(stdin) + +/* (DEPRECATED; use fgets() instead) Read a string from stdin. */ +extern char *gets(char *__s); + +extern int putc(int __c, FILE *__fp); +#define putc fputc + +/* Write a character to stdout */ +extern int putchar(int __c); +#define putchar(__c) fputc(__c, stdout) + +/* Write a string to stdout, followed by a newline */ +extern int puts(char const *__s); + +/* Un-read a character back to the stream; only one ungetc() is guaranteed, and + the character is lost after reading, writing or flushing. */ +extern int ungetc(int __c, FILE *__fp); + +/* +** Direct input/output functions. +*/ + +/* Read an array of items from a stream. */ +extern size_t fread(void * __restrict__ __ptr, size_t __size, size_t __nmemb, + FILE * __restrict__ __fp); + +/* Write an array of items to a stream. */ +extern size_t fwrite(void const * __restrict__ __ptr, size_t __size, + size_t __nmemb, FILE * __restrict__ __fp); + +/* +** File positioning functions. */ /* Get current position (same as ftell() unless wide-oriented). */ diff --git a/src/libc/stdio/fflush.c b/src/libc/stdio/fflush.c index d6cb4b9..41f9eb3 100644 --- a/src/libc/stdio/fflush.c +++ b/src/libc/stdio/fflush.c @@ -14,7 +14,7 @@ int fflush(FILE *fp) return 0; /* In reading mode, reset the file offset */ - if(fp->bufmode == __FILE_BUF_READ && fp->bufpos < fp->bufread) { + if(fp->bufdir == __FILE_BUF_READ && fp->bufpos < fp->bufread) { fp->fdpos = fp->fdpos - fp->bufread + fp->bufpos; lseek(fp->fd, fp->fdpos, SEEK_SET); fp->bufpos = 0; @@ -23,7 +23,7 @@ int fflush(FILE *fp) } /* In writing mode, write pending data */ - if(fp->bufmode == __FILE_BUF_WRITE && fp->bufpos > 0) { + if(fp->bufdir == __FILE_BUF_WRITE && fp->bufpos > 0) { ssize_t written = __fp_write(fp, fp->buf, fp->bufpos); fp->bufpos = 0; return (written == (ssize_t)fp->bufpos ? 0 : EOF); diff --git a/src/libc/stdio/fileutil.c b/src/libc/stdio/fileutil.c index 6338ac6..87302e9 100644 --- a/src/libc/stdio/fileutil.c +++ b/src/libc/stdio/fileutil.c @@ -1,6 +1,7 @@ #include "fileutil.h" #include #include +#include #include int __fp_open(FILE *fp, int fd, bool use_buffering) @@ -28,6 +29,23 @@ void __fp_close(FILE *fp, bool free_fp) free(fp); } +void __fp_buffer_mode_read(FILE *fp) +{ + if(fp->buf && fp->bufdir == __FILE_BUF_WRITE && fp->bufpos > 0) { + fflush(fp); + fp->bufdir = __FILE_BUF_READ; + } +} + +void __fp_buffer_mode_write(FILE *fp) +{ + if(fp->buf && fp->bufdir == __FILE_BUF_READ + && fp->bufpos < fp->bufread) { + fflush(fp); + fp->bufdir = __FILE_BUF_WRITE; + } +} + ssize_t __fp_read(FILE *fp, void *data, size_t size) { size_t read_ = 0; @@ -39,8 +57,10 @@ ssize_t __fp_read(FILE *fp, void *data, size_t size) fp->error = 1; return EOF; } - if(rc == 0) + if(rc == 0) { + fp->eof = 1; break; + } fp->fdpos += rc; } diff --git a/src/libc/stdio/fileutil.h b/src/libc/stdio/fileutil.h index a010921..8d06c7b 100644 --- a/src/libc/stdio/fileutil.h +++ b/src/libc/stdio/fileutil.h @@ -15,6 +15,12 @@ int __fp_open(FILE *fp, int fd, bool use_buffering); /* Close fp and free all of its resources. */ void __fp_close(FILE *fp, bool free_fp); +/* Set reading mode on the buffer. */ +void __fp_buffer_mode_read(FILE *fp); + +/* Set writing mode on the buffer. */ +void __fp_buffer_mode_write(FILE *fp); + /* Reads data from a file descriptor; updates the fdpos and sets the error indicator. Returns 0 on success, EOF on error. */ ssize_t __fp_read(FILE *fp, void *data, size_t size); diff --git a/src/libc/stdio/fread.c b/src/libc/stdio/fread.c new file mode 100644 index 0000000..5d6a241 --- /dev/null +++ b/src/libc/stdio/fread.c @@ -0,0 +1,45 @@ +#include +#include +#include "fileutil.h" + +size_t fread(void *data, size_t membsize, size_t nmemb, FILE *fp) +{ + size_t size; + if(__builtin_umul_overflow(membsize, nmemb, &size)) { + fp->error = 1; + return 0; + } + + if(!fp->buf) { + ssize_t rc = __fp_read(fp, data, size); + return (rc == EOF) ? 0 : rc; + } + + __fp_buffer_mode_read(fp); + + size_t size_read = 0; + while(size_read < size) { + /* Take what's available in the buffer (may be 0) */ + size_t size_frag = fp->bufread - fp->bufpos; + if(size_frag > size - size_read) + size_frag = size - size_read; + memcpy(data + size_read, fp->buf + fp->bufpos, size_frag); + size_read += size_frag; + fp->bufpos += size_frag; + + if(fp->bufpos >= fp->bufread) { + fp->bufread = 0; + fp->bufpos = 0; + } + if(size_read >= size) + break; + + /* Get more data */ + ssize_t rc = __fp_read(fp, fp->buf, fp->bufsize); + if(rc <= 0) /* EOF or error */ + break; + fp->bufread = rc; + } + + return size_read; +} diff --git a/src/libc/stdio/fwrite.c b/src/libc/stdio/fwrite.c new file mode 100644 index 0000000..065949d --- /dev/null +++ b/src/libc/stdio/fwrite.c @@ -0,0 +1,42 @@ +#include +#include +#include "fileutil.h" + +size_t fwrite(void const *data, size_t membsize, size_t nmemb, FILE *fp) +{ + size_t size; + if(__builtin_umul_overflow(membsize, nmemb, &size)) { + fp->error = 1; + return 0; + } + + if(!fp->buf) { + ssize_t rc = __fp_write(fp, data, size); + return (rc == EOF) ? 0 : rc; + } + + __fp_buffer_mode_write(fp); + + // TODO: fwrite: line buffering + + size_t size_written = 0; + while(size_written < size) { + /* Fill the buffer */ + size_t size_frag = fp->bufsize - fp->bufpos; + if(size_frag > size - size_written) + size_frag = size - size_written; + + memcpy(fp->buf + fp->bufpos, data + size_written, size_frag); + size_written += size_frag; + fp->bufpos += size_frag; + + if(fp->bufpos >= fp->bufsize) { + ssize_t rc = __fp_write(fp, fp->buf, fp->bufpos); + if(rc <= 0) /* error */ + break; + fp->bufpos = 0; + } + } + + return size_written; +} diff --git a/src/libc/stdio/getc.c b/src/libc/stdio/getc.c new file mode 100644 index 0000000..fd5f6ca --- /dev/null +++ b/src/libc/stdio/getc.c @@ -0,0 +1,7 @@ +#include +#undef getc + +int getc(FILE *fp) +{ + return fgetc(fp); +} diff --git a/src/libc/stdio/getchar.c b/src/libc/stdio/getchar.c new file mode 100644 index 0000000..6a33ff3 --- /dev/null +++ b/src/libc/stdio/getchar.c @@ -0,0 +1,7 @@ +#include +#undef getchar + +int getchar(void) +{ + return fgetc(stdin); +} diff --git a/src/libc/stdio/gets.c b/src/libc/stdio/gets.c new file mode 100644 index 0000000..bb235d6 --- /dev/null +++ b/src/libc/stdio/gets.c @@ -0,0 +1,7 @@ +#include +#include + +extern char *gets(char *s) +{ + return fgets(s, INT_MAX, stdin); +} diff --git a/src/libc/stdio/putc.c b/src/libc/stdio/putc.c index cfa9c94..ee789bd 100644 --- a/src/libc/stdio/putc.c +++ b/src/libc/stdio/putc.c @@ -1,11 +1,7 @@ #include -#include +#undef putc -int putchar(int c) +int putc(int c, FILE *fp) { - char n; - - n = (char)c; - write(STDOUT_FILENO, &n, 1); - return (n); + return fputc(c, fp); } diff --git a/src/libc/stdio/putchar.c b/src/libc/stdio/putchar.c new file mode 100644 index 0000000..6874d25 --- /dev/null +++ b/src/libc/stdio/putchar.c @@ -0,0 +1,7 @@ +#include +#undef putchar + +int putchar(int c) +{ + return fputc(c, stdout); +} diff --git a/src/libc/stdio/puts.c b/src/libc/stdio/puts.c index 418dcc8..68527d1 100644 --- a/src/libc/stdio/puts.c +++ b/src/libc/stdio/puts.c @@ -1,18 +1,11 @@ #include -#include -#include -/* -** puts() writes the string s and a trailing newline to stdout. -** FIXME: check last write error ! -*/ -int puts(const char *s) +int puts(char const *s) { - size_t size; - size_t n; - - size = strlen(s); - n = write(STDOUT_FILENO, s, size); - write(STDOUT_FILENO, "\n", 1); - return (-(n == size)); + int rc = 0; + if(fputs(s, stdout) != 0) + rc = -1; + if(fputc('\n', stdout) == EOF) + rc = -1; + return rc; }