[PATCH 3/3] kernel32: Fix NLS_GetDateTimeFormatA() Unicode to ANSI conversion.

Francois Gouget fgouget at codeweavers.com
Thu Aug 26 06:08:15 CDT 2021


Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
---
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, 11 insertions(+), 30 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 8d22c9bed9a..fa0ec374d34 100644
--- a/dlls/kernel32/tests/locale.c
+++ b/dlls/kernel32/tests/locale.c
@@ -639,23 +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);
-  todo_wine expect_str(ret, NULL, "12\x93\xfa");
+  expect_str(ret, NULL, "12\x93\xfa");
   ok(strcmp(buffer, "pristine") == 0, "Expected a pristine buffer, got '%s'\n", buffer);
 }
 
@@ -965,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)
-- 
2.20.1



More information about the wine-devel mailing list