From: "João Diogo Ferreira" Subject: [PATCH v3 2/3] kernel32: Implement GetGeoInfoEx(). Message-Id: Date: Sun, 24 Nov 2019 16:16:15 +0000 In-Reply-To: <20191124161458.158835-1-devilj@outlook.pt> References: <20191124161458.158835-1-devilj@outlook.pt> Signed-off-by: João Diogo Craveiro Ferreira --- Treat with low priority. This actually useful and urgent, rather I mistakingly wrote it for something else and now I might as well make it useful. --- dlls/kernel32/kernel32.spec | 1 + dlls/kernel32/locale.c | 225 ++++++++++++++++++++++++++++------- dlls/kernel32/tests/locale.c | 60 +++++++++- include/winnls.h | 4 +- 4 files changed, 243 insertions(+), 47 deletions(-) diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index a26d65edf7..30b0b1de67 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -694,6 +694,7 @@ @ stdcall -import GetFullPathNameW(wstr long ptr ptr) @ stdcall GetGeoInfoA(long long ptr long long) @ stdcall GetGeoInfoW(long long ptr long long) +@ stdcall GetGeoInfoEx(wstr long ptr long) @ stdcall GetHandleContext(long) @ stdcall -import GetHandleInformation(long ptr) @ stub -i386 GetLSCallbackTarget diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c index 4a2795cd1c..6031ddfd64 100644 --- a/dlls/kernel32/locale.c +++ b/dlls/kernel32/locale.c @@ -2993,7 +2993,12 @@ static const struct geoinfo_t geoinfodata[] = { { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 419, LOCATION_REGION }, /* Latin America and the Caribbean */ }; -static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid) +/****************************************************************************** +* get_geoinfoptr_by_id +* +* Returns a pointer to the geoinfo struct matching a GeoID. +*/ +static const struct geoinfo_t *get_geoinfoptr_by_id(GEOID geoid) { int min, max; @@ -3018,6 +3023,69 @@ static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid) return NULL; } +/****************************************************************************** + * get_geoinfoptr_by_type + * + * Returns a pointer to a geoinfo struct by + * matching a string to the specified geotype. + */ + +static inline const struct geoinfo_t *get_geoinfoptr_by_type(GEOTYPE geotype, const WCHAR *wstr) +{ + int i, num; + + if (!wstr || !*wstr) + return NULL; + + switch (geotype) + { + case GEO_ISO2: + for (i = 0; i < ARRAY_SIZE(geoinfodata); i++) + if (!(strcmpW(geoinfodata[i].iso2W, wstr))) return &geoinfodata[i]; + break; + case GEO_ISO_UN_NUMBER: + if (!(num = atoiW(wstr))) return NULL; + for (i = 0; i < ARRAY_SIZE(geoinfodata); i++) + if (num == geoinfodata[i].uncode) + return &geoinfodata[i]; + break; + } + return NULL; +} + +/****************************************************************************** + * get_geoinfoptr_by_name + * + * Parse a geoname and return a pointer to the matching geoinfo struct. + * This matches all the undocumented rules found in Windows. + */ +static inline const struct geoinfo_t *get_geoinfoptr_by_name(const WCHAR *name) +{ + static const WCHAR xx[] = {'X','X',0}; + const struct geoinfo_t *geoptr; + WCHAR buffer[4]; + int i; + + if (!name) + return NULL; + + switch (strlenW(name)) + { + case 2: + if (!strcmpW(name, xx)) + return get_geoinfoptr_by_id(39070); + for (i = 0; i <= 2; i++) + buffer[i] = toupperW(name[i]); + return get_geoinfoptr_by_type(GEO_ISO2, buffer); + case 3: + geoptr = get_geoinfoptr_by_type(GEO_ISO_UN_NUMBER, name); + if (geoptr && !strcmpW(xx, geoptr->iso2W)) + return geoptr; + default: + return NULL; + } +} + /****************************************************************************** * GetUserGeoID (KERNEL32.@) * @@ -3084,7 +3152,7 @@ GEOID WINAPI GetUserGeoID(GEOCLASS geoclass) */ BOOL WINAPI SetUserGeoID(GEOID geoid) { - const struct geoinfo_t *geoinfo = get_geoinfo_dataptr(geoid); + const struct geoinfo_t *geoinfo = get_geoinfoptr_by_id(geoid); static const WCHAR geoW[] = {'G','e','o',0}; static const WCHAR nationW[] = {'N','a','t','i','o','n',0}; static const WCHAR regionW[] = {'R','e','g','i','o','n',0}; @@ -3129,38 +3197,15 @@ BOOL WINAPI SetUserGeoID(GEOID geoid) } /****************************************************************************** - * GetGeoInfoW (KERNEL32.@) + * get_geo_info * - * Retrieves information about a geographic location by its GeoID. + * Implements GetGeoInfoW() and GetGeoInfoEx(). * - * PARAMS - * geoid [I] The GeoID of the location of interest. - * geotype [I] The type of information to be retrieved (SYSGEOTYPE enum from "winnls.h"). - * data [O] The output buffer to store the information. - * data_len [I] The length of the buffer, measured in WCHARs and including the null terminator. - * lang [I] Language identifier. Must be 0 unless geotype is GEO_RFC1766 or GEO_LCID. - * - * RETURNS - * Success: The number of WCHARs (including null) written to the buffer -or- - * if no buffer was provided, the minimum length required to hold the full data. - * Failure: Zero. Call GetLastError() to determine the cause. - * - * NOTES - * On failure, GetLastError() will return one of the following values: - * - ERROR_INVALID_PARAMETER: the GeoID provided was invalid. - * - ERROR_INVALID_FLAGS: the specified geotype was invalid. - * - ERROR_INSUFFICIENT_BUFFER: the provided buffer was too small to hold the full data. - * - ERROR_CALL_NOT_IMPLEMENTED: (Wine implementation) we don't handle that geotype yet. - * - * The list of available GeoIDs can be retrieved with EnumSystemGeoID(). - * - * TODO - * Currently, we only handle the following geotypes: GEO_ID, GEO_NATION, GEO_ISO_UN_NUMBER, - * GEO_ISO2, GEO_ISO3, GEO_PARENT and GEO_NAME. + * The return value and last error set here can and should be + * returned to the caller of those two functions. */ -INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang) +static int get_geo_info(const struct geoinfo_t *geoptr, GEOTYPE geotype, WCHAR *data, int data_len, LANGID lang) { - const struct geoinfo_t *ptr; WCHAR buffW[12]; const WCHAR *str = buffW; int len; @@ -3168,36 +3213,34 @@ INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, static const WCHAR id_fmtW[] = {'%','d',0}; static const WCHAR un_fmtW[] = {'%','0','3','d',0}; - TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang); - - if (!(ptr = get_geoinfo_dataptr(geoid))) { + if (!geoptr) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } switch (geotype) { case GEO_NATION: - if (ptr->kind != LOCATION_NATION) return 0; + if (geoptr->kind != LOCATION_NATION) return 0; case GEO_ID: - sprintfW(buffW, id_fmtW, ptr->id); + sprintfW(buffW, id_fmtW, geoptr->id); break; case GEO_ISO_UN_NUMBER: - sprintfW(buffW, un_fmtW, ptr->uncode); + sprintfW(buffW, un_fmtW, geoptr->uncode); break; case GEO_PARENT: - sprintfW(buffW, id_fmtW, ptr->parent); + sprintfW(buffW, id_fmtW, geoptr->parent); break; case GEO_ISO2: - str = ptr->iso2W; + str = geoptr->iso2W; break; case GEO_ISO3: - str = ptr->iso3W; + str = geoptr->iso3W; break; case GEO_NAME: - if (ptr->uncode && !strcmpW(xx, ptr->iso2W)) - sprintfW(buffW, un_fmtW, ptr->uncode); + if (geoptr->uncode && !strcmpW(xx, geoptr->iso2W)) + sprintfW(buffW, un_fmtW, geoptr->uncode); else - str = ptr->iso2W; + str = geoptr->iso2W; break; case GEO_RFC1766: case GEO_LCID: @@ -3229,10 +3272,51 @@ INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, return data_len < len ? 0 : len; } +/****************************************************************************** + * GetGeoInfoW (KERNEL32.@) + * + * Retrieves information about a geographic location by its GeoID. + * + * PARAMS + * geoid [I] The GeoID of the location of interest. + * geotype [I] The type of information to be retrieved (SYSGEOTYPE enum from "winnls.h"). + * data [O] The output buffer to store the information. + * data_len [I] The length of the buffer, measured in WCHARs and including the null terminator. + * lang [I] Language identifier. Must be 0 unless geotype is GEO_RFC1766 or GEO_LCID. + * + * RETURNS + * Success: The number of WCHARs (including null) written to the buffer -or- + * if no buffer was provided, the minimum length required to hold the full data. + * Failure: Zero. Call GetLastError() to determine the cause. + * + * NOTES + * On failure, GetLastError() will return one of the following values: + * - ERROR_INVALID_PARAMETER: the GeoID provided was invalid. + * - ERROR_INVALID_FLAGS: the specified geotype was invalid. + * - ERROR_INSUFFICIENT_BUFFER: the provided buffer was too small to hold the full data. + * - ERROR_CALL_NOT_IMPLEMENTED: (Wine implementation) we don't handle that geotype yet. + * + * The list of available GeoIDs can be retrieved with EnumSystemGeoID(); or check the + * currently set location with GetUserGeoID(). + * + * TODO + * Currently, we only handle the following geotypes: GEO_ID, GEO_NATION, GEO_ISO_UN_NUMBER, + * GEO_ISO2, GEO_ISO3, GEO_PARENT and GEO_NAME. + */ +int WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, WCHAR *data, int data_len, LANGID lang) +{ + TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang); + return get_geo_info(get_geoinfoptr_by_id(geoid), geotype, data, data_len, lang); +} + /****************************************************************************** * GetGeoInfoA (KERNEL32.@) + * + * Retrieves information about a geographic location by its GeoID. + * + * Narrow character version of GetGeoInfoW(). */ -INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang) +int WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, char *data, int data_len, LANGID lang) { WCHAR *buffW; INT len; @@ -3262,6 +3346,61 @@ INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, L return data_len < len ? 0 : len; } +/****************************************************************************** + * GetGeoInfoEx (KERNEL32.@) + * + * Retrieves information about a geographic location by its GeoName. + * + * PARAMS + * location [I] GeoName of the location to learn about. + * geotype [I] Type of information to be retrieved (except GEO_LCID, GEO_NATION and GEO_RFC1766). + * data [O] Output buffer to store the information. + * data_len [I] Length of the buffer, measured in WCHARs and including the null terminator. + * + * RETURNS + * Same as GetGeoInfoW(). + * + * NOTES + * A GeoName is a two-letter ISO 3166 country code + * or a three-digit UN M.49 code for anything other than countries (e.g. continents). + * + * This function disallows some values of geotype: GEO_LCID, GEO_NATION and GEO_RFC1776. + * For GEO_NATION, use GEO_ID instead; for the other two, call GetGeoInfoW(). + * + * The list of available GeoNames can be retrieved with EnumSystemGeoNames(); or check the + * currently set location with GetUserDefaultGeoName(). + * + * For more notes and unhandled cases, consult GetGeoInfoW(). + */ +int WINAPI GetGeoInfoEx(WCHAR *location, GEOTYPE geotype, WCHAR *data, int data_len) +{ + TRACE("%p=%s %d %p %d\n", + location, location ? wine_dbgstr_w(location) : "null", geotype, data, data_len); + + if (!location || !*location) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + switch (geotype) + { + case GEO_LCID: + case GEO_NATION: + case GEO_RFC1766: + SetLastError(ERROR_INVALID_FLAGS); + return 0; + case GEO_PARENT: + { + const struct geoinfo_t *geoptr = get_geoinfoptr_by_name(location); + const struct geoinfo_t *parent = geoptr ? get_geoinfoptr_by_id(geoptr->parent) : NULL; + return get_geo_info(parent, GEO_NAME, data, data_len, 0); + } + default: + return get_geo_info(get_geoinfoptr_by_name(location), geotype, data, data_len, 0); + } +} + /****************************************************************************** * EnumSystemGeoID (KERNEL32.@) * diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index b501976228..6a040c2624 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -72,8 +72,9 @@ static BOOL (WINAPI *pIsValidLocaleName)(LPCWSTR); static INT (WINAPI *pCompareStringOrdinal)(const WCHAR *, INT, const WCHAR *, INT, BOOL); static INT (WINAPI *pCompareStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPCWSTR, INT, LPNLSVERSIONINFO, LPVOID, LPARAM); -static INT (WINAPI *pGetGeoInfoA)(GEOID, GEOTYPE, LPSTR, INT, LANGID); -static INT (WINAPI *pGetGeoInfoW)(GEOID, GEOTYPE, LPWSTR, INT, LANGID); +static int (WINAPI *pGetGeoInfoA)(GEOID, GEOTYPE, char *, int, LANGID); +static int (WINAPI *pGetGeoInfoW)(GEOID, GEOTYPE, WCHAR *, int, LANGID); +static int (WINAPI *pGetGeoInfoEx)(WCHAR *, GEOTYPE, WCHAR *, int); static BOOL (WINAPI *pEnumSystemGeoID)(GEOCLASS, GEOID, GEO_ENUMPROC); static BOOL (WINAPI *pGetSystemPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*); static BOOL (WINAPI *pGetThreadPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*); @@ -114,6 +115,7 @@ static void InitFunctionPointers(void) X(CompareStringEx); X(GetGeoInfoA); X(GetGeoInfoW); + X(GetGeoInfoEx); X(EnumSystemGeoID); X(GetSystemPreferredUILanguages); X(GetThreadPreferredUILanguages); @@ -4981,6 +4983,60 @@ static void test_GetGeoInfo(void) ret = pGetGeoInfoA(203, GEO_ID + 1, NULL, 0, 0); ok(ret == 0, "got %d\n", ret); ok(GetLastError() == ERROR_INVALID_FLAGS, "got %d\n", GetLastError()); + + if (pGetGeoInfoEx) + { + WCHAR buffer[10]; + WCHAR pt[] = {'P','T',0}, ptlow[] = {'p','t',0}, parent[] = {'0','3','9',0}, + xx[] = {'X','X',0}, world[] = {'0','0','1',0}, empty[] = {0}; + + /* GEO_PARENT should return the parent in geo name form. */ + buffer[0] = 0; + ret = pGetGeoInfoEx(pt, GEO_PARENT, buffer, ARRAY_SIZE(buffer)); + ok(ret == 4, "expected 4, got %d\n", ret); + ok(!winetest_strcmpW(buffer, parent), "expected %s, got %s\n", wine_dbgstr_w(parent), wine_dbgstr_w(buffer)); + + /* Lowercase name should work the same. */ + buffer[0] = 0; + ret = pGetGeoInfoEx(ptlow, GEO_PARENT, buffer, ARRAY_SIZE(buffer)); + ok(ret == 4, "expected 4, got %d\n", ret); + ok(!winetest_strcmpW(buffer, parent), "expected %s, got %s\n", wine_dbgstr_w(parent), wine_dbgstr_w(buffer)); + + /* GeoName XX should map to 001. */ + buffer[0] = 0; + ret = pGetGeoInfoEx(xx, GEO_NAME, buffer, ARRAY_SIZE(buffer)); + ok(ret == 4, "expected 4, got %d\n", ret); + ok(!winetest_strcmpW(buffer, world), "expected %s, got %s\n", wine_dbgstr_w(world), wine_dbgstr_w(buffer)); + + /* Test types disallowed by GetGeoInfoEx. */ + SetLastError(0xdeadbeef); + ret = pGetGeoInfoEx(pt, GEO_LCID, buffer, ARRAY_SIZE(buffer)); + ok(ret == 0, "expected ret == 0, got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_FLAGS, "expected error 1004, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = pGetGeoInfoEx(pt, GEO_NATION, buffer, ARRAY_SIZE(buffer)); + ok(ret == 0, "expected ret == 0, got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_FLAGS, "expected error 1004, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = pGetGeoInfoEx(pt, GEO_RFC1766, buffer, ARRAY_SIZE(buffer)); + ok(ret == 0, "expected ret == 0, got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_FLAGS, "expected error 1004, got %d\n", GetLastError()); + + /* Test null names */ + SetLastError(0xdeadbeef); + ret = pGetGeoInfoEx(empty, GEO_NAME, buffer, ARRAY_SIZE(buffer)), + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, + "expected ret == 0 (got %d) and error == 87 (got %d)\n", ret, GetLastError()); + + SetLastError(0xdeadbeef); + ret = pGetGeoInfoEx(NULL, GEO_NAME, buffer, ARRAY_SIZE(buffer)), + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, + "expected ret == 0 (got %d) and error == 87 (got %d)\n", ret, GetLastError()); + } + else + win_skip("GetGeoInfoEx not supported.\n"); } static int geoidenum_count; diff --git a/include/winnls.h b/include/winnls.h index b233f7f917..7f5a782815 100644 --- a/include/winnls.h +++ b/include/winnls.h @@ -904,8 +904,8 @@ WINBASEAPI INT WINAPI GetDateFormatW(LCID,DWORD,const SYSTEMTIME*,LPCWST #define GetDateFormat WINELIB_NAME_AW(GetDateFormat) WINBASEAPI BOOL WINAPI GetFileMUIInfo(DWORD,PCWSTR,PFILEMUIINFO,DWORD*); WINBASEAPI BOOL WINAPI GetFileMUIPath(DWORD,PCWSTR,PWSTR,PULONG,PWSTR,PULONG,PULONGLONG); -WINBASEAPI INT WINAPI GetGeoInfoA(GEOID,GEOTYPE,LPSTR,INT,LANGID); -WINBASEAPI INT WINAPI GetGeoInfoW(GEOID,GEOTYPE,LPWSTR,INT,LANGID); +WINBASEAPI int WINAPI GetGeoInfoA(GEOID,GEOTYPE,char*,int,LANGID); +WINBASEAPI int WINAPI GetGeoInfoW(GEOID,GEOTYPE,WCHAR*,int,LANGID); #define GetGeoInfo WINELIB_NAME_AW(GetGeoInfo) WINBASEAPI INT WINAPI GetLocaleInfoA(LCID,LCTYPE,LPSTR,INT); WINBASEAPI INT WINAPI GetLocaleInfoW(LCID,LCTYPE,LPWSTR,INT); -- 2.24.0