Back to index

courier  0.68.2
langprefs.c
Go to the documentation of this file.
00001 /* Determine the user's language preferences.
00002    Copyright (C) 2004-2007 Free Software Foundation, Inc.
00003 
00004    This program is free software; you can redistribute it and/or modify it
00005    under the terms of the GNU Library General Public License as published
00006    by the Free Software Foundation; either version 2, or (at your option)
00007    any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public
00015    License along with this program; if not, write to the Free Software
00016    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
00017    USA.  */
00018 
00019 /* Written by Bruno Haible <bruno@clisp.org>.
00020    Win32 code originally by Michele Cicciotti <hackbunny@reactos.com>.  */
00021 
00022 #ifdef HAVE_CONFIG_H
00023 # include <config.h>
00024 #endif
00025 
00026 #include <stdlib.h>
00027 
00028 #if HAVE_CFPREFERENCESCOPYAPPVALUE
00029 # include <string.h>
00030 # include <CoreFoundation/CFPreferences.h>
00031 # include <CoreFoundation/CFPropertyList.h>
00032 # include <CoreFoundation/CFArray.h>
00033 # include <CoreFoundation/CFString.h>
00034 extern void _nl_locale_name_canonicalize (char *name);
00035 #endif
00036 
00037 #if defined _WIN32 || defined __WIN32__
00038 # define WIN32_NATIVE
00039 #endif
00040 
00041 #ifdef WIN32_NATIVE
00042 # define WIN32_LEAN_AND_MEAN
00043 # include <windows.h>
00044 
00045 # ifndef MUI_LANGUAGE_NAME
00046 # define MUI_LANGUAGE_NAME 8
00047 # endif
00048 # ifndef STATUS_BUFFER_OVERFLOW
00049 # define STATUS_BUFFER_OVERFLOW 0x80000005
00050 # endif
00051 
00052 extern void _nl_locale_name_canonicalize (char *name);
00053 extern const char *_nl_locale_name_from_win32_LANGID (LANGID langid);
00054 extern const char *_nl_locale_name_from_win32_LCID (LCID lcid);
00055 
00056 /* Get the preferences list through the MUI APIs. This works on Windows Vista
00057    and newer.  */
00058 static const char *
00059 _nl_language_preferences_win32_mui (HMODULE kernel32)
00060 {
00061   /* DWORD GetUserPreferredUILanguages (ULONG dwFlags,
00062                                         PULONG pulNumLanguages,
00063                                         PWSTR pwszLanguagesBuffer,
00064                                         PULONG pcchLanguagesBuffer);  */
00065   typedef DWORD (WINAPI *GetUserPreferredUILanguages_func) (ULONG, PULONG, PWSTR, PULONG);
00066   GetUserPreferredUILanguages_func p_GetUserPreferredUILanguages;
00067 
00068   p_GetUserPreferredUILanguages =
00069    (GetUserPreferredUILanguages_func)
00070    GetProcAddress (kernel32, "GetUserPreferredUILanguages");
00071   if (p_GetUserPreferredUILanguages != NULL)
00072     {
00073       ULONG num_languages;
00074       ULONG bufsize;
00075       DWORD ret;
00076 
00077       bufsize = 0;
00078       ret = p_GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,
00079                                            &num_languages,
00080                                            NULL, &bufsize);
00081       if (ret == 0
00082           && GetLastError () == STATUS_BUFFER_OVERFLOW
00083           && bufsize > 0)
00084         {
00085           WCHAR *buffer = (WCHAR *) malloc (bufsize * sizeof (WCHAR));
00086           if (buffer != NULL)
00087             {
00088               ret = p_GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,
00089                                                    &num_languages,
00090                                                    buffer, &bufsize);
00091               if (ret)
00092                 {
00093                   /* Convert the list from NUL-delimited WCHAR[] Win32 locale
00094                      names to colon-delimited char[] Unix locale names.
00095                      We assume that all these locale names are in ASCII,
00096                      nonempty and contain no colons.  */
00097                   char *languages =
00098                     (char *) malloc (bufsize + num_languages * 10 + 1);
00099                   if (languages != NULL)
00100                     {
00101                       const WCHAR *p = buffer;
00102                       char *q = languages;
00103                       ULONG i;
00104                       for (i = 0; i < num_languages; i++)
00105                         {
00106                           char *q1;
00107                           char *q2;
00108 
00109                           q1 = q;
00110                           if (i > 0)
00111                             *q++ = ':';
00112                           q2 = q;
00113                           for (; *p != (WCHAR)'\0'; p++)
00114                             {
00115                               if ((unsigned char) *p != *p || *p == ':')
00116                                 {
00117                                   /* A non-ASCII character or a colon inside
00118                                      the Win32 locale name! Punt.  */
00119                                   q = q1;
00120                                   break;
00121                                 }
00122                               *q++ = (unsigned char) *p;
00123                             }
00124                           if (q == q1)
00125                             /* An unexpected Win32 locale name occurred.  */
00126                             break;
00127                           *q = '\0';
00128                           _nl_locale_name_canonicalize (q2);
00129                           q = q2 + strlen (q2);
00130                           p++;
00131                         }
00132                       *q = '\0';
00133                       if (q > languages)
00134                         {
00135                           free (buffer);
00136                           return languages;
00137                         }
00138                       free (languages);
00139                     }
00140                 }
00141               free (buffer);
00142             }
00143         }
00144     }
00145   return NULL;
00146 }
00147 
00148 /* Get a preference.  This works on Windows ME and newer.  */
00149 static const char *
00150 _nl_language_preferences_win32_ME (HMODULE kernel32)
00151 {
00152   /* LANGID GetUserDefaultUILanguage (void);  */
00153   typedef LANGID (WINAPI *GetUserDefaultUILanguage_func) (void);
00154   GetUserDefaultUILanguage_func p_GetUserDefaultUILanguage;
00155 
00156   p_GetUserDefaultUILanguage =
00157    (GetUserDefaultUILanguage_func)
00158    GetProcAddress (kernel32, "GetUserDefaultUILanguage");
00159   if (p_GetUserDefaultUILanguage != NULL)
00160     return _nl_locale_name_from_win32_LANGID (p_GetUserDefaultUILanguage ());
00161   return NULL;
00162 }
00163 
00164 /* Get a preference.  This works on Windows 95 and newer.  */
00165 static const char *
00166 _nl_language_preferences_win32_95 ()
00167 {
00168   HKEY desktop_resource_locale_key;
00169 
00170   if (RegOpenKeyExA (HKEY_CURRENT_USER,
00171                      "Control Panel\\Desktop\\ResourceLocale",
00172                      0, KEY_QUERY_VALUE, &desktop_resource_locale_key)
00173       == NO_ERROR)
00174     {
00175       DWORD type;
00176       char data[8 + 1];
00177       DWORD data_size = sizeof (data);
00178       DWORD ret;
00179 
00180       ret = RegQueryValueExA (desktop_resource_locale_key, NULL, NULL,
00181                               &type, data, &data_size);
00182       RegCloseKey (desktop_resource_locale_key);
00183 
00184       if (ret == NO_ERROR)
00185         {
00186           /* We expect a string, at most 8 bytes long, that parses as a
00187              hexadecimal number.  */
00188           if (type == REG_SZ
00189               && data_size <= sizeof (data)
00190               && (data_size < sizeof (data)
00191                   || data[sizeof (data) - 1] == '\0'))
00192             {
00193               LCID lcid;
00194               char *endp;
00195               /* Ensure it's NUL terminated.  */
00196               if (data_size < sizeof (data))
00197                 data[data_size] = '\0';
00198               /* Parse it as a hexadecimal number.  */
00199               lcid = strtoul (data, &endp, 16);
00200               if (endp > data && *endp == '\0')
00201                 return _nl_locale_name_from_win32_LCID (lcid);
00202             }
00203         }
00204     }
00205   return NULL;
00206 }
00207 
00208 /* Get the system's preference.  This can be used as a fallback.  */
00209 static BOOL CALLBACK
00210 ret_first_language (HMODULE h, LPCSTR type, LPCSTR name, WORD lang, LONG_PTR param)
00211 {
00212   *(const char **)param = _nl_locale_name_from_win32_LANGID (lang);
00213   return FALSE;
00214 }
00215 static const char *
00216 _nl_language_preferences_win32_system (HMODULE kernel32)
00217 {
00218   const char *languages = NULL;
00219   /* Ignore the warning on mingw here. mingw has a wrong definition of the last
00220      parameter type of ENUMRESLANGPROC.  */
00221   EnumResourceLanguages (kernel32, RT_VERSION, MAKEINTRESOURCE (1),
00222                          ret_first_language, (LONG_PTR)&languages);
00223   return languages;
00224 }
00225 
00226 #endif
00227 
00228 /* Determine the user's language preferences, as a colon separated list of
00229    locale names in XPG syntax
00230      language[_territory][.codeset][@modifier]
00231    The result must not be freed; it is statically allocated.
00232    The LANGUAGE environment variable does not need to be considered; it is
00233    already taken into account by the caller.  */
00234 
00235 const char *
00236 _nl_language_preferences_default (void)
00237 {
00238 #if HAVE_CFPREFERENCESCOPYAPPVALUE /* MacOS X 10.2 or newer */
00239   {
00240     /* Cache the preferences list, since CoreFoundation calls are expensive.  */
00241     static const char *cached_languages;
00242     static int cache_initialized;
00243 
00244     if (!cache_initialized)
00245       {
00246         CFTypeRef preferences =
00247           CFPreferencesCopyAppValue (CFSTR ("AppleLanguages"),
00248                                      kCFPreferencesCurrentApplication);
00249         if (preferences != NULL
00250             && CFGetTypeID (preferences) == CFArrayGetTypeID ())
00251           {
00252             CFArrayRef prefArray = (CFArrayRef)preferences;
00253             int n = CFArrayGetCount (prefArray);
00254             char buf[256];
00255             size_t size = 0;
00256             int i;
00257 
00258             for (i = 0; i < n; i++)
00259               {
00260                 CFTypeRef element = CFArrayGetValueAtIndex (prefArray, i);
00261                 if (element != NULL
00262                     && CFGetTypeID (element) == CFStringGetTypeID ()
00263                     && CFStringGetCString ((CFStringRef)element,
00264                                            buf, sizeof (buf),
00265                                            kCFStringEncodingASCII))
00266                   {
00267                     _nl_locale_name_canonicalize (buf);
00268                     size += strlen (buf) + 1;
00269                     /* Most GNU programs use msgids in English and don't ship
00270                        an en.mo message catalog.  Therefore when we see "en"
00271                        in the preferences list, arrange for gettext() to
00272                        return the msgid, and ignore all further elements of
00273                        the preferences list.  */
00274                     if (strcmp (buf, "en") == 0)
00275                       break;
00276                   }
00277                 else
00278                   break;
00279               }
00280             if (size > 0)
00281               {
00282                 char *languages = (char *) malloc (size);
00283 
00284                 if (languages != NULL)
00285                   {
00286                     char *p = languages;
00287 
00288                     for (i = 0; i < n; i++)
00289                       {
00290                         CFTypeRef element =
00291                           CFArrayGetValueAtIndex (prefArray, i);
00292                         if (element != NULL
00293                             && CFGetTypeID (element) == CFStringGetTypeID ()
00294                             && CFStringGetCString ((CFStringRef)element,
00295                                                    buf, sizeof (buf),
00296                                                    kCFStringEncodingASCII))
00297                           {
00298                             _nl_locale_name_canonicalize (buf);
00299                             strcpy (p, buf);
00300                             p += strlen (buf);
00301                             *p++ = ':';
00302                             if (strcmp (buf, "en") == 0)
00303                               break;
00304                           }
00305                         else
00306                           break;
00307                       }
00308                     *--p = '\0';
00309 
00310                     cached_languages = languages;
00311                   }
00312               }
00313           }
00314         cache_initialized = 1;
00315       }
00316     if (cached_languages != NULL)
00317       return cached_languages;
00318   }
00319 #endif
00320 
00321 #ifdef WIN32_NATIVE
00322   {
00323     /* Cache the preferences list, since computing it is expensive.  */
00324     static const char *cached_languages;
00325     static int cache_initialized;
00326 
00327     /* Activate the new code only when the GETTEXT_MUI environment variable is
00328        set, for the time being, since the new code is not well tested.  */
00329     if (!cache_initialized && getenv ("GETTEXT_MUI") != NULL)
00330       {
00331         const char *languages = NULL;
00332         HMODULE kernel32 = GetModuleHandle ("kernel32");
00333 
00334         if (kernel32 != NULL)
00335           languages = _nl_language_preferences_win32_mui (kernel32);
00336 
00337         if (languages == NULL && kernel32 != NULL)
00338           languages = _nl_language_preferences_win32_ME (kernel32);
00339 
00340         if (languages == NULL)
00341           languages = _nl_language_preferences_win32_95 ();
00342 
00343         if (languages == NULL && kernel32 != NULL)
00344           languages = _nl_language_preferences_win32_system (kernel32);
00345 
00346         cached_languages = languages;
00347         cache_initialized = 1;
00348       }
00349     if (cached_languages != NULL)
00350       return cached_languages;
00351   }
00352 #endif
00353 
00354   return NULL;
00355 }