/* set the locale with setlocale, to see if it accepts it. */
ret = (setlocale(category, value) != NULL);
- setlocale(category, save); /* assume this won't fail */
+ /* restore old value. */
+ if (!setlocale(category, save))
+ elog(WARNING, "failed to restore old locale");
pfree(save);
return ret;
/* Try to restore internal settings */
if (save_lc_monetary)
{
- setlocale(LC_MONETARY, save_lc_monetary);
+ if (!setlocale(LC_MONETARY, save_lc_monetary))
+ elog(WARNING, "failed to restore old locale");
pfree(save_lc_monetary);
}
if (save_lc_numeric)
{
- setlocale(LC_NUMERIC, save_lc_numeric);
+ if (!setlocale(LC_NUMERIC, save_lc_numeric))
+ elog(WARNING, "failed to restore old locale");
pfree(save_lc_numeric);
}
/* Try to restore internal ctype settings */
if (save_lc_ctype)
{
- setlocale(LC_CTYPE, save_lc_ctype);
+ if (!setlocale(LC_CTYPE, save_lc_ctype))
+ elog(WARNING, "failed to restore old locale");
pfree(save_lc_ctype);
}
#endif
/* try to restore internal settings */
if (save_lc_time)
{
- setlocale(LC_TIME, save_lc_time);
+ if (!setlocale(LC_TIME, save_lc_time))
+ elog(WARNING, "failed to restore old locale");
pfree(save_lc_time);
}
/* try to restore internal ctype settings */
if (save_lc_ctype)
{
- setlocale(LC_CTYPE, save_lc_ctype);
+ if (!setlocale(LC_CTYPE, save_lc_ctype))
+ elog(WARNING, "failed to restore old locale");
pfree(save_lc_ctype);
}
#endif
static bool check_locale_name(const char *locale);
static bool check_locale_encoding(const char *locale, int encoding);
static void setlocales(void);
-static char *localemap(char *locale);
static void usage(const char *progname);
#ifdef WIN32
}
#endif /* WIN32 */
-/*
- * Windows has a problem with locale names that have a dot in the country
- * name. For example:
- *
- * "Chinese (Traditional)_Hong Kong S.A.R..950"
- *
- * For some reason, setlocale() doesn't accept that. Fortunately, Windows'
- * setlocale() accepts various alternative names for such countries, so we
- * map the full country names to accepted aliases.
- *
- * The returned string is always malloc'd - if no mapping is done it is
- * just a malloc'd copy of the original.
- */
-static char *
-localemap(char *locale)
-{
- locale = xstrdup(locale);
-
-#ifdef WIN32
-
- /*
- * Map the full country name to an abbreviation that setlocale() accepts.
- *
- * "HKG" is listed here:
- * http://msdn.microsoft.com/en-us/library/cdax410z%28v=vs.71%29.aspx
- * (Country/Region Strings).
- *
- * "ARE" is the ISO-3166 three-letter code for U.A.E. It is not on the
- * above list, but seems to work anyway.
- */
- strreplace(locale, "Hong Kong S.A.R.", "HKG");
- strreplace(locale, "U.A.E.", "ARE");
-
- /*
- * The ISO-3166 country code for Macau S.A.R. is MAC, but Windows doesn't
- * seem to recognize that. And Macau isn't listed in the table of accepted
- * abbreviations linked above.
- *
- * Fortunately, "ZHM" seems to be accepted as an alias for "Chinese
- * (Traditional)_Macau S.A.R..950", so we use that. Note that it's unlike
- * HKG and ARE, ZHM is an alias for the whole locale name, not just the
- * country part. I'm not sure where that "ZHM" comes from, must be some
- * legacy naming scheme. But hey, it works.
- *
- * Some versions of Windows spell it "Macau", others "Macao".
- */
- strreplace(locale, "Chinese (Traditional)_Macau S.A.R..950", "ZHM");
- strreplace(locale, "Chinese_Macau S.A.R..950", "ZHM");
- strreplace(locale, "Chinese (Traditional)_Macao S.A.R..950", "ZHM");
- strreplace(locale, "Chinese_Macao S.A.R..950", "ZHM");
-#endif /* WIN32 */
-
- return locale;
-}
-
/*
* set up the locale variables
*
*/
if (strlen(lc_ctype) == 0 || !check_locale_name(lc_ctype))
- lc_ctype = localemap(setlocale(LC_CTYPE, NULL));
+ lc_ctype = xstrdup(setlocale(LC_CTYPE, NULL));
if (strlen(lc_collate) == 0 || !check_locale_name(lc_collate))
- lc_collate = localemap(setlocale(LC_COLLATE, NULL));
+ lc_collate = xstrdup(setlocale(LC_COLLATE, NULL));
if (strlen(lc_numeric) == 0 || !check_locale_name(lc_numeric))
- lc_numeric = localemap(setlocale(LC_NUMERIC, NULL));
+ lc_numeric = xstrdup(setlocale(LC_NUMERIC, NULL));
if (strlen(lc_time) == 0 || !check_locale_name(lc_time))
- lc_time = localemap(setlocale(LC_TIME, NULL));
+ lc_time = xstrdup(setlocale(LC_TIME, NULL));
if (strlen(lc_monetary) == 0 || !check_locale_name(lc_monetary))
- lc_monetary = localemap(setlocale(LC_MONETARY, NULL));
+ lc_monetary = xstrdup(setlocale(LC_MONETARY, NULL));
if (strlen(lc_messages) == 0 || !check_locale_name(lc_messages))
#if defined(LC_MESSAGES) && !defined(WIN32)
{
/* when available get the current locale setting */
- lc_messages = localemap(setlocale(LC_MESSAGES, NULL));
+ lc_messages = xstrdup(setlocale(LC_MESSAGES, NULL));
}
#else
{
/* when not available, get the CTYPE setting */
- lc_messages = localemap(setlocale(LC_CTYPE, NULL));
+ lc_messages = xstrdup(setlocale(LC_CTYPE, NULL));
}
#endif
#endif
#endif /* USE_REPL_SNPRINTF */
+#if defined(WIN32)
/*
* Versions of libintl >= 0.18? try to replace setlocale() with a macro
* to their own versions. Remove the macro, if it exists, because it
* ends up calling the wrong version when the backend and libintl use
* different versions of msvcrt.
*/
-#if defined(setlocale) && defined(WIN32)
+#if defined(setlocale)
#undef setlocale
#endif
+/*
+ * Define our own wrapper macro around setlocale() to work around bugs in
+ * Windows' native setlocale() function.
+ */
+extern char *pgwin32_setlocale(int category, const char *locale);
+
+#define setlocale(a,b) pgwin32_setlocale(a,b)
+
+#endif /* WIN32 */
+
/* Portable prompt handling */
extern char *simple_prompt(const char *prompt, int maxlen, bool echo);
}
#endif /* (HAVE_LANGINFO_H && CODESET) || WIN32 */
+
+#ifdef WIN32
+/*
+ * Windows has a problem with locale names that have a dot in the country
+ * name. For example:
+ *
+ * "Chinese (Traditional)_Hong Kong S.A.R..950"
+ *
+ * For some reason, setlocale() doesn't accept that. Fortunately, Windows'
+ * setlocale() accepts various alternative names for such countries, so we
+ * provide a wrapper setlocale() function that maps the troublemaking locale
+ * names to accepted aliases.
+ */
+
+#undef setlocale
+
+struct locale_map
+{
+ const char *locale_name_part; /* string in locale name to replace */
+ const char *replacement; /* string to replace it with */
+};
+
+static const struct locale_map locale_map_list[] = {
+
+ /*
+ * "HKG" is listed here:
+ * http://msdn.microsoft.com/en-us/library/cdax410z%28v=vs.71%29.aspx
+ * (Country/Region Strings).
+ *
+ * "ARE" is the ISO-3166 three-letter code for U.A.E. It is not on the
+ * above list, but seems to work anyway.
+ */
+ { "Hong Kong S.A.R.", "HKG" },
+ { "U.A.E.", "ARE" },
+
+ /*
+ * The ISO-3166 country code for Macau S.A.R. is MAC, but Windows doesn't
+ * seem to recognize that. And Macau isn't listed in the table of
+ * accepted abbreviations linked above. Fortunately, "ZHM" seems to be
+ * accepted as an alias for "Chinese (Traditional)_Macau S.A.R..950". I'm
+ * not sure where "ZHM" comes from, must be some legacy naming scheme. But
+ * hey, it works.
+ *
+ * Note that unlike HKG and ARE, ZHM is an alias for the *whole* locale
+ * name, not just the country part.
+ *
+ * Some versions of Windows spell it "Macau", others "Macao".
+ */
+ { "Chinese (Traditional)_Macau S.A.R..950", "ZHM" },
+ { "Chinese_Macau S.A.R..950", "ZHM" },
+ { "Chinese (Traditional)_Macao S.A.R..950", "ZHM" },
+ { "Chinese_Macao S.A.R..950", "ZHM" }
+};
+
+char *
+pgwin32_setlocale(int category, const char *locale)
+{
+ char *result;
+ char *alias;
+ int i;
+
+ if (locale == NULL)
+ return setlocale(category, locale);
+
+ /* Check if the locale name matches any of the problematic ones. */
+ alias = NULL;
+ for (i = 0; i < lengthof(locale_map_list); i++)
+ {
+ const char *needle = locale_map_list[i].locale_name_part;
+ const char *replacement = locale_map_list[i].replacement;
+ char *match;
+
+ match = strstr(locale, needle);
+ if (match != NULL)
+ {
+ /* Found a match. Replace the matched string. */
+ int matchpos = match - locale;
+ int replacementlen = strlen(replacement);
+ char *rest = match + strlen(needle);
+ int restlen = strlen(rest);
+
+ alias = malloc(matchpos + replacementlen + restlen + 1);
+ if (!alias)
+ return NULL;
+
+ memcpy(&alias[0], &locale[0], matchpos);
+ memcpy(&alias[matchpos], replacement, replacementlen);
+ memcpy(&alias[matchpos + replacementlen], rest, restlen + 1); /* includes null terminator */
+
+ break;
+ }
+ }
+
+ /* Call the real setlocale() function */
+ if (alias)
+ {
+ result = setlocale(category, alias);
+ free(alias);
+ }
+ else
+ result = setlocale(category, locale);
+
+ return result;
+}
+
+#endif /* WIN32 */