@@ -234,6 +234,7 @@ pg_perm_setlocale(int category, const char *locale)
234234 result = IsoLocaleName (locale );
235235 if (result == NULL )
236236 result = (char * ) locale ;
237+ elog (DEBUG3 , "IsoLocaleName() executed; locale: \"%s\"" , result );
237238#endif /* WIN32 */
238239 break ;
239240#endif /* LC_MESSAGES */
@@ -971,65 +972,234 @@ cache_locale_time(void)
971972 * string. Furthermore, msvcr110.dll changed the undocumented _locale_t
972973 * content to carry locale names instead of locale identifiers.
973974 *
974- * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol.
975- * IsoLocaleName() always fails in a MinGW-built postgres.exe, so only
976- * Unix-style values of the lc_messages GUC can elicit localized messages. In
977- * particular, every lc_messages setting that initdb can select automatically
978- * will yield only C-locale messages. XXX This could be fixed by running the
979- * fully-qualified locale name through a lookup table.
975+ * Visual Studio 2015 should still be able to do the same as Visual Studio
976+ * 2012, but the declaration of locale_name is missing in _locale_t, causing
977+ * this code compilation to fail, hence this falls back instead on to
978+ * enumerating all system locales by using EnumSystemLocalesEx to find the
979+ * required locale name. If the input argument is in Unix-style then we can
980+ * get ISO Locale name directly by using GetLocaleInfoEx() with LCType as
981+ * LOCALE_SNAME.
982+ *
983+ * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol in
984+ * releases before Windows 8. IsoLocaleName() always fails in a MinGW-built
985+ * postgres.exe, so only Unix-style values of the lc_messages GUC can elicit
986+ * localized messages. In particular, every lc_messages setting that initdb
987+ * can select automatically will yield only C-locale messages. XXX This could
988+ * be fixed by running the fully-qualified locale name through a lookup table.
980989 *
981990 * This function returns a pointer to a static buffer bearing the converted
982991 * name or NULL if conversion fails.
983992 *
984- * [1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373763.aspx
985- * [2] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373814.aspx
993+ * [1] https://docs.microsoft.com/en-us/windows/win32/intl/locale-identifiers
994+ * [2] https://docs.microsoft.com/en-us/windows/win32/intl/locale-names
995+ */
996+
997+ #if _MSC_VER >= 1900
998+ /*
999+ * Callback function for EnumSystemLocalesEx() in get_iso_localename().
1000+ *
1001+ * This function enumerates all system locales, searching for one that matches
1002+ * an input with the format: <Language>[_<Country>], e.g.
1003+ * English[_United States]
1004+ *
1005+ * The input is a three wchar_t array as an LPARAM. The first element is the
1006+ * locale_name we want to match, the second element is an allocated buffer
1007+ * where the Unix-style locale is copied if a match is found, and the third
1008+ * element is the search status, 1 if a match was found, 0 otherwise.
1009+ */
1010+ static BOOL CALLBACK
1011+ search_locale_enum (LPWSTR pStr , DWORD dwFlags , LPARAM lparam )
1012+ {
1013+ wchar_t test_locale [LOCALE_NAME_MAX_LENGTH ];
1014+ wchar_t * * argv ;
1015+
1016+ (void ) (dwFlags );
1017+
1018+ argv = (wchar_t * * ) lparam ;
1019+ * argv [2 ] = (wchar_t ) 0 ;
1020+
1021+ memset (test_locale , 0 , sizeof (test_locale ));
1022+
1023+ /* Get the name of the <Language> in English */
1024+ if (GetLocaleInfoEx (pStr , LOCALE_SENGLISHLANGUAGENAME ,
1025+ test_locale , LOCALE_NAME_MAX_LENGTH ))
1026+ {
1027+ /*
1028+ * If the enumerated locale does not have a hyphen ("en") OR the
1029+ * lc_message input does not have an underscore ("English"), we only
1030+ * need to compare the <Language> tags.
1031+ */
1032+ if (wcsrchr (pStr , '-' ) == NULL || wcsrchr (argv [0 ], '_' ) == NULL )
1033+ {
1034+ if (_wcsicmp (argv [0 ], test_locale ) == 0 )
1035+ {
1036+ wcscpy (argv [1 ], pStr );
1037+ * argv [2 ] = (wchar_t ) 1 ;
1038+ return FALSE;
1039+ }
1040+ }
1041+
1042+ /*
1043+ * We have to compare a full <Language>_<Country> tag, so we append
1044+ * the underscore and name of the country/region in English, e.g.
1045+ * "English_United States".
1046+ */
1047+ else
1048+ {
1049+ size_t len ;
1050+
1051+ wcscat (test_locale , L"_" );
1052+ len = wcslen (test_locale );
1053+ if (GetLocaleInfoEx (pStr , LOCALE_SENGLISHCOUNTRYNAME ,
1054+ test_locale + len ,
1055+ LOCALE_NAME_MAX_LENGTH - len ))
1056+ {
1057+ if (_wcsicmp (argv [0 ], test_locale ) == 0 )
1058+ {
1059+ wcscpy (argv [1 ], pStr );
1060+ * argv [2 ] = (wchar_t ) 1 ;
1061+ return FALSE;
1062+ }
1063+ }
1064+ }
1065+ }
1066+
1067+ return TRUE;
1068+ }
1069+
1070+ /*
1071+ * This function converts a Windows locale name to an ISO formatted version
1072+ * for Visual Studio 2015 or greater.
1073+ *
1074+ * Returns NULL, if no valid conversion was found.
9861075 */
9871076static char *
988- IsoLocaleName (const char * winlocname )
1077+ get_iso_localename (const char * winlocname )
9891078{
990- #ifdef _MSC_VER
991- static char iso_lc_messages [32 ];
992- _locale_t loct = NULL ;
1079+ wchar_t wc_locale_name [LOCALE_NAME_MAX_LENGTH ];
1080+ wchar_t buffer [LOCALE_NAME_MAX_LENGTH ];
1081+ static char iso_lc_messages [LOCALE_NAME_MAX_LENGTH ];
1082+ char * period ;
1083+ int len ;
1084+ int ret_val ;
9931085
994- if (pg_strcasecmp ("c" , winlocname ) == 0 ||
995- pg_strcasecmp ("posix" , winlocname ) == 0 )
1086+ /*
1087+ * Valid locales have the following syntax:
1088+ * <Language>[_<Country>[.<CodePage>]]
1089+ *
1090+ * GetLocaleInfoEx can only take locale name without code-page and for the
1091+ * purpose of this API the code-page doesn't matter.
1092+ */
1093+ period = strchr (winlocname , '.' );
1094+ if (period != NULL )
1095+ len = period - winlocname ;
1096+ else
1097+ len = pg_mbstrlen (winlocname );
1098+
1099+ memset (wc_locale_name , 0 , sizeof (wc_locale_name ));
1100+ memset (buffer , 0 , sizeof (buffer ));
1101+ MultiByteToWideChar (CP_ACP , 0 , winlocname , len , wc_locale_name ,
1102+ LOCALE_NAME_MAX_LENGTH );
1103+
1104+ /*
1105+ * If the lc_messages is already an Unix-style string, we have a direct
1106+ * match with LOCALE_SNAME, e.g. en-US, en_US.
1107+ */
1108+ ret_val = GetLocaleInfoEx (wc_locale_name , LOCALE_SNAME , (LPWSTR ) & buffer ,
1109+ LOCALE_NAME_MAX_LENGTH );
1110+ if (!ret_val )
9961111 {
997- strcpy (iso_lc_messages , "C" );
998- return iso_lc_messages ;
1112+ /*
1113+ * Search for a locale in the system that matches language and country
1114+ * name.
1115+ */
1116+ wchar_t * argv [3 ];
1117+
1118+ argv [0 ] = wc_locale_name ;
1119+ argv [1 ] = buffer ;
1120+ argv [2 ] = (wchar_t * ) & ret_val ;
1121+ EnumSystemLocalesEx (search_locale_enum , LOCALE_WINDOWS , (LPARAM ) argv ,
1122+ NULL );
9991123 }
10001124
1001- loct = _create_locale (LC_CTYPE , winlocname );
1002- if (loct != NULL )
1125+ if (ret_val )
10031126 {
10041127 size_t rc ;
10051128 char * hyphen ;
10061129
10071130 /* Locale names use only ASCII, any conversion locale suffices. */
1008- rc = wchar2char (iso_lc_messages , loct -> locinfo -> locale_name [LC_CTYPE ],
1009- sizeof (iso_lc_messages ), NULL );
1010- _free_locale (loct );
1131+ rc = wchar2char (iso_lc_messages , buffer , sizeof (iso_lc_messages ), NULL );
10111132 if (rc == -1 || rc == sizeof (iso_lc_messages ))
10121133 return NULL ;
10131134
10141135 /*
1015- * Since the message catalogs sit on a case-insensitive filesystem, we
1016- * need not standardize letter case here. So long as we do not ship
1017- * message catalogs for which it would matter, we also need not
1018- * translate the script/variant portion, e.g. uz-Cyrl-UZ to
1019- * uz_UZ@cyrillic. Simply replace the hyphen with an underscore.
1020- *
1021- * Note that the locale name can be less-specific than the value we
1022- * would derive under earlier Visual Studio releases. For example,
1023- * French_France.1252 yields just "fr". This does not affect any of
1024- * the country-specific message catalogs available as of this writing
1025- * (pt_BR, zh_CN, zh_TW).
1136+ * Simply replace the hyphen with an underscore. See comments in
1137+ * IsoLocaleName.
10261138 */
10271139 hyphen = strchr (iso_lc_messages , '-' );
10281140 if (hyphen )
10291141 * hyphen = '_' ;
10301142 return iso_lc_messages ;
10311143 }
1032- #endif /* _MSC_VER */
1144+
1145+ return NULL ;
1146+ }
1147+ #endif /* _MSC_VER >= 1900 */
1148+
1149+ static char *
1150+ IsoLocaleName (const char * winlocname )
1151+ {
1152+ #if defined(_MSC_VER )
1153+ static char iso_lc_messages [LOCALE_NAME_MAX_LENGTH ];
1154+
1155+ if (pg_strcasecmp ("c" , winlocname ) == 0 ||
1156+ pg_strcasecmp ("posix" , winlocname ) == 0 )
1157+ {
1158+ strcpy (iso_lc_messages , "C" );
1159+ return iso_lc_messages ;
1160+ }
1161+ else
1162+ {
1163+ #if (_MSC_VER >= 1900 ) /* Visual Studio 2015 or later */
1164+ return get_iso_localename (winlocname );
1165+ #else
1166+ _locale_t loct ;
1167+
1168+ loct = _create_locale (LC_CTYPE , winlocname );
1169+ if (loct != NULL )
1170+ {
1171+ size_t rc ;
1172+ char * hyphen ;
1173+
1174+ /* Locale names use only ASCII, any conversion locale suffices. */
1175+ rc = wchar2char (iso_lc_messages , loct -> locinfo -> locale_name [LC_CTYPE ],
1176+ sizeof (iso_lc_messages ), NULL );
1177+ _free_locale (loct );
1178+ if (rc == -1 || rc == sizeof (iso_lc_messages ))
1179+ return NULL ;
1180+
1181+ /*
1182+ * Since the message catalogs sit on a case-insensitive
1183+ * filesystem, we need not standardize letter case here. So long
1184+ * as we do not ship message catalogs for which it would matter,
1185+ * we also need not translate the script/variant portion, e.g.
1186+ * uz-Cyrl-UZ to uz_UZ@cyrillic. Simply replace the hyphen with
1187+ * an underscore.
1188+ *
1189+ * Note that the locale name can be less-specific than the value
1190+ * we would derive under earlier Visual Studio releases. For
1191+ * example, French_France.1252 yields just "fr". This does not
1192+ * affect any of the country-specific message catalogs available
1193+ * as of this writing (pt_BR, zh_CN, zh_TW).
1194+ */
1195+ hyphen = strchr (iso_lc_messages , '-' );
1196+ if (hyphen )
1197+ * hyphen = '_' ;
1198+ return iso_lc_messages ;
1199+ }
1200+ #endif /* Visual Studio 2015 or later */
1201+ }
1202+ #endif /* defined(_MSC_VER) */
10331203 return NULL ; /* Not supported on this version of msvc/mingw */
10341204}
10351205#endif /* WIN32 && LC_MESSAGES */
0 commit comments