kernel32: Fix lstrcmp(i)A/W to correct return values and document this behaviour

Rolf Kalbermatter rolf.kalbermatter at citeng.com
Fri Oct 24 15:13:55 CDT 2003


Changelog
  - dlls/kernel/locale.c
    Change lstrcmpA/W and lstrcmpiA/W to always return a meaningful return value

  - dlls/kernel/tests/locale.c
    Add some tests for lstrcmpA/W and lstrcmpiA/W to the regression test framework

These tests run all fine on W2K, although I get 16 errors later in test_EnumLanguageGroupLocalesA.

There remain a few todo_wine in those new tests. They are all about treating
upper case characters always as being greater than any lower case character in
CompareStringA/W. 
  lstrcmpA("Salut", "SAlut") and lstrcmpA("Salut", "SOlut") should return -1 and not 1
  (this is with english local)

I guess this all has to do with the fact that we actually do only implement the
SORT_STRINGSORT flag in CompareStringA/W eventhough that flag is usually not present
which means the function should do a WordSort.

Maybe we should add a FIXME in CompareStringA/W for this, but that would clutter any
output tremendously as that is the mode used for lstrcmpA/W.

Licence: X11/LGPL

Rolf Kalbermatter
 
Index: dlls/kernel/locale.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/locale.c,v
retrieving revision 1.24
diff -u -r1.24 locale.c
--- dlls/kernel/locale.c	24 Oct 2003 00:24:46 -0000	1.24
+++ dlls/kernel/locale.c	24 Oct 2003 19:51:29 -0000
@@ -2273,44 +2273,66 @@
  *           lstrcmp     (KERNEL32.@)
  *           lstrcmpA    (KERNEL32.@)
  *
- * Compare two strings using the current thread locale.
+ * Compare two strings using the current thread locale and if that fails the
+ * system default locale.
  *
  * PARAMS
  *  str1  [I] First string to compare
  *  str2  [I] Second string to compare
  *
  * RETURNS
