Make %c parsing in *scanf() respect the C standard

Currently, `%c`-style directives always succeed even if there
are actually fewer characters in the input than requested.

Before the fix, the added test fails like this:

        ...
        EXPECT_EQ(2, sscanf("ab", "%c %c %c", &c2, &c3, &c4))
                need 2 (or 0x02 or '\2' or ENOENT) =
                 got 3 (or 0x03 or '\3' or ESRCH)
        ...
        EXPECT_EQ(0, sscanf("abcd", "%5c", s2))
                need 0 (or 0x0 or '\0') =
                 got 1 (or 0x01 or '\1' or EPERM)

musl and glibc pass this test.
This commit is contained in:
Ivan Komarov 2024-02-18 15:32:06 +01:00
parent e5d877ba4f
commit a57c6067f0
2 changed files with 16 additions and 3 deletions

View file

@ -550,6 +550,11 @@ int __vcscanf(int callback(void *), //
if (!j && c == -1 && !items) {
items = -1;
goto Done;
} else if (rawmode && j != width) {
/* The C standard says that %c "matches a sequence of characters of
* **exactly** the number specified by the field width". If we have
* fewer characters, what we've just read is invalid. */
goto Done;
} else if (!rawmode && j < bufsize) {
if (charbytes == sizeof(char)) {
buf[j] = '\0';

View file

@ -69,9 +69,17 @@ TEST(sscanf, testNonDirectiveCharacterMatching) {
}
TEST(sscanf, testCharacter) {
char c = 0;
EXPECT_EQ(1, sscanf("a", "%c", &c));
EXPECT_EQ('a', c);
char c1 = 0, c2 = c1, c3 = c2, c4 = c3;
char s1[32] = {0}, s2[32] = {0};
EXPECT_EQ(1, sscanf("a", "%c", &c1));
EXPECT_EQ(2, sscanf("ab", "%c %c %c", &c2, &c3, &c4));
EXPECT_EQ(1, sscanf("abcde", "%5c", s1));
EXPECT_EQ(0, sscanf("abcd", "%5c", s2));
EXPECT_EQ('a', c1);
EXPECT_EQ('a', c2);
EXPECT_EQ('b', c3);
EXPECT_STREQ("abcde", &s1[0]);
}
TEST(sscanf, testStringBuffer) {