-- v4: shlwapi: Use printf implementation from ntdll. ntdll: Make 'h' take precedence over 'l' in pf_vsnprintf(). ntdll: Make 'l' modifier also affect char wideness. ntdll: Output unrecognized format symbol in pf_vsnprintf(). ntdll: Fix passing char argument to pf_handle_string_format(). ntdll/tests: Add more tests for printf format.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/string.c | 142 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+)
diff --git a/dlls/ntdll/tests/string.c b/dlls/ntdll/tests/string.c index 51f309209a8..aad42be7a82 100644 --- a/dlls/ntdll/tests/string.c +++ b/dlls/ntdll/tests/string.c @@ -1816,6 +1816,147 @@ static void test__snwprintf_s(void)
}
+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 }, + { "%ll", 0, "", NULL, 10 }, + { "%lu", sizeof(ULONG), "65537", NULL, 65537 }, + { "%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), "1", NULL, 65537 }, + { "%hllx", sizeof(ULONG64), "100000010", NULL, 0x100000010 }, + { "%hlllx", sizeof(ULONG64), "100000010", NULL, 0x100000010 }, + { "%llhx", sizeof(ULONG64), "100000010", NULL, 0x100000010 }, + { "%lllhx", sizeof(ULONG64), "100000010", NULL, 0x100000010 }, + { "%lhu", sizeof(ULONG), "1", NULL, 65537 }, + { "%hhu", sizeof(ULONG), "1", NULL, 65537 }, + { "%hwu", sizeof(ULONG), "1", NULL, 65537 }, + { "%whu", sizeof(ULONG), "1", NULL, 65537 }, + { "%##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" }, + { "%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" }, + { "%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" }, + { "%c", sizeof(SHORT), "\xc8", L"\x95c8", 0x95c8 }, + { "%llc", sizeof(SHORT), "\xc8", L"\x95c8", 0x95c8 }, + { "%llllc", sizeof(SHORT), "\xc8", 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 }, + { "%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; + char expected[256], spec[256], s[256]; + int len; + + p_snprintf(s, ARRAY_SIZE(s), "%C", 0x95c8); + p_snwprintf(ws, ARRAY_SIZE(ws), L"%c", 0x95c8); + p_snwprintf(expectedw, ARRAY_SIZE(expectedw), L"%C", 0x95c8); + if (s[0] != 0x3f || ws[0] != 0x95c8 || expectedw[0] != 0xc8) + { + /* The test is not designed for testing locale but some of the test expected results depend on locale. */ + skip("An English non-UTF8 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++]); + if (tests[i].argw) + len = p_snwprintf(ws, ~(size_t)0, specw, tests[i].argw, L"end"); + switch (tests[i].arg_size) + { + case 0: + p_snprintf(s, ~(size_t)0, spec, "end"); + len = p_snwprintf(ws, ~(size_t)0, specw, L"end"); + break; + case 1: + case 2: + case 4: + p_snprintf(s, ARRAY_SIZE(s), spec, (ULONG)tests[i].arg, "end"); + if (!tests[i].argw) + len = p_snwprintf(ws, ~(size_t)0, specw, (ULONG)tests[i].arg, L"end"); + break; + case 8: + p_snprintf(s, ~(size_t)0, spec, (ULONG64)tests[i].arg, "end"); + if (!tests[i].argw) + len = p_snwprintf(ws, ~(size_t)0, 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(); + } +} + static void test_tolower(void) { int i, ret, exp_ret; @@ -2086,6 +2227,7 @@ START_TEST(string) test__snprintf_s(); test__snwprintf(); test__snwprintf_s(); + test_printf_format(); test_tolower(); test_toupper(); test__strnicmp();
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/printf.h | 2 +- dlls/ntdll/tests/string.c | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/printf.h b/dlls/ntdll/printf.h index 4fad5c0ddec..b7dba6594a5 100644 --- a/dlls/ntdll/printf.h +++ b/dlls/ntdll/printf.h @@ -446,7 +446,7 @@ static int FUNC_NAME(pf_vsnprintf)( FUNC_NAME(pf_output) *out, const APICHAR *fo /* output a single character */ else if( flags.Format == 'c' || flags.Format == 'C' ) { - APICHAR ch = (APICHAR)va_arg( valist, int ); + wchar_t ch = (wchar_t)va_arg( valist, int ); r = FUNC_NAME(pf_handle_string_format)( out, &ch, 1, &flags, (flags.Format == 'C') ); }
diff --git a/dlls/ntdll/tests/string.c b/dlls/ntdll/tests/string.c index aad42be7a82..6454720b645 100644 --- a/dlls/ntdll/tests/string.c +++ b/dlls/ntdll/tests/string.c @@ -1870,11 +1870,22 @@ static void test_printf_format(void) { "%wwws", sizeof(ULONG_PTR), "str", NULL, (ULONG_PTR)L"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 }, { "%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 }, { "%hhhc", sizeof(BYTE), "\xc8", L"\xc8", 0x95c8 }, + { "%wc", sizeof(BYTE), "\x3f", L"\x95c8", 0x95c8 }, + { "%wC", sizeof(BYTE), "\x3f", L"\x95c8", 0x95c8 }, { "%I64u", sizeof(ULONG64), "10", NULL, 10 }, { "%llI64u", sizeof(ULONG64), "10", NULL, 10 }, { "%I64llu", sizeof(ULONG64), "10", NULL, 10 },
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/printf.h | 4 ++-- dlls/ntdll/tests/string.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/printf.h b/dlls/ntdll/printf.h index b7dba6594a5..43ed6bd23fc 100644 --- a/dlls/ntdll/printf.h +++ b/dlls/ntdll/printf.h @@ -498,8 +498,8 @@ static int FUNC_NAME(pf_vsnprintf)( FUNC_NAME(pf_output) *out, const APICHAR *fo if( x != number ) RtlFreeHeap( GetProcessHeap(), 0, x ); } - else - continue; + else if (*p) r = FUNC_NAME(pf_output_string)( out, p, 1 ); + else break;
if( r<0 ) return r; diff --git a/dlls/ntdll/tests/string.c b/dlls/ntdll/tests/string.c index 6454720b645..43330b2006f 100644 --- a/dlls/ntdll/tests/string.c +++ b/dlls/ntdll/tests/string.c @@ -1891,7 +1891,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 },
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/printf.h | 18 +++++++----------- dlls/ntdll/tests/string.c | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/dlls/ntdll/printf.h b/dlls/ntdll/printf.h index 43ed6bd23fc..e57cb01338b 100644 --- a/dlls/ntdll/printf.h +++ b/dlls/ntdll/printf.h @@ -200,19 +200,14 @@ static int FUNC_NAME(pf_handle_string_format)( FUNC_NAME(pf_output) *out, const if (str == NULL) /* catch NULL pointer */ return FUNC_NAME(pf_output_format)( out, APISTR("(null)"), -1, flags );
- /* prefixes take priority over %c,%s vs. %C,%S, so we handle them first */ - if (flags->WideString || flags->IntegerLength == LEN_LONG) - return FUNC_NAME(pf_output_format_wstr)( out, str, len, flags ); - if (flags->IntegerLength == LEN_SHORT) - return FUNC_NAME(pf_output_format_str)( out, str, len, flags ); - - /* %s,%c -> chars in ansi functions & wchars in unicode - * %S,%C -> wchars in ansi functions & chars in unicode */ - if (!inverted) return FUNC_NAME(pf_output_format)( out, str, len, flags); #ifdef PRINTF_WIDE - return FUNC_NAME(pf_output_format_str)( out, str, len, flags ); -#else + if (flags->IntegerLength == LEN_SHORT || (inverted && !flags->WideString)) + return FUNC_NAME(pf_output_format_str)( out, str, len, flags ); return FUNC_NAME(pf_output_format_wstr)( out, str, len, flags ); +#else + if (flags->WideString || (inverted && flags->IntegerLength != LEN_SHORT)) + return FUNC_NAME(pf_output_format_wstr)( out, str, len, flags ); + return FUNC_NAME(pf_output_format_str)( out, str, len, flags ); #endif }
@@ -378,6 +373,7 @@ static int FUNC_NAME(pf_vsnprintf)( FUNC_NAME(pf_output) *out, const APICHAR *fo else if( *p == 'l' ) { flags.IntegerLength = LEN_LONG; + flags.WideString = TRUE; p++; } else if( *p == 'L' ) diff --git a/dlls/ntdll/tests/string.c b/dlls/ntdll/tests/string.c index 43330b2006f..c9901c10883 100644 --- a/dlls/ntdll/tests/string.c +++ b/dlls/ntdll/tests/string.c @@ -1869,6 +1869,16 @@ static void test_printf_format(void) { "%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" }, + { "%hwlls", 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 }, { "%llc", sizeof(SHORT), "\xc8", L"\x95c8", 0x95c8 }, @@ -1886,6 +1896,10 @@ static void test_printf_format(void) { "%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 },
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/printf.h | 6 +++++- dlls/ntdll/tests/string.c | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/printf.h b/dlls/ntdll/printf.h index e57cb01338b..6e0c7832d05 100644 --- a/dlls/ntdll/printf.h +++ b/dlls/ntdll/printf.h @@ -372,7 +372,11 @@ static int FUNC_NAME(pf_vsnprintf)( FUNC_NAME(pf_output) *out, const APICHAR *fo } else if( *p == 'l' ) { - flags.IntegerLength = LEN_LONG; + if (flags.IntegerLength != LEN_SHORT) + { + flags.IntegerLength = LEN_LONG; + flags.WideString = TRUE; + } flags.WideString = TRUE; p++; } diff --git a/dlls/ntdll/tests/string.c b/dlls/ntdll/tests/string.c index c9901c10883..3d975d12a10 100644 --- a/dlls/ntdll/tests/string.c +++ b/dlls/ntdll/tests/string.c @@ -1838,6 +1838,7 @@ static void test_printf_format(void) { "%#llx", sizeof(ULONG64), "0x1000000000", NULL, 0x1000000000 }, { "%#lllx", sizeof(ULONG64), "0x1000000000", NULL, 0x1000000000 }, { "%hu", sizeof(ULONG), "1", NULL, 65537 }, + { "%hlu", sizeof(ULONG), "1", NULL, 65537 }, { "%hllx", sizeof(ULONG64), "100000010", NULL, 0x100000010 }, { "%hlllx", sizeof(ULONG64), "100000010", NULL, 0x100000010 }, { "%llhx", sizeof(ULONG64), "100000010", NULL, 0x100000010 }, @@ -1873,7 +1874,9 @@ static void test_printf_format(void) { "%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" },
From: Paul Gofman pgofman@codeweavers.com
--- dlls/shlwapi/Makefile.in | 3 +- dlls/shlwapi/shlwapi_main.c | 124 ++++++- dlls/shlwapi/tests/string.c | 215 ++++++++++++ dlls/shlwapi/wsprintf.c | 629 ------------------------------------ dlls/user32/wsprintf.c | 4 - 5 files changed, 331 insertions(+), 644 deletions(-) delete mode 100644 dlls/shlwapi/wsprintf.c
diff --git a/dlls/shlwapi/Makefile.in b/dlls/shlwapi/Makefile.in index 2c94cfefce1..6bf580f42d5 100644 --- a/dlls/shlwapi/Makefile.in +++ b/dlls/shlwapi/Makefile.in @@ -18,5 +18,4 @@ SOURCES = \ stopwatch.c \ string.c \ thread.c \ - url.c \ - wsprintf.c + url.c diff --git a/dlls/shlwapi/shlwapi_main.c b/dlls/shlwapi/shlwapi_main.c index 7820bc56281..ea45bdf32d5 100644 --- a/dlls/shlwapi/shlwapi_main.c +++ b/dlls/shlwapi/shlwapi_main.c @@ -29,9 +29,109 @@ #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell); +WINE_DECLARE_DEBUG_CHANNEL(string);
HINSTANCE shlwapi_hInstance = 0;
+static int (CDECL *ntdll__vsnprintf)( char *str, size_t len, const char *format, va_list args ); +static int (CDECL *ntdll__vsnwprintf)( WCHAR *str, size_t len, const WCHAR *format, va_list args ); + + +/*********************************************************************** + * wvnsprintfA (SHLWAPI.@) + * + * Print formatted output to a string, up to a maximum number of chars. + * + * PARAMS + * buffer [O] Destination for output string + * maxlen [I] Maximum number of characters to write + * spec [I] Format string + * + * RETURNS + * Success: The number of characters written. + * Failure: -1. + */ +INT WINAPI wvnsprintfA( LPSTR buffer, INT maxlen, LPCSTR spec, va_list args ) +{ + INT ret; + + TRACE_(string)( "%p %u %s\n", buffer, maxlen, debugstr_a(spec) ); + + if (!maxlen) return -1; + if (maxlen < 0) + { + buffer[0] = 0; + return -1; + } + if ((ret = ntdll__vsnprintf( buffer, maxlen, spec, args )) == -1) + buffer[maxlen - 1] = 0; + return ret; +} + +/*********************************************************************** + * wvnsprintfW (SHLWAPI.@) + * + * See wvnsprintfA. + */ +INT WINAPI wvnsprintfW( LPWSTR buffer, INT maxlen, LPCWSTR spec, va_list args ) +{ + INT ret; + + TRACE_(string)( "%p %u %s\n", buffer, maxlen, debugstr_w(spec) ); + + if (!maxlen) return -1; + if (maxlen < 0) + { + buffer[0] = 0; + return -1; + } + if ((ret = ntdll__vsnwprintf( buffer, maxlen, spec, args )) == -1) + buffer[maxlen - 1] = 0; + return ret; +} + +/************************************************************************* + * wnsprintfA (SHLWAPI.@) + * + * Print formatted output to a string, up to a maximum number of chars. + * + * PARAMS + * lpOut [O] Destination for output string + * cchLimitIn [I] Maximum number of characters to write + * lpFmt [I] Format string + * + * RETURNS + * Success: The number of characters written. + * Failure: -1. + */ +int WINAPIV wnsprintfA(LPSTR lpOut, int cchLimitIn, LPCSTR lpFmt, ...) +{ + va_list valist; + INT res; + + va_start( valist, lpFmt ); + res = wvnsprintfA( lpOut, cchLimitIn, lpFmt, valist ); + va_end( valist ); + return res; +} + +/************************************************************************* + * wnsprintfW (SHLWAPI.@) + * + * See wnsprintfA. + */ +int WINAPIV wnsprintfW(LPWSTR lpOut, int cchLimitIn, LPCWSTR lpFmt, ...) +{ + va_list valist; + INT res; + + va_start( valist, lpFmt ); + res = wvnsprintfW( lpOut, cchLimitIn, lpFmt, valist ); + va_end( valist ); + return res; +} + + /************************************************************************* * SHLWAPI {SHLWAPI} * @@ -54,15 +154,21 @@ HINSTANCE shlwapi_hInstance = 0; */ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad) { - TRACE("%p 0x%lx %p\n", hinstDLL, fdwReason, fImpLoad); - switch (fdwReason) - { - case DLL_PROCESS_ATTACH: - DisableThreadLibraryCalls(hinstDLL); - shlwapi_hInstance = hinstDLL; - break; - } - return TRUE; + TRACE("%p 0x%lx %p\n", hinstDLL, fdwReason, fImpLoad); + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + { + HANDLE hntdll = GetModuleHandleW(L"ntdll.dll"); + + ntdll__vsnprintf = (void *)GetProcAddress(hntdll, "_vsnprintf"); + ntdll__vsnwprintf = (void *)GetProcAddress(hntdll, "_vsnwprintf"); + DisableThreadLibraryCalls(hinstDLL); + shlwapi_hInstance = hinstDLL; + break; + } + } + return TRUE; }
/*********************************************************************** diff --git a/dlls/shlwapi/tests/string.c b/dlls/shlwapi/tests/string.c index 8379a523733..19430f27296 100644 --- a/dlls/shlwapi/tests/string.c +++ b/dlls/shlwapi/tests/string.c @@ -1173,6 +1173,29 @@ if (0) ok(broken(ret == 9) || ret == -1 /* Vista */, "Unexpected wnsprintfA return %d, expected 9 or -1\n", ret); expect_eq(buf[9], 0, CHAR, "%x"); expect_eq(buf[10], (CHAR)0xbf, CHAR, "%x"); + + memset(buf, 0xbf, sizeof(buf)); + ret = pwnsprintfA(buf + 1, -1, "%s", str1); + ok(ret == -1, "got %d.\n", ret); + expect_eq(buf[0], (CHAR)0xbf, CHAR, "%x"); + if (!broken(1)) + { + /* This is 0xbf before Win8. */ + expect_eq(buf[1], 0, CHAR, "%x"); + } + expect_eq(buf[2], (CHAR)0xbf, CHAR, "%x"); + + memset(buf, 0xbf, sizeof(buf)); + ret = pwnsprintfA(buf + 1, 0, "%s", str1); + ok(ret == -1, "got %d.\n", ret); + expect_eq(buf[0], (CHAR)0xbf, CHAR, "%x"); + expect_eq(buf[1], (CHAR)0xbf, CHAR, "%x"); + + memset(buf, 0xbf, sizeof(buf)); + ret = pwnsprintfA(buf, 1, ""); + ok(!ret, "got %d.\n", ret); + expect_eq(buf[0], 0, CHAR, "%x"); + expect_eq(buf[1], (CHAR)0xbf, CHAR, "%x"); } else win_skip("wnsprintfA() is not available\n"); @@ -1184,6 +1207,29 @@ if (0) ok(broken(ret == 9) || ret == -1 /* Vista */, "Unexpected wnsprintfW return %d, expected 9 or -1\n", ret); expect_eq(wbuf[9], 0, WCHAR, "%x"); expect_eq(wbuf[10], (WCHAR)0xbfbf, WCHAR, "%x"); + + memset(wbuf, 0xbf, sizeof(wbuf)); + ret = pwnsprintfW(wbuf + 1, -1, fmt, wstr1); + ok(ret == -1, "got %d.\n", ret); + expect_eq(wbuf[0], (WCHAR)0xbfbf, WCHAR, "%x"); + if (!broken(1)) + { + /* This is 0xbfbf before Win8. */ + expect_eq(wbuf[1], 0, WCHAR, "%x"); + } + expect_eq(wbuf[2], (WCHAR)0xbfbf, WCHAR, "%x"); + + memset(wbuf, 0xbf, sizeof(wbuf)); + ret = pwnsprintfW(wbuf + 1, 0, fmt, wstr1); + ok(ret == -1, "got %d.\n", ret); + expect_eq(wbuf[0], (WCHAR)0xbfbf, WCHAR, "%x"); + expect_eq(wbuf[1], (WCHAR)0xbfbf, WCHAR, "%x"); + + memset(wbuf, 0xbf, sizeof(wbuf)); + ret = pwnsprintfW(wbuf, 1, L""); + ok(!ret, "got %d.\n", ret); + expect_eq(wbuf[0], 0, WCHAR, "%x"); + expect_eq(wbuf[1], (WCHAR)0xbfbf, WCHAR, "%x"); } else win_skip("wnsprintfW() is not available\n"); @@ -1689,6 +1735,174 @@ 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; + ULONG64 arg; + const void *argw; + } + tests[] = + { + { "%qu", 0, 10 }, + { "%ll", 0, 10 }, + { "%lu", sizeof(ULONG), 65537 }, + { "%llu", sizeof(ULONG64), 10 }, + { "%lllllllu", sizeof(ULONG64), 10 }, + { "%#lx", sizeof(ULONG), 10 }, + { "%#llx", sizeof(ULONG64), 0x1000000000 }, + { "%#lllx", sizeof(ULONG64), 0x1000000000 }, + { "%hu", sizeof(ULONG), 65537 }, + { "%hlu", sizeof(ULONG), 65537 }, + { "%hllx", sizeof(ULONG64), 0x100000010 }, + { "%hlllx", sizeof(ULONG64), 0x100000010 }, + { "%llhx", sizeof(ULONG64), 0x100000010 }, + { "%lllhx", sizeof(ULONG64), 0x100000010 }, + { "%lhu", sizeof(ULONG), 65537 }, + { "%hhu", sizeof(ULONG), 65537 }, + { "%hwu", sizeof(ULONG), 65537 }, + { "%whu", sizeof(ULONG), 65537 }, + { "%##lhllwlx", sizeof(ULONG64), 0x1000000010 }, + { "%##lhlwlx", sizeof(ULONG), 0x1000000010 }, + { "%04lhlwllx", sizeof(ULONG64), 0x1000000010 }, + { "%s", sizeof(ULONG_PTR), (ULONG_PTR)"str", L"str" }, + { "%S", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%ls", sizeof(ULONG_PTR), (ULONG_PTR)L"str" }, + { "%lS", sizeof(ULONG_PTR), (ULONG_PTR)L"str" }, + { "%lls", sizeof(ULONG_PTR), (ULONG_PTR)"str", L"str" }, + { "%llS", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%llls", sizeof(ULONG_PTR), (ULONG_PTR)L"str" }, + { "%lllS", sizeof(ULONG_PTR), (ULONG_PTR)L"str" }, + { "%lllls", sizeof(ULONG_PTR), (ULONG_PTR)"str", L"str" }, + { "%llllS", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%hs", sizeof(ULONG_PTR), (ULONG_PTR)"str" }, + { "%hS", sizeof(ULONG_PTR), (ULONG_PTR)"str" }, + { "%ws", sizeof(ULONG_PTR), (ULONG_PTR)L"str" }, + { "%wS", sizeof(ULONG_PTR), (ULONG_PTR)L"str" }, + { "%hhs", sizeof(ULONG_PTR), (ULONG_PTR)"str" }, + { "%hhS", sizeof(ULONG_PTR), (ULONG_PTR)"str" }, + { "%wws", sizeof(ULONG_PTR), (ULONG_PTR)L"str" }, + { "%wwS", sizeof(ULONG_PTR), (ULONG_PTR)L"str" }, + { "%wwws", sizeof(ULONG_PTR), (ULONG_PTR)L"str" }, + { "%wwwS", sizeof(ULONG_PTR), (ULONG_PTR)L"str" }, + { "%hws", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%hwS", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%whs", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%whS", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%hwls", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%hwlls", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%hwlS", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%hwllS", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%lhws", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%llhws", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%lhwS", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%llhwS", sizeof(ULONG_PTR), (ULONG_PTR)L"str", "str" }, + { "%c", sizeof(SHORT), 0x95c8 }, + { "%lc", sizeof(SHORT), 0x95c8 }, + { "%llc", sizeof(SHORT), 0x95c8 }, + { "%lllc", sizeof(SHORT), 0x95c8 }, + { "%llllc", sizeof(SHORT), 0x95c8 }, + { "%lllllc", sizeof(SHORT), 0x95c8 }, + { "%C", sizeof(SHORT), 0x95c8 }, + { "%lC", sizeof(SHORT), 0x95c8 }, + { "%llC", sizeof(SHORT), 0x95c8 }, + { "%lllC", sizeof(SHORT), 0x95c8 }, + { "%llllC", sizeof(SHORT), 0x95c8 }, + { "%lllllC", sizeof(SHORT), 0x95c8 }, + { "%hc", sizeof(BYTE), 0x95c8 }, + { "%hhc", sizeof(BYTE), 0x95c8 }, + { "%hhhc", sizeof(BYTE), 0x95c8 }, + { "%wc", sizeof(BYTE), 0x95c8 }, + { "%wC", sizeof(BYTE), 0x95c8 }, + { "%hwc", sizeof(BYTE), 0x95c8 }, + { "%whc", sizeof(BYTE), 0x95c8 }, + { "%hwC", sizeof(BYTE), 0x95c8 }, + { "%whC", sizeof(BYTE), 0x95c8 }, + { "%I64u", sizeof(ULONG64), 10 }, + { "%llI64u", sizeof(ULONG64), 10 }, + { "%I64llu", sizeof(ULONG64), 10 }, + { "%I64s", sizeof(ULONG_PTR), (ULONG_PTR)"str", L"str" }, + { "%q%u", sizeof(ULONG), 10 }, + { "%lhw%u", 0, 10 }, + { "%u% ", sizeof(ULONG), 10 }, + { "%u% %u", sizeof(ULONG), 10 }, + { "% ll u", 0, 10 }, + { "% llu", sizeof(ULONG64), 10 }, + { "%# llx", sizeof(ULONG64), 10 }, + { "% #llx", sizeof(ULONG64), 10 }, + }; + int (WINAPIV *ntdll__snprintf)(char *str, size_t len, const char *format, ...); + int (WINAPIV *ntdll__snwprintf)( WCHAR *str, size_t len, const WCHAR *format, ... ); + WCHAR ws[256], expectedw[256], specw[256]; + unsigned int i, j; + char expected[256], spec[256], s[256]; + int len_a, len_w = 0, expected_len_a, expected_len_w = 0; + HANDLE hntdll = GetModuleHandleW(L"ntdll.dll"); + + ntdll__snprintf = (void *)GetProcAddress(hntdll, "_snprintf"); + ok(!!ntdll__snprintf, "_snprintf not found.\n"); + ntdll__snwprintf = (void *)GetProcAddress(hntdll, "_snwprintf"); + ok(!!ntdll__snwprintf, "_snwprintf not found.\n"); + + 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++]); + if (tests[i].argw) + { + len_w = pwnsprintfW(ws, ARRAY_SIZE(ws), specw, tests[i].argw, L"end"); + expected_len_w = ntdll__snwprintf(expectedw, ARRAY_SIZE(expectedw), specw, tests[i].argw, L"end"); + } + switch (tests[i].arg_size) + { + case 0: + len_a = pwnsprintfA(s, ARRAY_SIZE(s), spec, "end"); + expected_len_a = ntdll__snprintf(expected, ARRAY_SIZE(expected), spec, "end"); + len_w = pwnsprintfW(ws, ARRAY_SIZE(ws), specw, L"end"); + expected_len_w = ntdll__snwprintf(expectedw, ARRAY_SIZE(expectedw), specw, L"end"); + break; + case 1: + case 2: + case 4: + len_a = pwnsprintfA(s, ARRAY_SIZE(s), spec, (ULONG)tests[i].arg, "end"); + expected_len_a = ntdll__snprintf(expected, ARRAY_SIZE(expected), spec, (ULONG)tests[i].arg, "end"); + if (!tests[i].argw) + { + len_w = pwnsprintfW(ws, ARRAY_SIZE(ws), specw, (ULONG)tests[i].arg, L"end"); + expected_len_w = ntdll__snwprintf(expectedw, ARRAY_SIZE(expectedw), specw, (ULONG)tests[i].arg, L"end"); + } + break; + case 8: + len_a = pwnsprintfA(s, ARRAY_SIZE(s), spec, (ULONG64)tests[i].arg, "end"); + expected_len_a = ntdll__snprintf(expected, ARRAY_SIZE(s), spec, (ULONG64)tests[i].arg, "end"); + if (!tests[i].argw) + { + len_w = pwnsprintfW(ws, ARRAY_SIZE(ws), specw, (ULONG64)tests[i].arg, L"end"); + expected_len_w = ntdll__snwprintf(expectedw, ARRAY_SIZE(expectedw), specw, (ULONG64)tests[i].arg, L"end"); + } + break; + default: + len_a = len_w = expected_len_a = expected_len_w = 0; + ok(0, "unknown length %u.\n", tests[i].arg_size); + break; + } + ok(len_a == expected_len_a, "got len %d, expected %d.\n", len_a, expected_len_a); + ok(!strcmp(s, expected), "got %s, expected %s.\n", debugstr_a(s), debugstr_a(expected)); + ok(len_w == expected_len_w, "got len %d, expected %d.\n", len_a, expected_len_a); + 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 +1991,7 @@ START_TEST(string) test_StrStrNW(); test_StrStrNIW(); test_StrCatChainW(); + test_printf_format();
CoUninitialize(); } diff --git a/dlls/shlwapi/wsprintf.c b/dlls/shlwapi/wsprintf.c deleted file mode 100644 index fbd2a7e75bf..00000000000 --- a/dlls/shlwapi/wsprintf.c +++ /dev/null @@ -1,629 +0,0 @@ -/* - * wsprintf functions - * - * Copyright 1996 Alexandre Julliard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * NOTE: - * This code is duplicated in user32. If you change something here make sure - * to change it in user32 too. - */ - -#include <stdarg.h> -#include <string.h> -#include <stdio.h> - -#include "windef.h" -#include "winbase.h" -#define NO_SHLWAPI_REG -#include "shlwapi.h" - -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(string); - - -#define WPRINTF_LEFTALIGN 0x0001 /* Align output on the left ('-' prefix) */ -#define WPRINTF_PREFIX_HEX 0x0002 /* Prefix hex with 0x ('#' prefix) */ -#define WPRINTF_ZEROPAD 0x0004 /* Pad with zeros ('0' prefix) */ -#define WPRINTF_LONG 0x0008 /* Long arg ('l' prefix) */ -#define WPRINTF_SHORT 0x0010 /* Short arg ('h' prefix) */ -#define WPRINTF_UPPER_HEX 0x0020 /* Upper-case hex ('X' specifier) */ -#define WPRINTF_WIDE 0x0040 /* Wide arg ('w' prefix) */ -#define WPRINTF_INTPTR 0x0080 /* Pointer-size arg ('I' prefix) */ -#define WPRINTF_I64 0x0100 /* 64-bit arg ('I64' prefix) */ - -typedef enum -{ - WPR_UNKNOWN, - WPR_CHAR, - WPR_WCHAR, - WPR_STRING, - WPR_WSTRING, - WPR_SIGNED, - WPR_UNSIGNED, - WPR_HEXA -} WPRINTF_TYPE; - -typedef struct -{ - UINT flags; - UINT width; - UINT precision; - WPRINTF_TYPE type; -} WPRINTF_FORMAT; - -typedef union { - WCHAR wchar_view; - CHAR char_view; - LPCSTR lpcstr_view; - LPCWSTR lpcwstr_view; - LONGLONG int_view; -} WPRINTF_DATA; - -static const CHAR null_stringA[] = "(null)"; -static const WCHAR null_stringW[] = { '(', 'n', 'u', 'l', 'l', ')', 0 }; - -/*********************************************************************** - * WPRINTF_ParseFormatA - * - * Parse a format specification. A format specification has the form: - * - * [-][#][0][width][.precision]type - * - * Return value is the length of the format specification in characters. - */ -static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res ) -{ - LPCSTR p = format; - - res->flags = 0; - res->width = 0; - res->precision = 0; - if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; } - if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; } - if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; } - while ((*p >= '0') && (*p <= '9')) /* width field */ - { - res->width = res->width * 10 + *p - '0'; - p++; - } - if (*p == '.') /* precision field */ - { - p++; - while ((*p >= '0') && (*p <= '9')) - { - res->precision = res->precision * 10 + *p - '0'; - 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') - { - 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 'c': - res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR; - break; - case 'C': - res->type = (res->flags & 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; - break; - case 'S': - res->type = (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING; - break; - case 'u': - res->type = WPR_UNSIGNED; - break; - case 'p': - res->width = 2 * sizeof(void *); - res->flags |= WPRINTF_ZEROPAD | WPRINTF_INTPTR; - /* fall through */ - case 'X': - res->flags |= WPRINTF_UPPER_HEX; - /* fall through */ - case 'x': - res->type = WPR_HEXA; - break; - default: /* unknown format char */ - res->type = WPR_UNKNOWN; - p--; /* print format as normal char */ - break; - } - return (INT)(p - format) + 1; -} - - -/*********************************************************************** - * WPRINTF_ParseFormatW - * - * Parse a format specification. A format specification has the form: - * - * [-][#][0][width][.precision]type - * - * Return value is the length of the format specification in characters. - */ -static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res ) -{ - LPCWSTR p = format; - - res->flags = 0; - res->width = 0; - res->precision = 0; - if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; } - if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; } - if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; } - while ((*p >= '0') && (*p <= '9')) /* width field */ - { - res->width = res->width * 10 + *p - '0'; - p++; - } - if (*p == '.') /* precision field */ - { - p++; - while ((*p >= '0') && (*p <= '9')) - { - res->precision = res->precision * 10 + *p - '0'; - 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') - { - 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 'c': - res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR; - break; - case 'C': - res->type = (res->flags & WPRINTF_LONG) ? 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; - break; - case 'S': - res->type = (res->flags & (WPRINTF_LONG|WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING; - break; - case 'u': - res->type = WPR_UNSIGNED; - break; - case 'p': - res->width = 2 * sizeof(void *); - res->flags |= WPRINTF_ZEROPAD | WPRINTF_INTPTR; - /* fall through */ - case 'X': - res->flags |= WPRINTF_UPPER_HEX; - /* fall through */ - case 'x': - res->type = WPR_HEXA; - break; - default: - res->type = WPR_UNKNOWN; - p--; /* print format as normal char */ - break; - } - return (INT)(p - format) + 1; -} - - -/*********************************************************************** - * WPRINTF_GetLen - */ -static UINT WPRINTF_GetLen( WPRINTF_FORMAT *format, WPRINTF_DATA *arg, - LPSTR number, UINT maxlen, BOOL dst_is_wide ) -{ - UINT len; - - if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD; - if (format->width > maxlen) format->width = maxlen; - switch(format->type) - { - case WPR_CHAR: - return (format->precision = 1); - case WPR_WCHAR: - if (dst_is_wide) len = 1; - else len = WideCharToMultiByte( CP_ACP, 0, &arg->wchar_view, 1, NULL, 0, NULL, NULL ); - return (format->precision = len); - case WPR_STRING: - if (!arg->lpcstr_view) arg->lpcstr_view = null_stringA; - if (dst_is_wide) - { - LPCSTR p = arg->lpcstr_view; - for (len = 0; (!format->precision || len < format->precision) && *p; p++) - { - /* This isn't applicable for UTF-8 and UTF-7 */ - if (IsDBCSLeadByte( *p )) p++; - len++; - if (!*p) break; - } - } - else - { - for (len = 0; !format->precision || (len < format->precision); len++) - if (!*(arg->lpcstr_view + len)) break; - } - if (len > maxlen) len = maxlen; - return (format->precision = len); - case WPR_WSTRING: - if (!arg->lpcwstr_view) arg->lpcwstr_view = null_stringW; - if (dst_is_wide) - { - for (len = 0; !format->precision || (len < format->precision); len++) - if (!*(arg->lpcwstr_view + len)) break; - } - else - { - LPCWSTR p = arg->lpcwstr_view; - for (len = 0; (!format->precision || len < format->precision) && *p; p++) - len += WideCharToMultiByte( CP_ACP, 0, p, 1, NULL, 0, NULL, NULL ); - if (format->precision && len > format->precision) len = format->precision; - } - if (len > maxlen) len = maxlen; - return (format->precision = len); - case WPR_SIGNED: - case WPR_UNSIGNED: - case WPR_HEXA: - { - const char *digits = (format->flags & WPRINTF_UPPER_HEX) ? "0123456789ABCDEF" : "0123456789abcdef"; - ULONGLONG num = arg->int_view; - int base = format->type == WPR_HEXA ? 16 : 10; - char buffer[20], *p = buffer, *dst = number; - - if (format->type == WPR_SIGNED && arg->int_view < 0) - { - *dst++ = '-'; - num = -arg->int_view; - } - if (format->flags & WPRINTF_INTPTR) num = (UINT_PTR)num; - else if (!(format->flags & WPRINTF_I64)) num = (UINT)num; - - do - { - *p++ = digits[num % base]; - num /= base; - } while (num); - while (p > buffer) *dst++ = *(--p); - *dst = 0; - len = dst - number; - break; - } - default: - return 0; - } - if (len > maxlen) len = maxlen; - if (format->precision < len) format->precision = len; - if (format->precision > maxlen) format->precision = maxlen; - if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision)) - format->precision = format->width; - if (format->flags & WPRINTF_PREFIX_HEX) len += 2; - return len; -} - - -/*********************************************************************** - * wvnsprintfA (SHLWAPI.@) - * - * Print formatted output to a string, up to a maximum number of chars. - * - * PARAMS - * buffer [O] Destination for output string - * maxlen [I] Maximum number of characters to write - * spec [I] Format string - * - * RETURNS - * Success: The number of characters written. - * Failure: -1. - */ -INT WINAPI wvnsprintfA( LPSTR buffer, INT maxlen, LPCSTR spec, va_list args ) -{ - WPRINTF_FORMAT format; - LPSTR p = buffer; - UINT i, len, sign; - CHAR number[21]; /* 64bit number can be 18446744073709551616 which is 20 chars. and a \0 */ - WPRINTF_DATA argData; - - TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec)); - - while (*spec && (maxlen > 1)) - { - if (*spec != '%') { *p++ = *spec++; maxlen--; continue; } - spec++; - if (*spec == '%') { *p++ = *spec++; maxlen--; continue; } - spec += WPRINTF_ParseFormatA( spec, &format ); - - switch(format.type) - { - case WPR_WCHAR: - argData.wchar_view = (WCHAR)va_arg( args, int ); - break; - case WPR_CHAR: - argData.char_view = (CHAR)va_arg( args, int ); - break; - case WPR_STRING: - argData.lpcstr_view = va_arg( args, LPCSTR ); - break; - case WPR_WSTRING: - argData.lpcwstr_view = va_arg( args, LPCWSTR ); - break; - case WPR_HEXA: - case WPR_SIGNED: - case WPR_UNSIGNED: - if (format.flags & WPRINTF_INTPTR) argData.int_view = va_arg(args, INT_PTR); - else if (format.flags & WPRINTF_I64) argData.int_view = va_arg(args, LONGLONG); - else argData.int_view = va_arg(args, INT); - break; - default: - argData.wchar_view = 0; - break; - } - - len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1, FALSE ); - sign = 0; - if (!(format.flags & WPRINTF_LEFTALIGN)) - for (i = format.precision; i < format.width; i++, maxlen--) - *p++ = ' '; - switch(format.type) - { - case WPR_WCHAR: - { - CHAR mb[5]; - if (WideCharToMultiByte( CP_ACP, 0, &argData.wchar_view, 1, mb, sizeof(mb), NULL, NULL )) - { - memcpy( p, mb, len ); - p += len; - } - } - break; - case WPR_CHAR: - *p++ = argData.char_view; - break; - case WPR_STRING: - memcpy( p, argData.lpcstr_view, len ); - p += len; - break; - case WPR_WSTRING: - { - LPCWSTR ptr = argData.lpcwstr_view; - for (i = 0; i < len; ptr++) - { - CHAR mb[5]; /* 5 is MB_LEN_MAX */ - int ret = WideCharToMultiByte( CP_ACP, 0, ptr, 1, mb, sizeof(mb), NULL, NULL ); - i += ret; - if (i > len) ret = len - (p - buffer); - memcpy( p, mb, ret ); - p += ret; - } - } - break; - case WPR_HEXA: - if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3)) - { - *p++ = '0'; - *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x'; - maxlen -= 2; - len -= 2; - } - /* fall through */ - case WPR_SIGNED: - /* Transfer the sign now, just in case it will be zero-padded*/ - if (number[0] == '-') - { - *p++ = '-'; - sign = 1; - } - /* fall through */ - case WPR_UNSIGNED: - for (i = len; i < format.precision; i++, maxlen--) *p++ = '0'; - memcpy( p, number + sign, len - sign ); - p += len - sign; - break; - case WPR_UNKNOWN: - continue; - } - if (format.flags & WPRINTF_LEFTALIGN) - for (i = format.precision; i < format.width; i++, maxlen--) - *p++ = ' '; - maxlen -= len; - } - *p = 0; - TRACE("%s\n",debugstr_a(buffer)); - return (maxlen > 1) ? (INT)(p - buffer) : -1; -} - - -/*********************************************************************** - * wvnsprintfW (SHLWAPI.@) - * - * See wvnsprintfA. - */ -INT WINAPI wvnsprintfW( LPWSTR buffer, INT maxlen, LPCWSTR spec, va_list args ) -{ - WPRINTF_FORMAT format; - LPWSTR p = buffer; - UINT i, len, sign; - CHAR number[21]; /* 64bit number can be 18446744073709551616 which is 20 chars. and a \0 */ - WPRINTF_DATA argData; - - TRACE("%p %u %s\n", buffer, maxlen, debugstr_w(spec)); - - while (*spec && (maxlen > 1)) - { - if (*spec != '%') { *p++ = *spec++; maxlen--; continue; } - spec++; - if (*spec == '%') { *p++ = *spec++; maxlen--; continue; } - spec += WPRINTF_ParseFormatW( spec, &format ); - - switch(format.type) - { - case WPR_WCHAR: - argData.wchar_view = (WCHAR)va_arg( args, int ); - break; - case WPR_CHAR: - argData.char_view = (CHAR)va_arg( args, int ); - break; - case WPR_STRING: - argData.lpcstr_view = va_arg( args, LPCSTR ); - break; - case WPR_WSTRING: - argData.lpcwstr_view = va_arg( args, LPCWSTR ); - break; - case WPR_HEXA: - case WPR_SIGNED: - case WPR_UNSIGNED: - if (format.flags & WPRINTF_INTPTR) argData.int_view = va_arg(args, INT_PTR); - else if (format.flags & WPRINTF_I64) argData.int_view = va_arg(args, LONGLONG); - else argData.int_view = va_arg(args, INT); - break; - default: - argData.wchar_view = 0; - break; - } - - len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1, TRUE ); - sign = 0; - if (!(format.flags & WPRINTF_LEFTALIGN)) - for (i = format.precision; i < format.width; i++, maxlen--) - *p++ = ' '; - switch(format.type) - { - case WPR_WCHAR: - *p++ = argData.wchar_view; - break; - case WPR_CHAR: - { - WCHAR wc; - if (!IsDBCSLeadByte( (BYTE)argData.char_view ) - && MultiByteToWideChar( CP_ACP, 0, &argData.char_view, 1, &wc, 1 ) > 0) - *p++ = wc; - else - *p++ = 0; - break; - } - case WPR_STRING: - { - LPCSTR ptr = argData.lpcstr_view; - for (i = 0; i < len; i++) - { - WCHAR buf[2]; /* for LeadByte + NUL case, we need 2 WCHARs. */ - int ret, mb_len = IsDBCSLeadByte( *ptr ) ? 2 : 1; - ret = MultiByteToWideChar( CP_ACP, 0, ptr, mb_len, buf, ARRAY_SIZE( buf )); - *p++ = buf[ret - 1]; - ptr += mb_len; - } - } - break; - case WPR_WSTRING: - if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) ); - p += len; - break; - case WPR_HEXA: - if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3)) - { - *p++ = '0'; - *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x'; - maxlen -= 2; - len -= 2; - } - /* fall through */ - case WPR_SIGNED: - /* Transfer the sign now, just in case it will be zero-padded*/ - if (number[0] == '-') - { - *p++ = '-'; - sign = 1; - } - /* fall through */ - case WPR_UNSIGNED: - for (i = len; i < format.precision; i++, maxlen--) *p++ = '0'; - for (i = sign; i < len; i++) *p++ = (BYTE)number[i]; - break; - case WPR_UNKNOWN: - continue; - } - if (format.flags & WPRINTF_LEFTALIGN) - for (i = format.precision; i < format.width; i++, maxlen--) - *p++ = ' '; - maxlen -= len; - } - *p = 0; - TRACE("%s\n",debugstr_w(buffer)); - return (maxlen > 1) ? (INT)(p - buffer) : -1; -} - - -/************************************************************************* - * wnsprintfA (SHLWAPI.@) - * - * Print formatted output to a string, up to a maximum number of chars. - * - * PARAMS - * lpOut [O] Destination for output string - * cchLimitIn [I] Maximum number of characters to write - * lpFmt [I] Format string - * - * RETURNS - * Success: The number of characters written. - * Failure: -1. - */ -int WINAPIV wnsprintfA(LPSTR lpOut, int cchLimitIn, LPCSTR lpFmt, ...) -{ - va_list valist; - INT res; - - va_start( valist, lpFmt ); - res = wvnsprintfA( lpOut, cchLimitIn, lpFmt, valist ); - va_end( valist ); - return res; -} - - -/************************************************************************* - * wnsprintfW (SHLWAPI.@) - * - * See wnsprintfA. - */ -int WINAPIV wnsprintfW(LPWSTR lpOut, int cchLimitIn, LPCWSTR lpFmt, ...) -{ - va_list valist; - INT res; - - va_start( valist, lpFmt ); - res = wvnsprintfW( lpOut, cchLimitIn, lpFmt, valist ); - va_end( valist ); - return res; -} diff --git a/dlls/user32/wsprintf.c b/dlls/user32/wsprintf.c index 1b396f0269c..cbf74226418 100644 --- a/dlls/user32/wsprintf.c +++ b/dlls/user32/wsprintf.c @@ -16,10 +16,6 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * NOTE: - * This code is duplicated in shlwapi. If you change something here make sure - * to change it in shlwapi too. */
#include <stdarg.h>
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=148960
Your paranoid android.
=== debian11b (64 bit WoW report) ===
user32: input.c:4305: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 00000000018400E6, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
Changes: - Make ntdll printf implementation match extended tests and use that one in shlwapi.
I was also going to use that in user32 print implementation but when I ported the test included in shlwapi there turned out user32 printf behaves quite different. In particular, it doesn't understand double 'll' modifier (from which this MR started), 'h' modifier (I didn't dig into all the differences). So it looks apparent that user32 printf is different both from msvcrt and ntdll and I left it as is.
I also looked a bit into msvcrt printf if that requires similar fixes as ntdll printf and found that the parts which seem potentially important ('ll', correctly handling wide char in narrow print, outputting unrecognized format symbol) already work correctly. The remaining corner cases (like conflicting width and wideness specifiers) so far look more of theoretical issue and is harder to do in msvcrt in the presence of flags, different versions and new modifiers, so I guess it doesn't make sense to change anything there now.