[PATCH] kernel32: Implement EnumSystemGeoNames().

João Diogo Ferreira devilj at outlook.pt
Sun Nov 24 08:09:39 CST 2019


Signed-off-by: João Diogo Craveiro Ferreira <devilj at outlook.pt>
---
Treat with low priority. This patch doesn't fix anything anymore,
and I'm only posting because I already spent time writing it.
---
 dlls/kernel32/kernel32.spec  |   1 +
 dlls/kernel32/locale.c       |  78 ++++++++++++++++++++++-
 dlls/kernel32/tests/locale.c | 119 +++++++++++++++++++++++++++++++++++
 include/winnls.h             |   2 +
 4 files changed, 199 insertions(+), 1 deletion(-)

diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index a26d65edf7..07f8c1a93e 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -420,6 +420,7 @@
 @ stdcall -import EnumSystemCodePagesW(ptr long)
 @ stdcall -import EnumSystemFirmwareTables(long ptr long)
 @ stdcall EnumSystemGeoID(long long ptr)
+@ stdcall EnumSystemGeoNames(long ptr long)
 @ stdcall EnumSystemLanguageGroupsA(ptr long ptr)
 @ stdcall -import EnumSystemLanguageGroupsW(ptr long ptr)
 @ stdcall -import EnumSystemLocalesA(ptr long)
diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c
index a5ec6593b0..3a8dea294a 100644
--- a/dlls/kernel32/locale.c
+++ b/dlls/kernel32/locale.c
@@ -3244,9 +3244,11 @@ INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, L
  *   - ERROR_INVALID_PARAMETER: no callback function was provided.
  *   - ERROR_INVALID_FLAGS: the location type was invalid.
  *
+ *  You can retrieve information about each location with GetGeoInfoW().
+ *
  * TODO
  *  On Windows 10, this function filters out those locations which
- *  simultaneously lack ISO and UN codes (e.g. Johnson Atoll).
+ *  simultaneously lack ISO and UN codes (e.g. Johnston Atoll).
  */
 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
 {
@@ -3284,6 +3286,80 @@ BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumpr
     return TRUE;
 }
 
