diff --git a/include/ft/all-tests.h b/include/ft/all-tests.h index 84e29f6..1fd8ebd 100644 --- a/include/ft/all-tests.h +++ b/include/ft/all-tests.h @@ -31,6 +31,8 @@ extern ft_test ft_stdio_open; extern ft_test ft_stdio_simple_read; extern ft_test ft_stdio_simple_write; extern ft_test ft_stdio_line_buffering; +extern ft_test ft_stdio_update_ungetc; +extern ft_test ft_stdio_append; /* stdlib */ extern ft_test ft_stdlib_arith; diff --git a/src/main.c b/src/main.c index 2b2524a..13eba97 100644 --- a/src/main.c +++ b/src/main.c @@ -47,6 +47,8 @@ ft_list headers_libc[] = { &ft_stdio_simple_read, &ft_stdio_simple_write, &ft_stdio_line_buffering, + &ft_stdio_update_ungetc, + &ft_stdio_append, NULL, }}, { _("stdlib.h", ""), (ft_test*[]){ diff --git a/src/stdio/files.c b/src/stdio/files.c index b9d912a..a72be32 100644 --- a/src/stdio/files.c +++ b/src/stdio/files.c @@ -29,8 +29,8 @@ static void _stdio_open_switch(ft_test *t) /* Try to open it with */ DO_E(fp, fopen("ft_stdio.txt", "r"), t, "%p"); ft_assert(t, fp != NULL); + if(fp == NULL) return; ft_log_FILE(t, "", fp); - if(!fp) return; /* Change buffer settings a couple times */ DO_E(rc, setvbuf(fp, NULL, _IONBF, 0), t, "%d"); @@ -46,15 +46,19 @@ static void _stdio_open_switch(ft_test *t) /* Reopen with different permissions */ DO_E(fp, freopen("ft_stdio.txt", "w+", fp), t, "%p"); ft_assert(t, fp != NULL); + if(fp == NULL) return; ft_log_FILE(t, "", fp); DO_E(fp, freopen("ft_stdio.txt", "rb+", fp), t, "%p"); ft_assert(t, fp != NULL); + if(fp == NULL) return; ft_log_FILE(t, "", fp); DO_E(fp, freopen("ft_stdio.txt", "ab", fp), t, "%p"); ft_assert(t, fp != NULL); + if(fp == NULL) return; ft_log_FILE(t, "", fp); DO_E(fp, freopen("ft_stdio.txt", "w", fp), t, "%p"); ft_assert(t, fp != NULL); + if(fp == NULL) return; ft_log_FILE(t, "", fp); DO_E(rc, fclose(fp), t, "%d"); @@ -80,7 +84,7 @@ static char const *filler = "defined) is advanced by the number of characters successfully read. If an " "error occurs, the resulting value of the file position indicator for the " "stream is indeterminate. If a partial element is read, its value is " -"indeterminate.\n"; /* length 591 */ +"indeterminate.\n"; /* ISO/IEC 9899:1999, 7.19.8.1ยง2 (length 591) */ static void _ft_stdio_simple_read_switch(ft_test *t) { @@ -100,6 +104,7 @@ static void _ft_stdio_simple_read_switch(ft_test *t) /* Read it through a buffer of size 128 */ DO_E(fp, fopen("ft_stdio.txt", "r"), t, "%p"); ft_assert(t, fp != NULL); + if(fp == NULL) return; ft_log_FILE(t, "", fp); DO_E(rc, setvbuf(fp, NULL, _IOFBF, 128), t, "%d"); ft_assert(t, rc == 0); @@ -264,6 +269,7 @@ static void _ft_stdio_simple_write_switch(ft_test *t) /* Open a new file */ DO_E(fp, fopen("ft_stdio.txt", "w"), t, "%p"); ft_assert(t, fp != NULL); + if(fp == NULL) return; ft_log_FILE(t, "", fp); DO_E(rc, setvbuf(fp, NULL, _IOFBF, 128), t, "%d"); ft_assert(t, rc == 0); @@ -347,6 +353,7 @@ static void _ft_stdio_simple_write_switch(ft_test *t) /* Check the new file size */ DO_E(fp, fopen("ft_stdio.txt", "r"), t, "%p"); ft_assert(t, fp != NULL); + if(fp == NULL) return; DO_E(rc, fseek(fp, 0, SEEK_END), t, "%d"); ft_assert(t, rc == 0); DO_E(rc, ftell(fp), t, "%d"); @@ -373,6 +380,7 @@ static void _ft_stdio_line_buffering_switch(ft_test *t) /* Open a new line-buffered file */ DO_E(fp, fopen("ft_stdio.txt", "w"), t, "%p"); ft_assert(t, fp != NULL); + if(fp == NULL) return; ft_log_FILE(t, "", fp); DO_E(rc, setvbuf(fp, NULL, _IOLBF, 128), t, "%d"); ft_assert(t, rc == 0); @@ -424,3 +432,241 @@ ft_test ft_stdio_line_buffering = { .name = "Writing with line buffering", .function = _ft_stdio_line_buffering, }; + +static void _ft_stdio_update_ungetc_switch(ft_test *t) +{ + FILE *fp; + int rc; + char str[128]; + + /* Create a file in update mode with w+ */ + DO_E(fp, fopen("ft_stdio.txt", "w+"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) return; + ft_log_FILE(t, "", fp); + /* Write some data, then re-read it, then re-write it */ + DO_E(rc, fwrite(filler, 1, 64, fp), t, "%d"); + ft_assert(t, rc == 64); + ft_log_FILE(t, "", fp); + DO_E(rc, fseek(fp, 0, SEEK_SET), t, "%d"); + ft_assert(t, rc == 0); + ft_log_FILE(t, "", fp); + DO_E(rc, fread(str, 1, 16, fp), t, "%d"); + ft_assert(t, rc == 16); + ft_log_FILE(t, "", fp); + DO_E(rc, fread(str, 1, 48, fp), t, "%d"); + ft_assert(t, rc == 48); + ft_log_FILE(t, "", fp); + ft_assert(t, ftell(fp) == 64); + DO_E(rc, fseek(fp, -16, SEEK_CUR), t, "%d"); + ft_assert(t, rc == 0); + DO_E(rc, fwrite(str, 1, 48, fp), t, "%d"); + ft_assert(t, rc == 48); + ft_log_FILE(t, "", fp); + DO_E(rc, fwrite(str, 1, 16, fp), t, "%d"); + ft_assert(t, rc == 16); + ft_log_FILE(t, "", fp); + ft_assert(t, ftell(fp) == 112); + /* Re-read it and check it */ + rewind(fp); + ft_log(t, "rewind(fp)\n"); + ft_log_FILE(t, "", fp); + DO_E(rc, fread(str, 1, 112, fp), t, "%d"); + ft_assert(t, rc == 112); + ft_log_FILE(t, "", fp); + ft_assert(t, !memcmp(str, filler, 48)); + ft_assert(t, !memcmp(str+48, filler+16, 48)); + ft_assert(t, !memcmp(str+96, filler+16, 16)); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); + ft_log(t, "\n"); + + /* Reopen that file with r+ */ + DO_E(fp, fopen("ft_stdio.txt", "r+"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) return; + ft_log_FILE(t, "", fp); + /* ungetc() some characters before position 0 */ + DO_E(rc, ungetc('#', fp), t, "'%c'"); + ft_assert(t, rc == '#'); + ft_log_FILE(t, "", fp); + DO_E(rc, ungetc('!', fp), t, "'%c'"); + ft_assert(t, rc == '!'); + ft_log_FILE(t, "", fp); + DO_E(rc, ungetc('$', fp), t, "'%c'"); + ft_assert(t, rc == '$'); + ft_log_FILE(t, "", fp); + /* Read them back along with some actual data */ + DO_E(rc, fread(str, 1, 51, fp), t, "%d"); + ft_assert(t, rc == 51); + ft_log_FILE(t, "", fp); + ft_assert(t, !memcmp(str, "$!#", 3) && !memcmp(str+3, filler, 48)); + /* Do it again after position 0 */ + DO_E(rc, ungetc('#', fp), t, "'%c'"); + ft_assert(t, rc == '#'); + ft_log_FILE(t, "", fp); + DO_E(rc, ungetc('!', fp), t, "'%c'"); + ft_assert(t, rc == '!'); + ft_log_FILE(t, "", fp); + DO_E(rc, ungetc('$', fp), t, "'%c'"); + ft_assert(t, rc == '$'); + ft_log_FILE(t, "", fp); + /* Read them back as well as the second segment */ + DO_E(rc, fread(str, 1, 51, fp), t, "%d"); + ft_assert(t, rc == 51); + ft_log_FILE(t, "", fp); + ft_assert(t, !memcmp(str, "$!#", 3) && !memcmp(str+3, filler+16, 48)); + /* Push some characters but discard them by rewind() */ + DO_E(rc, ungetc('#', fp), t, "'%c'"); + ft_assert(t, rc == '#'); + ft_log_FILE(t, "", fp); + DO_E(rc, fseek(fp, 0, SEEK_SET), t, "%d"); + ft_assert(t, rc == 0); + ft_log_FILE(t, "", fp); + DO_E(rc, fread(str, 1, 48, fp), t, "%d"); + ft_assert(t, rc == 48); + ft_log_FILE(t, "", fp); + ft_assert(t, !memcmp(str, filler, 48)); + /* Discard buffered data by a write (nonstandard but supported) */ + DO_E(rc, fwrite(filler, 1, 48, fp), t, "%d"); + ft_assert(t, rc == 48); + ft_log_FILE(t, "", fp); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); + + /* Try ungetc() like previousy but with a non-buffered stream */ + DO_E(fp, fopen("ft_stdio.txt", "r+"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) return; + ft_log_FILE(t, "", fp); + DO_E(rc, setvbuf(fp, NULL, _IONBF, 0), t, "%d"); + ft_assert(t, rc == 0); + ft_log_FILE(t, "", fp); + DO_E(rc, fseek(fp, 0, SEEK_END), t, "%d"); + ft_assert(t, rc == 0); + DO_E(rc, ftell(fp), t, "%d"); + ft_assert(t, rc == 112); + DO_E(rc, fseek(fp, 0, SEEK_SET), t, "%d"); + ft_assert(t, rc == 0); + /* ungetc() some characters before position 0 */ + DO_E(rc, ungetc('#', fp), t, "'%c'"); + ft_assert(t, rc == '#'); + ft_log_FILE(t, "", fp); + DO_E(rc, ungetc('!', fp), t, "'%c'"); + ft_assert(t, rc == '!'); + ft_log_FILE(t, "", fp); + DO_E(rc, ungetc('$', fp), t, "'%c'"); + ft_assert(t, rc == '$'); + ft_log_FILE(t, "", fp); + /* Read them back along with some actual data */ + DO_E(rc, fread(str, 1, 51, fp), t, "%d"); + ft_assert(t, rc == 51); + ft_log_FILE(t, "", fp); + ft_assert(t, !memcmp(str, "$!#", 3) && !memcmp(str+3, filler, 48)); + /* Do it again after position 0 */ + DO_E(rc, ungetc('#', fp), t, "'%c'"); + ft_assert(t, rc == '#'); + ft_log_FILE(t, "", fp); + DO_E(rc, ungetc('!', fp), t, "'%c'"); + ft_assert(t, rc == '!'); + ft_log_FILE(t, "", fp); + DO_E(rc, ungetc('$', fp), t, "'%c'"); + ft_assert(t, rc == '$'); + ft_log_FILE(t, "", fp); + /* Read them back as well as the second segment */ + DO_E(rc, fread(str, 1, 51, fp), t, "%d"); + ft_assert(t, rc == 51); + ft_log_FILE(t, "", fp); + ft_assert(t, !memcmp(str, "$!#", 3) && !memcmp(str+3, filler, 48)); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); +} + +static void _ft_stdio_update_ungetc(ft_test *t) +{ + gint_world_switch(GINT_CALL(_ft_stdio_update_ungetc_switch, + (void *)t)); +} + +ft_test ft_stdio_update_ungetc = { + .name = "Update modes and ungetc()", + .function = _ft_stdio_update_ungetc, +}; + +static void _ft_stdio_append_switch(ft_test *t) +{ + FILE *fp; + int rc; + char str[40]; + + /* Remove the file if it exists */ + DO_E(rc, remove("ft_append.txt"), t, "%d"); + ft_assert(t, rc == 0 || (rc == -1 && errno == ENOENT)); + + /* Append some data with mode a */ + DO_E(fp, fopen("ft_append.txt", "a"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) return; + ft_log_FILE(t, "", fp); + DO_E(rc, fwrite("Line #1\n", 1, 8, fp), t, "%d"); + ft_assert(t, rc == 8); + ft_log_FILE(t, "", fp); + DO_E(rc, fseek(fp, 0, SEEK_SET), t, "%d"); + ft_assert(t, rc == 0); + ft_assert(t, ftell(fp) == 0); + DO_E(rc, fwrite("Line #2\n", 1, 8, fp), t, "%d"); + ft_assert(t, rc == 8); + ft_log_FILE(t, "", fp); + DO_E(rc, ftell(fp), t, "%d"); + ft_assert(t, rc == 16); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); + + /* Append more with a+ */ + DO_E(fp, fopen("ft_append.txt", "a+"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) return; + DO_E(rc, fseek(fp, 0, SEEK_SET), t, "%d"); + ft_assert(t, rc == 0); + ft_assert(t, ftell(fp) == 0); + DO_E(rc, fread(str, 1, 8, fp), t, "%d"); + ft_assert(t, rc == 8); + ft_assert(t, ftell(fp) == 8); + ft_log_FILE(t, "", fp); + DO_E(rc, fwrite(str, 1, 8, fp), t, "%d"); + ft_assert(t, rc == 8); + ft_log_FILE(t, "", fp); + DO_E(rc, ftell(fp), t, "%d"); + ft_assert(t, rc == 24); + DO_E(rc, fseek(fp, 8, SEEK_SET), t, "%d"); + ft_assert(t, rc == 0); + DO_E(rc, fread(str, 1, 16, fp), t, "%d"); + ft_assert(t, rc == 16); + ft_log_FILE(t, "", fp); + DO_E(rc, fseek(fp, -8, SEEK_END), t, "%d"); + ft_assert(t, rc == 0); + ft_assert(t, ftell(fp) == 16); + DO_E(rc, fwrite(str, 1, 16, fp), t, "%d"); + ft_assert(t, rc == 16); + ft_log_FILE(t, "", fp); + DO_E(rc, ftell(fp), t, "%d"); + ft_assert(t, rc == 40); + DO_E(rc, fseek(fp, 0, SEEK_SET), t, "%d"); + ft_assert(t, rc == 0); + DO_E(rc, fread(str, 1, 40, fp), t, "%d"); + ft_assert(t, rc == 40); + ft_assert(t, !memcmp(str, + "Line #1\nLine #2\nLine #1\nLine #2\nLine #1\n", 40)); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); +} + +static void _ft_stdio_append(ft_test *t) +{ + gint_world_switch(GINT_CALL(_ft_stdio_append_switch, (void *)t)); +} + +ft_test ft_stdio_append = { + .name = "Append modes", + .function = _ft_stdio_append, +}; diff --git a/src/stdio/util.h b/src/stdio/util.h index b60e02e..89a9494 100644 --- a/src/stdio/util.h +++ b/src/stdio/util.h @@ -25,8 +25,8 @@ static inline void ft_log_FILE(ft_test *t, char const *prefix, FILE *fp) fp->bufmode == _IOLBF ? "_IOLBF" : fp->bufmode == _IOFBF ? "_IOFBF" : "_???"); - if(fp->bufmode != _IONBF) { - ft_log(t, " %p/%d", fp->buf, fp->bufsize); + if(fp->buf) { + ft_log(t, " %s%d", fp->buf ? "" : "NULL/", fp->bufsize); if(fp->bufpos == 0) ft_log(t, " EMPTY"); @@ -34,6 +34,9 @@ static inline void ft_log_FILE(ft_test *t, char const *prefix, FILE *fp) ft_log(t, " READ %d/%d", fp->bufpos, fp->bufread); else if(fp->bufdir == __FILE_BUF_WRITE) ft_log(t, " WRITE %d", fp->bufpos); + + if(fp->bufungetc > 0) + ft_log(t, " UNGETC %d", fp->bufungetc); } ft_log(t, "}\n");