Signed-off-by: João Diogo Craveiro Ferreira <devilj(a)outlook.pt>
---
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