-- v3: shlwapi: Output unrecognized format symbol in wvnsprintf(). shlwapi: Skip duplicate '#' in WPRINTF_ParseFormat(). shlwapi: Skip some spaces in WPRINTF_ParseFormat(). shlwapi: Handle ll in WPRINTF_ParseFormat(). shlwapi: Fix char width modifiers handling in WPRINTF_ParseFormat(). shlwapi: Handle repeated modifiers in WPRINTF_ParseFormat(). shlwapi/tests: Add some tests for printf format.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/shlwapi/tests/string.c | 109 ++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+)
diff --git a/dlls/shlwapi/tests/string.c b/dlls/shlwapi/tests/string.c index 8379a523733..bd46bb38ae1 100644 --- a/dlls/shlwapi/tests/string.c +++ b/dlls/shlwapi/tests/string.c @@ -1689,6 +1689,114 @@ static void test_StrCatChainW(void) ok(buf[5] == 'e', "Expected buf[5] = 'e', got %x\n", buf[5]); }
+static void test_printf_format(void) +{ + const struct + { + const char *spec; + unsigned int arg_size; + const char *expected; + const WCHAR *expectedw; + ULONG64 arg; + const void *argw; + } + tests[] = + { + { "%qu", 0, "qu", NULL, 10 }, + { "%lu", sizeof(ULONG), "10", NULL, 10 }, + { "%#lx", sizeof(ULONG), "0xa", NULL, 10 }, + { "%hu", sizeof(ULONG), "10", NULL, 10 }, + { "%s", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str", L"str" }, + { "%S", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%ls", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, + { "%lS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, + { "%hs", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, + { "%hS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, + { "%ws", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, + { "%c", sizeof(SHORT), "\xc8", L"\x95c8", 0x95c8 }, + { "%lc", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, + { "%C", sizeof(SHORT), "\x3f", L"\xc8", 0x95c8 }, + { "%lC", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, + { "%hc", sizeof(BYTE), "\xc8", L"\xc8", 0x95c8 }, + { "%I64u", sizeof(ULONG64), "10", NULL, 10 }, + { "%I64s", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str", L"str" }, + { "%q%u", sizeof(ULONG), "q10", NULL, 10 }, + }; + WCHAR ws[256], expectedw[256], specw[256]; + unsigned int i, j; + char expected[256], spec[256], s[256]; + int len; + + *s = 0; + WideCharToMultiByte(CP_ACP, 0, L"\x95c8", -1, s, ARRAY_SIZE(s), NULL, NULL); + if (!is_locale_english() || strcmp(s, "\x3f")) + { + /* The test is not designed for testing locale but some of the test expected results depend on locale. */ + skip("An English locale is required for the printf tests.\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + strcpy(spec, tests[i].spec); + winetest_push_context("%s", spec); + strcat(spec,"|%s"); + *s = 0; + *ws = 0; + j = 0; + do + specw[j] = spec[j]; + while (specw[j++]); + switch (tests[i].arg_size) + { + case 0: + wnsprintfA(s, ARRAY_SIZE(s), spec, "end"); + if (tests[i].argw) + len = wnsprintfW(ws, ARRAY_SIZE(ws), specw, L"end"); + else + len = wnsprintfW(ws, ARRAY_SIZE(ws), specw, L"end"); + break; + case 1: + case 2: + case 4: + wnsprintfA(s, ARRAY_SIZE(s), spec, (ULONG)tests[i].arg, "end"); + if (tests[i].argw) + len = wnsprintfW(ws, ARRAY_SIZE(ws), specw, tests[i].argw, L"end"); + else + len = wnsprintfW(ws, ARRAY_SIZE(ws), specw, (ULONG)tests[i].arg, L"end"); + break; + case 8: + wnsprintfA(s, ARRAY_SIZE(s), spec, (ULONG64)tests[i].arg, "end"); + if (tests[i].argw) + len = wnsprintfW(ws, ARRAY_SIZE(ws), specw, tests[i].argw, L"end"); + else + len = wnsprintfW(ws, ARRAY_SIZE(ws), specw, (ULONG64)tests[i].arg, L"end"); + break; + default: + len = 0; + ok(0, "unknown length %u.\n", tests[i].arg_size); + break; + } + strcpy(expected, tests[i].expected); + strcat(expected, "|end"); + ok(len == strlen(expected), "got len %d, expected %Id.\n", len, strlen(expected)); + ok(!strcmp(s, expected), "got %s, expected %s.\n", debugstr_a(s), debugstr_a(expected)); + if (tests[i].expectedw) + { + wcscpy(expectedw, tests[i].expectedw); + wcscat(expectedw, L"|end"); + } + else + { + for (j = 0; j < len; ++j) + expectedw[j] = expected[j]; + } + expectedw[len] = 0; + ok(!wcscmp(ws, expectedw), "got %s, expected %s.\n", debugstr_w(ws), debugstr_w(expectedw)); + winetest_pop_context(); + } +} + START_TEST(string) { HMODULE hShlwapi; @@ -1777,6 +1885,7 @@ START_TEST(string) test_StrStrNW(); test_StrStrNIW(); test_StrCatChainW(); + test_printf_format();
CoUninitialize(); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/shlwapi/tests/string.c | 11 +++++++++++ dlls/shlwapi/wsprintf.c | 38 +++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 14 deletions(-)
diff --git a/dlls/shlwapi/tests/string.c b/dlls/shlwapi/tests/string.c index bd46bb38ae1..f74ebf13c22 100644 --- a/dlls/shlwapi/tests/string.c +++ b/dlls/shlwapi/tests/string.c @@ -1703,9 +1703,13 @@ static void test_printf_format(void) tests[] = { { "%qu", 0, "qu", NULL, 10 }, + { "%ll", 0, "", NULL, 10 }, { "%lu", sizeof(ULONG), "10", NULL, 10 }, { "%#lx", sizeof(ULONG), "0xa", NULL, 10 }, { "%hu", sizeof(ULONG), "10", NULL, 10 }, + { "%hhu", sizeof(ULONG), "10", NULL, 10 }, + { "%hwu", sizeof(ULONG), "10", NULL, 10 }, + { "%whu", sizeof(ULONG), "10", NULL, 10 }, { "%s", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str", L"str" }, { "%S", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, { "%ls", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, @@ -1713,12 +1717,19 @@ static void test_printf_format(void) { "%hs", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, { "%hS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, { "%ws", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, + { "%hhs", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, + { "%hhS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, + { "%wwws", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, { "%c", sizeof(SHORT), "\xc8", L"\x95c8", 0x95c8 }, { "%lc", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, { "%C", sizeof(SHORT), "\x3f", L"\xc8", 0x95c8 }, { "%lC", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, { "%hc", sizeof(BYTE), "\xc8", L"\xc8", 0x95c8 }, + { "%hhc", sizeof(BYTE), "\xc8", L"\xc8", 0x95c8 }, + { "%hhhc", sizeof(BYTE), "\xc8", L"\xc8", 0x95c8 }, { "%I64u", sizeof(ULONG64), "10", NULL, 10 }, + { "%llI64u", sizeof(ULONG64), "10", NULL, 10 }, + { "%I64llu", sizeof(ULONG64), "10", NULL, 10 }, { "%I64s", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str", L"str" }, { "%q%u", sizeof(ULONG), "q10", NULL, 10 }, }; diff --git a/dlls/shlwapi/wsprintf.c b/dlls/shlwapi/wsprintf.c index fbd2a7e75bf..ed385081821 100644 --- a/dlls/shlwapi/wsprintf.c +++ b/dlls/shlwapi/wsprintf.c @@ -110,14 +110,19 @@ static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res ) p++; } } - if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; } - else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; } - else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; } - else if (*p == 'I') + while (*p && strchr("lhwI", *p)) { - if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; } - else if (p[1] == '3' && p[2] == '2') p += 3; - else { res->flags |= WPRINTF_INTPTR; p++; } + switch (*p) + { + case 'l': res->flags |= WPRINTF_LONG; ++p; break; + case 'I': + if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; } + else if (p[1] == '3' && p[2] == '2') p += 3; + else { res->flags |= WPRINTF_INTPTR; p++; } + break; + case 'h': res->flags |= WPRINTF_SHORT; ++p; break; + case 'w': res->flags |= WPRINTF_WIDE; ++p; break; + } } switch(*p) { @@ -192,14 +197,19 @@ static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res ) p++; } } - if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; } - else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; } - else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; } - else if (*p == 'I') + while (*p && strchr("lhwI", *p)) { - if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; } - else if (p[1] == '3' && p[2] == '2') p += 3; - else { res->flags |= WPRINTF_INTPTR; p++; } + switch (*p) + { + case 'l': res->flags |= WPRINTF_LONG; ++p; break; + case 'I': + if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; } + else if (p[1] == '3' && p[2] == '2') p += 3; + else { res->flags |= WPRINTF_INTPTR; p++; } + break; + case 'h': res->flags |= WPRINTF_SHORT; ++p; break; + case 'w': res->flags |= WPRINTF_WIDE; ++p; break; + } } switch(*p) {
From: Paul Gofman pgofman@codeweavers.com
--- dlls/shlwapi/tests/string.c | 26 ++++++++++++++++++++++++++ dlls/shlwapi/wsprintf.c | 18 +++++++++--------- 2 files changed, 35 insertions(+), 9 deletions(-)
diff --git a/dlls/shlwapi/tests/string.c b/dlls/shlwapi/tests/string.c index f74ebf13c22..d3016837f9f 100644 --- a/dlls/shlwapi/tests/string.c +++ b/dlls/shlwapi/tests/string.c @@ -1717,16 +1717,42 @@ static void test_printf_format(void) { "%hs", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, { "%hS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, { "%ws", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, + { "%wS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, { "%hhs", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, { "%hhS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, + { "%wws", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, + { "%wwS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, { "%wwws", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, + { "%wwwS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, + { "%hws", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%hwS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%whs", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%whS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%hwls", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%hwlls", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%hwlS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%hwllS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%lhws", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%llhws", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%lhwS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%llhwS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, { "%c", sizeof(SHORT), "\xc8", L"\x95c8", 0x95c8 }, { "%lc", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, + { "%lllc", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, + { "%lllllc", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, { "%C", sizeof(SHORT), "\x3f", L"\xc8", 0x95c8 }, { "%lC", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, + { "%lllC", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, + { "%lllllC", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, { "%hc", sizeof(BYTE), "\xc8", L"\xc8", 0x95c8 }, { "%hhc", sizeof(BYTE), "\xc8", L"\xc8", 0x95c8 }, { "%hhhc", sizeof(BYTE), "\xc8", L"\xc8", 0x95c8 }, + { "%wc", sizeof(BYTE), "\x3f", L"\x95c8", 0x95c8 }, + { "%wC", sizeof(BYTE), "\x3f", L"\x95c8", 0x95c8 }, + { "%hwc", sizeof(BYTE), "\x3f", L"\xc8", 0x95c8 }, + { "%whc", sizeof(BYTE), "\x3f", L"\xc8", 0x95c8 }, + { "%hwC", sizeof(BYTE), "\x3f", L"\xc8", 0x95c8 }, + { "%whC", sizeof(BYTE), "\x3f", L"\xc8", 0x95c8 }, { "%I64u", sizeof(ULONG64), "10", NULL, 10 }, { "%llI64u", sizeof(ULONG64), "10", NULL, 10 }, { "%I64llu", sizeof(ULONG64), "10", NULL, 10 }, diff --git a/dlls/shlwapi/wsprintf.c b/dlls/shlwapi/wsprintf.c index ed385081821..eb64b5e3845 100644 --- a/dlls/shlwapi/wsprintf.c +++ b/dlls/shlwapi/wsprintf.c @@ -114,7 +114,7 @@ static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res ) { switch (*p) { - case 'l': res->flags |= WPRINTF_LONG; ++p; break; + case 'l': res->flags |= WPRINTF_LONG | WPRINTF_WIDE; ++p; break; case 'I': if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; } else if (p[1] == '3' && p[2] == '2') p += 3; @@ -127,20 +127,20 @@ static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res ) switch(*p) { case 'c': - res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR; + res->type = (res->flags & WPRINTF_WIDE) ? WPR_WCHAR : WPR_CHAR; break; case 'C': - res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR; + res->type = (res->flags & (WPRINTF_SHORT | WPRINTF_WIDE)) == WPRINTF_SHORT ? WPR_CHAR : WPR_WCHAR; break; case 'd': case 'i': res->type = WPR_SIGNED; break; case 's': - res->type = (res->flags & (WPRINTF_LONG |WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING; + res->type = (res->flags & WPRINTF_WIDE) ? WPR_WSTRING : WPR_STRING; break; case 'S': - res->type = (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING; + res->type = (res->flags & (WPRINTF_SHORT | WPRINTF_WIDE)) == WPRINTF_SHORT ? WPR_STRING : WPR_WSTRING; break; case 'u': res->type = WPR_UNSIGNED; @@ -201,7 +201,7 @@ static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res ) { switch (*p) { - case 'l': res->flags |= WPRINTF_LONG; ++p; break; + case 'l': res->flags |= WPRINTF_LONG | WPRINTF_WIDE; ++p; break; case 'I': if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; } else if (p[1] == '3' && p[2] == '2') p += 3; @@ -217,17 +217,17 @@ static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res ) res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR; break; case 'C': - res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR; + res->type = (res->flags & (WPRINTF_SHORT | WPRINTF_WIDE)) == WPRINTF_WIDE ? WPR_WCHAR : WPR_CHAR; break; case 'd': case 'i': res->type = WPR_SIGNED; break; case 's': - res->type = ((res->flags & WPRINTF_SHORT) && !(res->flags & WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING; + res->type = (res->flags & WPRINTF_SHORT) ? WPR_STRING : WPR_WSTRING; break; case 'S': - res->type = (res->flags & (WPRINTF_LONG|WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING; + res->type = (res->flags & (WPRINTF_SHORT | WPRINTF_WIDE)) == WPRINTF_WIDE ? WPR_WSTRING : WPR_STRING; break; case 'u': res->type = WPR_UNSIGNED;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/shlwapi/tests/string.c | 15 +++++++++++++++ dlls/shlwapi/wsprintf.c | 20 ++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/dlls/shlwapi/tests/string.c b/dlls/shlwapi/tests/string.c index d3016837f9f..d5594fc4c21 100644 --- a/dlls/shlwapi/tests/string.c +++ b/dlls/shlwapi/tests/string.c @@ -1705,15 +1705,26 @@ static void test_printf_format(void) { "%qu", 0, "qu", NULL, 10 }, { "%ll", 0, "", NULL, 10 }, { "%lu", sizeof(ULONG), "10", NULL, 10 }, + { "%llu", sizeof(ULONG64), "10", NULL, 10 }, + { "%lllllllu", sizeof(ULONG64), "10", NULL, 10 }, { "%#lx", sizeof(ULONG), "0xa", NULL, 10 }, + { "%#llx", sizeof(ULONG64), "0x1000000000", NULL, 0x1000000000 }, + { "%#lllx", sizeof(ULONG64), "0x1000000000", NULL, 0x1000000000 }, { "%hu", sizeof(ULONG), "10", NULL, 10 }, { "%hhu", sizeof(ULONG), "10", NULL, 10 }, { "%hwu", sizeof(ULONG), "10", NULL, 10 }, { "%whu", sizeof(ULONG), "10", NULL, 10 }, + { "%04lhlwllx", sizeof(ULONG64), "1000000010", NULL, 0x1000000010 }, { "%s", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str", L"str" }, { "%S", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, { "%ls", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, { "%lS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, + { "%lls", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str", L"str" }, + { "%llS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, + { "%llls", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, + { "%lllS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, + { "%lllls", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str", L"str" }, + { "%llllS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, { "%hs", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, { "%hS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str" }, { "%ws", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str" }, @@ -1738,11 +1749,15 @@ static void test_printf_format(void) { "%llhwS", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, { "%c", sizeof(SHORT), "\xc8", L"\x95c8", 0x95c8 }, { "%lc", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, + { "%llc", sizeof(SHORT), "\xc8", L"\x95c8", 0x95c8 }, { "%lllc", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, + { "%llllc", sizeof(SHORT), "\xc8", L"\x95c8", 0x95c8 }, { "%lllllc", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, { "%C", sizeof(SHORT), "\x3f", L"\xc8", 0x95c8 }, { "%lC", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, + { "%llC", sizeof(SHORT), "\x3f", L"\xc8", 0x95c8 }, { "%lllC", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, + { "%llllC", sizeof(SHORT), "\x3f", L"\xc8", 0x95c8 }, { "%lllllC", sizeof(SHORT), "\x3f", L"\x95c8", 0x95c8 }, { "%hc", sizeof(BYTE), "\xc8", L"\xc8", 0x95c8 }, { "%hhc", sizeof(BYTE), "\xc8", L"\xc8", 0x95c8 }, diff --git a/dlls/shlwapi/wsprintf.c b/dlls/shlwapi/wsprintf.c index eb64b5e3845..771c130be70 100644 --- a/dlls/shlwapi/wsprintf.c +++ b/dlls/shlwapi/wsprintf.c @@ -114,7 +114,15 @@ static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res ) { switch (*p) { - case 'l': res->flags |= WPRINTF_LONG | WPRINTF_WIDE; ++p; break; + case 'l': + ++p; + if (*p == 'l') + { + res->flags |= WPRINTF_I64; + ++p; + } + else res->flags |= WPRINTF_LONG | WPRINTF_WIDE; + break; case 'I': if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; } else if (p[1] == '3' && p[2] == '2') p += 3; @@ -201,7 +209,15 @@ static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res ) { switch (*p) { - case 'l': res->flags |= WPRINTF_LONG | WPRINTF_WIDE; ++p; break; + case 'l': + ++p; + if (*p == 'l') + { + res->flags |= WPRINTF_I64; + ++p; + } + else res->flags |= WPRINTF_LONG | WPRINTF_WIDE; + break; case 'I': if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; } else if (p[1] == '3' && p[2] == '2') p += 3;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/shlwapi/tests/string.c | 5 +++++ dlls/shlwapi/wsprintf.c | 4 ++++ 2 files changed, 9 insertions(+)
diff --git a/dlls/shlwapi/tests/string.c b/dlls/shlwapi/tests/string.c index d5594fc4c21..ab9b305f032 100644 --- a/dlls/shlwapi/tests/string.c +++ b/dlls/shlwapi/tests/string.c @@ -1773,6 +1773,11 @@ static void test_printf_format(void) { "%I64llu", sizeof(ULONG64), "10", NULL, 10 }, { "%I64s", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str", L"str" }, { "%q%u", sizeof(ULONG), "q10", NULL, 10 }, + { "%u% ", sizeof(ULONG), "10", NULL, 10 }, + { "% ll u", 0, " u", NULL, 10 }, + { "% llu", sizeof(ULONG64), "10", NULL, 10 }, + { "%# llx", sizeof(ULONG64), "0xa", NULL, 10 }, + { "% #llx", sizeof(ULONG64), "0xa", NULL, 10 }, }; WCHAR ws[256], expectedw[256], specw[256]; unsigned int i, j; diff --git a/dlls/shlwapi/wsprintf.c b/dlls/shlwapi/wsprintf.c index 771c130be70..b4113d2d563 100644 --- a/dlls/shlwapi/wsprintf.c +++ b/dlls/shlwapi/wsprintf.c @@ -90,11 +90,13 @@ static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res ) { LPCSTR p = format;
+ while (*p == ' ') ++p; res->flags = 0; res->width = 0; res->precision = 0; if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; } if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; } + while (*p == ' ') ++p; if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; } while ((*p >= '0') && (*p <= '9')) /* width field */ { @@ -185,11 +187,13 @@ static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res ) { LPCWSTR p = format;
+ while (*p == ' ') ++p; res->flags = 0; res->width = 0; res->precision = 0; if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; } if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; } + while (*p == ' ') ++p; if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; } while ((*p >= '0') && (*p <= '9')) /* width field */ {
From: Paul Gofman pgofman@codeweavers.com
--- dlls/shlwapi/tests/string.c | 2 ++ dlls/shlwapi/wsprintf.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/shlwapi/tests/string.c b/dlls/shlwapi/tests/string.c index ab9b305f032..85b5c653fa1 100644 --- a/dlls/shlwapi/tests/string.c +++ b/dlls/shlwapi/tests/string.c @@ -1714,6 +1714,8 @@ static void test_printf_format(void) { "%hhu", sizeof(ULONG), "10", NULL, 10 }, { "%hwu", sizeof(ULONG), "10", NULL, 10 }, { "%whu", sizeof(ULONG), "10", NULL, 10 }, + { "%##lhllwlx", sizeof(ULONG64), "0x1000000010", NULL, 0x1000000010 }, + { "%##lhlwlx", sizeof(ULONG), "0x10", NULL, 0x1000000010 }, { "%04lhlwllx", sizeof(ULONG64), "1000000010", NULL, 0x1000000010 }, { "%s", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str", L"str" }, { "%S", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"str", "str" }, diff --git a/dlls/shlwapi/wsprintf.c b/dlls/shlwapi/wsprintf.c index b4113d2d563..abf217e9474 100644 --- a/dlls/shlwapi/wsprintf.c +++ b/dlls/shlwapi/wsprintf.c @@ -95,7 +95,7 @@ static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res ) res->width = 0; res->precision = 0; if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; } - if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; } + while (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; } while (*p == ' ') ++p; if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; } while ((*p >= '0') && (*p <= '9')) /* width field */ @@ -192,7 +192,7 @@ static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res ) res->width = 0; res->precision = 0; if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; } - if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; } + while (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; } while (*p == ' ') ++p; if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; } while ((*p >= '0') && (*p <= '9')) /* width field */
From: Paul Gofman pgofman@codeweavers.com
--- dlls/shlwapi/tests/string.c | 2 ++ dlls/shlwapi/wsprintf.c | 2 ++ 2 files changed, 4 insertions(+)
diff --git a/dlls/shlwapi/tests/string.c b/dlls/shlwapi/tests/string.c index 85b5c653fa1..478b2416ffa 100644 --- a/dlls/shlwapi/tests/string.c +++ b/dlls/shlwapi/tests/string.c @@ -1775,7 +1775,9 @@ static void test_printf_format(void) { "%I64llu", sizeof(ULONG64), "10", NULL, 10 }, { "%I64s", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)"str", L"str" }, { "%q%u", sizeof(ULONG), "q10", NULL, 10 }, + { "%lhw%u", 0, "%u", NULL, 10 }, { "%u% ", sizeof(ULONG), "10", NULL, 10 }, + { "%u% %u", sizeof(ULONG), "10%u", NULL, 10 }, { "% ll u", 0, " u", NULL, 10 }, { "% llu", sizeof(ULONG64), "10", NULL, 10 }, { "%# llx", sizeof(ULONG64), "0xa", NULL, 10 }, diff --git a/dlls/shlwapi/wsprintf.c b/dlls/shlwapi/wsprintf.c index abf217e9474..daff024d9c3 100644 --- a/dlls/shlwapi/wsprintf.c +++ b/dlls/shlwapi/wsprintf.c @@ -483,6 +483,7 @@ INT WINAPI wvnsprintfA( LPSTR buffer, INT maxlen, LPCSTR spec, va_list args ) p += len - sign; break; case WPR_UNKNOWN: + if (*spec) *p++ = *spec++; continue; } if (format.flags & WPRINTF_LEFTALIGN) @@ -603,6 +604,7 @@ INT WINAPI wvnsprintfW( LPWSTR buffer, INT maxlen, LPCWSTR spec, va_list args ) for (i = sign; i < len; i++) *p++ = (BYTE)number[i]; break; case WPR_UNKNOWN: + if (*spec) *p++ = *spec++; continue; } if (format.flags & WPRINTF_LEFTALIGN)
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=148942
Your paranoid android.
=== debian11b (64 bit WoW report) ===
Report validation errors: mshtml:script crashed (c0000005)
Do we really need to reimplement the function in shlwapi? All the tests you have added are passing after changing the test to use msvcrt _swprintf_c instead of wnsprintfW (on Windows, there's a crash on Wine). There's also ntdll implementation that can be used but I haven't tested if it supports all the features.
I don't know why exactly that was duplicated, but that was separated explicitly back then in df6fa302882eb317e80b94ffaaccaaf2af4205e1 . Now looking at that I realized that it is also duplicated in user32 and that part probably needs the same change.
@julliard I am not sure is there still the reason to keep that separate and not use ucrtbase printf if that passes the tests?
On Wed Oct 9 14:38:16 2024 +0000, Paul Gofman wrote:
I don't know why exactly that was duplicated, but that was separated explicitly back then in df6fa302882eb317e80b94ffaaccaaf2af4205e1 . Now looking at that I realized that it is also duplicated in user32 and that part probably needs the same change. @julliard I am not sure is there still the reason to keep that separate and not use ucrtbase printf if that passes the tests?
I guess it was duplicated to get rid of wine-specific exports from user32.dll.
On Wed Oct 9 14:38:16 2024 +0000, Piotr Caban wrote:
I guess it was duplicated to get rid of wine-specific exports from user32.dll.
Ok I will make the fixes to msvcrt implementation then which allows to pass these tests (which is probably useful regardless) and then probably use those in shlwapi / user32.
On Wed Oct 9 14:40:12 2024 +0000, Paul Gofman wrote:
Ok I will make the fixes to msvcrt implementation then which allows to pass these tests (which is probably useful regardless) and then probably use those in shlwapi / user32.
These don't support floating point, so I doubt you can use msvcrt. You may be able to use the ntdll version.
On Wed Oct 9 14:56:24 2024 +0000, Alexandre Julliard wrote:
These don't support floating point, so I doubt you can use msvcrt. You may be able to use the ntdll version.
It looks like %g format is not supported in shlwapi. That suggests that ntdll may be a better match.