[PATCH v2 2/3] kernel32: Implement GetGeoInfoEx().

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


Signed-off-by: João Diogo Craveiro Ferreira <devilj at 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 b77c3617aa..bb56eeb053 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);
@@ -4980,6 +4982,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



More information about the wine-devel mailing list