diff --git a/CMakeLists.txt b/CMakeLists.txt index e73409e..1627252 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,8 @@ set(SOURCES # stdio src/libc/stdio/asprintf.c src/libc/stdio/dprintf.c + src/libc/stdio/fflush.c + src/libc/stdio/fileutil.c src/libc/stdio/fprintf.c src/libc/stdio/printf.c src/libc/stdio/printf/format_fixed.c @@ -129,6 +131,9 @@ set(SOURCES src/libc/stdio/printf/util.c src/libc/stdio/putc.c src/libc/stdio/puts.c + src/libc/stdio/remove.c + src/libc/stdio/setbuf.c + src/libc/stdio/setvbuf.c src/libc/stdio/snprintf.c src/libc/stdio/sprintf.c src/libc/stdio/vasprintf.c diff --git a/STATUS b/STATUS index b1b4c6f..5f0f449 100644 --- a/STATUS +++ b/STATUS @@ -31,51 +31,49 @@ TODO: Function/symbol/macro is not implemented/defined BDEPS(...): Function/symbol/macro needs ... to build LDEPS(...): Function/symbol/macro needs ... to link TEST: Function/symbol/macro needs to be tested -DONE: Function/symbol/macro is defined, builds, links, and is tested +-: Function/symbol/macro is defined, builds, links, and is tested 7.2 -! 7.2.1 assert: LDEPS(fprintf,stderr) + 7.2.1 assert LDEPS(fprintf,stderr) 7.3 => OpenLibm 7.4 - 7.4.1 is*: DONE - 7.4.2 to*: DONE + 7.4.1 is* - + 7.4.2 to* - 7.5 - 7.5.2 EDOM EILSEQ ERANGE: DONE + 7.5.2 EDOM, EILSEQ, ERANGE - 7.6 => OpenLibm 7.7 => GCC 7.8 -! 7.8.1 PRI* macros: LDEPS(*printf) -! 7.8.1 SCN* macros: LDEPS(*scanf) - 7.8.2.1 imaxabs: DONE - 7.8.2.2 imaxdiv: DONE - 7.8.2.3 strtoimax strtoumax: DONE -! 7.8.2.4 wcstoimax wcstoumax: TODO + 7.8.1 PRI* macros - + 7.8.1 SCN* macros - + 7.8.2.1 imaxabs - + 7.8.2.2 imaxdiv - + 7.8.2.3 strtoimax, strtoumax - + 7.8.2.4 wcstoimax, wcstoumax TODO 7.9 => GCC 7.10 => GCC 7.11 -! 7.11.1 setlocale: TEST -! 7.11.2 localeconv: TEST + 7.11.1 setlocale TEST + 7.11.2 localeconv TEST 7.12 => OpenLibm 7.13 - 7.13.1 setjmp: DONE - 7.13.2 longjmp: DONE + 7.13.1 setjmp - + 7.13.2 longjmp - 7.14 - 7.14 sig_atomic_t SIG_DFL SIG_ERR SIG_IGN: DONE - 7.14 SIGABRT SIGFPE SIGILL SIGINT SIGSEGV SIGTERM: DONE - 7.14.1.1 signal: DONE - 7.14.1.2 raise: DONE + 7.14.1.1 signal - + 7.14.1.2 raise - 7.15 => GCC @@ -86,93 +84,114 @@ DONE: Function/symbol/macro is defined, builds, links, and is tested 7.18 => GCC 7.19 - 7.19.1 Introduction: TEST - TODO: Handle wide-oriented streams? (see notes) -! 7.19.4 Operations on files: TODO -! 7.19.5 File access functions: TODO -! 7.19.6 Formatted input/output functions: TODO -! 7.19.7 Character input/output functions: TODO -! 7.19.8 Direct input/output functions: TODO -! 7.19.9 File positioning functions: TODO -! 7.19.10 Error-handling functions: TODO + 7.19.1 Introduction TEST + No wide-oriented streams (see notes) + 7.19.4.1 remove TEST + 7.19.4.2 rename TODO + 7.19.4.3 tmpfile TODO + 7.19.4.4 tmpnam TODO + 7.19.5.1 fclose TODO + 7.19.5.2 fflush TODO + 7.19.5.3 fopen TODO + 7.19.5.4 freopen TODO + 7.19.5.5 setbuf TODO + 7.19.5.6 setvbuf TODO + 7.19.6.1 fprintf LDEPS(fwrite) + 7.19.6.2 fscanf TODO + 7.19.6.3 printf LDEPS(fwrite, stdout) + 7.19.6.4 scanf TODO + 7.19.6.5 snprintf - + 7.19.6.6 sprintf - + 7.19.6.7 sscanf TODO + 7.19.6.8 vfprintf LDEPS(fwrite) + 7.19.6.9 vfscanf TODO + 7.19.6.10 vprintf LDEPS(fwrite, stdout) + 7.19.6.11 vscanf TODO + 7.19.6.12 vsnprintf - + 7.19.6.13 vsprintf - + 7.19.6.14 vsscanf TODO + (EXT) asprintf - + (EXT) vasprintf - + (EXT) dprintf TEST + (EXT) vdprintf TEST + 7.19.7 Character input/output TODO + 7.19.8 Direct input/output TODO + 7.19.9 File positioning TODO + 7.19.10 Error-handling TODO 7.20 - 7.20 RAND_MAX: DONE -! 7.20 MB_CUR_MAX: TODO - 7.20.1.1 atof: DONE - 7.20.1.2 atoi, atol, atoll: DONE - 7.20.1.3 strtod, strtof, strtold: DONE - 7.20.1.4 strtol, strtoul, strtoll, strtoull: DONE - 7.20.2.1 rand: DONE - 7.20.2.2 srand: DONE - 7.20.3.1 calloc: DONE - 7.20.3.2 free: DONE (at least gint) - 7.20.3.3 malloc: DONE (at least gint) - 7.20.3.4 realloc: DONE (at least gint) - 7.20.4.1 abort: DONE -! 7.20.4.2 atexit: TODO - 7.20.4.3 exit: DONE (missing stream flushing/closing/etc) - 7.20.4.4 _Exit: DONE (gint only) -! 7.20.4.5 getenv: TODO -! 7.20.4.6 system: TODO -! 7.20.5.1 bsearch: TODO -! 7.20.5.2 qsort: TEST - 7.20.6.1 abs, labs, llabs: DONE - 7.20.6.2 div, ldiv, lldiv: DONE -! 7.20.7 Multibyte/wide character conversion functions: TODO -! 7.20.8 Multibyte/wide string conversion functions: TODO + 7.20 MB_CUR_MAX TODO + 7.20.1.1 atof - + 7.20.1.2 atoi, atol, atoll - + 7.20.1.3 strtod, strtof, strtold - + 7.20.1.4 strtol, strtoul - + 7.20.1.4 strtoll, strtoull - + 7.20.2.1 rand - + 7.20.2.2 srand - + 7.20.3.1 calloc - + 7.20.3.2 free - (gint) + 7.20.3.3 malloc - (gint) + 7.20.3.4 realloc - (gint) + 7.20.4.1 abort - (stream flushing/closing/etc?) + 7.20.4.2 atexit TODO + 7.20.4.3 exit - (stream flushing/closing/etc?) + 7.20.4.4 _Exit - (gint) + 7.20.4.5 getenv TODO + 7.20.4.6 system TODO + 7.20.5.1 bsearch TODO + 7.20.5.2 qsort TEST + 7.20.6.1 abs, labs, llabs - + 7.20.6.2 div, ldiv, lldiv - + 7.20.7 Multibyte/wide char conv TODO + 7.20.8 Multibyte/wide string conv TODO 7.21 - 7.21.2.1 memcpy: DONE - 7.21.2.2 memmove: DONE (Unoptimized: byte-by-byte) - 7.21.2.3 strcpy: DONE - 7.21.2.4 strncpy: DONE - 7.21.3.1 strcat: DONE - 7.21.3.2 strncat: DONE - 7.21.4.1 memcmp: DONE - 7.21.4.2 strcmp: DONE - 7.21.4.3 strcoll: DONE - 7.21.4.4 strncmp: DONE - 7.21.4.5 strxfrm: DONE - 7.21.5.1 memchr: DONE - 7.21.5.2 strchr: DONE - 7.21.5.3 strcspn: DONE - 7.21.5.4 strpbrk: DONE - 7.21.5.5 strrchr: DONE - 7.21.5.6 strspn: DONE - 7.21.5.7 strstr: DONE - 7.21.5.8 strtok: DONE - 7.21.6.1 memset: DONE - 7.21.6.2 strerror: DONE - 7.21.6.3 strlen: DONE - Extensions: - - strnlen: DONE - - strchrnul: DONE - - strcasestr: DONE - - strcasecmp: DONE - - strncasecmp: DONE - - strdup: DONE - - strndup: DONE + 7.21.2.1 memcpy - + 7.21.2.2 memmove - (Unoptimized: byte-by-byte) + 7.21.2.3 strcpy - + 7.21.2.4 strncpy - + 7.21.3.1 strcat - + 7.21.3.2 strncat - + 7.21.4.1 memcmp - + 7.21.4.2 strcmp - + 7.21.4.3 strcoll - + 7.21.4.4 strncmp - + 7.21.4.5 strxfrm - + 7.21.5.1 memchr - + 7.21.5.2 strchr - + 7.21.5.3 strcspn - + 7.21.5.4 strpbrk - + 7.21.5.5 strrchr - + 7.21.5.6 strspn - + 7.21.5.7 strstr - + 7.21.5.8 strtok - + 7.21.6.1 memset - + 7.21.6.2 strerror - + 7.21.6.3 strlen - + (EXT) strnlen - + (EXT) strchrnul - + (EXT) strcasestr - + (EXT) strcasecmp - + (EXT) strncasecmp - + (EXT) strdup - + (EXT) strndup - 7.22 => GCC 7.23 - 7.23.1 Components of time: DONE - 7.23.2.1 clock: DONE - 7.23.2.2 difftime: DONE - 7.23.2.3 mktime: DONE (DST flag ignored) - 7.23.2.4 time: DONE - 7.23.3.1 asctime: DONE - 7.23.3.2 ctime: DONE - 7.23.3.3 gmtime: DONE - 7.23.3.4 localtime: DONE (No timezones; same as gmtime) - 7.23.3.5 strftime: DONE (No %g/%G/%U/%V/%W; timezones %z/%Z empty) + 7.23.2.1 clock - + 7.23.2.2 difftime - + 7.23.2.3 mktime - (DST flag ignored) + 7.23.2.4 time - + 7.23.3.1 asctime - + 7.23.3.2 ctime - + 7.23.3.3 gmtime - + 7.23.3.4 localtime - (No timezones; same as gmtime) + 7.23.3.5 strftime - (No %g, %G, %U, %V, %W, %z, %Z) -7.24 - TODO (not a priority) +7.24 TODO (not a priority) -7.25 - TODO (not a priority) +7.25 TODO (not a priority) # Supporting locales @@ -188,7 +207,7 @@ What if we wanted to support more locales? # Supporting text and binary files (newline translation) -Because of 7.19.2%1,223 we don't need to support newline translation. +Because of 7.19.2§1.223 we don't need to support newline translation. # Support wide-oriented streams diff --git a/include/inttypes.h b/include/inttypes.h index 3f93cc1..c77cbb9 100644 --- a/include/inttypes.h +++ b/include/inttypes.h @@ -8,7 +8,7 @@ extern "C" { #include #include -/* Hide by default in C++ (7.8.1§1.181) */ +/* Hide by default in C++ (7.8.1§1.182) */ #if !defined __cplusplus || defined __STDC_FORMAT_MACROS /* Printing signed fixed-width types, decimal */ diff --git a/include/stdio.h b/include/stdio.h index d7529e5..729029e 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -20,8 +20,7 @@ typedef size_t fpos_t; #define _IONBF 2 /* Some buffer size for file buffering. */ -/* TODO: We might want a larger BUFSIZ than 256 on fx-CG 50. */ -#define BUFSIZ 256 +#define BUFSIZ 512 /* End-of-file marker. */ #define EOF ((int)(-1)) @@ -55,6 +54,28 @@ extern FILE *stderr; #define stdout stdout #define stderr stderr +/* +** Operations on files. +*/ + +/* Remove a file from the filesystem. + In gint, the file must not be open (open files' names are not tracked). */ +extern int remove(char const *__filename); + +/* +** File access functions. +*/ + +/* Flush any written data in the FILE's internal buffer. */ +extern int fflush(FILE *__fp); + +/* Use __buf as a buffer (of size BUFSIZ) for access to __fp. */ +extern void setbuf(FILE * __restrict__ __fp, char * __restrict__ __buf); + +/* Changer the buffering mode and buffer address for __fp. */ +extern int setvbuf(FILE * __restrict__ __fp, char * __restrict__ __buf, + int __mode, size_t __size); + /* ** Formatted input/output functions. ** diff --git a/include/target/gint/bits/types/FILE.h b/include/target/gint/bits/types/FILE.h index fb12011..b147e46 100644 --- a/include/target/gint/bits/types/FILE.h +++ b/include/target/gint/bits/types/FILE.h @@ -2,16 +2,77 @@ # define __BITS_TYPES_FILE_H__ #include +#include +#include +#define __FILE_BUF_READ 0 +#define __FILE_BUF_WRITE 1 + +/* The FILE structure is mostly a buffer around kernel-level I/O. Most of the + work is maintaining that buffer to provide the basic fgetc()/fputc() + functions, then everything else is built on top of the abstracted stream. + + The buffer of a FILE can either be in reading or writing mode. When in + reading mode, the buffer contains pre-fetched data from a previous read that + hasn't yet been requested by the user. When in writing mode, the buffer + contains data written by the user which hasn't yet been sent to the kernel. + + The buffer has the following structure: + + 0 bufpos bufread bufsize + +--------+---------+---------+ + |xxxxxxxx|rrrrrrrrr|uuuuuuuuu| (When reading) + +--------+---------+---------+ + |wwwwwwww|uuuuuuuuuuuuuuuuuuu| (When writing) + +--------+-------------------+ + + x: Data read from file descriptor and returned to user + r: Data read from file descriptor not yet returned to user + w: Data written by user not yet sent to file descriptor + u: Undefined + + In reading mode, the region [0..bufread) contains data obtained from the + file. bufpos marks how much has been read, in the sense that [0..bufpos) has + already been returned to the user while [bufpos..bufread) has not. The offset + on the underlying file descriptor sits at bufread, so the position reported + by ftell() is fdpos - bufread + bufpos. The rest of the buffer is undefined. + + In writing mode, the region [0..bufpos) contains data received through API + but not yet written to the file descriptor. ftell() reports fdpos + bufpos. + The rest of the buffer is undefined. */ typedef struct { - /* BFile handler */ + /* File descriptor */ int fd; - /* Current position in file */ - size_t pos; - /* Buffering mode */ - // TODO - /* Opening mode */ - // TODO + /* Current position in file, as tracked by the file descriptor (ie. not + accounting for buffer operations) */ + size_t fdpos; + + /* Pointer to buffer (NULL if _IONBF) */ + char *buf; + /* Current position in buffer, water mark of read data in buffer, and + buffer size; see header comment for details */ + size_t bufpos; + size_t bufread; + size_t bufsize; + /* Buffering mode; one of _IOFBF, _IOLBF, or _IONBF */ + uint8_t bufmode :2; + /* We own the buffer and it needs to be freed */ + uint8_t bufowned :1; + /* __FILE_BUF_READ if the buffer is in reading mode + __FILE_BUF_WRITE if it's in writing mode + This mode can only be changed when bufpos=0, ie. just after fflush(). */ + uint8_t bufdir :1; + + /* Opening flags */ + uint8_t readable :1; + uint8_t writable :1; + uint8_t append :1; + /* Non-zero if text mode, zero if binary mode */ + uint8_t text :1; + /* EOF indicator */ + uint8_t eof :1; + /* Error indicator */ + uint8_t error :1; } FILE; #endif /*__BITS_TYPES_FILE_H__*/ diff --git a/src/libc/stdio/fflush.c b/src/libc/stdio/fflush.c new file mode 100644 index 0000000..9e1cf08 --- /dev/null +++ b/src/libc/stdio/fflush.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include "fileutil.h" + +int fflush(FILE *fp) +{ + // TODO: fflush(NULL) should flush "all" files (do we track them?) + if(!fp) { + errno = EINVAL; + return EOF; + } + if(!fp->buf) + return 0; + + int rc = 0; + + /* In reading mode, reset the file offset */ + if(fp->bufmode == __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; + fp->bufread = 0; + } + + /* In writing mode, write pending data */ + else if(fp->bufmode == __FILE_BUF_WRITE && fp->bufpos > 0) { + rc = __fp_write(fp, fp->buf, fp->bufpos); + fp->bufpos = 0; + } + + return rc; +} diff --git a/src/libc/stdio/fileutil.c b/src/libc/stdio/fileutil.c new file mode 100644 index 0000000..a8662a3 --- /dev/null +++ b/src/libc/stdio/fileutil.c @@ -0,0 +1,19 @@ +#include "fileutil.h" +#include + +int __fp_write(FILE *fp, void const *data, size_t size) +{ + if(!fp) + return EOF; + + ssize_t rc = write(fp->fd, data, size); + + if(rc < 0) { + fp->error = 1; + return EOF; + } + else { + fp->fdpos += rc; + return 0; + } +} diff --git a/src/libc/stdio/fileutil.h b/src/libc/stdio/fileutil.h new file mode 100644 index 0000000..a008676 --- /dev/null +++ b/src/libc/stdio/fileutil.h @@ -0,0 +1,18 @@ +#ifndef __FILEUTIL_H__ +# define __FILEUTIL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Write data to a file descriptor; updates the fdpos and sets the error + indicator. Returns 0 on success, EOF on error. */ +int __fp_write(FILE *fp, void const *data, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /*__FILEUTIL_H__*/ diff --git a/src/libc/stdio/remove.c b/src/libc/stdio/remove.c new file mode 100644 index 0000000..c5f2732 --- /dev/null +++ b/src/libc/stdio/remove.c @@ -0,0 +1,19 @@ +#include +#include +#include + +int remove(char const *filename) +{ + struct stat st; + + int rc = stat(filename, &st); + if(rc < 0) + return -1; + + if(S_ISDIR(st.st_mode)) { + return rmdir(filename); + } + else { + return unlink(filename); + } +} diff --git a/src/libc/stdio/setbuf.c b/src/libc/stdio/setbuf.c new file mode 100644 index 0000000..0163c93 --- /dev/null +++ b/src/libc/stdio/setbuf.c @@ -0,0 +1,11 @@ +#include + +void setbuf(FILE * restrict fp, char * restrict buf) +{ + if(buf) { + setvbuf(fp, buf, _IOFBF, BUFSIZ); + } + else { + setvbuf(fp, buf, _IONBF, 0); + } +} diff --git a/src/libc/stdio/setvbuf.c b/src/libc/stdio/setvbuf.c new file mode 100644 index 0000000..7accec8 --- /dev/null +++ b/src/libc/stdio/setvbuf.c @@ -0,0 +1,36 @@ +#include +#include +#include + +int setvbuf(FILE * restrict fp, char * restrict buf, int mode, size_t size) +{ + if(fp->bufmode != _IONBF) { + fflush(fp); + if(fp->bufowned) + free(fp->buf); + } + + fp->buf = NULL; + fp->bufowned = false; + fp->bufmode = _IONBF; + fp->bufsize = 0; + + if(mode == _IONBF) + return 0; + + if(buf) { + fp->buf = buf; + fp->bufsize = size; + fp->bufmode = mode; + } + else { + fp->buf = malloc(size); + if(!fp->buf) + return -1; + fp->bufsize = size; + fp->bufowned = true; + fp->bufmode = mode; + } + + return 0; +}