Skip to content

Commit

Permalink
Add POSIX's C conversion specifier to printf funcs (#1276)
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
GabrielRavier authored Sep 3, 2024
1 parent 3c61a54 commit 8f81451
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 0 deletions.
3 changes: 3 additions & 0 deletions libc/stdio/fmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,9 @@ int __fmt(void *fn, void *arg, const char *format, va_list va, int *wrote) {
}
break;
}
case 'C':
signbit = 63;
// fallthrough
case 'c':
if ((charbuf[0] = va_arg(va, int))) {
p = charbuf;
Expand Down
30 changes: 30 additions & 0 deletions test/libc/stdio/snprintf_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,33 @@ TEST(snprintf, testInf) {
for (i = 4; i < 10; ++i)
ASSERT_EQ(buf[i], '\0');
}

TEST(snprintf, testUppercaseCConversionSpecifier) {
char buf[10] = {};
int i = snprintf(buf, sizeof(buf), "%C", L'a');

ASSERT_EQ(i, 1);
ASSERT_STREQ(buf, "a");

i = snprintf(buf, sizeof(buf), "%C", L'☺');
ASSERT_EQ(i, 3);
ASSERT_STREQ(buf, "☺");
}

// Make sure we don't va_arg the wrong argument size on wide character
// conversion specifiers
TEST(snprintf,
testWideCConversionSpecifierWithLotsOfArgumentsBeforeAndOneAfter) {
char buf[20] = {};
int i = snprintf(buf, sizeof(buf), "%d%d%d%d%d%d%d%d%lc%d", 0, 0, 0, 0, 0, 0,
0, 0, L'x', 1);

ASSERT_EQ(i, 10);
ASSERT_STREQ(buf, "00000000x1");

memset(buf, 0, sizeof(buf));
i = snprintf(buf, sizeof(buf), "%d%d%d%d%d%d%d%d%C%d", 0, 0, 0, 0, 0, 0, 0, 0,
L'x', 1);
ASSERT_EQ(i, 10);
ASSERT_STREQ(buf, "00000000x1");
}

0 comments on commit 8f81451

Please sign in to comment.