Skip to content

Commit 8f81451

Browse files
Add POSIX's C conversion specifier to printf funcs (#1276)
POSIX specifies the C conversion specifier as being "equivalent to %lc", i.e. printf("%C", arg) is equivalent in behaviour to printf("%lc", arg). This patch implements this conversion specifier, and adds a test for it, alongside another test, which ensures that va_arg uses the correct size, even though we set signbit to 63 in the code (which one might think will result in the wrong size of argument being va_arg-ed, but having signbit set to 63 is in fact what __fmt_stoa expects and is a requirement for it properly formatting the wchar_t argument - this does not result in wrong usage of va_arg because the implementation of the c conversion specifier (which the implementation of the C conversion specifier fallsthrough to) always calls va_arg with an argument type of int, to avoid the very same bug occuring with %lc, as the l length modifier also sets signbit to 63)
1 parent 3c61a54 commit 8f81451

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

libc/stdio/fmt.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,9 @@ int __fmt(void *fn, void *arg, const char *format, va_list va, int *wrote) {
10731073
}
10741074
break;
10751075
}
1076+
case 'C':
1077+
signbit = 63;
1078+
// fallthrough
10761079
case 'c':
10771080
if ((charbuf[0] = va_arg(va, int))) {
10781081
p = charbuf;

test/libc/stdio/snprintf_test.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,33 @@ TEST(snprintf, testInf) {
7171
for (i = 4; i < 10; ++i)
7272
ASSERT_EQ(buf[i], '\0');
7373
}
74+
75+
TEST(snprintf, testUppercaseCConversionSpecifier) {
76+
char buf[10] = {};
77+
int i = snprintf(buf, sizeof(buf), "%C", L'a');
78+
79+
ASSERT_EQ(i, 1);
80+
ASSERT_STREQ(buf, "a");
81+
82+
i = snprintf(buf, sizeof(buf), "%C", L'☺');
83+
ASSERT_EQ(i, 3);
84+
ASSERT_STREQ(buf, "☺");
85+
}
86+
87+
// Make sure we don't va_arg the wrong argument size on wide character
88+
// conversion specifiers
89+
TEST(snprintf,
90+
testWideCConversionSpecifierWithLotsOfArgumentsBeforeAndOneAfter) {
91+
char buf[20] = {};
92+
int i = snprintf(buf, sizeof(buf), "%d%d%d%d%d%d%d%d%lc%d", 0, 0, 0, 0, 0, 0,
93+
0, 0, L'x', 1);
94+
95+
ASSERT_EQ(i, 10);
96+
ASSERT_STREQ(buf, "00000000x1");
97+
98+
memset(buf, 0, sizeof(buf));
99+
i = snprintf(buf, sizeof(buf), "%d%d%d%d%d%d%d%d%C%d", 0, 0, 0, 0, 0, 0, 0, 0,
100+
L'x', 1);
101+
ASSERT_EQ(i, 10);
102+
ASSERT_STREQ(buf, "00000000x1");
103+
}

0 commit comments

Comments
 (0)