From d769df34824e5f73c91d5f16c69078b02e29e5a8 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 6 Mar 2021 16:06:15 -0800 Subject: [PATCH] Fix issues with stdio needed for Lua See #61 --- libc/stdio/favail.c | 28 ---------- libc/stdio/fdopen.c | 2 +- libc/stdio/fflush.c | 4 +- libc/stdio/fmemopen.c | 1 - libc/stdio/fopenflags.c | 17 +++--- libc/stdio/fputc.c | 39 +++++++------- libc/stdio/fputcfb.c | 52 ------------------- libc/stdio/fseek.c | 9 ++-- libc/stdio/ftell.c | 22 +++++++- libc/stdio/fwrite.c | 10 +--- libc/stdio/setbuffer.c | 2 - libc/stdio/setvbuf.c | 2 - libc/stdio/stdio.h | 1 - libc/stdio/tmpfile.c | 3 +- libc/stdio/ungetc.c | 8 ++- libc/unicode/setlocale.c | 8 ++- .../stdio/{favail_test.c => fwrite_test.c} | 49 ++++++++++------- 17 files changed, 102 insertions(+), 155 deletions(-) delete mode 100644 libc/stdio/favail.c delete mode 100644 libc/stdio/fputcfb.c rename test/libc/stdio/{favail_test.c => fwrite_test.c} (67%) diff --git a/libc/stdio/favail.c b/libc/stdio/favail.c deleted file mode 100644 index fd0ec2fc2..000000000 --- a/libc/stdio/favail.c +++ /dev/null @@ -1,28 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/stdio.h" - -/* TODO(jart): Delete or rework */ - -/** - * Returns number of bytes available in stream buffer. - */ -unsigned favail(FILE *f) { - return ((f->end - f->beg - 1) & (f->size - 1)) + 1; -} diff --git a/libc/stdio/fdopen.c b/libc/stdio/fdopen.c index 7a6ceb652..3b05e83b5 100644 --- a/libc/stdio/fdopen.c +++ b/libc/stdio/fdopen.c @@ -40,7 +40,7 @@ FILE *fdopen(int fd, const char *mode) { f->bufmode = ischardev(fd) ? _IOLBF : _IOFBF; f->iomode = fopenflags(mode); f->size = BUFSIZ; - if ((f->buf = valloc(f->size))) { + if ((f->buf = malloc(f->size))) { if ((f->iomode & O_ACCMODE) != O_RDONLY) { __fflush_register(f); } diff --git a/libc/stdio/fflush.c b/libc/stdio/fflush.c index 18d120fb7..3f925ec32 100644 --- a/libc/stdio/fflush.c +++ b/libc/stdio/fflush.c @@ -50,9 +50,11 @@ int fflush(FILE *f) { } } } else if (f->fd != -1) { - while (!f->state && f->beg && !f->end) { + while (f->beg && !f->end) { if ((wrote = __fwritebuf(f)) != -1) { res += wrote; + } else { + break; } } } else if (f->beg && f->beg < f->size) { diff --git a/libc/stdio/fmemopen.c b/libc/stdio/fmemopen.c index d315ee52a..56d2f5e76 100644 --- a/libc/stdio/fmemopen.c +++ b/libc/stdio/fmemopen.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/popcnt.h" #include "libc/mem/mem.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" diff --git a/libc/stdio/fopenflags.c b/libc/stdio/fopenflags.c index 51192f7eb..268ea6111 100644 --- a/libc/stdio/fopenflags.c +++ b/libc/stdio/fopenflags.c @@ -16,29 +16,32 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/stdio.h" #include "libc/calls/calls.h" +#include "libc/stdio/stdio.h" #include "libc/sysv/consts/o.h" /** * Turns stdio flags description string into bitmask. */ int fopenflags(const char *mode) { - unsigned flags = 0; + unsigned omode, flags; + omode = flags = 0; do { if (*mode == 'r') { - flags |= O_RDONLY; + omode = O_RDONLY; } else if (*mode == 'w') { - flags |= O_WRONLY | O_CREAT | O_TRUNC; + omode = O_WRONLY; + flags |= O_CREAT | O_TRUNC; } else if (*mode == 'a') { - flags |= O_WRONLY | O_CREAT | O_APPEND; + omode = O_WRONLY; + flags |= O_CREAT | O_APPEND; } else if (*mode == '+') { - flags |= O_RDWR; + omode = O_RDWR; } else if (*mode == 'x') { flags |= O_EXCL; } else if (*mode == 'e') { flags |= O_CLOEXEC; } } while (*mode++); - return flags; + return omode | flags; } diff --git a/libc/stdio/fputc.c b/libc/stdio/fputc.c index 1202a6ca3..6fefbef82 100644 --- a/libc/stdio/fputc.c +++ b/libc/stdio/fputc.c @@ -17,25 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/errno.h" #include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" - -static noinstrument noinline int __fputc(int c, FILE *f) { - if (f->beg < f->size) { - f->buf[f->beg++] = c; - if (f->beg == f->size || f->bufmode == _IONBF || - (f->bufmode == _IOLBF && c == '\n')) { - if (f->writer) { - if (f->writer(f) == -1) return -1; - } else if (f->beg == f->size) { - f->beg = 0; - } - } - return c & 0xff; - } else { - return __fseteof(f); - } -} +#include "libc/sysv/consts/o.h" /** * Writes byte to stream. @@ -43,10 +28,22 @@ static noinstrument noinline int __fputc(int c, FILE *f) { * @see putc() if called within loop */ noinstrument int fputc(int c, FILE *f) { - if (f->beg + 1 < f->size && f->bufmode == _IOFBF) { - f->buf[f->beg++] = c; - return c & 0xff; + if ((f->iomode & O_ACCMODE) != O_RDONLY) { + if (f->beg < f->size) { + f->buf[f->beg++] = c; + if (f->beg == f->size || f->bufmode == _IONBF || + (f->bufmode == _IOLBF && c == '\n')) { + if (f->writer) { + if (f->writer(f) == -1) return -1; + } else if (f->beg == f->size) { + f->beg = 0; + } + } + return c & 0xff; + } else { + return __fseteof(f); + } } else { - return __fputc(c, f); + return __fseterr(f, EBADF); } } diff --git a/libc/stdio/fputcfb.c b/libc/stdio/fputcfb.c deleted file mode 100644 index 55473925e..000000000 --- a/libc/stdio/fputcfb.c +++ /dev/null @@ -1,52 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/internal.h" -#include "libc/stdio/stdio.h" - -static noinline int slowpath(int c, FILE *f) { - if (f->beg < f->size) { - c &= 0xff; - f->buf[f->beg++] = c; - if (f->beg == f->size) { - if (f->writer) { - if (f->writer(f) == -1) return -1; - } else if (f->beg == f->size) { - f->beg = 0; - } - } - return c; - } else { - return __fseteof(f); - } -} - -/** - * Writes byte to stream. - * - * @return c (as unsigned char) if written or -1 w/ errno - */ -noinstrument int fputcfb(int c, FILE *f) { - if (f->beg + 1 < f->size) { - c &= 0xff; - f->buf[f->beg++] = c; - return c; - } else { - return slowpath(c, f); - } -} diff --git a/libc/stdio/fseek.c b/libc/stdio/fseek.c index 156891d3e..6ba6fe9b2 100644 --- a/libc/stdio/fseek.c +++ b/libc/stdio/fseek.c @@ -35,8 +35,7 @@ * @returns new offset or -1 on error */ long fseek(FILE *f, long offset, int whence) { - int skew; - int64_t newpos; + int64_t pos; if (f->fd != -1) { if (whence == SEEK_CUR && f->beg < f->end) { offset -= f->end - f->beg; @@ -44,17 +43,17 @@ long fseek(FILE *f, long offset, int whence) { if (f->beg && !f->end) { f->writer(f); } - if ((newpos = lseek(f->fd, offset, whence)) != -1) { + if (lseek(f->fd, offset, whence) != -1) { f->state = 0; f->beg = 0; f->end = 0; - return newpos; + return 0; } else { f->state = errno == ESPIPE ? EBADF : errno; return -1; } } else { - f->beg = offset % f->size; + f->beg = (offset & 0xffffffff) % f->size; return -1; } } diff --git a/libc/stdio/ftell.c b/libc/stdio/ftell.c index 18bcdf22a..19207478e 100644 --- a/libc/stdio/ftell.c +++ b/libc/stdio/ftell.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/errno.h" #include "libc/stdio/stdio.h" /** @@ -25,6 +26,23 @@ * @param stream is a non-null stream handle * @returns current byte offset from beginning of file, or -1 */ -long ftell(FILE *stream) { - return fseek(stream, 0, SEEK_CUR); +long ftell(FILE *f) { + int64_t pos; + if (f->fd != -1) { + if (f->beg && !f->end) { + f->writer(f); + } + if ((pos = lseek(f->fd, 0, SEEK_CUR)) != -1) { + f->state = 0; + f->beg = 0; + f->end = 0; + return pos; + } else { + f->state = errno == ESPIPE ? EBADF : errno; + return -1; + } + } else { + errno = f->state; + return -1; + } } diff --git a/libc/stdio/fwrite.c b/libc/stdio/fwrite.c index 426dfde1e..3dbc5da79 100644 --- a/libc/stdio/fwrite.c +++ b/libc/stdio/fwrite.c @@ -25,20 +25,14 @@ * Writes data to stream. * * @param stride specifies the size of individual items - * @param count is the number of strides to fetch + * @param count is the number of strides to write * @return count on success, [0,count) on EOF, 0 on error or count==0 */ size_t fwrite(const void *data, size_t stride, size_t count, FILE *f) { size_t i, n; const unsigned char *p; for (n = stride * count, p = data, i = 0; i < n; ++i) { - if (fputc(p[i], f) == -1) { - if (!(i % stride)) { - return i / stride; - } else { - return __fseterr(f, EOVERFLOW); - } - } + if (fputc(p[i], f) == -1) return -1; } return count; } diff --git a/libc/stdio/setbuffer.c b/libc/stdio/setbuffer.c index 898557a18..cd404ef24 100644 --- a/libc/stdio/setbuffer.c +++ b/libc/stdio/setbuffer.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/popcnt.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/sysv/errfuns.h" @@ -25,7 +24,6 @@ * Sets buffer on stdio stream. */ void setbuffer(FILE *f, char *buf, size_t size) { - if (size && popcnt(size) != 1) abort(); if (buf && f->buf != (unsigned char *)buf) { free_s(&f->buf); if (!size) size = BUFSIZ; diff --git a/libc/stdio/setvbuf.c b/libc/stdio/setvbuf.c index 5ccdad474..8146a0ba3 100644 --- a/libc/stdio/setvbuf.c +++ b/libc/stdio/setvbuf.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/popcnt.h" #include "libc/stdio/stdio.h" #include "libc/sysv/errfuns.h" @@ -30,7 +29,6 @@ * @return 0 on success or -1 on error */ int setvbuf(FILE *f, char *buf, int mode, size_t size) { - if (size && popcnt(size) != 1) return einval(); setbuffer(f, buf, size); f->bufmode = mode; return 0; diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index e95988744..c9b612556 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -67,7 +67,6 @@ long fseek(FILE *, long, int) paramsnonnull(); long ftell(FILE *) paramsnonnull(); void rewind(FILE *) paramsnonnull(); int fopenflags(const char *) paramsnonnull(); -unsigned favail(FILE *); void setbuf(FILE *, char *); void setbuffer(FILE *, char *, size_t); int setvbuf(FILE *, char *, int, size_t); diff --git a/libc/stdio/tmpfile.c b/libc/stdio/tmpfile.c index ebf5efecb..66d4be900 100644 --- a/libc/stdio/tmpfile.c +++ b/libc/stdio/tmpfile.c @@ -28,6 +28,7 @@ */ FILE *tmpfile(void) { int fd; - if ((fd = mkostemps("/tmp/tmp.XXXXXX", 0, 0)) == -1) return NULL; + char template[] = "/tmp/tmp.XXXXXX"; + if ((fd = mkostemps(template, 0, 0)) == -1) return NULL; return fdopen(fd, "w+"); } diff --git a/libc/stdio/ungetc.c b/libc/stdio/ungetc.c index 6bbd5ed80..2ef432cfd 100644 --- a/libc/stdio/ungetc.c +++ b/libc/stdio/ungetc.c @@ -18,8 +18,12 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/stdio/stdio.h" +/** + * Pushes 𝑐 back to stream. + */ int ungetc(int c, FILE *f) { - f->beg = (f->beg - 1) & (f->size - 1); - f->buf[f->beg] = c; + uint32_t i; + if (c == -1) return c; + if (f->beg) f->buf[--f->beg] = c; return c; } diff --git a/libc/unicode/setlocale.c b/libc/unicode/setlocale.c index 2a61d163c..3273e361c 100644 --- a/libc/unicode/setlocale.c +++ b/libc/unicode/setlocale.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" +#include "libc/str/str.h" #include "libc/unicode/locale.h" /** @@ -25,5 +26,10 @@ * Cosmopolitan only supports the C or POSIX locale. */ char *setlocale(int category, const char *locale) { - return firstnonnull(locale, "C"); + if (!locale) return "C"; + if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) { + return locale; + } else { + return NULL; + } } diff --git a/test/libc/stdio/favail_test.c b/test/libc/stdio/fwrite_test.c similarity index 67% rename from test/libc/stdio/favail_test.c rename to test/libc/stdio/fwrite_test.c index 5bf1d37e9..d8f3fcf66 100644 --- a/test/libc/stdio/favail_test.c +++ b/test/libc/stdio/fwrite_test.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,29 +16,38 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/bits/popcnt.h" +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/errno.h" #include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" -unsigned naive(unsigned beg, unsigned end, unsigned size) { - assert(end < size); - assert(beg < size); - assert(popcnt(size) == 1); - if (beg == end) return size; - if (end > beg) return end - beg; - return (size - beg) + end; -} +char testlib_enable_tmp_setup_teardown; -unsigned fancy(unsigned beg, unsigned end, unsigned size) { - return ((end - beg - 1) & (size - 1)) + 1; -} +TEST(fwrite, test) { + FILE *f; + char buf[512]; -TEST(favail, test) { - unsigned i, j, n = 4; - for (i = 0; i < n; ++i) { - for (j = 0; j < n; ++j) { - ASSERT_EQ(naive(i, j, n), fancy(i, j, n), "%u %u %u", i, j, n); - } + ASSERT_NE(NULL, (f = fopen("hog", "wb"))); + EXPECT_EQ(-1, fgetc(f)); + EXPECT_EQ(5, fwrite("hello", 1, 5, f)); + EXPECT_EQ(5, ftell(f)); + EXPECT_NE(-1, fclose(f)); + + ASSERT_NE(NULL, (f = fopen("hog", "r"))); + EXPECT_EQ(-1, fwrite("hello", 1, 5, f)); + EXPECT_EQ(EBADF, ferror(f)); + EXPECT_NE(-1, fclose(f)); + + ASSERT_NE(NULL, (f = fopen("hog", "a+b"))); + EXPECT_EQ(5, fwrite("hello", 1, 5, f)); + EXPECT_NE(-1, fclose(f)); + + /* TODO(jart): O_APPEND on Windows */ + if (!IsWindows()) { + ASSERT_NE(NULL, (f = fopen("hog", "r"))); + EXPECT_EQ(10, fread(buf, 1, 10, f)); + EXPECT_TRUE(!memcmp(buf, "hellohello", 10)); + EXPECT_NE(-1, fclose(f)); } }