Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d5a7bf8

Browse files
committed
setlocale() on Windows doesn't work correctly if the locale name contains
apostrophes or dots. There isn't much hope of Microsoft fixing it any time soon, it's been like that for ages, so we better work around it. So, map a few common Windows locale names known to cause problems to aliases that work.
1 parent 1f943dc commit d5a7bf8

File tree

1 file changed

+82
-7
lines changed

1 file changed

+82
-7
lines changed

src/bin/initdb/initdb.c

+82-7
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ static int locale_date_order(const char *locale);
185185
static bool check_locale_name(const char *locale);
186186
static bool check_locale_encoding(const char *locale, int encoding);
187187
static void setlocales(void);
188+
static void strreplace(char *str, char *needle, char *replacement);
189+
static char *localemap(char *locale);
188190
static void usage(const char *progname);
189191

190192
#ifdef WIN32
@@ -2250,6 +2252,79 @@ check_locale_encoding(const char *locale, int user_enc)
22502252
return 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

22842359
if (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));
22862361
if (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));
22882363
if (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));
22902365
if (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));
22922367
if (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));
22942369
if (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

0 commit comments

Comments
 (0)