/* * vlibc — buffered stdio test (todo 15). * * Exercises the stdio FILE core end to end: * * 1. Round trip: fopen "w+b", fwrite a 1 MiB deterministic binary * pattern, fflush, ftello == size, fseek(0, SEEK_SET), fread the * pattern back, byte-identical. Then read past the end: fgetc == * EOF, feof set, ferror clear. * 2. Text lines: fputs lines, fgets line boundaries (newline kept, * NUL-terminated), the n-1 clamp, NULL + feof at end of file. * 3. Seek: fseeko SEEK_SET/CUR/END positions, ftell/ftello after * fgetc, fgetpos/fsetpos round trip, rewind. * 4. ungetc: pushback of a read character yields the same character; * ungetc(EOF) == EOF; a second pushback fails; fseek discards the * pushed-back byte. * 5. fflush mid-stream then continue; fflush(NULL) flushes an open * file stream and a freopen'd stdout. * 6. setvbuf modes: _IONBF (NULL buffer), _IOFBF (user buffer), * _IOLBF flushes on newline without an explicit fflush; setbuf * with NULL selects unbuffered. * 7. fdopen over a raw descriptor; fileno returns it; fclose closes * it (probed with raw fcntl). * 8. freopen: re-open a different path on the same FILE; NULL path * changes the mode only. * 9. remove/rename lifecycle; tmpfile write+rewind+read. * 10. Level 2: tmpnam/ctermid/setbuffer/setlinebuf/fopen64. * * Failure mode (-f): fseek past EOF then fread returns 0 with feof set * and no crash; fopen of a nonexistent path returns NULL; fdopen(-1) * returns NULL; fputc on a read-only stream returns EOF with ferror; * remove of a nonexistent path returns -1. The -f mode makes the * library write errno in several places and then leaves through a raw * SYS_exit_group before any host-libc cleanup runs (the * tests/syscall_test.c discipline). * * The default mode keeps the stream error paths free of library errno * writes, except one setvbuf EINVAL case which is bracketed with a * save/restore of the host TCB slot 1 (the tests/test_env.c pattern). * * errno is never READ here. All diagnostics go through raw SYS_write; * the only header included is vlibc's own (host headers would * pull GCC internals shadowed by -Iinclude). * * Not part of the library proper; compiled manually for this todo (the * tests/ + make check wiring is owned by a later todo). */ #include #include "../src/internal/syscall.h" static int failures; /* Write a NUL-terminated string to fd via the raw syscall layer. */ static void say(int fd, const char *s) { long n = 0; while (s[n] != '\0') { n++; } __syscall3(SYS_write, fd, (long)s, n); } /* Write v in decimal to fd. */ static void say_dec(int fd, unsigned long v) // NOLINT(bugprone-easily-swappable-parameters) { char buf[24]; int i = (int)sizeof(buf); buf[--i] = '\0'; do { buf[--i] = (char)('0' + (v % 10)); v /= 10; } while (v != 0); __syscall3(SYS_write, fd, (long)(buf + i), (long)(sizeof(buf) - 1 - i)); } static void check(int cond, const char *what) { if (cond) { say(1, "PASS: "); say(1, what); say(1, "\n"); } else { say(2, "FAIL: "); say(2, what); say(2, "\n"); failures++; } } /* Byte-compare two buffers. noipa keeps -O2 from folding the check. * From here through the scenarios the analyzer is waived: the stream * checker cannot model vlibc's FILE semantics (deliberate reads at EOF, * reads after a failed probe, buffers written by our own fread), and its * raw-syscall modeling flags byte comparisons as reading garbage. */ // NOLINTBEGIN(clang-analyzer-unix.Stream, clang-analyzer-core.UndefinedBinaryOperatorResult, // clang-analyzer-unix.StdCLibraryFunctions) static __attribute__((noipa)) int mem_eq(const unsigned char *a, const unsigned char *b, unsigned long n) { unsigned long i; for (i = 0; i < n; i++) { if (a[i] != b[i]) { return 0; } } return 1; } /* Byte-compare two NUL-terminated strings. */ static __attribute__((noipa)) int str_eq(const char *a, const char *b) { if (a == NULL || b == NULL) { return a == b; } while (*a == *b && *a != '\0') { a++; b++; } return *a == *b; } /* True when s starts with the given prefix (used by the L2 scenario). */ #if VLIBC_LEVEL_GE(2) static __attribute__((noipa)) int prefix_eq(const char *s, const char *prefix) { while (*prefix != '\0') { if (*s != *prefix) { return 0; } s++; prefix++; } return 1; } #endif /* Save/restore the host libc's TCB slot 1 (%fs:0+8), see the banner. */ static unsigned long tcb_slot1_save(void) { return *(unsigned long *)((char *)__builtin_thread_pointer() + 8); } static void tcb_slot1_restore(unsigned long v) { *(unsigned long *)((char *)__builtin_thread_pointer() + 8) = v; } /* Deterministic pattern generator (64-bit LCG, like the library's). */ // NOLINTBEGIN(bugprone-easily-swappable-parameters) static void fill_pattern(unsigned char *p, unsigned long n, unsigned long long seed) { unsigned long i; unsigned long long s = seed; for (i = 0; i < n; i++) { s = (s * 6364136223846793005ULL) + 1ULL; p[i] = (unsigned char)(s >> 33); } } // NOLINTEND(bugprone-easily-swappable-parameters) /* Read a whole small file through raw syscalls into buf; returns size. */ static long raw_read_all(const char *path, char *buf, unsigned long cap) { long fd = __syscall4(SYS_openat, -100, (long)path, 0x0, 0); long got = -1; long total = 0; if (fd < 0) { return -1; } while (total < (long)cap) { got = __syscall3(SYS_read, fd, (long)(buf + total), (long)(cap - (unsigned long)total)); if (got <= 0) { break; } total += got; } __syscall1(SYS_close, fd); return got < 0 ? -1 : total; } static unsigned char pattern[1 << 20]; static unsigned char back[1 << 20]; /* 1. Binary round trip. */ static void roundtrip_scenario(void) { const char path[] = "/tmp/vlibc-test-stdio-roundtrip.bin"; FILE *f; unsigned long w; unsigned long r; f = fopen(path, "w+b"); check(f != NULL, "fopen w+b succeeds"); if (f == NULL) { return; } fill_pattern(pattern, sizeof(pattern), 42); w = (unsigned long)fwrite(pattern, 1, sizeof(pattern), f); check(w == sizeof(pattern), "fwrite writes the full 1 MiB"); check(fflush(f) == 0, "fflush after fwrite returns 0"); check(ftello(f) == (off_t)sizeof(pattern), "ftello after the write == size"); check(fseek(f, 0, SEEK_SET) == 0, "fseek(0, SEEK_SET) returns 0"); r = (unsigned long)fread(back, 1, sizeof(back), f); check(r == sizeof(back), "fread reads the full 1 MiB"); check(mem_eq(pattern, back, sizeof(pattern)), "1 MiB round trip is byte-identical"); check(feof(f) == 0, "feof is clear after reading exactly to the end"); check(fgetc(f) == EOF, "fgetc past the end returns EOF"); check(feof(f) != 0, "feof is set after reading past the end"); check(ferror(f) == 0, "ferror stays clear at EOF"); clearerr(f); check(feof(f) == 0, "clearerr clears feof"); check(fclose(f) == 0, "fclose returns 0"); check(remove(path) == 0, "remove deletes the round-trip file"); } /* 2. Text lines. */ static void text_scenario(void) { const char path[] = "/tmp/vlibc-test-stdio-text.txt"; FILE *f; char line[128]; char small[4]; f = fopen(path, "w"); check(f != NULL, "text fopen w succeeds"); if (f == NULL) { return; } check(fputs("alpha\n", f) >= 0, "fputs line 1 returns non-negative"); check(fputs("beta gamma\n", f) >= 0, "fputs line 2 returns non-negative"); check(fputs("delta-no-newline", f) >= 0, "fputs final line without newline"); check(fclose(f) == 0, "fclose text writer returns 0"); f = fopen(path, "r"); check(f != NULL, "text fopen r succeeds"); if (f == NULL) { return; } check(fgets(line, (int)sizeof(line), f) == line, "fgets line 1 returns s"); check(str_eq(line, "alpha\n"), "fgets line 1 == \"alpha\\n\""); check(fgets(line, (int)sizeof(line), f) == line, "fgets line 2 returns s"); check(str_eq(line, "beta gamma\n"), "fgets line 2 == \"beta gamma\\n\""); check(fgets(line, (int)sizeof(line), f) == line, "fgets final line returns s"); check(str_eq(line, "delta-no-newline"), "fgets final line has no newline"); check(fgets(line, (int)sizeof(line), f) == NULL, "fgets at EOF returns NULL"); check(feof(f) != 0, "feof is set after the NULL fgets"); check(fclose(f) == 0, "fclose text reader returns 0"); f = fopen(path, "r"); check(fgets(small, (int)sizeof(small), f) == small, "fgets with n=4 returns s"); check(small[0] == 'a' && small[1] == 'l' && small[2] == 'p' && small[3] == '\0', "fgets clamps to n-1 chars and NUL-terminates"); check(fclose(f) == 0, "fclose after the clamp check returns 0"); check(remove(path) == 0, "remove deletes the text file"); } /* 3. Seek and position queries. */ static void seek_scenario(void) { const char path[] = "/tmp/vlibc-test-stdio-seek.bin"; FILE *f; fpos_t saved; int c; f = fopen(path, "w+"); check(f != NULL, "seek fopen w+ succeeds"); if (f == NULL) { return; } check(fputs("0123456789", f) >= 0, "seek setup writes digits"); check(fseeko(f, 3, SEEK_SET) == 0, "fseeko(3, SEEK_SET) returns 0"); c = fgetc(f); check(c == '3', "fgetc after SEEK_SET 3 == '3'"); check(ftello(f) == 4, "ftello after reading one char == 4"); check(fseek(f, 2, SEEK_CUR) == 0, "fseek(2, SEEK_CUR) returns 0"); c = fgetc(f); check(c == '6', "fgetc after SEEK_CUR 2 == '6'"); check(ftell(f) == 7, "ftell after the SEEK_CUR read == 7"); check(fseek(f, -2, SEEK_END) == 0, "fseek(-2, SEEK_END) returns 0"); c = fgetc(f); check(c == '8', "fgetc after SEEK_END -2 == '8'"); check(fgetpos(f, &saved) == 0, "fgetpos returns 0"); check((off_t)saved == 9, "fgetpos saved position == 9"); check(fseek(f, 0, SEEK_SET) == 0, "fseek(0, SEEK_SET) returns 0"); c = fgetc(f); check(c == '0', "fgetc at the start == '0'"); check(fsetpos(f, &saved) == 0, "fsetpos returns 0"); c = fgetc(f); check(c == '9', "fgetc after fsetpos == '9'"); rewind(f); // NOLINT: rewind has no error return; its effect is asserted below check(ftello(f) == 0, "rewind positions at 0"); c = fgetc(f); check(c == '0', "fgetc after rewind == '0'"); check(fclose(f) == 0, "fclose seek stream returns 0"); check(remove(path) == 0, "remove deletes the seek file"); } /* 4. ungetc pushback. */ static void ungetc_scenario(void) { const char path[] = "/tmp/vlibc-test-stdio-ungetc.txt"; FILE *f; int c; f = fopen(path, "w+"); check(f != NULL, "ungetc fopen w+ succeeds"); if (f == NULL) { return; } check(fputs("hello", f) >= 0, "ungetc setup writes \"hello\""); check(fseek(f, 0, SEEK_SET) == 0, "ungetc setup seeks back"); c = fgetc(f); check(c == 'h', "fgetc reads 'h'"); check(ungetc(c, f) == 'h', "ungetc('h') returns 'h'"); c = fgetc(f); check(c == 'h', "fgetc after ungetc reads 'h' again"); check(ungetc(EOF, f) == EOF, "ungetc(EOF) returns EOF"); check(ungetc('x', f) == 'x', "ungetc('x') returns 'x'"); check(ungetc('y', f) == EOF, "second pushback without a read fails"); c = fgetc(f); check(c == 'x', "the first pushed-back byte is still readable"); check(ungetc('z', f) == 'z', "ungetc('z') returns 'z'"); check(fseek(f, 0, SEEK_SET) == 0, "fseek after ungetc returns 0"); c = fgetc(f); check(c == 'h', "fseek discards the pushed-back byte"); check(fclose(f) == 0, "fclose ungetc stream returns 0"); check(remove(path) == 0, "remove deletes the ungetc file"); } /* 5. fflush mid-stream and fflush(NULL). */ static void flush_scenario(void) { const char path[] = "/tmp/vlibc-test-stdio-flush.txt"; const char outpath[] = "/tmp/vlibc-test-stdio-stdout.txt"; char buf[64]; FILE *f; long got; long saved_fd; int r1; int r2; int r3; int r4; f = fopen(path, "w"); check(f != NULL, "flush fopen w succeeds"); if (f == NULL) { return; } check(fwrite("abc", 1, 3, f) == 3, "fwrite the first half"); check(fflush(f) == 0, "fflush mid-stream returns 0"); check(fwrite("def", 1, 3, f) == 3, "fwrite the second half"); saved_fd = __syscall1(SYS_dup, 1); check(saved_fd >= 0, "dup(1) saves the stdout descriptor"); /* No check() output while fd 1 is the outpath file: the PASS lines * would pollute the very file content the checks assert below. */ r1 = (freopen(outpath, "w", stdout) == stdout); r2 = (fputs("STDOUT-MARKER", stdout) >= 0); r3 = (fflush(NULL) == 0); r4 = (fclose(f) == 0); __syscall2(SYS_dup2, saved_fd, 1); __syscall1(SYS_close, saved_fd); check(r1, "freopen stdout to a file"); check(r2, "fputs to the freopen'd stdout"); check(r3, "fflush(NULL) returns 0"); check(r4, "fclose after fflush(NULL) returns 0"); got = raw_read_all(path, buf, sizeof(buf)); check(got == 6 && mem_eq((const unsigned char *)buf, (const unsigned char *)"abcdef", 6), "fflush(NULL) left the whole file content"); got = raw_read_all(outpath, buf, sizeof(buf)); check(got == 13 && mem_eq((const unsigned char *)buf, (const unsigned char *)"STDOUT-MARKER", 13), "fflush(NULL) flushed the freopen'd stdout"); check(remove(path) == 0, "remove deletes the flush file"); check(remove(outpath) == 0, "remove deletes the stdout file"); } /* 6. Buffering modes. */ static void setvbuf_scenario(void) { const char path1[] = "/tmp/vlibc-test-stdio-setvbuf1.txt"; const char path2[] = "/tmp/vlibc-test-stdio-setvbuf2.txt"; const char path3[] = "/tmp/vlibc-test-stdio-setvbuf3.txt"; char buf[64]; static char userbuf[512]; FILE *f; unsigned long saved; long got; f = fopen(path1, "w"); check(f != NULL, "setvbuf fopen w succeeds"); if (f == NULL) { return; } check(setvbuf(f, NULL, _IONBF, 0) == 0, "setvbuf _IONBF NULL returns 0"); check(fputs("AB", f) >= 0, "fputs through the unbuffered stream"); got = raw_read_all(path1, buf, sizeof(buf)); check(got == 2 && buf[0] == 'A' && buf[1] == 'B', "_IONBF data reaches the file without fflush"); check(setvbuf(f, NULL, _IOFBF, BUFSIZ) == 0, "setvbuf _IOFBF NULL returns 0"); check(fputs("CD", f) >= 0, "fputs through the fully buffered stream"); check(fflush(f) == 0, "fflush the fully buffered stream"); check(setvbuf(f, userbuf, _IOFBF, sizeof(userbuf)) == 0, "setvbuf with a user buffer returns 0"); check(fputs("EF", f) >= 0, "fputs through the user buffer"); check(fflush(f) == 0, "fflush the user buffer"); got = raw_read_all(path1, buf, sizeof(buf)); check(got == 6 && mem_eq((const unsigned char *)buf, (const unsigned char *)"ABCDEF", 6), "all three modes wrote sequential content"); check(fclose(f) == 0, "fclose setvbuf stream returns 0"); check(remove(path1) == 0, "remove deletes the setvbuf file"); f = fopen(path2, "w"); check(setvbuf(f, NULL, _IOLBF, 0) == 0, "setvbuf _IOLBF NULL returns 0"); check(fputs("line-one\n", f) >= 0, "fputs a newline-terminated line"); got = raw_read_all(path2, buf, sizeof(buf)); check(got == 9 && mem_eq((const unsigned char *)buf, (const unsigned char *)"line-one\n", 9), "_IOLBF flushes on the newline without fflush"); check(fputs("line-two\n", f) >= 0, "fputs a second line"); check(fclose(f) == 0, "fclose line-buffered stream returns 0"); got = raw_read_all(path2, buf, sizeof(buf)); check(got == 18, "fclose flushed the second line"); check(remove(path2) == 0, "remove deletes the line-buffered file"); f = fopen(path3, "w"); setbuf(f, NULL); // NOLINT: setbuf has no error return; its effect is asserted below check(fputs("SB", f) >= 0, "fputs through setbuf(NULL) stream"); got = raw_read_all(path3, buf, sizeof(buf)); check(got == 2 && buf[0] == 'S' && buf[1] == 'B', "setbuf(NULL) data reaches the file without fflush"); check(fclose(f) == 0, "fclose setbuf stream returns 0"); check(remove(path3) == 0, "remove deletes the setbuf file"); saved = tcb_slot1_save(); check(setvbuf(f, NULL, 99, 0) == -1, "setvbuf with an invalid mode returns -1"); tcb_slot1_restore(saved); } /* 7. fdopen and fileno. */ static void fdopen_scenario(void) { const char path[] = "/tmp/vlibc-test-stdio-fdopen.txt"; char line[64]; FILE *g; long fd; long rc; fd = __syscall4(SYS_openat, -100, (long)path, (long)(0x2 | 0x40 | 0x200 /* RDWR|CREAT|TRUNC */), 0666); check(fd >= 0, "raw openat for fdopen succeeds"); if (fd < 0) { return; } g = fdopen((int)fd, "w+"); check(g != NULL, "fdopen wraps the descriptor"); if (g == NULL) { __syscall1(SYS_close, fd); return; } check(fileno(g) == (int)fd, "fileno returns the wrapped descriptor"); check(fputs("via-fdopen", g) >= 0, "fputs through the fdopen'd stream"); rewind(g); // NOLINT: rewind has no error return; its effect is asserted below check(fgets(line, (int)sizeof(line), g) == line, "fgets after rewind returns s"); check(str_eq(line, "via-fdopen"), "fgets reads back the fdopen'd content"); check(fclose(g) == 0, "fclose fdopen stream returns 0"); rc = __syscall3(SYS_fcntl, fd, 3 /* F_GETFL */, 0); check(rc < 0, "fclose closed the underlying descriptor"); check(remove(path) == 0, "remove deletes the fdopen file"); } /* 8. freopen. */ static void freopen_scenario(void) { const char path1[] = "/tmp/vlibc-test-stdio-freopen1.txt"; const char path2[] = "/tmp/vlibc-test-stdio-freopen2.txt"; char line[64]; FILE *f; f = fopen(path1, "w"); check(f != NULL, "freopen setup fopen w succeeds"); if (f == NULL) { return; } check(fputs("one", f) >= 0, "freopen setup writes the first file"); check(fclose(f) == 0, "fclose the first file"); f = fopen(path1, "r"); check(f != NULL, "freopen setup fopen r succeeds"); if (f == NULL) { return; } check(freopen(NULL, "r", f) == f, "freopen with NULL path changes the mode only"); check(fgets(line, (int)sizeof(line), f) == line, "fgets on the mode-changed stream"); check(str_eq(line, "one"), "the mode change kept the descriptor"); check(freopen(path2, "w", f) == f, "freopen rebinds to the second path"); check(fputs("two", f) >= 0, "fputs through the rebound stream"); check(fclose(f) == 0, "fclose the rebound stream"); check(fopen(path2, "r") != NULL, "the second path exists"); check(remove(path1) == 0, "remove deletes the first freopen file"); check(remove(path2) == 0, "remove deletes the second freopen file"); } /* 9. remove/rename and tmpfile. */ static void name_scenario(void) { const char path1[] = "/tmp/vlibc-test-stdio-name1.txt"; const char path2[] = "/tmp/vlibc-test-stdio-name2.txt"; char line[64]; FILE *f; FILE *t; f = fopen(path1, "w"); check(f != NULL, "name fopen w succeeds"); if (f == NULL) { return; } check(fclose(f) == 0, "fclose the name file"); check(rename(path1, path2) == 0, "rename returns 0"); check(fopen(path1, "r") == NULL, "the old name is gone after rename"); check(fopen(path2, "r") != NULL, "the new name exists after rename"); check(remove(path2) == 0, "remove returns 0"); check(fopen(path2, "r") == NULL, "the removed file is gone"); t = tmpfile(); check(t != NULL, "tmpfile returns a stream"); if (t != NULL) { check(fputs("temp-data", t) >= 0, "fputs through the tmpfile stream"); rewind(t); // NOLINT: rewind has no error return; its effect is asserted below check(fgets(line, (int)sizeof(line), t) == line, "fgets after rewind returns s"); check(str_eq(line, "temp-data"), "tmpfile content round-trips"); check(fclose(t) == 0, "fclose tmpfile returns 0"); } } #if VLIBC_LEVEL_GE(2) /* 10. Level 2 additions. */ static void level2_scenario(void) { const char path[] = "/tmp/vlibc-test-stdio-l2.txt"; char tname[L_tmpnam]; char buf[64]; char *p; FILE *f; static char userbuf[128]; long got; p = tmpnam(tname); check(p == tname, "tmpnam returns its argument"); check(prefix_eq(tname, "/tmp/"), "tmpnam produces a /tmp name"); check(tmpnam(NULL) != NULL, "tmpnam with NULL uses a static buffer"); p = ctermid(NULL); check(p != NULL && str_eq(p, "/dev/tty"), "ctermid returns \"/dev/tty\""); f = fopen64(path, "w"); check(f != NULL, "fopen64 opens a stream"); if (f != NULL) { check(fputs("l2", f) >= 0, "fputs through the fopen64 stream"); check(fclose(f) == 0, "fclose the fopen64 stream"); got = raw_read_all(path, buf, sizeof(buf)); check(got == 2 && buf[0] == 'l' && buf[1] == '2', "fopen64 wrote the content"); check(remove(path) == 0, "remove deletes the fopen64 file"); } f = fopen(path, "w"); setbuffer(f, userbuf, sizeof(userbuf)); check(fputs("setbuffer", f) >= 0, "fputs through the setbuffer stream"); check(fflush(f) == 0, "fflush the setbuffer stream"); setlinebuf(f); check(fputs("line\n", f) >= 0, "fputs a line through the setlinebuf stream"); got = raw_read_all(path, buf, sizeof(buf)); check(got == 14 && mem_eq((const unsigned char *)buf, (const unsigned char *)"setbufferline\n", 14), "setlinebuf flushed on the newline"); check(fclose(f) == 0, "fclose the setlinebuf stream"); check(remove(path) == 0, "remove deletes the setlinebuf file"); } #endif /* VLIBC_LEVEL_GE(2) */ /* Failure scenarios (-f). */ static int failure_scenarios(void) { const char path[] = "/tmp/vlibc-test-stdio-fail.bin"; unsigned char small[8]; FILE *f; int rc; f = fopen(path, "w+b"); if (f == NULL) { say(2, "FAIL: -f setup fopen failed\n"); failures++; } else { check(fwrite("abc", 1, 3, f) == 3, "-f setup fwrite"); check(fseek(f, 100, SEEK_SET) == 0, "fseek past EOF succeeds"); check(ftello(f) == 100, "ftello after fseek past EOF == 100"); rc = (int)fread(small, 1, sizeof(small), f); check(rc == 0, "fread past EOF returns 0 items"); check(feof(f) != 0, "feof is set after the past-EOF read"); check(ferror(f) == 0, "ferror stays clear: it was EOF, not an error"); check(fclose(f) == 0, "fclose the -f stream"); check(remove(path) == 0, "remove the -f file"); } check(fopen("/tmp/vlibc-no-such-file-000", "r") == NULL, "fopen of a nonexistent path returns NULL"); check(fdopen(-1, "r") == NULL, "fdopen(-1) returns NULL"); f = fopen(path, "r"); if (f != NULL) { check(fputc('x', f) == EOF, "fputc on a read-only stream returns EOF"); check(ferror(f) != 0, "ferror is set after the invalid fputc"); check(fclose(f) == 0, "fclose the read-only stream"); } check(remove("/tmp/vlibc-no-such-file-000") == -1, "remove of a missing path returns -1"); return failures > 0 ? 1 : 0; } // NOLINTEND(clang-analyzer-unix.Stream, clang-analyzer-core.UndefinedBinaryOperatorResult, // clang-analyzer-unix.StdCLibraryFunctions) int main(int argc, char **argv) { if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f') { /* * The failure scenarios make the library write errno several * times; leave via the raw syscall so the host cleanup never * runs after those writes (see the banner). */ int rc = failure_scenarios(); __syscall1(SYS_exit_group, rc); return rc; /* not reached */ } roundtrip_scenario(); text_scenario(); seek_scenario(); ungetc_scenario(); flush_scenario(); setvbuf_scenario(); fdopen_scenario(); freopen_scenario(); name_scenario(); #if VLIBC_LEVEL_GE(2) level2_scenario(); #endif if (failures > 0) { say(2, "FAILED ("); say_dec(2, (unsigned long)failures); say(2, " check(s))\n"); return 1; } say(1, "all stdio tests passed\n"); return 0; }