[PATCH] kernel32: Overhaul the locale determination on the Mac.
Ken Thomases
ken at codeweavers.com
Wed Jun 15 14:34:04 CDT 2016
The C library locale is inadequate for conveying the Mac user settings between
LOCALE_Init() and setup_unix_locales(). The set of locales supported by the C
library can't express the combinations allowed in user settings.
Setting LANG to a user-settings-derived locale when there's no corresponding C
library locale is actually worse than leaving it unset. It will prevent the
C library from honoring the LC_* variables (other than LC_ALL). That's why
Terminal.app won't set LANG in that case, it just sets LC_CTYPE=UTF-8. This
commit makes Wine follow similar logic in not setting LANG if the C library
doesn't support the Mac user settings.
Rather, it uses a wrapper around setlocale() to query the locale. That wrapper
returns a value representing the Mac user settings if the C library comes up
empty. It also has logic to handle Terminal's setting LC_CTYPE=UTF-8, since
parse_locale_name() can't handle that properly.
Signed-off-by: Ken Thomases <ken at codeweavers.com>
---
dlls/kernel32/locale.c | 206 +++++++++++++++++++++++++++++++++++--------------
1 file changed, 150 insertions(+), 56 deletions(-)
diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c
index 66ab213..39769ff 100644
--- a/dlls/kernel32/locale.c
+++ b/dlls/kernel32/locale.c
@@ -911,6 +911,139 @@ void LOCALE_InitRegistry(void)
}
+#ifdef __APPLE__
+/***********************************************************************
+ * get_mac_locale
+ *
+ * Return a locale identifer string reflecting the Mac locale, in a form
+ * that parse_locale_name() will understand. So, strip out unusual
+ * things like script, variant, etc. Or, rather, just construct it as
+ * <lang>[_<country>].UTF-8.
+ */
+static const char* get_mac_locale(void)
+{
+ static char mac_locale[50];
+
+ if (!mac_locale[0])
+ {
+ CFLocaleRef locale = CFLocaleCopyCurrent();
+ CFStringRef lang = CFLocaleGetValue( locale, kCFLocaleLanguageCode );
+ CFStringRef country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
+ CFStringRef locale_string;
+
+ if (country)
+ locale_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"), lang, country);
+ else
+ locale_string = CFStringCreateCopy(NULL, lang);
+
+ CFStringGetCString(locale_string, mac_locale, sizeof(mac_locale), kCFStringEncodingUTF8);
+ strcat(mac_locale, ".UTF-8");
+
+ CFRelease(locale);
+ CFRelease(locale_string);
+ }
+
+ return mac_locale;
+}
+
+
+/***********************************************************************
+ * has_env
+ */
+static BOOL has_env(const char* name)
+{
+ const char* value = getenv( name );
+ return value && value[0];
+}
+#endif
+
+
+/***********************************************************************
+ * get_locale
+ *
+ * Get the locale identifier for a given category. On most platforms,
+ * this is just a thin wrapper around setlocale(). On OS X, though, it
+ * is common for the Mac locale settings to not be supported by the C
+ * library. So, we sometimes override the result with the Mac locale.
+ */
+static const char* get_locale(int category, const char* category_name)
+{
+ const char* ret = setlocale(category, NULL);
+
+#ifdef __APPLE__
+ /* If LC_ALL is set, respect it as a user override.
+ If LC_* is set, respect it as a user override, except if it's LC_CTYPE
+ and equal to UTF-8. That's because, when the Mac locale isn't supported
+ by the C library, Terminal.app sets LC_CTYPE=UTF-8 and doesn't set LANG.
+ parse_locale_name() doesn't handle that properly, so we override that
+ with the Mac locale (which uses UTF-8 for the charset, anyway).
+ Otherwise:
+ For LC_MESSAGES, we override the C library because the user language
+ setting is separate from the locale setting on which LANG was based.
+ If the C library didn't get anything better from LANG than C or POSIX,
+ override that. That probably means the Mac locale isn't supported by
+ the C library. */
+ if (!has_env( "LC_ALL" ) &&
+ ((category == LC_CTYPE && !strcmp( ret, "UTF-8" )) ||
+ (!has_env( category_name ) &&
+ (category == LC_MESSAGES || !strcmp( ret, "C" ) || !strcmp( ret, "POSIX" )))))
+ {
+ const char* override = get_mac_locale();
+
+ if (category == LC_MESSAGES)
+ {
+ /* Retrieve the preferred language as chosen in System Preferences. */
+ static char messages_locale[50];
+
+ if (!messages_locale[0])
+ {
+ CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
+ if (preferred_langs && CFArrayGetCount( preferred_langs ))
+ {
+ CFStringRef preferred_lang = CFArrayGetValueAtIndex( preferred_langs, 0 );
+ CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier( NULL, preferred_lang );
+ if (components)
+ {
+ CFStringRef lang = CFDictionaryGetValue( components, kCFLocaleLanguageCode );
+ CFStringRef country = CFDictionaryGetValue( components, kCFLocaleCountryCode );
+ CFLocaleRef locale = NULL;
+ CFStringRef locale_string;
+
+ if (!country)
+ {
+ locale = CFLocaleCopyCurrent();
+ country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
+ }
+
+ if (country)
+ locale_string = CFStringCreateWithFormat( NULL, NULL, CFSTR("%@_%@"), lang, country );
+ else
+ locale_string = CFStringCreateCopy( NULL, lang );
+ CFStringGetCString( locale_string, messages_locale, sizeof(messages_locale), kCFStringEncodingUTF8 );
+ strcat( messages_locale, ".UTF-8" );
+
+ CFRelease( locale_string );
+ if (locale) CFRelease( locale );
+ CFRelease( components );
+ }
+ }
+ if (preferred_langs)
+ CFRelease( preferred_langs );
+ }
+
+ if (messages_locale[0])
+ override = messages_locale;
+ }
+
+ TRACE( "%s is %s; overriding with %s\n", category_name, debugstr_a(ret), debugstr_a(override) );
+ ret = override;
+ }
+#endif
+
+ return ret;
+}
+
+
/***********************************************************************
* setup_unix_locales
*/
@@ -918,10 +1051,10 @@ static UINT setup_unix_locales(void)
{
struct locale_name locale_name;
WCHAR buffer[128], ctype_buff[128];
- char *locale;
+ const char *locale;
UINT unix_cp = 0;
- if ((locale = setlocale( LC_CTYPE, NULL )))
+ if ((locale = get_locale( LC_CTYPE, "LC_CTYPE" )))
{
strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
parse_locale_name( ctype_buff, &locale_name );
@@ -935,7 +1068,7 @@ static UINT setup_unix_locales(void)
locale_name.lcid, locale_name.matches, debugstr_a(locale) );
#define GET_UNIX_LOCALE(cat) do \
- if ((locale = setlocale( cat, NULL ))) \
+ if ((locale = get_locale( cat, #cat ))) \
{ \
strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
@@ -3612,71 +3745,32 @@ void LOCALE_Init(void)
UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
+ setlocale( LC_ALL, "" );
+
#ifdef __APPLE__
/* MacOS doesn't set the locale environment variables so we have to do it ourselves */
- char user_locale[50];
-
- CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
- CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
- CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
- CFStringRef user_locale_string_ref;
-
- if (user_locale_country_ref)
- {
- user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
- user_locale_lang_ref, user_locale_country_ref);
- }
- else
+ if (!has_env("LANG"))
{
- user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
+ const char* mac_locale = get_mac_locale();
+
+ setenv( "LANG", mac_locale, 1 );
+ if (setlocale( LC_ALL, "" ))
+ TRACE( "setting LANG to '%s'\n", mac_locale );
+ else
+ {
+ /* no C library locale matching Mac locale; don't pass garbage to children */
+ unsetenv("LANG");
+ TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
+ }
}
-
- CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
- strcat(user_locale, ".UTF-8");
-
- setenv( "LANG", user_locale, 0 );
- TRACE( "setting locale to '%s'\n", user_locale );
#endif /* __APPLE__ */
- setlocale( LC_ALL, "" );
-
unix_cp = setup_unix_locales();
if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
#ifdef __APPLE__
if (!unix_cp)
unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
-
- /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
- if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
- {
- /* Retrieve the preferred language as chosen in System Preferences. */
- /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
- leave things be. */
- CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
- CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
- CFStringRef user_language_string_ref;
- if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
- (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
- !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
- !CFEqual(user_language_string_ref, canonical_lang_string_ref))
- {
- struct locale_name locale_name;
- WCHAR buffer[128];
- CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
- strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
- parse_locale_name( buffer, &locale_name );
- lcid_LC_MESSAGES = locale_name.lcid;
- TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
- }
- if (preferred_langs)
- CFRelease( preferred_langs );
- if (canonical_lang_string_ref)
- CFRelease( canonical_lang_string_ref );
- }
-
- CFRelease( user_locale_ref );
- CFRelease( user_locale_string_ref );
#endif
NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
--
2.8.2
More information about the wine-patches
mailing list