@@ -185,6 +185,8 @@ static intlocale_date_order(const char *locale);
185185static bool check_locale_name (const char * locale );
186186static bool check_locale_encoding (const char * locale ,int encoding );
187187static void setlocales (void );
188+ static void strreplace (char * str ,char * needle ,char * replacement );
189+ static char * localemap (char * locale );
188190static void usage (const char * progname );
189191
190192#ifdef WIN32
@@ -2250,6 +2252,79 @@ check_locale_encoding(const char *locale, int user_enc)
22502252return true;
22512253}
22522254
2255+ /*
2256+ * Replace 'needle' with 'replacement' in 'str' . Note that the replacement
2257+ * is done in-place, so 'replacement' must be shorter than 'needle'.
2258+ */
2259+ static void
2260+ strreplace (char * str ,char * needle ,char * replacement )
2261+ {
2262+ char * s ;
2263+
2264+ s = strstr (str ,needle );
2265+ if (s != NULL )
2266+ {
2267+ int replacementlen = strlen (replacement );
2268+ char * rest = s + strlen (needle );
2269+
2270+ memcpy (s ,replacement ,replacementlen );
2271+ memmove (s + replacementlen ,rest ,strlen (rest )+ 1 );
2272+ }
2273+ }
2274+
2275+ /*
2276+ * Windows has a problem with locale names that have a dot or apostrophe in
2277+ * the country name. For example:
2278+ *
2279+ * "Chinese (Traditional)_Hong Kong S.A.R..950"
2280+ *
2281+ * For some reason, setlocale() doesn't accept that. Fortunately, Windows'
2282+ * setlocale() accepts various alternative names for such countries, so we
2283+ * map the full country names to accepted aliases.
2284+ *
2285+ * The returned string is always malloc'd - if no mapping is done it is
2286+ * just a malloc'd copy of the original.
2287+ */
2288+ static char *
2289+ localemap (char * locale )
2290+ {
2291+ locale = xstrdup (locale );
2292+
2293+ #ifdef WIN32
2294+ /*
2295+ * Map the full country name to an abbreviation that setlocale() accepts
2296+ * "China" and "HKG" are listed here:
2297+ * http://msdn.microsoft.com/en-us/library/cdax410z%28v=vs.71%29.aspx
2298+ * (Country/Region Strings).
2299+ *
2300+ * "ARE" is the ISO-3166 three-letter code for U.A.E. It is not on the
2301+ * above list, but seems to work anyway.
2302+ */
2303+ strreplace (locale ,"People's Republic of China" ,"China" );
2304+ strreplace (locale ,"Hong Kong S.A.R." ,"HKG" );
2305+ strreplace (locale ,"U.A.E." ,"ARE" );
2306+
2307+ /*
2308+ * The ISO-3166 country code for Macau S.A.R. is MAC, but Windows doesn't
2309+ * seem to recognize that. And Macau isn't listed in the table of
2310+ * accepted abbreviations linked above.
2311+ *
2312+ * Fortunately, "ZHM" seems to be accepted as an alias for
2313+ * "Chinese (Traditional)_Macau S.A.R..950", so we use that. Note that
2314+ * it's unlike HKG and ARE, ZHM is an alias for the whole locale name,
2315+ * not just the country part. I'm not sure where that "ZHM" comes from,
2316+ * must be some legacy naming scheme. But hey, it works.
2317+ *
2318+ * Some versions of Windows spell it "Macau", others "Macao".
2319+ */
2320+ strreplace (locale ,"Chinese (Traditional)_Macau S.A.R..950" ,"ZHM" );
2321+ strreplace (locale ,"Chinese_Macau S.A.R..950" ,"ZHM" );
2322+ strreplace (locale ,"Chinese (Traditional)_Macao S.A.R..950" ,"ZHM" );
2323+ strreplace (locale ,"Chinese_Macao S.A.R..950" ,"ZHM" );
2324+ #endif
2325+
2326+ return locale ;
2327+ }
22532328
22542329/*
22552330 * set up the locale variables
@@ -2282,25 +2357,25 @@ setlocales(void)
22822357 */
22832358
22842359if (strlen (lc_ctype )== 0 || !check_locale_name (lc_ctype ))
2285- lc_ctype = xstrdup (setlocale (LC_CTYPE ,NULL ));
2360+ lc_ctype = localemap (setlocale (LC_CTYPE ,NULL ));
22862361if (strlen (lc_collate )== 0 || !check_locale_name (lc_collate ))
2287- lc_collate = xstrdup (setlocale (LC_COLLATE ,NULL ));
2362+ lc_collate = localemap (setlocale (LC_COLLATE ,NULL ));
22882363if (strlen (lc_numeric )== 0 || !check_locale_name (lc_numeric ))
2289- lc_numeric = xstrdup (setlocale (LC_NUMERIC ,NULL ));
2364+ lc_numeric = localemap (setlocale (LC_NUMERIC ,NULL ));
22902365if (strlen (lc_time )== 0 || !check_locale_name (lc_time ))
2291- lc_time = xstrdup (setlocale (LC_TIME ,NULL ));
2366+ lc_time = localemap (setlocale (LC_TIME ,NULL ));
22922367if (strlen (lc_monetary )== 0 || !check_locale_name (lc_monetary ))
2293- lc_monetary = xstrdup (setlocale (LC_MONETARY ,NULL ));
2368+ lc_monetary = localemap (setlocale (LC_MONETARY ,NULL ));
22942369if (strlen (lc_messages )== 0 || !check_locale_name (lc_messages ))
22952370#if defined(LC_MESSAGES )&& !defined(WIN32 )
22962371{
22972372/* when available get the current locale setting */
2298- lc_messages = xstrdup (setlocale (LC_MESSAGES ,NULL ));
2373+ lc_messages = localemap (setlocale (LC_MESSAGES ,NULL ));
22992374}
23002375#else
23012376{
23022377/* when not available, get the CTYPE setting */
2303- lc_messages = xstrdup (setlocale (LC_CTYPE ,NULL ));
2378+ lc_messages = localemap (setlocale (LC_CTYPE ,NULL ));
23042379}
23052380#endif
23062381