From 04c5c8da7aa1045abe6d4b0bb6665babc1549253 Mon Sep 17 00:00:00 2001 From: milescoviello Date: Mon, 12 Oct 2026 02:43:00 +0000 Subject: [PATCH] fix(libc): accept the ' flag in the wide printf family The wide formatter behind swprintf, vswprintf, wprintf and snwprintf_s does not know the ' (thousands grouping) flag: it takes ' for the conversion and fails, so on main swprintf(buffer, 32, L"%'d", 1234567) returns -1 with errno EINVAL and an empty buffer. The narrow guest formatter accepts the flag since #3567. FreeBSD 11.4 lib/libc/stdio/vfwprintf.c takes it as a flag (lines 613-615): case '\'': flags |= GROUPING; goto rflag; and grouping_init (lines 129-147) adds separators only while *gs->grouping != CHAR_MAX. In the C locale, the only one the guest's setlocale accepts, lib/libc/locale/lnumeric.c (lines 44-50) has static char numempty[] = { CHAR_MAX, '\0' }; ... "", /* thousands_sep */ numempty /* grouping */ so the flag prints the number unchanged. The wide formatter now skips ' with the other flags before it hands the conversion to the host's snprintf, as the narrow one does. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libc/src/FormattingWide.cpp | 5 +++-- core/libs/tests/GuestWideFormatting.cpp | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libc/src/FormattingWide.cpp b/core/libs/prx/libc/src/FormattingWide.cpp index b99a1ac9b..0fe8628bc 100644 --- a/core/libs/prx/libc/src/FormattingWide.cpp +++ b/core/libs/prx/libc/src/FormattingWide.cpp @@ -100,9 +100,10 @@ std::u16string FormatWide(const char16_t* format, VaList* source, bool secure = bool left = false; int width = 0; int precision = -1; - while (In(*format, "-+ #0")) { + while (In(*format, "-+ #0'")) { if (*format == u'-') left = true; - spec += static_cast(*format++); + if (*format != u'\'') spec += static_cast(*format); + ++format; } if (*format == u'*') { ++format; diff --git a/core/libs/tests/GuestWideFormatting.cpp b/core/libs/tests/GuestWideFormatting.cpp index 7710661e3..fb7127c03 100644 --- a/core/libs/tests/GuestWideFormatting.cpp +++ b/core/libs/tests/GuestWideFormatting.cpp @@ -121,6 +121,14 @@ static void CheckCount() { Require(Format(buffer, 16, u"a%n", nullCount) < 0, "vswprintf null %n argument"); } +static void CheckGroupingFlag() { + char16_t buffer[48]; + Require(swprintf_nid_postfix(buffer, 48, u"%'d|%'-6u|%'.2f|%'+05d|%'g", 1234567, 42u, 1234.5, 12, 1e6) == 34 && + buffer == std::u16string(u"1234567|42 |1234.50|+0012|1e+06"), "swprintf ' flag"); + Require(snwprintf_s_nid_postfix(buffer, 48, u"%'lld %ls", 9876543210ll, u"x") == 12 && buffer == std::u16string(u"9876543210 x"), + "snwprintf_s ' flag"); +} + static void CheckSwprintf() { char16_t buffer[8]; for (auto& unit : buffer) unit = u'x'; @@ -161,4 +169,5 @@ int main() { Check(u"%.2s", "\xf0\x9f\x98\x80x", u"\U0001f600"); Check(u"%.1s", "\xf0\x9f\x98\x80x", u""); Check(u"%.3s", "\xf0\x9f\x98\x80x", u"\U0001f600x"); + CheckGroupingFlag(); }