The ANSI string may be longer than the Unicode one and the return value should reflect that.
Signed-off-by: Francois Gouget fgouget@codeweavers.com --- Supersedes: 212527-212529 v2: Fix the last todo_wine(). --- dlls/kernel32/tests/locale.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index b83cdc6b956..45bf56d8ad7 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -631,6 +631,34 @@ static void test_GetTimeFormatA(void) curtime.wMonth = 60; ret = GetTimeFormatA(lcid, 0, &curtime, "h:m:s", buffer, ARRAY_SIZE(buffer)); expect_str(ret, buffer, "12:56:13"); + + /* The ANSI string may be longer than the Unicode one. + * In particular, in the Japanese code page, "\x93\xfa" = L"\x65e5". + */ + + lcid = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), SORT_DEFAULT); + + ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", buffer, 5); + if (broken(1)) /* FIXME Remove once Wine is less broken */ + expect_str(ret, buffer, "12\x93\xfa"); /* only 3+1 WCHARs */ + todo_wine ok(ret == strlen("12\x93\xfa") + 1, "Expected ret %d, got %d\n", strlen("12\x93\xfa") + 1, ret); + ok(strcmp(buffer, "12\x93\xfa") == 0, "Expected '12\x93\xfa', got '%s'\n", buffer); + + ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", buffer, 4); + todo_wine expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER); + SetLastError(0xdeadbeef); + + ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", NULL, 0); + if (broken(1)) /* FIXME Remove once Wine is less broken */ + expect_str(ret, NULL, "12\x93\xfa"); + todo_wine ok(ret == strlen("12\x93\xfa") + 1, "Expected ret %d, got %d\n", strlen("12\x93\xfa") + 1, ret); + + strcpy(buffer, "pristine"); /* clear previous identical result */ + ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", buffer, 0); + if (broken(1)) /* FIXME Remove once Wine is less broken */ + expect_str(ret, NULL, "12\x93\xfa"); + todo_wine ok(ret == strlen("12\x93\xfa") + 1, "Expected ret %d, got %d\n", strlen("12\x93\xfa") + 1, ret); + ok(strcmp(buffer, "pristine") == 0, "Expected a pristine buffer, got '%s'\n", buffer); }
static void test_GetTimeFormatEx(void)
The ANSI string may be longer than the Unicode one and GetDateFormatA()'s return value should reflect that.
Signed-off-by: Francois Gouget fgouget@codeweavers.com --- v2: No change. --- dlls/kernel32/tests/locale.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+)
diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index 45bf56d8ad7..43dcf2a7dbf 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -839,6 +839,7 @@ static void test_GetDateFormatA(void) SYSTEMTIME curtime; LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); LCID lcid_ru = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT); + LCID lcid_ja = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), SORT_DEFAULT); char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE]; char short_day[10], month[10], genitive_month[10];
@@ -959,6 +960,27 @@ static void test_GetDateFormatA(void) ret = GetDateFormatA(lcid_ru, 0, &curtime, "ddd',' MMMM dd", buffer, ARRAY_SIZE(buffer)); sprintf(Expected, "%s, %s 04", short_day, genitive_month); expect_str(ret, buffer, Expected); + + /* The ANSI string may be longer than the Unicode one. + * In particular, in the Japanese code page, "\x93\xfa" = L"\x65e5". + * See the corresponding GetDateFormatW() test. + */ + + ret = GetDateFormatA(lcid_ja, 0, &curtime, "d\x93\xfa", buffer, 4); + if (broken(1)) /* FIXME Remove once Wine is less broken */ + expect_str(ret, buffer, "4\x93\xfa"); /* only 2+1 WCHARs */ + todo_wine ok(ret == strlen("4\x93\xfa") + 1, "Expected ret %d, got %d\n", strlen("4\x93\xfa") + 1, ret); + ok(strcmp(buffer, "4\x93\xfa") == 0, "Expected '4\x93\xfa', got '%s'\n", buffer); + + ret = GetDateFormatA(lcid_ja, 0, &curtime, "d\x93\xfa", buffer, 3); + todo_wine expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER); + SetLastError(0xdeadbeef); + + strcpy(buffer, "pristine"); /* clear previous identical result */ + ret = GetDateFormatA(lcid_ja, 0, &curtime, "d\x93\xfa", NULL, 0); + if (broken(1)) /* FIXME Remove once Wine is less broken */ + expect_str(ret, NULL, "4\x93\xfa"); + todo_wine ok(ret == strlen("4\x93\xfa") + 1, "Expected ret %d, got %d\n", strlen("4\x93\xfa") + 1, ret); }
static void test_GetDateFormatEx(void) @@ -1115,6 +1137,20 @@ static void test_GetDateFormatW(void) wcscpy(buffer, L"pristine"); ret = GetDateFormatW (lcid, 0, &curtime, L"dddd d MMMM yyyy", buffer, ARRAY_SIZE(buffer)); expect_werr(ret, buffer, ERROR_INVALID_PARAMETER); + SetLastError(0xdeadbeef); + + /* See the corresponding GetDateFormatA() test */ + + lcid = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), SORT_DEFAULT); + + curtime.wYear = 2002; + curtime.wMonth = 5; + curtime.wDay = 4; + ret = GetDateFormatW(lcid, 0, &curtime, L"d\x65e5", buffer, 3); + expect_wstr(ret, buffer, L"4\x65e5"); + + ret = GetDateFormatW(lcid, 0, &curtime, L"d\x65e5", NULL, 0); + expect_wstr(ret, NULL, L"4\x65e5"); }
Signed-off-by: Francois Gouget fgouget@codeweavers.com --- v2: Rebased.
I suspect most of our Unicode to ANSI conversions are buggy, particularly when given a NULL output buffer. This is true of GetNumberFormatA() and GetCurrencyFormatA() for instance.
GetNumberFormatA() probably only has to deal with [0-9,.] so it may not run into the issue of a Unicode character converting to multiple ANSI characters. But it's quite possible that there is some currency symbol out there that will trigger the issue in GetCurrencyFormatA(). These cases are just harder to test for.
But it's not just these functions. There's also SHFormatDateTimeA() in shlwapi which causes a test failure. And then SHGetWebFolderFilePathA() does not return a failure if the W->A conversion fails which is most likely wrong; neither does PathCompactPathExA() plus it half ignores cchMax, etc. --- dlls/kernel32/lcformat.c | 25 ++++++++----------------- dlls/kernel32/tests/locale.c | 16 ++-------------- 2 files changed, 10 insertions(+), 31 deletions(-)
diff --git a/dlls/kernel32/lcformat.c b/dlls/kernel32/lcformat.c index a9dfbf09be2..5711d05b76f 100644 --- a/dlls/kernel32/lcformat.c +++ b/dlls/kernel32/lcformat.c @@ -737,7 +737,7 @@ invalid_flags: /****************************************************************************** * NLS_GetDateTimeFormatA <internal> * - * ASCII wrapper for GetDateFormatA/GetTimeFormatA. + * ANSI wrapper for GetDateFormatA/GetTimeFormatA. */ static INT NLS_GetDateTimeFormatA(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime, @@ -745,12 +745,12 @@ static INT NLS_GetDateTimeFormatA(LCID lcid, DWORD dwFlags, { DWORD cp = CP_ACP; WCHAR szFormat[128], szOut[128]; - INT iRet; + INT iRet, cchOutW;
TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime, debugstr_a(lpFormat), lpStr, cchOut);
- if (NLS_IsUnicodeOnlyLcid(lcid)) + if ((cchOut && !lpStr) || NLS_IsUnicodeOnlyLcid(lcid)) { SetLastError(ERROR_INVALID_PARAMETER); return 0; @@ -771,21 +771,12 @@ static INT NLS_GetDateTimeFormatA(LCID lcid, DWORD dwFlags, if (lpFormat) MultiByteToWideChar(cp, 0, lpFormat, -1, szFormat, ARRAY_SIZE(szFormat));
- if (cchOut > (int) ARRAY_SIZE(szOut)) - cchOut = ARRAY_SIZE(szOut); - - szOut[0] = '\0'; - + /* If cchOut == 0 we need the full string to get the accurate ANSI size */ + cchOutW = (cchOut && cchOut <= ARRAY_SIZE(szOut)) ? cchOut : ARRAY_SIZE(szOut); iRet = NLS_GetDateTimeFormatW(lcid, dwFlags, lpTime, lpFormat ? szFormat : NULL, - lpStr ? szOut : NULL, cchOut); - - if (lpStr) - { - if (szOut[0]) - WideCharToMultiByte(cp, 0, szOut, iRet ? -1 : cchOut, lpStr, cchOut, 0, 0); - else if (cchOut && iRet) - *lpStr = '\0'; - } + szOut, cchOutW); + if (iRet) + iRet = WideCharToMultiByte(cp, 0, szOut, -1, lpStr, cchOut, NULL, NULL); return iRet; }
diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index 43dcf2a7dbf..fa0ec374d34 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -639,25 +639,18 @@ static void test_GetTimeFormatA(void) lcid = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), SORT_DEFAULT);
ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", buffer, 5); - if (broken(1)) /* FIXME Remove once Wine is less broken */ expect_str(ret, buffer, "12\x93\xfa"); /* only 3+1 WCHARs */ - todo_wine ok(ret == strlen("12\x93\xfa") + 1, "Expected ret %d, got %d\n", strlen("12\x93\xfa") + 1, ret); - ok(strcmp(buffer, "12\x93\xfa") == 0, "Expected '12\x93\xfa', got '%s'\n", buffer);
ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", buffer, 4); - todo_wine expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER); + expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER); SetLastError(0xdeadbeef);
ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", NULL, 0); - if (broken(1)) /* FIXME Remove once Wine is less broken */ expect_str(ret, NULL, "12\x93\xfa"); - todo_wine ok(ret == strlen("12\x93\xfa") + 1, "Expected ret %d, got %d\n", strlen("12\x93\xfa") + 1, ret);
strcpy(buffer, "pristine"); /* clear previous identical result */ ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", buffer, 0); - if (broken(1)) /* FIXME Remove once Wine is less broken */ expect_str(ret, NULL, "12\x93\xfa"); - todo_wine ok(ret == strlen("12\x93\xfa") + 1, "Expected ret %d, got %d\n", strlen("12\x93\xfa") + 1, ret); ok(strcmp(buffer, "pristine") == 0, "Expected a pristine buffer, got '%s'\n", buffer); }
@@ -967,20 +960,15 @@ static void test_GetDateFormatA(void) */
ret = GetDateFormatA(lcid_ja, 0, &curtime, "d\x93\xfa", buffer, 4); - if (broken(1)) /* FIXME Remove once Wine is less broken */ expect_str(ret, buffer, "4\x93\xfa"); /* only 2+1 WCHARs */ - todo_wine ok(ret == strlen("4\x93\xfa") + 1, "Expected ret %d, got %d\n", strlen("4\x93\xfa") + 1, ret); - ok(strcmp(buffer, "4\x93\xfa") == 0, "Expected '4\x93\xfa', got '%s'\n", buffer);
ret = GetDateFormatA(lcid_ja, 0, &curtime, "d\x93\xfa", buffer, 3); - todo_wine expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER); + expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER); SetLastError(0xdeadbeef);
strcpy(buffer, "pristine"); /* clear previous identical result */ ret = GetDateFormatA(lcid_ja, 0, &curtime, "d\x93\xfa", NULL, 0); - if (broken(1)) /* FIXME Remove once Wine is less broken */ expect_str(ret, NULL, "4\x93\xfa"); - todo_wine ok(ret == strlen("4\x93\xfa") + 1, "Expected ret %d, got %d\n", strlen("4\x93\xfa") + 1, ret); }
static void test_GetDateFormatEx(void)