Fix vfprintf and derived functions badly handling +/` flag conflict

The C standard states, for conversion specifications containing the +
flag:
> The result of a signed conversion always begins with a plus or minus
> sign. (It begins with a sign only when a value with a negative sign
> is converted if this flag is not specified.)
- C Standard, 7.23.6.2.6. The fprintf function

...meaning that for all conversion but signed conversions, the flag
does nothing (note that flags which would invoke UB when used on an
unexpected conversion explicitly state so).

However, cosmopolitan currently interprets a + flag as equivalent to a
` flag, where the ` flag stands for quoting a character/string,
meaning a program such as:

 #include <stdio.h>

int main()
{
    printf("%+c\n", '=');
}

will, under cosmopolitan, print:

'='

instead of:

=

(i.e. the output is quoted as if %`c was used as the conversion
specification)

This patch fixes this by making it so the ` flags and + flags are not
internally considered identical, and adds a test for the change.
This commit is contained in:
Gabriel Ravier 2024-08-30 03:21:47 +02:00
parent 06a1193b4d
commit 04d59edfb0
2 changed files with 9 additions and 1 deletions

View file

@ -76,9 +76,9 @@
#define FLAGS_PRECISION 0x20
#define FLAGS_ISSIGNED 0x40
#define FLAGS_NOQUOTE 0x80
#define FLAGS_REPR 0x100
#define FLAGS_QUOTE FLAGS_SPACE
#define FLAGS_GROUPING FLAGS_NOQUOTE
#define FLAGS_REPR FLAGS_PLUS
#define __FMT_PUT(C) \
do { \

View file

@ -27,3 +27,11 @@ TEST(snprintf, testVeryLargePrecision) {
ASSERT_EQ(i, 9999);
ASSERT_EQ(strlen(buf), 511);
}
TEST(snprintf, testPlusFlagOnChar) {
char buf[10] = {};
int i = snprintf(buf, sizeof(buf), "%+c", '=');
ASSERT_EQ(i, 1);
ASSERT_STREQ(buf, "=");
}