kernel32: Add LCMapStringEx implementation and test it reusing tests from LCMapStringW

André Hentschel nerv at dawncrow.de
Sun Jan 29 13:27:37 CST 2012


---
 dlls/kernel32/kernel32.spec  |    1 +
 dlls/kernel32/locale.c       |   33 ++++++++
 dlls/kernel32/tests/locale.c |  175 ++++++++++++++++++++++++++++++++++++++++++
 include/winnls.h             |   20 +++++
 4 files changed, 229 insertions(+), 0 deletions(-)

diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index 072cfa1..2629469 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -804,6 +804,7 @@
 @ stdcall LCIDToLocaleName(long ptr long long)
 @ stdcall LCMapStringA(long long str long ptr long)
 @ stdcall LCMapStringW(long long wstr long ptr long)
+@ stdcall LCMapStringEx(long long wstr long ptr long ptr ptr long)
 @ stdcall LZClose(long)
 # @ stub LZCloseFile
 @ stdcall LZCopy(long long)
diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c
index 2c3760c..6cffd84 100644
--- a/dlls/kernel32/locale.c
+++ b/dlls/kernel32/locale.c
@@ -2680,6 +2680,39 @@ map_string_exit:
 }
 
 /*************************************************************************
+ *           LCMapStringEx   (KERNEL32.@)
+ *
+ * Map characters in a locale sensitive string.
+ *
+ * PARAMS
+ *  name     [I] na for the conversion.
+ *  flags    [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
+ *  src      [I] String to map
+ *  srclen   [I] Length of src in chars, or -1 if src is NUL terminated
+ *  dst      [O] Destination for mapped string
+ *  dstlen   [I] Length of dst in characters
+ *  version  [I] reserved, must be NULL
+ *  reserved [I] reserved, must be NULL
+ *  lparam   [I] reserved, must be 0
+ *
+ * RETURNS
+ *  Success: The length of the mapped string in dst, including the NUL terminator.
+ *  Failure: 0. Use GetLastError() to determine the cause.
+ */
+INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
+                         LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
+{
+    LCID lcid;
+
+    if (version) FIXME("unsupported version structure %p\n", version);
+    if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
+    if (lparam) FIXME("unsupported lparam %p\n", lparam);
+
+    lcid = LocaleNameToLCID(name, 0);
+    return LCMapStringW(lcid, flags, src, srclen, dst, dstlen);
+}
+
+/*************************************************************************
  *           FoldStringA    (KERNEL32.@)
  *
  * Map characters in a string.
diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c
index f5b77f7..667bf59 100644
--- a/dlls/kernel32/tests/locale.c
+++ b/dlls/kernel32/tests/locale.c
@@ -74,6 +74,8 @@ static BOOL (WINAPI *pEnumSystemLocalesEx)(LOCALE_ENUMPROCEX, DWORD, LPARAM, LPV
 static INT (WINAPI *pFoldStringA)(DWORD, LPCSTR, INT, LPSTR, INT);
 static INT (WINAPI *pFoldStringW)(DWORD, LPCWSTR, INT, LPWSTR, INT);
 static BOOL (WINAPI *pIsValidLanguageGroup)(LGRPID, DWORD);
+static INT (WINAPI *pLCMapStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPWSTR, INT, LPNLSVERSIONINFO, LPVOID, LPARAM);
+static INT (WINAPI *pLCIDToLocaleName)(LCID, LPWSTR, INT, DWORD);
 
 static void InitFunctionPointers(void)
 {
@@ -85,6 +87,8 @@ static void InitFunctionPointers(void)
   pIsValidLanguageGroup = (void*)GetProcAddress(hKernel32, "IsValidLanguageGroup");
   pEnumUILanguagesA = (void*)GetProcAddress(hKernel32, "EnumUILanguagesA");
   pEnumSystemLocalesEx = (void*)GetProcAddress(hKernel32, "EnumSystemLocalesEx");
+  pLCMapStringEx = (void*)GetProcAddress(hKernel32, "LCMapStringEx");
+  pLCIDToLocaleName = (void*)GetProcAddress(hKernel32, "LCIDToLocaleName");
 }
 
 #define eq(received, expected, label, type) \
@@ -1637,6 +1641,176 @@ static void test_LCMapStringW(void)
        "unexpected error code %d\n", GetLastError());
 }
 
+static void test_LCMapStringEx(void)
+{
+    int ret, ret2;
+    WCHAR buf[256], buf2[256];
+    WCHAR locale_user_default[LOCALE_NAME_MAX_LENGTH];
+    char *p_buf = (char *)buf, *p_buf2 = (char *)buf2;
+    static const WCHAR upper_case[] = {'\t','J','U','S','T','!',' ','A',',',' ','T','E','S','T',';',' ','S','T','R','I','N','G',' ','1','/','*','+','-','.','\r','\n',0};
+    static const WCHAR lower_case[] = {'\t','j','u','s','t','!',' ','a',',',' ','t','e','s','t',';',' ','s','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0};
+    static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0};
+    static const WCHAR fooW[] = {'f','o','o',0};
+
+    if (!pLCMapStringEx)
+    {
+        win_skip( "EnumSystemLocalesEx not available\n" );
+        return;
+    }
+
+    if(!pLCIDToLocaleName)
+    {
+        win_skip( "LCIDToLocaleName not available\n" );
+        return;
+    }
+
+    ret = pLCIDToLocaleName(LOCALE_USER_DEFAULT, locale_user_default,
+                            sizeof(locale_user_default)/sizeof(WCHAR), 0);
+    ok(ret, "LCIDToLocaleName failed: %u\n", GetLastError());
+
+    ret = pLCMapStringEx(locale_user_default, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
+                         upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    if (broken(ret))
+        ok(lstrcmpW(buf, upper_case) == 0, "Expected upper case string\n");
+    else
+    {
+        ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
+        ok(GetLastError() == ERROR_INVALID_FLAGS,
+           "unexpected error code %d\n", GetLastError());
+    }
+
+    ret = pLCMapStringEx(locale_user_default, LCMAP_HIRAGANA | LCMAP_KATAKANA,
+                         upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    ok(!ret, "LCMAP_HIRAGANA and LCMAP_KATAKANA are mutually exclusive\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+       "unexpected error code %d\n", GetLastError());
+
+    ret = pLCMapStringEx(locale_user_default, LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
+                         upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    ok(!ret, "LCMAP_HALFWIDTH | LCMAP_FULLWIDTH are mutually exclusive\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+       "unexpected error code %d\n", GetLastError());
+
+    ret = pLCMapStringEx(locale_user_default, LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
+                         upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    ok(!ret, "LCMAP_TRADITIONAL_CHINESE and LCMAP_SIMPLIFIED_CHINESE are mutually exclusive\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+       "unexpected error code %d\n", GetLastError());
+
+    /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
+    SetLastError(0xdeadbeef);
+    ret = pLCMapStringEx(locale_user_default, LCMAP_LOWERCASE | SORT_STRINGSORT,
+                         upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    ok(GetLastError() == ERROR_INVALID_FLAGS, "expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
+    ok(!ret, "SORT_STRINGSORT without LCMAP_SORTKEY must fail\n");
+
+    /* test LCMAP_LOWERCASE */
+    ret = pLCMapStringEx(locale_user_default, LCMAP_LOWERCASE,
+                         upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    ok(ret == lstrlenW(upper_case) + 1,
+       "ret %d, error %d, expected value %d\n",
+       ret, GetLastError(), lstrlenW(upper_case) + 1);
+    ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
+
+    /* test LCMAP_UPPERCASE */
+    ret = pLCMapStringEx(locale_user_default, LCMAP_UPPERCASE,
+                         lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    ok(ret == lstrlenW(lower_case) + 1,
+       "ret %d, error %d, expected value %d\n",
+       ret, GetLastError(), lstrlenW(lower_case) + 1);
+    ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n");
+
+    /* test buffer overflow */
+    SetLastError(0xdeadbeef);
+    ret = pLCMapStringEx(locale_user_default, LCMAP_UPPERCASE,
+                         lower_case, -1, buf, 4, NULL, NULL, 0);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+       "should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", ret);
+
+    /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
+    lstrcpyW(buf, lower_case);
+    ret = pLCMapStringEx(locale_user_default, LCMAP_UPPERCASE,
+                         buf, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    ok(ret == lstrlenW(lower_case) + 1,
+       "ret %d, error %d, expected value %d\n",
+       ret, GetLastError(), lstrlenW(lower_case) + 1);
+    ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n");
+
+    lstrcpyW(buf, upper_case);
+    ret = pLCMapStringEx(locale_user_default, LCMAP_LOWERCASE,
+                         buf, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    ok(ret == lstrlenW(upper_case) + 1,
+       "ret %d, error %d, expected value %d\n",
+       ret, GetLastError(), lstrlenW(lower_case) + 1);
+    ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
+
+    /* otherwise src == dst should fail */
+    SetLastError(0xdeadbeef);
+    ret = pLCMapStringEx(locale_user_default, LCMAP_SORTKEY | LCMAP_UPPERCASE,
+                         buf, 10, buf, sizeof(buf), NULL, NULL, 0);
+    ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
+       GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
+       "unexpected error code %d\n", GetLastError());
+    ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
+
+    /* test whether '\0' is always appended */
+    ret = pLCMapStringEx(locale_user_default, LCMAP_SORTKEY,
+                         upper_case, -1, buf, sizeof(buf), NULL, NULL, 0);
+    ok(ret, "pLCMapStringEx must succeed\n");
+    ret2 = pLCMapStringEx(locale_user_default, LCMAP_SORTKEY,
+                          upper_case, lstrlenW(upper_case), buf2, sizeof(buf2), NULL, NULL, 0);
+    ok(ret, "pLCMapStringEx must succeed\n");
+    ok(ret == ret2, "lengths of sort keys must be equal\n");
+    ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+
+    /* test LCMAP_SORTKEY | NORM_IGNORECASE */
+    ret = pLCMapStringEx(locale_user_default, LCMAP_SORTKEY | NORM_IGNORECASE,
+                         upper_case, -1, buf, sizeof(buf), NULL, NULL, 0);
+    ok(ret, "pLCMapStringEx must succeed\n");
+    ret2 = pLCMapStringEx(locale_user_default, LCMAP_SORTKEY,
+                          lower_case, -1, buf2, sizeof(buf2), NULL, NULL, 0);
+    ok(ret2, "pLCMapStringEx must succeed\n");
+    ok(ret == ret2, "lengths of sort keys must be equal\n");
+    ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+
+    /* Don't test LCMAP_SORTKEY | NORM_IGNORENONSPACE, produces different
+       results from plain LCMAP_SORTKEY on Vista */
+
+    /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
+    ret = pLCMapStringEx(locale_user_default, LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
+                         lower_case, -1, buf, sizeof(buf), NULL, NULL, 0);
+    ok(ret, "pLCMapStringEx must succeed\n");
+    ret2 = pLCMapStringEx(locale_user_default, LCMAP_SORTKEY,
+                          symbols_stripped, -1, buf2, sizeof(buf2), NULL, NULL, 0);
+    ok(ret2, "pLCMapStringEx must succeed\n");
+    ok(ret == ret2, "lengths of sort keys must be equal\n");
+    ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+
+    /* test NORM_IGNORENONSPACE */
+    lstrcpyW(buf, fooW);
+    ret = pLCMapStringEx(locale_user_default, NORM_IGNORENONSPACE,
+                         lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    ok(ret == lstrlenW(lower_case) + 1, "pLCMapStringEx should return %d, ret = %d\n",
+    lstrlenW(lower_case) + 1, ret);
+    ok(!lstrcmpW(buf, lower_case), "string comparison mismatch\n");
+
+    /* test NORM_IGNORESYMBOLS */
+    lstrcpyW(buf, fooW);
+    ret = pLCMapStringEx(locale_user_default, NORM_IGNORESYMBOLS,
+                         lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    ok(ret == lstrlenW(symbols_stripped) + 1, "pLCMapStringEx should return %d, ret = %d\n",
+    lstrlenW(symbols_stripped) + 1, ret);
+    ok(!lstrcmpW(buf, symbols_stripped), "string comparison mismatch\n");
+
+    /* test srclen = 0 */
+    SetLastError(0xdeadbeef);
+    ret = pLCMapStringEx(locale_user_default, 0,
+                         upper_case, 0, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    ok(!ret, "pLCMapStringEx should fail with srclen = 0\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "unexpected error code %d\n", GetLastError());
+}
+
 /* this requires collation table patch to make it MS compatible */
 static const char * const strings_sorted[] =
 {
@@ -2782,6 +2956,7 @@ START_TEST(locale)
   test_CompareStringA();
   test_LCMapStringA();
   test_LCMapStringW();
+  test_LCMapStringEx();
   test_FoldStringA();
   test_FoldStringW();
   test_ConvertDefaultLocale();
diff --git a/include/winnls.h b/include/winnls.h
index de6f875..5347f0b 100644
--- a/include/winnls.h
+++ b/include/winnls.h
@@ -321,6 +321,12 @@ extern "C" {
 #define LOCALE_SSORTNAME              0x1013
 #define LOCALE_IDIGITSUBSTITUTION     0x1014
 
+/* Locale name special values */
+#define LOCALE_NAME_INVARIANT       L""
+#define LOCALE_NAME_MAX_LENGTH      85
+#define LOCALE_NAME_SYSTEM_DEFAULT  L"!sys-default-locale"
+#define LOCALE_NAME_USER_DEFAULT    NULL
+
 #define LOCALE_IDEFAULTUNIXCODEPAGE   0x1030 /* Wine extension */
 
 #define NORM_IGNORECASE     0x00001
@@ -648,6 +654,19 @@ typedef struct _currencyfmtW
 DECL_WINELIB_TYPE_AW(CURRENCYFMT)
 DECL_WINELIB_TYPE_AW(LPCURRENCYFMT)
 
+typedef struct _nlsversioninfo {
+    DWORD dwNLSVersionInfoSize;
+    DWORD dwNLSVersion;
+    DWORD dwDefinedVersion;
+} NLSVERSIONINFO, *LPNLSVERSIONINFO;
+
+typedef struct _nlsversioninfoex {
+    DWORD dwNLSVersionInfoSize;
+    DWORD dwNLSVersion;
+    DWORD dwDefinedVersion;
+    DWORD dwEffectiveId;
+    GUID  guidCustomVersion;
+} NLSVERSIONINFOEX, *LPNLSVERSIONINFOEX;
 
 /* Define a bunch of callback types */
 
@@ -830,6 +849,7 @@ WINBASEAPI INT         WINAPI LCIDToLocaleName(LCID,LPWSTR,INT,DWORD);
 WINBASEAPI INT         WINAPI LCMapStringA(LCID,DWORD,LPCSTR,INT,LPSTR,INT);
 WINBASEAPI INT         WINAPI LCMapStringW(LCID,DWORD,LPCWSTR,INT,LPWSTR,INT);
 #define                       LCMapString WINELIB_NAME_AW(LCMapString)
+WINBASEAPI INT         WINAPI LCMapStringEx(LPCWSTR,DWORD,LPCWSTR,INT,LPWSTR,INT,LPNLSVERSIONINFO,LPVOID,LPARAM);
 WINBASEAPI LCID        WINAPI LocaleNameToLCID(LPCWSTR,DWORD);
 WINBASEAPI INT         WINAPI MultiByteToWideChar(UINT,DWORD,LPCSTR,INT,LPWSTR,INT);
 WINNORMALIZEAPI INT    WINAPI NormalizeString(NORM_FORM,LPCWSTR,INT,LPWSTR,INT);
-- 

Best Regards, André Hentschel
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: Nachrichtenteil als Anhang
URL: <http://www.winehq.org/pipermail/wine-patches/attachments/20120129/7ae988cf/attachment.ksh>


More information about the wine-patches mailing list