Skip to content

Commit 33b7728

Browse files
author
Amit Kapila
committed
Fix the MSVC build for versions 2015 and later.
Visual Studio 2015 and later versions should still be able to do the same as Visual Studio 2012, but the declaration of locale_name is missing in _locale_t, causing the code compilation to fail, hence this falls back instead on to enumerating all system locales by using EnumSystemLocalesEx to find the required locale name.  If the input argument is in Unix-style then we can get ISO Locale name directly by using GetLocaleInfoEx() with LCType as LOCALE_SNAME. In passing, change the documentation references of the now obsolete links. Note that this problem occurs only with NLS enabled builds. Author: Juan José Santamaría Flecha, Davinder Singh and Amit Kapila Reviewed-by: Ranier Vilela and Amit Kapila Backpatch-through: 9.5 Discussion: https://postgr.es/m/CAHzhFSFoJEWezR96um4-rg5W6m2Rj9Ud2CNZvV4NWc9tXV7aXQ@mail.gmail.com
1 parent 970ed44 commit 33b7728

File tree

1 file changed

+175
-8
lines changed

1 file changed

+175
-8
lines changed

src/backend/utils/adt/pg_locale.c

Lines changed: 175 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ pg_perm_setlocale(int category, const char *locale)
220220
result = IsoLocaleName(locale);
221221
if (result == NULL)
222222
result = (char *) locale;
223+
elog(DEBUG3, "IsoLocaleName() executed; locale: \"%s\"", result);
223224
#endif /* WIN32 */
224225
break;
225226
#endif /* LC_MESSAGES */
@@ -953,19 +954,181 @@ cache_locale_time(void)
953954
* string. Furthermore, msvcr110.dll changed the undocumented _locale_t
954955
* content to carry locale names instead of locale identifiers.
955956
*
956-
* MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol.
957-
* IsoLocaleName() always fails in a MinGW-built postgres.exe, so only
958-
* Unix-style values of the lc_messages GUC can elicit localized messages. In
959-
* particular, every lc_messages setting that initdb can select automatically
960-
* will yield only C-locale messages. XXX This could be fixed by running the
961-
* fully-qualified locale name through a lookup table.
957+
* Visual Studio 2015 should still be able to do the same as Visual Studio
958+
* 2012, but the declaration of locale_name is missing in _locale_t, causing
959+
* this code compilation to fail, hence this falls back instead on to
960+
* enumerating all system locales by using EnumSystemLocalesEx to find the
961+
* required locale name. If the input argument is in Unix-style then we can
962+
* get ISO Locale name directly by using GetLocaleInfoEx() with LCType as
963+
* LOCALE_SNAME.
964+
*
965+
* MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol in
966+
* releases before Windows 8. IsoLocaleName() always fails in a MinGW-built
967+
* postgres.exe, so only Unix-style values of the lc_messages GUC can elicit
968+
* localized messages. In particular, every lc_messages setting that initdb
969+
* can select automatically will yield only C-locale messages. XXX This could
970+
* be fixed by running the fully-qualified locale name through a lookup table.
962971
*
963972
* This function returns a pointer to a static buffer bearing the converted
964973
* name or NULL if conversion fails.
965974
*
966-
* [1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373763.aspx
967-
* [2] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373814.aspx
975+
* [1] https://docs.microsoft.com/en-us/windows/win32/intl/locale-identifiers
976+
* [2] https://docs.microsoft.com/en-us/windows/win32/intl/locale-names
977+
*/
978+
979+
#if _MSC_VER >= 1900
980+
/*
981+
* Callback function for EnumSystemLocalesEx() in get_iso_localename().
982+
*
983+
* This function enumerates all system locales, searching for one that matches
984+
* an input with the format: <Language>[_<Country>], e.g.
985+
* English[_United States]
986+
*
987+
* The input is a three wchar_t array as an LPARAM. The first element is the
988+
* locale_name we want to match, the second element is an allocated buffer
989+
* where the Unix-style locale is copied if a match is found, and the third
990+
* element is the search status, 1 if a match was found, 0 otherwise.
991+
*/
992+
static BOOL CALLBACK
993+
search_locale_enum(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
994+
{
995+
wchar_t test_locale[LOCALE_NAME_MAX_LENGTH];
996+
wchar_t **argv;
997+
998+
(void) (dwFlags);
999+
1000+
argv = (wchar_t **) lparam;
1001+
*argv[2] = (wchar_t) 0;
1002+
1003+
memset(test_locale, 0, sizeof(test_locale));
1004+
1005+
/* Get the name of the <Language> in English */
1006+
if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHLANGUAGENAME,
1007+
test_locale, LOCALE_NAME_MAX_LENGTH))
1008+
{
1009+
/*
1010+
* If the enumerated locale does not have a hyphen ("en") OR the
1011+
* lc_message input does not have an underscore ("English"), we only
1012+
* need to compare the <Language> tags.
1013+
*/
1014+
if (wcsrchr(pStr, '-') == NULL || wcsrchr(argv[0], '_') == NULL)
1015+
{
1016+
if (_wcsicmp(argv[0], test_locale) == 0)
1017+
{
1018+
wcscpy(argv[1], pStr);
1019+
*argv[2] = (wchar_t) 1;
1020+
return FALSE;
1021+
}
1022+
}
1023+
1024+
/*
1025+
* We have to compare a full <Language>_<Country> tag, so we append
1026+
* the underscore and name of the country/region in English, e.g.
1027+
* "English_United States".
1028+
*/
1029+
else
1030+
{
1031+
size_t len;
1032+
1033+
wcscat(test_locale, L"_");
1034+
len = wcslen(test_locale);
1035+
if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHCOUNTRYNAME,
1036+
test_locale + len,
1037+
LOCALE_NAME_MAX_LENGTH - len))
1038+
{
1039+
if (_wcsicmp(argv[0], test_locale) == 0)
1040+
{
1041+
wcscpy(argv[1], pStr);
1042+
*argv[2] = (wchar_t) 1;
1043+
return FALSE;
1044+
}
1045+
}
1046+
}
1047+
}
1048+
1049+
return TRUE;
1050+
}
1051+
1052+
/*
1053+
* This function converts a Windows locale name to an ISO formatted version
1054+
* for Visual Studio 2015 or greater.
1055+
*
1056+
* Returns NULL, if no valid conversion was found.
9681057
*/
1058+
static char *
1059+
get_iso_localename(const char *winlocname)
1060+
{
1061+
wchar_t wc_locale_name[LOCALE_NAME_MAX_LENGTH];
1062+
wchar_t buffer[LOCALE_NAME_MAX_LENGTH];
1063+
static char iso_lc_messages[LOCALE_NAME_MAX_LENGTH];
1064+
char *period;
1065+
int len;
1066+
int ret_val;
1067+
1068+
/*
1069+
* Valid locales have the following syntax:
1070+
* <Language>[_<Country>[.<CodePage>]]
1071+
*
1072+
* GetLocaleInfoEx can only take locale name without code-page and for the
1073+
* purpose of this API the code-page doesn't matter.
1074+
*/
1075+
period = strchr(winlocname, '.');
1076+
if (period != NULL)
1077+
len = period - winlocname;
1078+
else
1079+
len = pg_mbstrlen(winlocname);
1080+
1081+
memset(wc_locale_name, 0, sizeof(wc_locale_name));
1082+
memset(buffer, 0, sizeof(buffer));
1083+
MultiByteToWideChar(CP_ACP, 0, winlocname, len, wc_locale_name,
1084+
LOCALE_NAME_MAX_LENGTH);
1085+
1086+
/*
1087+
* If the lc_messages is already an Unix-style string, we have a direct
1088+
* match with LOCALE_SNAME, e.g. en-US, en_US.
1089+
*/
1090+
ret_val = GetLocaleInfoEx(wc_locale_name, LOCALE_SNAME, (LPWSTR) &buffer,
1091+
LOCALE_NAME_MAX_LENGTH);
1092+
if (!ret_val)
1093+
{
1094+
/*
1095+
* Search for a locale in the system that matches language and country
1096+
* name.
1097+
*/
1098+
wchar_t *argv[3];
1099+
1100+
argv[0] = wc_locale_name;
1101+
argv[1] = buffer;
1102+
argv[2] = (wchar_t *) &ret_val;
1103+
EnumSystemLocalesEx(search_locale_enum, LOCALE_WINDOWS, (LPARAM) argv,
1104+
NULL);
1105+
}
1106+
1107+
if (ret_val)
1108+
{
1109+
size_t rc;
1110+
char *hyphen;
1111+
1112+
/* Locale names use only ASCII, any conversion locale suffices. */
1113+
rc = wchar2char(iso_lc_messages, buffer, sizeof(iso_lc_messages), NULL);
1114+
if (rc == -1 || rc == sizeof(iso_lc_messages))
1115+
return NULL;
1116+
1117+
/*
1118+
* Simply replace the hyphen with an underscore. See comments in
1119+
* IsoLocaleName.
1120+
*/
1121+
hyphen = strchr(iso_lc_messages, '-');
1122+
if (hyphen)
1123+
*hyphen = '_';
1124+
1125+
return iso_lc_messages;
1126+
}
1127+
1128+
return NULL;
1129+
}
1130+
#endif /* _MSC_VER >= 1900 */
1131+
9691132
static char *
9701133
IsoLocaleName(const char *winlocname)
9711134
{
@@ -980,6 +1143,9 @@ IsoLocaleName(const char *winlocname)
9801143
return iso_lc_messages;
9811144
}
9821145

1146+
#if (_MSC_VER >= 1900) /* Visual Studio 2015 or later */
1147+
return get_iso_localename(winlocname);
1148+
#else
9831149
loct = _create_locale(LC_CTYPE, winlocname);
9841150
if (loct != NULL)
9851151
{
@@ -1029,6 +1195,7 @@ IsoLocaleName(const char *winlocname)
10291195
return iso_lc_messages;
10301196
}
10311197
return NULL;
1198+
#endif /* Visual Studio 2015 or later */
10321199
#else
10331200
return NULL; /* Not supported on this version of msvc/mingw */
10341201
#endif /* _MSC_VER >= 1400 */

0 commit comments

Comments
 (0)