From 04d59edfb049ff71e74f99220dbe8a3ba2a13c2b Mon Sep 17 00:00:00 2001 From: Gabriel Ravier Date: Fri, 30 Aug 2024 03:21:47 +0200 Subject: [PATCH] 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 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. --- libc/stdio/fmt.c | 2 +- test/libc/stdio/snprintf_test.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/libc/stdio/fmt.c b/libc/stdio/fmt.c index 5ef9b4209..ee558e6f0 100644 --- a/libc/stdio/fmt.c +++ b/libc/stdio/fmt.c @@ -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 { \ diff --git a/test/libc/stdio/snprintf_test.c b/test/libc/stdio/snprintf_test.c index 63a702bf2..d0428eaa1 100644 --- a/test/libc/stdio/snprintf_test.c +++ b/test/libc/stdio/snprintf_test.c @@ -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, "="); +}