From: "João Diogo Ferreira" Subject: [PATCH] kernel32: Implement EnumSystemGeoNames(). Message-Id: Date: Sun, 24 Nov 2019 14:09:39 +0000 Signed-off-by: João Diogo Craveiro Ferreira --- 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