From 63a1636e1f744371b0b68ec8c98227fe3bd57027 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 21 Aug 2023 09:00:40 -0700 Subject: [PATCH] Get GNU GMP test suite fully passing - Fix stdio fmemopen() buffer behaviors - Fix scanf() to return EOF when appropriate - Prefer fseek/ftell names over fseeko/ftello - Ensure locale field is always set in the TIB - Fix recent regression in vfprintf() return count - Make %n directive in scanf() have standard behavior --- libc/calls/chdir.c | 3 +- libc/libc.mk | 1 - libc/log/vflogf.c | 6 +- libc/runtime/enable_tls.c | 2 + libc/stdio/fmemopen.c | 6 +- libc/stdio/{fseeko.c => fseek.c} | 8 +- .../{fseeko_unlocked.c => fseek_unlocked.c} | 4 +- libc/stdio/{ftello.c => ftell.c} | 8 +- libc/stdio/rewind.c | 2 +- libc/stdio/stdio.h | 2 +- libc/stdio/vcscanf.c | 85 ++++++++++++++----- libc/stdio/vfprintf_unlocked.c | 1 - libc/str/locale.c | 34 ++++++++ libc/str/locale.h | 4 + libc/thread/mktls.c | 2 + test/libc/stdio/fgetln_test.c | 5 +- test/libc/stdio/fmemopen_test.c | 52 ++++++++++-- test/libc/stdio/ftell_test.c | 2 +- test/libc/stdio/sscanf_test.c | 28 +++++- test/libc/str/locale_test.c | 24 ++++++ 20 files changed, 228 insertions(+), 51 deletions(-) rename libc/stdio/{fseeko.c => fseek.c} (92%) rename libc/stdio/{fseeko_unlocked.c => fseek_unlocked.c} (96%) rename libc/stdio/{ftello.c => ftell.c} (95%) create mode 100644 libc/str/locale.c create mode 100644 test/libc/str/locale_test.c diff --git a/libc/calls/chdir.c b/libc/calls/chdir.c index ba4f5d94b..f2a7d3b60 100644 --- a/libc/calls/chdir.c +++ b/libc/calls/chdir.c @@ -29,7 +29,8 @@ /** * Sets current directory. * - * This does *not* update the `PWD` environment variable. + * The current directory is shared by all threads in a process. This + * does not update the `PWD` environment variable. * * @return 0 on success, or -1 w/ errno * @raise ELOOP if a loop was detected resolving components of `path` diff --git a/libc/libc.mk b/libc/libc.mk index ffa06e2a8..e0ad8f07d 100644 --- a/libc/libc.mk +++ b/libc/libc.mk @@ -35,5 +35,4 @@ o/$(MODE)/libc: o/$(MODE)/libc/calls \ o/$(MODE)/libc/tinymath \ o/$(MODE)/libc/vga \ o/$(MODE)/libc/x \ - o/$(MODE)/libc/zipos \ $(LIBC_CHECKS) diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index 3508ce3ec..3cadd97cd 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -59,11 +59,11 @@ static void vflogf_onfail(FILE *f) { (err == ENOSPC || err == EDQUOT || err == EFBIG) && (fstat(fileno_unlocked(f), &st) == -1 || st.st_size > kNontrivialSize)) { ftruncate(fileno_unlocked(f), 0); - fseeko_unlocked(f, SEEK_SET, 0); + fseek_unlocked(f, SEEK_SET, 0); f->beg = f->end = 0; clearerr_unlocked(f); - (fprintf_unlocked)(f, "performed emergency log truncation: %s\n", - strerror(err)); + fprintf_unlocked(f, "performed emergency log truncation: %s\n", + strerror(err)); } } diff --git a/libc/runtime/enable_tls.c b/libc/runtime/enable_tls.c index 245ebdb6f..93bef1449 100644 --- a/libc/runtime/enable_tls.c +++ b/libc/runtime/enable_tls.c @@ -29,6 +29,7 @@ #include "libc/macros.internal.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" +#include "libc/str/locale.h" #include "libc/str/str.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" @@ -191,6 +192,7 @@ textstartup void __enable_tls(void) { tib->tib_errno = __errno; tib->tib_strace = __strace; tib->tib_ftrace = __ftrace; + tib->tib_locale = (intptr_t)&__c_dot_utf8_locale; tib->tib_pthread = (pthread_t)&_pthread_main; if (IsLinux() || IsXnuSilicon()) { // gnu/systemd guarantees pid==tid for the main thread so we can diff --git a/libc/stdio/fmemopen.c b/libc/stdio/fmemopen.c index 363ac1437..b68d6bcb4 100644 --- a/libc/stdio/fmemopen.c +++ b/libc/stdio/fmemopen.c @@ -25,8 +25,6 @@ #include "libc/sysv/errfuns.h" #include "libc/thread/thread.h" -// TODO(jart): POSIX says buffer needs to grow in write modes? - /** * Opens buffer as stream. * @@ -63,7 +61,9 @@ FILE *fmemopen(void *buf, size_t size, const char *mode) { } f->fd = -1; f->buf = buf; - f->end = size; + if (!(iomode & O_TRUNC)) { + f->end = size; + } f->size = size; f->iomode = iomode; if (iomode & O_APPEND) { diff --git a/libc/stdio/fseeko.c b/libc/stdio/fseek.c similarity index 92% rename from libc/stdio/fseeko.c rename to libc/stdio/fseek.c index 4d0bb3b93..712362f23 100644 --- a/libc/stdio/fseeko.c +++ b/libc/stdio/fseek.c @@ -35,14 +35,14 @@ * @returns 0 on success or -1 on error * @threadsafe */ -int fseeko(FILE *f, int64_t offset, int whence) { +int fseek(FILE *f, int64_t offset, int whence) { int rc; flockfile(f); - rc = fseeko_unlocked(f, offset, whence); - STDIOTRACE("fseeko(%p, %'ld, %s) → %d %s", f, offset, DescribeWhence(whence), + rc = fseek_unlocked(f, offset, whence); + STDIOTRACE("fseek(%p, %'ld, %s) → %d %s", f, offset, DescribeWhence(whence), rc, DescribeStdioState(f->state)); funlockfile(f); return rc; } -__strong_reference(fseeko, fseek); +__strong_reference(fseek, fseeko); diff --git a/libc/stdio/fseeko_unlocked.c b/libc/stdio/fseek_unlocked.c similarity index 96% rename from libc/stdio/fseeko_unlocked.c rename to libc/stdio/fseek_unlocked.c index 2bcac84e0..1a9eefdee 100644 --- a/libc/stdio/fseeko_unlocked.c +++ b/libc/stdio/fseek_unlocked.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/errno.h" +#include "libc/macros.internal.h" #include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/o.h" @@ -35,7 +36,7 @@ * @param whence can be SEET_SET, SEEK_CUR, or SEEK_END * @returns 0 on success or -1 on error */ -int fseeko_unlocked(FILE *f, int64_t offset, int whence) { +int fseek_unlocked(FILE *f, int64_t offset, int whence) { int res; ssize_t rc; int64_t pos; @@ -68,6 +69,7 @@ int fseeko_unlocked(FILE *f, int64_t offset, int whence) { pos = -1; break; } + f->end = MAX(f->beg, f->end); if (0 <= pos && pos <= f->end) { f->beg = pos; f->state = 0; diff --git a/libc/stdio/ftello.c b/libc/stdio/ftell.c similarity index 95% rename from libc/stdio/ftello.c rename to libc/stdio/ftell.c index 782f3579b..eb188372a 100644 --- a/libc/stdio/ftello.c +++ b/libc/stdio/ftell.c @@ -24,7 +24,7 @@ #include "libc/stdio/stdio.h" #include "libc/sysv/consts/o.h" -static inline int64_t ftello_unlocked(FILE *f) { +static inline int64_t ftell_unlocked(FILE *f) { int64_t pos; uint32_t skew; if (f->fd != -1) { @@ -48,12 +48,12 @@ static inline int64_t ftello_unlocked(FILE *f) { * @returns current byte offset from beginning, or -1 w/ errno * @threadsafe */ -int64_t ftello(FILE *f) { +int64_t ftell(FILE *f) { int64_t rc; flockfile(f); - rc = ftello_unlocked(f); + rc = ftell_unlocked(f); funlockfile(f); return rc; } -__strong_reference(ftello, ftell); +__strong_reference(ftell, ftello); diff --git a/libc/stdio/rewind.c b/libc/stdio/rewind.c index fa2693270..21fede138 100644 --- a/libc/stdio/rewind.c +++ b/libc/stdio/rewind.c @@ -29,7 +29,7 @@ */ void rewind(FILE *f) { flockfile(f); - fseeko_unlocked(f, 0, SEEK_SET); + fseek_unlocked(f, 0, SEEK_SET); f->state = 0; funlockfile(f); } diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index 1b22f1a4d..8b3933e46 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -147,7 +147,7 @@ wchar_t *fgetws_unlocked(wchar_t *, int, FILE *); int fputws_unlocked(const wchar_t *, FILE *); wint_t ungetwc_unlocked(wint_t, FILE *) paramsnonnull(); int ungetc_unlocked(int, FILE *) paramsnonnull(); -int fseeko_unlocked(FILE *, int64_t, int) paramsnonnull(); +int fseek_unlocked(FILE *, int64_t, int) paramsnonnull(); ssize_t getdelim_unlocked(char **, size_t *, int, FILE *) paramsnonnull(); int fprintf_unlocked(FILE *, const char *, ...) printfesque(2) paramsnonnull((1, 2)) dontthrow nocallback; diff --git a/libc/stdio/vcscanf.c b/libc/stdio/vcscanf.c index e0011e314..3094f7dac 100644 --- a/libc/stdio/vcscanf.c +++ b/libc/stdio/vcscanf.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" #include "libc/fmt/fmt.h" +#include "libc/intrin/kprintf.h" #include "libc/intrin/weaken.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" @@ -27,6 +28,13 @@ #include "libc/str/utf16.h" #include "libc/sysv/errfuns.h" +#define READ \ + ({ \ + int c = callback(arg); \ + if (c != -1) ++consumed; \ + c; \ + }) + /** * String / file / stream decoder. * @@ -54,10 +62,12 @@ int __vcscanf(int callback(void *), // void *ptr; } *freeme = NULL; const unsigned char *p = (const unsigned char *)fmt; - unsigned i = 0; + int *n_ptr; int items = 0; - int c = callback(arg); - while (c != -1) { + int consumed = 0; + unsigned i = 0; + int c = READ; + for (;;) { switch (p[i++]) { case '\0': if (c != -1 && unget) { @@ -70,7 +80,7 @@ int __vcscanf(int callback(void *), // case '\r': case '\v': while (isspace(c)) { - c = callback(arg); + c = READ; } break; case '%': { @@ -115,8 +125,11 @@ int __vcscanf(int callback(void *), // case 'c': rawmode = true; if (!width) width = 1; - /* εpsilon transition */ + // fallthrough case 's': + while (isspace(c)) { + c = READ; + } goto DecodeString; case '\'': thousands = true; @@ -144,38 +157,61 @@ int __vcscanf(int callback(void *), // case 'b': /* binary */ base = 2; prefix = 'b'; + while (isspace(c)) { + c = READ; + } goto ConsumeBasePrefix; case 'p': /* pointer (NexGen32e) */ bits = 48; + while (isspace(c)) { + c = READ; + } /* fallthrough */ case 'x': case 'X': /* hexadecimal */ base = 16; prefix = 'x'; + while (isspace(c)) { + c = READ; + } goto ConsumeBasePrefix; case 'o': /* octal */ base = 8; - goto DecodeNumber; - case 'd': /* decimal */ - case 'n': /* TODO(jart): flexidecimal */ - issigned = true; - if (c == '+' || (isneg = c == '-')) { - c = callback(arg); + while (isspace(c)) { + c = READ; } - /* εpsilon transition */ + goto DecodeNumber; + case 'n': + goto ReportConsumed; + case 'd': // decimal + issigned = true; + while (isspace(c)) { + c = READ; + } + if (c == '+' || (isneg = c == '-')) { + c = READ; + } + // fallthrough case 'u': base = 10; + while (isspace(c)) { + c = READ; + } goto DecodeNumber; default: items = einval(); goto Done; } } + ReportConsumed: + n_ptr = va_arg(va, int *); + *n_ptr = consumed - 1; // minus lookahead + continue; ConsumeBasePrefix: if (c == '0') { - c = callback(arg); + c = READ; if (c == prefix || c == prefix + ('a' - 'A')) { - c = callback(arg); + c = READ; } else if (c == -1) { c = '0'; } @@ -195,7 +231,7 @@ int __vcscanf(int callback(void *), // } else { break; } - } while ((c = callback(arg)) != -1 && width > 0); + } while ((c = READ) != -1 && width > 0); if (!discard) { uint128_t bane = (uint128_t)1 << (bits - 1); if (!(number & ~((bane - 1) | (issigned ? 0 : bane))) || @@ -228,7 +264,13 @@ int __vcscanf(int callback(void *), // *(uint8_t *)out = (uint8_t)number; break; } + } else if (!items && c == -1) { + items = -1; + goto Done; } + } else if (!items) { + items = -1; + goto Done; } continue; DecodeString: @@ -256,7 +298,7 @@ int __vcscanf(int callback(void *), // if (c != -1 && j + !rawmode < bufsize && (rawmode || !isspace(c))) { if (charbytes == 1) { ((unsigned char *)buf)[j++] = (unsigned char)c; - c = callback(arg); + c = READ; } else if (tpdecodecb((wint_t *)&c, c, (void *)callback, arg) != -1) { if (charbytes == sizeof(char16_t)) { @@ -270,10 +312,13 @@ int __vcscanf(int callback(void *), // } else { ((wchar_t *)buf)[j++] = (wchar_t)c; } - c = callback(arg); + c = READ; } } else { - if (!rawmode && j < bufsize) { + if (!j && c == -1 && !items) { + items = -1; + goto Done; + } else if (!rawmode && j < bufsize) { if (charbytes == sizeof(char)) { ((unsigned char *)buf)[j] = '\0'; } else if (charbytes == sizeof(char16_t)) { @@ -292,13 +337,13 @@ int __vcscanf(int callback(void *), // } else { do { if (isspace(c)) break; - } while ((c = callback(arg)) != -1); + } while ((c = READ) != -1); } break; } default: NonDirectiveCharacter: - c = (c == p[i - 1]) ? callback(arg) : -1; + c = (c == p[i - 1]) ? READ : -1; break; } } diff --git a/libc/stdio/vfprintf_unlocked.c b/libc/stdio/vfprintf_unlocked.c index f2f291ca8..9e28a5201 100644 --- a/libc/stdio/vfprintf_unlocked.c +++ b/libc/stdio/vfprintf_unlocked.c @@ -39,7 +39,6 @@ static int __vfprintf_flbuf(const char *s, struct state *t, size_t n) { if (n) { if (n == 1 && *s != '\n' && t->f->beg < t->f->size) { t->f->buf[t->f->beg++] = *s; - t->n += n; rc = 0; } else if (fwrite_unlocked(s, 1, n, t->f)) { rc = 0; diff --git a/libc/str/locale.c b/libc/str/locale.c new file mode 100644 index 000000000..0922e3509 --- /dev/null +++ b/libc/str/locale.c @@ -0,0 +1,34 @@ +/*-*- 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 2023 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/str/locale.h" +#include "libc/str/str.h" + +static const uint32_t empty_mo[] = {0x950412de, 0, -1, -1, -1}; + +const struct __locale_map __c_dot_utf8 = { + .map = empty_mo, + .map_size = sizeof empty_mo, + .name = "C.UTF-8", +}; + +const struct __locale_struct __c_locale; + +const struct __locale_struct __c_dot_utf8_locale = { + .cat[LC_CTYPE] = &__c_dot_utf8, +}; diff --git a/libc/str/locale.h b/libc/str/locale.h index d9868fa2f..e9d5e7939 100644 --- a/libc/str/locale.h +++ b/libc/str/locale.h @@ -37,6 +37,10 @@ struct __locale_struct { typedef struct __locale_struct *locale_t; +extern const struct __locale_map __c_dot_utf8; +extern const struct __locale_struct __c_locale; +extern const struct __locale_struct __c_dot_utf8_locale; + char *nl_langinfo_l(int, locale_t); char *setlocale(int, const char *); double strtod_l(const char *, char **, locale_t); diff --git a/libc/thread/mktls.c b/libc/thread/mktls.c index e75c9811e..5c687fb2e 100644 --- a/libc/thread/mktls.c +++ b/libc/thread/mktls.c @@ -25,6 +25,7 @@ #include "libc/mem/mem.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" +#include "libc/str/locale.h" #include "libc/str/str.h" #include "libc/thread/spawn.h" #include "libc/thread/tls.h" @@ -41,6 +42,7 @@ static char *_mktls_finish(struct CosmoTib **out_tib, char *mem, tib->tib_ftrace = old->tib_ftrace; tib->tib_strace = old->tib_strace; tib->tib_sigmask = old->tib_sigmask; + tib->tib_locale = (intptr_t)&__c_dot_utf8_locale; atomic_store_explicit(&tib->tib_tid, -1, memory_order_relaxed); if (out_tib) { *out_tib = tib; diff --git a/test/libc/stdio/fgetln_test.c b/test/libc/stdio/fgetln_test.c index 77e0eb5eb..c31bd7127 100644 --- a/test/libc/stdio/fgetln_test.c +++ b/test/libc/stdio/fgetln_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/mem/gc.h" +#include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" @@ -28,7 +29,7 @@ TEST(fgetln, test) { FILE *f; - f = fmemopen(_gc(strdup(kHyperion)), kHyperionSize, "r+"); + f = fmemopen(gc(strdup(kHyperion)), kHyperionSize, "r+"); EXPECT_STREQ("The fall of Hyperion - a Dream\n", fgetln(f, 0)); EXPECT_STREQ("John Keats\n", fgetln(f, 0)); EXPECT_STREQ("\n", fgetln(f, 0)); @@ -81,7 +82,7 @@ TEST(fgetln, testReadingFromStdin_doesntLeakMemory) { } BENCH(fgetln, bench) { - FILE *f = fmemopen(_gc(strdup(kHyperion)), kHyperionSize, "r+"); + FILE *f = fmemopen(gc(strdup(kHyperion)), kHyperionSize, "r+"); EZBENCH2("fgetln", donothing, fgetln(f, 0)); EZBENCH2("xgetline", donothing, free(xgetline(f))); fclose(f); diff --git a/test/libc/stdio/fmemopen_test.c b/test/libc/stdio/fmemopen_test.c index 4477ef5fd..bdfd2c391 100644 --- a/test/libc/stdio/fmemopen_test.c +++ b/test/libc/stdio/fmemopen_test.c @@ -16,7 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/errno.h" +#include "libc/mem/gc.internal.h" +#include "libc/mem/mem.h" #include "libc/stdio/stdio.h" +#include "libc/stdio/temp.h" #include "libc/testlib/testlib.h" TEST(fmemopen, testWriteRewindRead) { @@ -30,10 +35,43 @@ TEST(fmemopen, testWriteRewindRead) { fclose(f); } -/* TEST(fmemopen, testWriteRead_readsNothingButNotEof) { */ -/* char c; */ -/* FILE *f; */ -/* f = fmemopen(NULL, BUFSIZ, "w+"); */ -/* EXPECT_EQ(1, fwrite("c", 1, 1, f)); */ -/* EXPECT_EQ(0, fread(&c, 1, 1, f)); */ -/* } */ +TEST(fmemopen_fprintf, test) { + FILE *f = fmemopen(NULL, BUFSIZ, "w+"); + EXPECT_EQ(1, fprintf(f, "%ld", 0L)); + rewind(f); + char buf[8] = {0}; + EXPECT_EQ(1, fread(&buf, 1, 8, f)); + EXPECT_STREQ("0", buf); + fclose(f); +} + +TEST(fmemopen, seekEofRead) { + FILE *f = fmemopen("x", 1, "r+"); + ASSERT_SYS(EINVAL, -1, fseek(f, -1, SEEK_SET)); + ASSERT_SYS(EINVAL, -1, fseek(f, +1, SEEK_END)); + ASSERT_EQ(0, fseek(f, 0, SEEK_END)); + ASSERT_FALSE(feof(f)); + ASSERT_EQ(-1, fgetc(f)); + ASSERT_TRUE(feof(f)); + fclose(f); +} + +TEST(tmpfile_fprintf, test) { + FILE *f = tmpfile(); + EXPECT_EQ(1, fprintf(f, "%ld", 0L)); + rewind(f); + char buf[8] = {0}; + EXPECT_EQ(1, fread(&buf, 1, 8, f)); + EXPECT_STREQ("0", buf); + fclose(f); +} + +TEST(fmemopen, small) { + FILE *f = fmemopen(gc(malloc(1)), 1, "w+"); + EXPECT_EQ(3, fprintf(f, "%ld", 123L)); + rewind(f); + char buf[8] = {0}; + EXPECT_EQ(1, fread(&buf, 1, 8, f)); + EXPECT_STREQ("1", buf); + fclose(f); +} diff --git a/test/libc/stdio/ftell_test.c b/test/libc/stdio/ftell_test.c index d9ee8bb60..7931c628a 100644 --- a/test/libc/stdio/ftell_test.c +++ b/test/libc/stdio/ftell_test.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/mem/mem.h" #include "libc/mem/gc.internal.h" +#include "libc/mem/mem.h" #include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" diff --git a/test/libc/stdio/sscanf_test.c b/test/libc/stdio/sscanf_test.c index 2fed3445a..00415f254 100644 --- a/test/libc/stdio/sscanf_test.c +++ b/test/libc/stdio/sscanf_test.c @@ -42,7 +42,6 @@ TEST(sscanf, testMultiple) { TEST(sscanf, testDecimal) { EXPECT_EQ(123, sscanf1("123", "%d")); - EXPECT_EQ(123, sscanf1("123", "%n")); EXPECT_EQ(123, sscanf1("123", "%u")); EXPECT_EQ((uint32_t)-123, sscanf1("-123", "%d")); } @@ -258,3 +257,30 @@ TEST(sscanf, test0) { ASSERT_EQ(sscanf("0", "%b", &v), 1); ASSERT_EQ(v, 0); } + +TEST(sscanf, n) { + int x, y; + EXPECT_EQ(1, sscanf("7 2 3 4", "%d%n", &x, &y)); + EXPECT_EQ(7, x); + EXPECT_EQ(1, y); +} + +TEST(sscanf, eofForNoMatching) { + int y = 666; + char x[8] = "hi"; + EXPECT_EQ(-1, sscanf(" ", "%s%n", &x, &y)); + EXPECT_STREQ("hi", x); + EXPECT_EQ(666, y); +} + +TEST(sscanf, eofConditions) { + int x = 666; + EXPECT_EQ(-1, sscanf("", "%d", &x)); + EXPECT_EQ(666, x); + EXPECT_EQ(-1, sscanf(" ", "%d", &x)); + EXPECT_EQ(666, x); + EXPECT_EQ(-1, sscanf("123", "%*d%d", &x)); + EXPECT_EQ(666, x); + EXPECT_EQ(-1, sscanf("123", "%*d%n", &x)); + EXPECT_EQ(666, x); +} diff --git a/test/libc/str/locale_test.c b/test/libc/str/locale_test.c new file mode 100644 index 000000000..d748c9992 --- /dev/null +++ b/test/libc/str/locale_test.c @@ -0,0 +1,24 @@ +/*-*- 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 2023 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/str/unicode.h" +#include "libc/testlib/testlib.h" + +TEST(locale, test) { + EXPECT_STREQ(".", localeconv()->decimal_point); +}