From: Eli Zaretskii Date: Tue, 15 Jul 2014 19:18:48 +0000 (-0700) Subject: localcharset, localename: MS-Windows support for non-default locales X-Git-Tag: v1.0~7330 X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=b2f9ff7b19fedfe58144693c999b9a0c067cc131;p=gnulib.git localcharset, localename: MS-Windows support for non-default locales * lib/localcharset.c (locale_charset) [WINDOWS_NATIVE]: Before falling back on the default system codepage, try extracting the codepage from what 'setlocale' returns. This allows to take into account changes of the codeset due to non-default locale set by a previous call to 'setlocale'. * lib/localename.c (LOCALE_NAME_MAX_LENGTH) [WINDOWS_NATIVE]: Define if not already defined. (enum_locales_fn, get_lcid) [WINDOWS_NATIVE]: New functions. (gl_locale_name_thread) [WINDOWS_NATIVE]: Produce the current locale by calling 'setlocale', then converting the locale name into LCID by calling 'get_lcid'. This allows to take into account changes in the current locale from the default one, in contrast to GetThreadLocale. --- diff --git a/ChangeLog b/ChangeLog index e3d86b3508..94e5b84025 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2014-07-15 Eli Zaretskii + + localcharset, localename: MS-Windows support for non-default locales + * lib/localcharset.c (locale_charset) [WINDOWS_NATIVE]: Before + falling back on the default system codepage, try extracting + the codepage from what 'setlocale' returns. This allows to + take into account changes of the codeset due to non-default + locale set by a previous call to 'setlocale'. + * lib/localename.c (LOCALE_NAME_MAX_LENGTH) [WINDOWS_NATIVE]: + Define if not already defined. + (enum_locales_fn, get_lcid) [WINDOWS_NATIVE]: New functions. + (gl_locale_name_thread) [WINDOWS_NATIVE]: Produce the + current locale by calling 'setlocale', then converting the + locale name into LCID by calling 'get_lcid'. This allows to + take into account changes in the current locale from the + default one, in contrast to GetThreadLocale. + 2014-07-14 Daiki Ueno announce-gen: avoid failure when Digest::SHA is installed diff --git a/lib/localcharset.c b/lib/localcharset.c index b80ee895d2..1c17af0eac 100644 --- a/lib/localcharset.c +++ b/lib/localcharset.c @@ -34,6 +34,7 @@ #if defined _WIN32 || defined __WIN32__ # define WINDOWS_NATIVE +# include #endif #if defined __EMX__ @@ -461,14 +462,34 @@ locale_charset (void) static char buf[2 + 10 + 1]; - /* The Windows API has a function returning the locale's codepage as a - number: GetACP(). - When the output goes to a console window, it needs to be provided in - GetOEMCP() encoding if the console is using a raster font, or in - GetConsoleOutputCP() encoding if it is using a TrueType font. - But in GUI programs and for output sent to files and pipes, GetACP() - encoding is the best bet. */ - sprintf (buf, "CP%u", GetACP ()); + /* The Windows API has a function returning the locale's codepage as + a number, but the value doesn't change according to what the + 'setlocale' call specified. So we use it as a last resort, in + case the string returned by 'setlocale' doesn't specify the + codepage. */ + char *current_locale = setlocale (LC_ALL, NULL); + char *pdot; + + /* If they set different locales for different categories, + 'setlocale' will return a semi-colon separated list of locale + values. To make sure we use the correct one, we choose LC_CTYPE. */ + if (strchr (current_locale, ';')) + current_locale = setlocale (LC_CTYPE, NULL); + + pdot = strrchr (current_locale, '.'); + if (pdot) + sprintf (buf, "CP%s", pdot + 1); + else + { + /* The Windows API has a function returning the locale's codepage as a + number: GetACP(). + When the output goes to a console window, it needs to be provided in + GetOEMCP() encoding if the console is using a raster font, or in + GetConsoleOutputCP() encoding if it is using a TrueType font. + But in GUI programs and for output sent to files and pipes, GetACP() + encoding is the best bet. */ + sprintf (buf, "CP%u", GetACP ()); + } codeset = buf; #elif defined OS2 diff --git a/lib/localename.c b/lib/localename.c index 9b6f67c55c..bcef8a08ca 100644 --- a/lib/localename.c +++ b/lib/localename.c @@ -60,6 +60,7 @@ #if defined WINDOWS_NATIVE || defined __CYGWIN__ /* Native Windows or Cygwin */ # define WIN32_LEAN_AND_MEAN # include +# include /* List of language codes, sorted by value: 0x01 LANG_ARABIC 0x02 LANG_BULGARIAN @@ -1124,6 +1125,9 @@ # ifndef LOCALE_SNAME # define LOCALE_SNAME 0x5c # endif +# ifndef LOCALE_NAME_MAX_LENGTH +# define LOCALE_NAME_MAX_LENGTH 85 +# endif #endif @@ -2502,6 +2506,68 @@ gl_locale_name_from_win32_LCID (LCID lcid) return gl_locale_name_from_win32_LANGID (langid); } +# ifdef WINDOWS_NATIVE + +/* Two variables to interface between get_lcid and the EnumLocales + callback function below. */ +static LCID found_lcid; +static char lname[LC_MAX * (LOCALE_NAME_MAX_LENGTH + 1) + 1]; + +/* Callback function for EnumLocales. */ +static BOOL CALLBACK +enum_locales_fn (LPTSTR locale_num_str) +{ + char *endp; + char locval[2 * LOCALE_NAME_MAX_LENGTH + 1 + 1]; + LCID try_lcid = strtoul (locale_num_str, &endp, 16); + + if (GetLocaleInfo (try_lcid, LOCALE_SENGLANGUAGE, + locval, LOCALE_NAME_MAX_LENGTH)) + { + strcat (locval, "_"); + if (GetLocaleInfo (try_lcid, LOCALE_SENGCOUNTRY, + locval + strlen (locval), LOCALE_NAME_MAX_LENGTH)) + { + size_t locval_len = strlen (locval); + + if (strncmp (locval, lname, locval_len) == 0 + && (lname[locval_len] == '.' + || lname[locval_len] == '\0')) + { + found_lcid = try_lcid; + return FALSE; + } + } + } + return TRUE; +} + +/* Return the Locale ID (LCID) number given the locale's name, a + string, in LOCALE_NAME. This works by enumerating all the locales + supported by the system, until we find one whose name matches + LOCALE_NAME. */ +static LCID +get_lcid (const char *locale_name) +{ + /* A simple cache. */ + static LCID last_lcid; + static char last_locale[1000]; + + if (last_lcid > 0 && strcmp (locale_name, last_locale) == 0) + return last_lcid; + strncpy (lname, locale_name, sizeof (lname) - 1); + lname[sizeof (lname) - 1] = '\0'; + found_lcid = 0; + EnumSystemLocales (enum_locales_fn, LCID_SUPPORTED); + if (found_lcid > 0) + { + last_lcid = found_lcid; + strcpy (last_locale, locale_name); + } + return found_lcid; +} + +# endif #endif @@ -2660,6 +2726,26 @@ gl_locale_name_thread (int category, const char *categoryname) const char *name = gl_locale_name_thread_unsafe (category, categoryname); if (name != NULL) return struniq (name); +#elif defined WINDOWS_NATIVE + if (LC_MIN <= category && category <= LC_MAX) + { + char *locname = setlocale (category, NULL); + + /* If CATEGORY is LC_ALL, the result might be a semi-colon + separated list of locales. We need only one, so we take the + one corresponding to LC_CTYPE, as the most important for + character translations. */ + if (strchr (locname, ';')) + locname = setlocale (LC_CTYPE, NULL); + + /* Convert locale name to LCID. We don't want to use + LocaleNameToLCID because (a) it is only available since Vista, + and (b) it doesn't accept locale names returned by 'setlocale'. */ + LCID lcid = get_lcid (locname); + + if (lcid > 0) + return gl_locale_name_from_win32_LCID (lcid); + } #endif return NULL; }