X-Git-Url: https://git.deb.at/w?a=blobdiff_plain;f=intl%2Flangprefs.c;h=de66ad8ccb80a7bedce460b22cf80c082ea0daa6;hb=8f1adc6cc353b1b7d0460ccec6023aefdeb1829b;hp=416398ded7bd5869dc0395644e9d373d739e969a;hpb=4b8f9231090ada43f7e16987ec46ac7f45a914ec;p=pkg%2Fabook.git diff --git a/intl/langprefs.c b/intl/langprefs.c index 416398d..de66ad8 100644 --- a/intl/langprefs.c +++ b/intl/langprefs.c @@ -1,5 +1,5 @@ /* Determine the user's language preferences. - Copyright (C) 2004-2005 Free Software Foundation, Inc. + Copyright (C) 2004-2007 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published @@ -13,10 +13,11 @@ You should have received a copy of the GNU Library General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* Written by Bruno Haible . */ +/* Written by Bruno Haible . + Win32 code originally by Michele Cicciotti . */ #ifdef HAVE_CONFIG_H # include @@ -26,16 +27,207 @@ #if HAVE_CFPREFERENCESCOPYAPPVALUE # include -# include -# include -# include -# include +# include +# include +# include +# include extern void _nl_locale_name_canonicalize (char *name); #endif +#if defined _WIN32 || defined __WIN32__ +# define WIN32_NATIVE +#endif + +#ifdef WIN32_NATIVE +# define WIN32_LEAN_AND_MEAN +# include + +# ifndef MUI_LANGUAGE_NAME +# define MUI_LANGUAGE_NAME 8 +# endif +# ifndef STATUS_BUFFER_OVERFLOW +# define STATUS_BUFFER_OVERFLOW 0x80000005 +# endif + +extern void _nl_locale_name_canonicalize (char *name); +extern const char *_nl_locale_name_from_win32_LANGID (LANGID langid); +extern const char *_nl_locale_name_from_win32_LCID (LCID lcid); + +/* Get the preferences list through the MUI APIs. This works on Windows Vista + and newer. */ +static const char * +_nl_language_preferences_win32_mui (HMODULE kernel32) +{ + /* DWORD GetUserPreferredUILanguages (ULONG dwFlags, + PULONG pulNumLanguages, + PWSTR pwszLanguagesBuffer, + PULONG pcchLanguagesBuffer); */ + typedef DWORD (WINAPI *GetUserPreferredUILanguages_func) (ULONG, PULONG, PWSTR, PULONG); + GetUserPreferredUILanguages_func p_GetUserPreferredUILanguages; + + p_GetUserPreferredUILanguages = + (GetUserPreferredUILanguages_func) + GetProcAddress (kernel32, "GetUserPreferredUILanguages"); + if (p_GetUserPreferredUILanguages != NULL) + { + ULONG num_languages; + ULONG bufsize; + DWORD ret; + + bufsize = 0; + ret = p_GetUserPreferredUILanguages (MUI_LANGUAGE_NAME, + &num_languages, + NULL, &bufsize); + if (ret == 0 + && GetLastError () == STATUS_BUFFER_OVERFLOW + && bufsize > 0) + { + WCHAR *buffer = (WCHAR *) malloc (bufsize * sizeof (WCHAR)); + if (buffer != NULL) + { + ret = p_GetUserPreferredUILanguages (MUI_LANGUAGE_NAME, + &num_languages, + buffer, &bufsize); + if (ret) + { + /* Convert the list from NUL-delimited WCHAR[] Win32 locale + names to colon-delimited char[] Unix locale names. + We assume that all these locale names are in ASCII, + nonempty and contain no colons. */ + char *languages = + (char *) malloc (bufsize + num_languages * 10 + 1); + if (languages != NULL) + { + const WCHAR *p = buffer; + char *q = languages; + ULONG i; + for (i = 0; i < num_languages; i++) + { + char *q1; + char *q2; + + q1 = q; + if (i > 0) + *q++ = ':'; + q2 = q; + for (; *p != (WCHAR)'\0'; p++) + { + if ((unsigned char) *p != *p || *p == ':') + { + /* A non-ASCII character or a colon inside + the Win32 locale name! Punt. */ + q = q1; + break; + } + *q++ = (unsigned char) *p; + } + if (q == q1) + /* An unexpected Win32 locale name occurred. */ + break; + *q = '\0'; + _nl_locale_name_canonicalize (q2); + q = q2 + strlen (q2); + p++; + } + *q = '\0'; + if (q > languages) + { + free (buffer); + return languages; + } + free (languages); + } + } + free (buffer); + } + } + } + return NULL; +} + +/* Get a preference. This works on Windows ME and newer. */ +static const char * +_nl_language_preferences_win32_ME (HMODULE kernel32) +{ + /* LANGID GetUserDefaultUILanguage (void); */ + typedef LANGID (WINAPI *GetUserDefaultUILanguage_func) (void); + GetUserDefaultUILanguage_func p_GetUserDefaultUILanguage; + + p_GetUserDefaultUILanguage = + (GetUserDefaultUILanguage_func) + GetProcAddress (kernel32, "GetUserDefaultUILanguage"); + if (p_GetUserDefaultUILanguage != NULL) + return _nl_locale_name_from_win32_LANGID (p_GetUserDefaultUILanguage ()); + return NULL; +} + +/* Get a preference. This works on Windows 95 and newer. */ +static const char * +_nl_language_preferences_win32_95 () +{ + HKEY desktop_resource_locale_key; + + if (RegOpenKeyExA (HKEY_CURRENT_USER, + "Control Panel\\Desktop\\ResourceLocale", + 0, KEY_QUERY_VALUE, &desktop_resource_locale_key) + == NO_ERROR) + { + DWORD type; + char data[8 + 1]; + DWORD data_size = sizeof (data); + DWORD ret; + + ret = RegQueryValueExA (desktop_resource_locale_key, NULL, NULL, + &type, data, &data_size); + RegCloseKey (desktop_resource_locale_key); + + if (ret == NO_ERROR) + { + /* We expect a string, at most 8 bytes long, that parses as a + hexadecimal number. */ + if (type == REG_SZ + && data_size <= sizeof (data) + && (data_size < sizeof (data) + || data[sizeof (data) - 1] == '\0')) + { + LCID lcid; + char *endp; + /* Ensure it's NUL terminated. */ + if (data_size < sizeof (data)) + data[data_size] = '\0'; + /* Parse it as a hexadecimal number. */ + lcid = strtoul (data, &endp, 16); + if (endp > data && *endp == '\0') + return _nl_locale_name_from_win32_LCID (lcid); + } + } + } + return NULL; +} + +/* Get the system's preference. This can be used as a fallback. */ +static BOOL CALLBACK +ret_first_language (HMODULE h, LPCSTR type, LPCSTR name, WORD lang, LONG_PTR param) +{ + *(const char **)param = _nl_locale_name_from_win32_LANGID (lang); + return FALSE; +} +static const char * +_nl_language_preferences_win32_system (HMODULE kernel32) +{ + const char *languages = NULL; + /* Ignore the warning on mingw here. mingw has a wrong definition of the last + parameter type of ENUMRESLANGPROC. */ + EnumResourceLanguages (kernel32, RT_VERSION, MAKEINTRESOURCE (1), + ret_first_language, (LONG_PTR)&languages); + return languages; +} + +#endif + /* Determine the user's language preferences, as a colon separated list of locale names in XPG syntax - language[_territory[.codeset]][@modifier] + language[_territory][.codeset][@modifier] The result must not be freed; it is statically allocated. The LANGUAGE environment variable does not need to be considered; it is already taken into account by the caller. */ @@ -51,75 +243,108 @@ _nl_language_preferences_default (void) if (!cache_initialized) { - CFTypeRef preferences = - CFPreferencesCopyAppValue (CFSTR ("AppleLanguages"), - kCFPreferencesCurrentApplication); - if (preferences != NULL - && CFGetTypeID (preferences) == CFArrayGetTypeID ()) - { - CFArrayRef prefArray = (CFArrayRef)preferences; - int n = CFArrayGetCount (prefArray); - char buf[256]; - size_t size = 0; - int i; - - for (i = 0; i < n; i++) - { - CFTypeRef element = CFArrayGetValueAtIndex (prefArray, i); - if (element != NULL - && CFGetTypeID (element) == CFStringGetTypeID () - && CFStringGetCString ((CFStringRef)element, - buf, sizeof (buf), - kCFStringEncodingASCII)) - { - _nl_locale_name_canonicalize (buf); - size += strlen (buf) + 1; - /* Most GNU programs use msgids in English and don't ship - an en.mo message catalog. Therefore when we see "en" - in the preferences list, arrange for gettext() to - return the msgid, and ignore all further elements of - the preferences list. */ - if (strcmp (buf, "en") == 0) - break; - } - else - break; - } - if (size > 0) - { - char *languages = (char *) malloc (size); - - if (languages != NULL) - { - char *p = languages; - - for (i = 0; i < n; i++) - { - CFTypeRef element = - CFArrayGetValueAtIndex (prefArray, i); - if (element != NULL - && CFGetTypeID (element) == CFStringGetTypeID () - && CFStringGetCString ((CFStringRef)element, - buf, sizeof (buf), - kCFStringEncodingASCII)) - { - _nl_locale_name_canonicalize (buf); - strcpy (p, buf); - p += strlen (buf); - *p++ = ':'; - if (strcmp (buf, "en") == 0) - break; - } - else - break; - } - *--p = '\0'; - - cached_languages = languages; - } - } - } - cache_initialized = 1; + CFTypeRef preferences = + CFPreferencesCopyAppValue (CFSTR ("AppleLanguages"), + kCFPreferencesCurrentApplication); + if (preferences != NULL + && CFGetTypeID (preferences) == CFArrayGetTypeID ()) + { + CFArrayRef prefArray = (CFArrayRef)preferences; + int n = CFArrayGetCount (prefArray); + char buf[256]; + size_t size = 0; + int i; + + for (i = 0; i < n; i++) + { + CFTypeRef element = CFArrayGetValueAtIndex (prefArray, i); + if (element != NULL + && CFGetTypeID (element) == CFStringGetTypeID () + && CFStringGetCString ((CFStringRef)element, + buf, sizeof (buf), + kCFStringEncodingASCII)) + { + _nl_locale_name_canonicalize (buf); + size += strlen (buf) + 1; + /* Most GNU programs use msgids in English and don't ship + an en.mo message catalog. Therefore when we see "en" + in the preferences list, arrange for gettext() to + return the msgid, and ignore all further elements of + the preferences list. */ + if (strcmp (buf, "en") == 0) + break; + } + else + break; + } + if (size > 0) + { + char *languages = (char *) malloc (size); + + if (languages != NULL) + { + char *p = languages; + + for (i = 0; i < n; i++) + { + CFTypeRef element = + CFArrayGetValueAtIndex (prefArray, i); + if (element != NULL + && CFGetTypeID (element) == CFStringGetTypeID () + && CFStringGetCString ((CFStringRef)element, + buf, sizeof (buf), + kCFStringEncodingASCII)) + { + _nl_locale_name_canonicalize (buf); + strcpy (p, buf); + p += strlen (buf); + *p++ = ':'; + if (strcmp (buf, "en") == 0) + break; + } + else + break; + } + *--p = '\0'; + + cached_languages = languages; + } + } + } + cache_initialized = 1; + } + if (cached_languages != NULL) + return cached_languages; + } +#endif + +#ifdef WIN32_NATIVE + { + /* Cache the preferences list, since computing it is expensive. */ + static const char *cached_languages; + static int cache_initialized; + + /* Activate the new code only when the GETTEXT_MUI environment variable is + set, for the time being, since the new code is not well tested. */ + if (!cache_initialized && getenv ("GETTEXT_MUI") != NULL) + { + const char *languages = NULL; + HMODULE kernel32 = GetModuleHandle ("kernel32"); + + if (kernel32 != NULL) + languages = _nl_language_preferences_win32_mui (kernel32); + + if (languages == NULL && kernel32 != NULL) + languages = _nl_language_preferences_win32_ME (kernel32); + + if (languages == NULL) + languages = _nl_language_preferences_win32_95 (); + + if (languages == NULL && kernel32 != NULL) + languages = _nl_language_preferences_win32_system (kernel32); + + cached_languages = languages; + cache_initialized = 1; } if (cached_languages != NULL) return cached_languages;