In ucrtbase (but not in msvcrt), when a printf format specifier has two width specifiers (both a '*' and an explicit number), only the latter should take effect. (In msvcrt, the width of one is combined with the other).
This patch builds on the prior patch which implements the msvcrt behaviour, and needs to be applied on top of it: msvcrt: The '*' character should be interpreted as the beginning of the width specification[1,2]
It also supercedes the previous (incorrect) patch which used the ucrtbase behaviour on msvcrt as well: msvcrt: printf: Later width specifiers should override earlier ones[3]
For further details, see [4], [5], and [6].
[1]: https://source.winehq.org/patches/data/218904 [2]: https://www.winehq.org/pipermail/wine-devel/2021-November/200009.html [3]: https://www.winehq.org/pipermail/wine-devel/2021-November/200040.html [4]: https://www.winehq.org/pipermail/wine-devel/2021-November/200039.html [5]: https://github.com/ValveSoftware/Proton/issues/5258#issuecomment-962423092 [6]: https://www.winehq.org/pipermail/wine-devel/2021-November/200063.html
Signed-off-by: David Gow david@davidgow.net --- dlls/msvcrt/printf.h | 6 ++++++ dlls/ucrtbase/tests/printf.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+)
diff --git a/dlls/msvcrt/printf.h b/dlls/msvcrt/printf.h index 40a8a13d861..52dec97f8bc 100644 --- a/dlls/msvcrt/printf.h +++ b/dlls/msvcrt/printf.h @@ -1050,6 +1050,12 @@ int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const API flags.LeftAlign = TRUE; flags.FieldLength = -flags.FieldLength; } + + /* On ucrtbase, we ignore all but the last specifier. */ +#if _MSVCR_VER >= 140 + if (*p >= '0' && *p <= '9') + flags.FieldLength = 0; +#endif }
while (*p >= '0' && *p <= '9') { diff --git a/dlls/ucrtbase/tests/printf.c b/dlls/ucrtbase/tests/printf.c index 4f20ccff9a4..328300d7827 100644 --- a/dlls/ucrtbase/tests/printf.c +++ b/dlls/ucrtbase/tests/printf.c @@ -845,6 +845,35 @@ static void test_printf_fp(void) } }
+static void test_printf_width_specifiers(void) +{ + char buf[150]; + int r; + + /* In msvcrt, multiple width specifiers will 'stack', producing + * a longer width. In ucrtbase, only the last width specifier will + * take effect. */ + r = vsprintf_wrapper(0, buf, sizeof(buf), "%0*02d", 1, 0); + ok(r==2, "r = %d\n", r); + ok(!strcmp(buf, "00"), "failed: "%s"\n", buf); + + r = vsprintf_wrapper(0, buf, sizeof(buf), "%0*02d", 2, 17); + ok(r==2, "r = %d\n", r); + ok(!strcmp(buf, "17"), "failed: "%s"\n", buf); + + r = vsprintf_wrapper(0, buf, sizeof(buf), "%*1d", 1, 3); + ok(r==1, "r = %d\n", r); + ok(!strcmp(buf, "3"), "failed: "%s"\n", buf); + + r = vsprintf_wrapper(0, buf, sizeof(buf), "%0*0d", 1, 2); + ok(r==1, "r = %d\n", r); + ok(!strcmp(buf, "2"), "failed: "%s"\n", buf); + + r = vsprintf_wrapper(0, buf, sizeof(buf), "% *2d", 0, 7); + ok(r==2, "r = %d\n", r); + ok(!strcmp(buf, " 7"), "failed: "%s"\n", buf); +} + START_TEST(printf) { ok(_set_invalid_parameter_handler(test_invalid_parameter_handler) == NULL, @@ -862,4 +891,5 @@ START_TEST(printf) test_printf_c99(); test_printf_natural_string(); test_printf_fp(); + test_printf_width_specifiers(); }