- *  Success: A number less than, equal to or greater than 0 depending on whether
- *           str2 is less than, equal to or greater than str1 respectively.
- *  Failure: FALSE. Use GetLastError() to determine the cause.
+ *  A number less than, equal to or greater than 0 depending on whether
+ *  str1 is less than, equal to or greater than str2 respectively.
  */
 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
 {
-    int ret = CompareStringA(GetThreadLocale(), 0, str1, -1, str2, -1);
-    if (ret) ret -= 2;
-    return ret;
+   int ret = CompareStringA(GetThreadLocale(), 0, str1, -1, str2, -1);
+    if (!ret)
+    {
+      ret = CompareStringA(GetSystemDefaultLCID(), 0, str1, -1, str2, -1);
+      if (!ret)
+      {
+        if (!str1)
+          return (str2 ? -1 : 0);
+        else if (!str2)
+          return 1;
+        return strcasecmp(str1, str2);
+      }
+    }
+    return ret - 2;
 }
 
 /*************************************************************************
  *           lstrcmpi     (KERNEL32.@)
  *           lstrcmpiA    (KERNEL32.@)
  *
- * Compare two strings using the current thread locale, ignoring case.
+ * Compare two strings using the current thread locale and if that fails the
+ * system default locale, ignoring case.
  *
  * PARAMS
  *  str1  [I] First string to compare
  *  str2  [I] Second string to compare
  *
  * RETURNS
- *  Success: A number less than, equal to or greater than 0 depending on whether
- *           str2 is less than, equal to or greater than str1 respectively.
- *  Failure: FALSE. Use GetLastError() to determine the cause.
+ *  A number less than, equal to or greater than 0 depending on whether
+ *  str1 is less than, equal to or greater than str2 respectively.
  */
 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
 {
     int ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
-    if (ret) ret -= 2;
-    return ret;
+    if (!ret)
+    {
+      ret = CompareStringA(GetSystemDefaultLCID(), NORM_IGNORECASE, str1, -1, str2, -1);
+      if (!ret)
+      {
+        if (!str1)
+          return (str2 ? -1 : 0);
+        else if (!str2)
+          return 1;
+        return strcmp(str1, str2);
+      }
+    }
+    return ret - 2;
 }
 
 /*************************************************************************
@@ -2321,8 +2343,19 @@
 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
 {
     int ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
-    if (ret) ret -= 2;
-    return ret;
+    if (!ret)
+    {
+      ret = CompareStringW(GetSystemDefaultLCID(), 0, str1, -1, str2, -1);
+      if (!ret)
+      {
+        if (!str1)
+          return (str2 ? -1 : 0);
+        else if (!str2)
+          return 1;
+        return strcmpW(str1, str2);
+      }
+    }
+    return ret - 2;
 }
 
 /*************************************************************************
@@ -2333,8 +2366,19 @@
 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
 {
     int ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
-    if (ret) ret -= 2;
-    return ret;
+    if (!ret)
+    {
+      ret = CompareStringW(GetSystemDefaultLCID(), NORM_IGNORECASE, str1, -1, str2, -1);
+      if (!ret)
+      {
+        if (!str1)
+          return (str2 ? -1 : 0);
+        else if (!str2)
+          return 1;
+        return strcmpiW(str1, str2);
+      }
+    }
+    return ret - 2;
 }
 
 /******************************************************************************
Index: dlls/kernel/tests/locale.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/tests/locale.c,v
retrieving revision 1.21
diff -u -r1.21 locale.c
--- dlls/kernel/tests/locale.c	24 Oct 2003 00:26:18 -0000	1.21
+++ dlls/kernel/tests/locale.c	24 Oct 2003 19:51:31 -0000
@@ -754,28 +754,109 @@
   int ret;
   LCID lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
 
+  ret = CompareStringA(lcid, 0, NULL, -1, NULL, -1);
+  ok (ret == 0, "(NULL/NULL) Expected 0, got %d\n", ret);
+
+  ret = CompareStringA(lcid, 0, NULL, -1, "Z", -1);
+  ok (ret == 0, "(NULL/<string>) Expected 0, got %d\n", ret);
+
+  ret = CompareStringA(lcid, 0, "Z", -1, NULL, -1);
+  ok (ret == 0, "(<string>/NULL) Expected 0, got %d\n", ret);
+
   ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "Salute", -1);
-  ok (ret== 1, "(Salut/Salute) Expected 1, got %d\n", ret);
+  ok (ret == 1, "(Salut/Salute) Expected 1, got %d\n", ret);
 
   ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "SaLuT", -1);
-  ok (ret== 2, "(Salut/SaLuT) Expected 2, got %d\n", ret);
+  ok (ret == 2, "(Salut/SaLuT) case insensitive, Expected 2, got %d\n", ret);
 
+  todo_wine
+  {
+    ret = CompareStringA(lcid, 0, "Salut", -1, "SaLuT", -1);
+    ok (ret == 1, "(Salut/SaLuT) case sensitive, Expected 1, got %d\n", ret);
+  }
   ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "hola", -1);
-  ok (ret== 3, "(Salut/hola) Expected 3, got %d\n", ret);
+  ok (ret == 3, "(Salut/hola) Expected 3, got %d\n", ret);
 
   ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
-  ok (ret== 1, "(haha/hoho) Expected 1, got %d\n", ret);
+  ok (ret == 1, "(haha/hoho) Expected 1, got %d\n", ret);
 
   lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
 
   ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
-  ok (ret== 1, "(haha/hoho) Expected 1, got %d\n", ret);
+  ok (ret == 1, "(haha/hoho) Expected 1, got %d\n", ret);
 
   ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", 0);
-  ok (ret== 3, "(haha/hoho) Expected 3, got %d\n", ret);
+  ok (ret == 3, "(haha/hoho) Expected 3, got %d\n", ret);
 
   ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", 5, "SaLuT", -1);
-  ok (ret== 2, "(Salut/SaLuT) Expected 2, got %d\n", ret);
+  ok (ret == 2, "(Salut/SaLuT) case insensitive, Expected 2, got %d\n", ret);
+
+  todo_wine
+  {
+    ret = CompareStringA(lcid, 0, "Salut", -1, "SaLuT", -1);
+    ok (ret == 1, "(Salut/SaLuT) case sensitive, Expected 1, got %d\n", ret);
+  }
+}
+
+static void test_lstrcmpA()
+{
+  int ret;
+
+  ret = lstrcmpA(NULL, NULL);
+  ok(!ret, "(NULL/NULL) case sensitive, Expected 0, got %d\n", ret);
+
+  ret = lstrcmpA(NULL, "Z");
+  ok(ret < 0, "(NULL/string) case sensitive, Expected <0, got %d\n", ret);
+
+  ret = lstrcmpA("Z", NULL);
+  ok(ret > 0, "(string/NULL) case sensitive, Expected >0, got %d\n", ret);
+
+  ret = lstrcmpA("Salut", "Salute");
+  ok(ret < 0, "(Salut/Salute) case sensitive, Expected <0, got %d\n", ret);
+
+  ret = lstrcmpA("Salut", "Solut");
+  ok(ret < 0, "(Salut/Solut) case sensitive, Expected <0, got %d\n", ret);
+
+  ret = lstrcmpA("SAlut", "Solut");
+  ok(ret < 0, "(SAlut/Solut) case sensitive, Expected <0, got %d\n", ret);
+
+  todo_wine
+  {
+    ret = lstrcmpA("Salut", "SOlut");
+    ok(ret < 0, "(Salut/SOlut) case sensitive, Expected <0, got %d\n", ret);
+
+    ret = lstrcmpA("Salut", "SaLuT");
+    ok(ret < 0, "(Salut/SaLuT) case sensitive, Expected <0, got %d\n", ret);
+  }
+}
+
+static void test_lstrcmpiA()
+{
+  int ret;
+
+  ret = lstrcmpiA(NULL, NULL);
+  ok(!ret, "(NULL/NULL) case insensitive, Expected 0, got %d\n", ret);
+
+  ret = lstrcmpiA(NULL, "Z");
+  ok(ret < 0, "(NULL/string) case insensitive, Expected <0, got %d\n", ret);
+
+  ret = lstrcmpiA("Z", NULL);
+  ok(ret > 0, "(string/NULL) case insensitive, Expected >0, got %d\n", ret);
+
+  ret = lstrcmpiA("Salut", "Salute");
+  ok(ret < 0, "(Salut/Salute) case insensitive, Expected <0, got %d\n", ret);
+
+  ret = lstrcmpiA("Salut", "Solut");
+  ok(ret < 0, "(Salut/Solute) case insensitive, Expected <0, got %d\n", ret);
+
+  ret = lstrcmpiA("SAlut", "Solut");
+  ok(ret < 0, "(SAlut/Solut) case insensitive, Expected <0, got %d\n", ret);
+
+  ret = lstrcmpiA("Salut", "SOlut");
+  ok(ret < 0, "(Salut/SOlut) case insensitive, Expected <0, got %d\n", ret);
+
+  ret = lstrcmpiA("Salut", "SaLuT");
+  ok(!ret, "(Salut/SaLuT) case insensitive, Expected 0, got %d\n", ret);
 }
 
 void test_LCMapStringA(void)
@@ -1702,6 +1783,8 @@
   test_GetCurrencyFormatA(); /* Also tests the W version */
   test_GetNumberFormatA();   /* Also tests the W version */
   test_CompareStringA();
+  test_lstrcmpA();
+  test_lstrcmpiA();
   test_LCMapStringA();
   test_LCMapStringW();
   test_FoldStringA();





More information about the wine-patches mailing list