+/******************************************************************************
+ *           EnumSystemGeoNames    (KERNEL32.@)
+ *
+ * Calls a user's function for every location available on the system.
+ *
+ * PARAMS
+ *  geoclass   [I] Type of location desired (SYSGEOTYPE enum from "winnls.h").
+ *  enumproc   [I] Callback function to call for each location (prototype in "winnls.h").
+ *  data       [I] Any user-defined data to be passed to the enumproc callback function.
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE. Use GetLastError() to determine the cause.
+ *
+ * NOTES
+ *  The enumproc function returns TRUE to continue enumerating
+ *  or FALSE to interrupt the enumeration.
+ *
+ *  On failure, GetLastError() will return one of the following values:
+ *    - ERROR_INVALID_PARAMETER: no callback function was provided;
+ *    - ERROR_INVALID_FLAGS: the location type was invalid.
+ *
+ *  You can retrieve information about each location with GetGeoInfoEx().
+ */
+BOOL WINAPI EnumSystemGeoNames(GEOCLASS geoclass, GEO_ENUMNAMEPROC enumproc, LONG_PTR data)
+{
+    int i;
+    static WCHAR xx[] = {'X','X',0};
+
+    TRACE("(%d, %p, %ld)\n", geoclass, enumproc, data);
+
+    if (!enumproc)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION && geoclass != GEOCLASS_ALL)
+    {
+        SetLastError(ERROR_INVALID_FLAGS);
+        return FALSE;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(geoinfodata); i++)
+    {
+        const struct geoinfo_t *ptr = &geoinfodata[i];
+        static const WCHAR un_fmt[] = {'%','0','3','i',0};
+        WCHAR name[4];
+
+        if (geoclass == GEOCLASS_NATION && (ptr->kind != LOCATION_NATION))
+            continue;
+
+        /* LOCATION_BOTH counts as region. */
+        if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
+            continue;
+
+        if (ptr->uncode && !strcmpW(xx, ptr->iso2W))
+            sprintfW(name, un_fmt, ptr->uncode);
+        else
+            strcpyW(name, ptr->iso2W);
+
+        /* Filter out locations without a geo name */
+        if (!strcmpW(name, xx))
+            continue;
+
+        if (!enumproc(name, data))
+            return TRUE;
+    }
+
+    /* Windows also enumerates a single XX, which maps to 001 (ID: 39070, World) */
+    if (geoclass != GEOCLASS_NATION)
+        enumproc(xx, data);
+    return TRUE;
+}
 
 enum {
     BASE = 36,
diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c
index 0e2c3496a9..48f20db981 100644
--- a/dlls/kernel32/tests/locale.c
+++ b/dlls/kernel32/tests/locale.c
@@ -75,6 +75,7 @@ static INT (WINAPI *pCompareStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPCWSTR, INT
 static INT (WINAPI *pGetGeoInfoA)(GEOID, GEOTYPE, LPSTR, INT, LANGID);
 static INT (WINAPI *pGetGeoInfoW)(GEOID, GEOTYPE, LPWSTR, INT, LANGID);
 static BOOL (WINAPI *pEnumSystemGeoID)(GEOCLASS, GEOID, GEO_ENUMPROC);
+static BOOL (WINAPI *pEnumSystemGeoNames)(GEOCLASS, GEO_ENUMNAMEPROC, LONG_PTR);
 static BOOL (WINAPI *pGetSystemPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
 static BOOL (WINAPI *pGetThreadPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
 static BOOL (WINAPI *pGetUserPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
@@ -115,6 +116,7 @@ static void InitFunctionPointers(void)
   X(GetGeoInfoA);
   X(GetGeoInfoW);
   X(EnumSystemGeoID);
+  X(EnumSystemGeoNames);
   X(GetSystemPreferredUILanguages);
   X(GetThreadPreferredUILanguages);
   X(GetUserPreferredUILanguages);
@@ -5000,6 +5002,122 @@ static void test_EnumSystemGeoID(void)
     }
 }
 
+static BOOL CALLBACK test_geoname_enumproc_deadbeef(WCHAR *name, LONG_PTR data)
+{
+    (void)name;
+    ok(data == 0xdeadbeef, "deadbeef: expected 0xdeadbeef, got 0x%lx", data);
+    return FALSE;
+}
+
+struct test_enum_geoname_count_types {
+    unsigned int total, nation, region, both, xx;
+};
+
+static BOOL CALLBACK test_geoname_enumproc_counts(WCHAR *name, LONG_PTR data)
+{
+    struct test_enum_geoname_count_types *ptr = (void*)data;
+
+    /* Known names */
+    static const WCHAR nation[] = {'A','U',0},
+                       region[] = {'0','0','1',0},
+                       both[] = {'A','N',0},
+                       xx[] = {'X','X',0};
+
+    if (!ptr)
+    {
+        ok((INT_PTR)ptr, "geoname_enumproc_counts: expected some pointer, got %p\n", ptr);
+        return FALSE;
+    }
+
+    ptr->total++;
+
+    if (!winetest_strcmpW(name, nation))
+        ptr->nation++;
+    if (!winetest_strcmpW(name, region))
+        ptr->region++;
+    if (!winetest_strcmpW(name, both))
+        ptr->both++;
+    if (!winetest_strcmpW(name, xx))
+        ptr->xx++;
+
+    return TRUE;
+}
+
+static BOOL CALLBACK test_geoname_enumproc_dummy(WCHAR *name, LONG_PTR data)
+{
+    (void)name;
+    (void)data;
+    return FALSE;
+}
+
+static void test_EnumSystemGeoNames(void)
+{
+    struct test_enum_geoname_count_types counts;
+    unsigned int nations, regions, total;
+    BOOL ret;
+
+    if (!pEnumSystemGeoNames)
+    {
+        win_skip("unsupported platform.\n");
+        return;
+    }
+
+    ok(pEnumSystemGeoNames(GEOCLASS_ALL, test_geoname_enumproc_deadbeef, 0xdeadbeef),
+       "deadbeef: expected non-zero ret, got otherwise with error=%d.\n", GetLastError());
+
+    ZeroMemory(&counts, sizeof(counts));
+    ret = pEnumSystemGeoNames(GEOCLASS_NATION, test_geoname_enumproc_counts, (LONG_PTR)&counts);
+    if (ret)
+    {
+        ok(counts.total > 0 && counts.nation > 0 && counts.region == 0 && counts.xx == 0 && counts.both == 0,
+           "GEOCLASS_NATION: got total=%u, nation=%u, region=%u, xx=%u, both=%u (expected to find only nations).\n",
+           counts.total, counts.nation, counts.region, counts.xx, counts.both);
+    }
+    else
+        ok(ret, "GEONAME_NATION: expected non-zero, got otherwise.\n");
+
+    nations = counts.total;
+
+    ZeroMemory(&counts, sizeof(counts));
+    ret = pEnumSystemGeoNames(GEOCLASS_REGION, test_geoname_enumproc_counts, (LONG_PTR)&counts);
+    if (ret)
+    {
+        ok(counts.total > 0 && counts.nation == 0 && counts.region > 0 && counts.xx == 1 && counts.both > 0,
+           "GEOCLASS_REGION: got total=%u, nation=%u, region=%u, xx=%u, both=%u (expected nation==0, region>=1, xx==1, both>=1).\n",
+           counts.total, counts.nation, counts.region, counts.xx, counts.both);
+    }
+    else
+        ok(ret, "GEONAME_REGION: expected ret != 0, got otherwise.\n");
+
+    regions = counts.total;
+
+    ZeroMemory(&counts, sizeof(counts));
+    ret = pEnumSystemGeoNames(GEOCLASS_ALL, test_geoname_enumproc_counts, (LONG_PTR)&counts);
+    if (ret)
+    {
+        ok(counts.total > 0 && counts.nation > 0 && counts.region > 0 && counts.xx > 0 && counts.both > 0,
+           "GEOCLASS_ALL: got total=%u, nation=%u, region=%u, xx=%u, both=%u (expected at least 1 of each)\n",
+           counts.total, counts.nation, counts.region, counts.xx, counts.both);
+    }
+    else
+        ok(ret, "GEONAME_ALL: expected ret != 0, got otherwise.\n");
+
+    total = counts.total;
+    ok(total == nations + regions, "expected total count to match the amount of nations + regions (%u), got %u instead.\n",
+       nations + regions, total);
+
+    /* Try invalid values */
+    SetLastError(ERROR_SUCCESS);
+    ret = pEnumSystemGeoNames(GEOCLASS_ALL, NULL, 0xdeadbeef);
+    ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER,
+       "null enumproc: expected ret=0 and error=87, got ret=%d and error=%d\n", ret, GetLastError());
+
+    SetLastError(ERROR_SUCCESS);
+    ret = pEnumSystemGeoNames(-1, test_geoname_enumproc_dummy, 0xdeadbeef);
+    ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
+       "invalid geoclass: expected ret=0 and error=1004, got ret=%d and error=%d\n", ret, GetLastError());
+}
+
 struct invariant_entry {
   const char *name;
   int id;
@@ -6171,6 +6289,7 @@ START_TEST(locale)
   test_CompareStringOrdinal();
   test_GetGeoInfo();
   test_EnumSystemGeoID();
+  test_EnumSystemGeoNames();
   test_invariant();
   test_GetSystemPreferredUILanguages();
   test_GetThreadPreferredUILanguages();
diff --git a/include/winnls.h b/include/winnls.h
index b233f7f917..aab6ec4398 100644
--- a/include/winnls.h
+++ b/include/winnls.h
@@ -760,6 +760,7 @@ typedef BOOL    (CALLBACK *DATEFMT_ENUMPROCW)(LPWSTR);
 typedef BOOL    (CALLBACK *DATEFMT_ENUMPROCEXA)(LPSTR,CALID);
 typedef BOOL    (CALLBACK *DATEFMT_ENUMPROCEXW)(LPWSTR,CALID);
 typedef BOOL    (CALLBACK *GEO_ENUMPROC)(GEOID);
+typedef BOOL    (CALLBACK *GEO_ENUMNAMEPROC)(WCHAR*,LONG_PTR);
 typedef BOOL    (CALLBACK *LANGGROUPLOCALE_ENUMPROCA)(LGRPID,LCID,LPSTR,LONG_PTR);
 typedef BOOL    (CALLBACK *LANGGROUPLOCALE_ENUMPROCW)(LGRPID,LCID,LPWSTR,LONG_PTR);
 typedef BOOL    (CALLBACK *LANGUAGEGROUP_ENUMPROCA)(LGRPID,LPSTR,LPSTR,DWORD,LONG_PTR);
@@ -866,6 +867,7 @@ WINBASEAPI BOOL        WINAPI EnumSystemCodePagesA(CODEPAGE_ENUMPROCA,DWORD);
 WINBASEAPI BOOL        WINAPI EnumSystemCodePagesW(CODEPAGE_ENUMPROCW,DWORD);
 #define                       EnumSystemCodePages WINELIB_NAME_AW(EnumSystemCodePages)
 WINBASEAPI BOOL        WINAPI EnumSystemGeoID(GEOCLASS,GEOID,GEO_ENUMPROC);
+WINBASEAPI BOOL        WINAPI EnumSystemGeoNames(GEOCLASS,GEO_ENUMNAMEPROC,LONG_PTR);
 WINBASEAPI BOOL        WINAPI EnumSystemLocalesA(LOCALE_ENUMPROCA,DWORD);
 WINBASEAPI BOOL        WINAPI EnumSystemLocalesW(LOCALE_ENUMPROCW,DWORD);
 #define                       EnumSystemLocales WINELIB_NAME_AW(EnumSystemLocales)
-- 
2.24.0



More information about the wine-devel mailing list