From 4d1747f090e4fffca6b5b8529233161b0cec5ba5 Mon Sep 17 00:00:00 2001 From: Gabriel Ravier Date: Wed, 29 Mar 2023 10:48:38 +0200 Subject: [PATCH] Fix scanf x specifier with string of 0 The C standard states that, in the context of an x conversion specifier given to scanf: > Matches an optionally signed hexadecimal integer, whose format is > the same as expected for the subject sequence of the strtoul > function with the value 16 for the base argument. - C standard, 7.23.6.2.11. The fscanf function Cosmopolitan fails to do this, as 0 should be parsed as a 0 by such an invocation of strtoul. Instead, cosmopolitan errors out as though such input is invalid, which is wrong. This means that a program such as this: #include #undef NDEBUG #include int main() { int v = 0; assert(sscanf("0", "%x", &v) == 1); } will not run correctly on cosmpolitan, instead failing the assertion. This patch fixes this, along with the associated GitHub issue, https://github.com/jart/cosmopolitan/issues/778 --- libc/fmt/vcscanf.c | 2 ++ test/libc/fmt/sscanf_test.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/libc/fmt/vcscanf.c b/libc/fmt/vcscanf.c index 22aa93a26..3002113b9 100644 --- a/libc/fmt/vcscanf.c +++ b/libc/fmt/vcscanf.c @@ -174,6 +174,8 @@ int vcscanf(int callback(void *), int unget(int, void *), void *arg, c = callback(arg); if (c == prefix || c == prefix + ('a' - 'A')) { c = callback(arg); + } else if (c == -1) { + c = '0'; } } DecodeNumber: diff --git a/test/libc/fmt/sscanf_test.c b/test/libc/fmt/sscanf_test.c index 245c6fe86..399f7b604 100644 --- a/test/libc/fmt/sscanf_test.c +++ b/test/libc/fmt/sscanf_test.c @@ -231,3 +231,31 @@ TEST(sscanf, testInttypes_macros) { ASSERT_EQ(if64, 1); ASSERT_EQ(uf64, 1); } + +TEST(sscanf, test0) { + int v; + + v = 0xFFFFFFFF; + ASSERT_EQ(sscanf("0", "%x", &v), 1); + ASSERT_EQ(v, 0); + + v = 0xFFFFFFFF; + ASSERT_EQ(sscanf("0", "%X", &v), 1); + ASSERT_EQ(v, 0); + + v = 0xFFFFFFFF; + ASSERT_EQ(sscanf("0", "%d", &v), 1); + ASSERT_EQ(v, 0); + + v = 0xFFFFFFFF; + ASSERT_EQ(sscanf("0", "%o", &v), 1); + ASSERT_EQ(v, 0); + + v = 0xFFFFFFFF; + ASSERT_EQ(sscanf("0", "%u", &v), 1); + ASSERT_EQ(v, 0); + + v = 0xFFFFFFFF; + ASSERT_EQ(sscanf("0", "%b", &v), 1); + ASSERT_EQ(v, 0); +}