[PATCH] ucrtbase: Later printf width specifiers should override earlier ones

David Gow david at davidgow.net
Mon Nov 8 05:06:26 CST 2021


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 at 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();
 }
-- 
2.32.0




More information about the wine-devel mailing list