]> git.deb.at Git - pkg/abook.git/blob - intl/langprefs.c
autotools update: 1/2: main files
[pkg/abook.git] / intl / langprefs.c
1 /* Determine the user's language preferences.
2    Copyright (C) 2004-2007 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify it
5    under the terms of the GNU Library General Public License as published
6    by the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
17    USA.  */
18
19 /* Written by Bruno Haible <bruno@clisp.org>.
20    Win32 code originally by Michele Cicciotti <hackbunny@reactos.com>.  */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <stdlib.h>
27
28 #if HAVE_CFPREFERENCESCOPYAPPVALUE
29 # include <string.h>
30 # include <CoreFoundation/CFPreferences.h>
31 # include <CoreFoundation/CFPropertyList.h>
32 # include <CoreFoundation/CFArray.h>
33 # include <CoreFoundation/CFString.h>
34 extern void _nl_locale_name_canonicalize (char *name);
35 #endif
36
37 #if defined _WIN32 || defined __WIN32__
38 # define WIN32_NATIVE
39 #endif
40
41 #ifdef WIN32_NATIVE
42 # define WIN32_LEAN_AND_MEAN
43 # include <windows.h>
44
45 # ifndef MUI_LANGUAGE_NAME
46 # define MUI_LANGUAGE_NAME 8
47 # endif
48 # ifndef STATUS_BUFFER_OVERFLOW
49 # define STATUS_BUFFER_OVERFLOW 0x80000005
50 # endif
51
52 extern void _nl_locale_name_canonicalize (char *name);
53 extern const char *_nl_locale_name_from_win32_LANGID (LANGID langid);
54 extern const char *_nl_locale_name_from_win32_LCID (LCID lcid);
55
56 /* Get the preferences list through the MUI APIs. This works on Windows Vista
57    and newer.  */
58 static const char *
59 _nl_language_preferences_win32_mui (HMODULE kernel32)
60 {
61   /* DWORD GetUserPreferredUILanguages (ULONG dwFlags,
62                                         PULONG pulNumLanguages,
63                                         PWSTR pwszLanguagesBuffer,
64                                         PULONG pcchLanguagesBuffer);  */
65   typedef DWORD (WINAPI *GetUserPreferredUILanguages_func) (ULONG, PULONG, PWSTR, PULONG);
66   GetUserPreferredUILanguages_func p_GetUserPreferredUILanguages;
67
68   p_GetUserPreferredUILanguages =
69    (GetUserPreferredUILanguages_func)
70    GetProcAddress (kernel32, "GetUserPreferredUILanguages");
71   if (p_GetUserPreferredUILanguages != NULL)
72     {
73       ULONG num_languages;
74       ULONG bufsize;
75       DWORD ret;
76
77       bufsize = 0;
78       ret = p_GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,
79                                            &num_languages,
80                                            NULL, &bufsize);
81       if (ret == 0
82           && GetLastError () == STATUS_BUFFER_OVERFLOW
83           && bufsize > 0)
84         {
85           WCHAR *buffer = (WCHAR *) malloc (bufsize * sizeof (WCHAR));
86           if (buffer != NULL)
87             {
88               ret = p_GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,
89                                                    &num_languages,
90                                                    buffer, &bufsize);
91               if (ret)
92                 {
93                   /* Convert the list from NUL-delimited WCHAR[] Win32 locale
94                      names to colon-delimited char[] Unix locale names.
95                      We assume that all these locale names are in ASCII,
96                      nonempty and contain no colons.  */
97                   char *languages =
98                     (char *) malloc (bufsize + num_languages * 10 + 1);
99                   if (languages != NULL)
100                     {
101                       const WCHAR *p = buffer;
102                       char *q = languages;
103                       ULONG i;
104                       for (i = 0; i < num_languages; i++)
105                         {
106                           char *q1;
107                           char *q2;
108
109                           q1 = q;
110                           if (i > 0)
111                             *q++ = ':';
112                           q2 = q;
113                           for (; *p != (WCHAR)'\0'; p++)
114                             {
115                               if ((unsigned char) *p != *p || *p == ':')
116                                 {
117                                   /* A non-ASCII character or a colon inside
118                                      the Win32 locale name! Punt.  */
119                                   q = q1;
120                                   break;
121                                 }
122                               *q++ = (unsigned char) *p;
123                             }
124                           if (q == q1)
125                             /* An unexpected Win32 locale name occurred.  */
126                             break;
127                           *q = '\0';
128                           _nl_locale_name_canonicalize (q2);
129                           q = q2 + strlen (q2);
130                           p++;
131                         }
132                       *q = '\0';
133                       if (q > languages)
134                         {
135                           free (buffer);
136                           return languages;
137                         }
138                       free (languages);
139                     }
140                 }
141               free (buffer);
142             }
143         }
144     }
145   return NULL;
146 }
147
148 /* Get a preference.  This works on Windows ME and newer.  */
149 static const char *
150 _nl_language_preferences_win32_ME (HMODULE kernel32)
151 {
152   /* LANGID GetUserDefaultUILanguage (void);  */
153   typedef LANGID (WINAPI *GetUserDefaultUILanguage_func) (void);
154   GetUserDefaultUILanguage_func p_GetUserDefaultUILanguage;
155
156   p_GetUserDefaultUILanguage =
157    (GetUserDefaultUILanguage_func)
158    GetProcAddress (kernel32, "GetUserDefaultUILanguage");
159   if (p_GetUserDefaultUILanguage != NULL)
160     return _nl_locale_name_from_win32_LANGID (p_GetUserDefaultUILanguage ());
161   return NULL;
162 }
163
164 /* Get a preference.  This works on Windows 95 and newer.  */
165 static const char *
166 _nl_language_preferences_win32_95 ()
167 {
168   HKEY desktop_resource_locale_key;
169
170   if (RegOpenKeyExA (HKEY_CURRENT_USER,
171                      "Control Panel\\Desktop\\ResourceLocale",
172                      0, KEY_QUERY_VALUE, &desktop_resource_locale_key)
173       == NO_ERROR)
174     {
175       DWORD type;
176       char data[8 + 1];
177       DWORD data_size = sizeof (data);
178       DWORD ret;
179
180       ret = RegQueryValueExA (desktop_resource_locale_key, NULL, NULL,
181                               &type, data, &data_size);
182       RegCloseKey (desktop_resource_locale_key);
183
184       if (ret == NO_ERROR)
185         {
186           /* We expect a string, at most 8 bytes long, that parses as a
187              hexadecimal number.  */
188           if (type == REG_SZ
189               && data_size <= sizeof (data)
190               && (data_size < sizeof (data)
191                   || data[sizeof (data) - 1] == '\0'))
192             {
193               LCID lcid;
194               char *endp;
195               /* Ensure it's NUL terminated.  */
196               if (data_size < sizeof (data))
197                 data[data_size] = '\0';
198               /* Parse it as a hexadecimal number.  */
199               lcid = strtoul (data, &endp, 16);
200               if (endp > data && *endp == '\0')
201                 return _nl_locale_name_from_win32_LCID (lcid);
202             }
203         }
204     }
205   return NULL;
206 }
207
208 /* Get the system's preference.  This can be used as a fallback.  */
209 static BOOL CALLBACK
210 ret_first_language (HMODULE h, LPCSTR type, LPCSTR name, WORD lang, LONG_PTR param)
211 {
212   *(const char **)param = _nl_locale_name_from_win32_LANGID (lang);
213   return FALSE;
214 }
215 static const char *
216 _nl_language_preferences_win32_system (HMODULE kernel32)
217 {
218   const char *languages = NULL;
219   /* Ignore the warning on mingw here. mingw has a wrong definition of the last
220      parameter type of ENUMRESLANGPROC.  */
221   EnumResourceLanguages (kernel32, RT_VERSION, MAKEINTRESOURCE (1),
222                          ret_first_language, (LONG_PTR)&languages);
223   return languages;
224 }
225
226 #endif
227
228 /* Determine the user's language preferences, as a colon separated list of
229    locale names in XPG syntax
230      language[_territory][.codeset][@modifier]
231    The result must not be freed; it is statically allocated.
232    The LANGUAGE environment variable does not need to be considered; it is
233    already taken into account by the caller.  */
234
235 const char *
236 _nl_language_preferences_default (void)
237 {
238 #if HAVE_CFPREFERENCESCOPYAPPVALUE /* MacOS X 10.2 or newer */
239   {
240     /* Cache the preferences list, since CoreFoundation calls are expensive.  */
241     static const char *cached_languages;
242     static int cache_initialized;
243
244     if (!cache_initialized)
245       {
246         CFTypeRef preferences =
247           CFPreferencesCopyAppValue (CFSTR ("AppleLanguages"),
248                                      kCFPreferencesCurrentApplication);
249         if (preferences != NULL
250             && CFGetTypeID (preferences) == CFArrayGetTypeID ())
251           {
252             CFArrayRef prefArray = (CFArrayRef)preferences;
253             int n = CFArrayGetCount (prefArray);
254             char buf[256];
255             size_t size = 0;
256             int i;
257
258             for (i = 0; i < n; i++)
259               {
260                 CFTypeRef element = CFArrayGetValueAtIndex (prefArray, i);
261                 if (element != NULL
262                     && CFGetTypeID (element) == CFStringGetTypeID ()
263                     && CFStringGetCString ((CFStringRef)element,
264                                            buf, sizeof (buf),
265                                            kCFStringEncodingASCII))
266                   {
267                     _nl_locale_name_canonicalize (buf);
268                     size += strlen (buf) + 1;
269                     /* Most GNU programs use msgids in English and don't ship
270                        an en.mo message catalog.  Therefore when we see "en"
271                        in the preferences list, arrange for gettext() to
272                        return the msgid, and ignore all further elements of
273                        the preferences list.  */
274                     if (strcmp (buf, "en") == 0)
275                       break;
276                   }
277                 else
278                   break;
279               }
280             if (size > 0)
281               {
282                 char *languages = (char *) malloc (size);
283
284                 if (languages != NULL)
285                   {
286                     char *p = languages;
287
288                     for (i = 0; i < n; i++)
289                       {
290                         CFTypeRef element =
291                           CFArrayGetValueAtIndex (prefArray, i);
292                         if (element != NULL
293                             && CFGetTypeID (element) == CFStringGetTypeID ()
294                             && CFStringGetCString ((CFStringRef)element,
295                                                    buf, sizeof (buf),
296                                                    kCFStringEncodingASCII))
297                           {
298                             _nl_locale_name_canonicalize (buf);
299                             strcpy (p, buf);
300                             p += strlen (buf);
301                             *p++ = ':';
302                             if (strcmp (buf, "en") == 0)
303                               break;
304                           }
305                         else
306                           break;
307                       }
308                     *--p = '\0';
309
310                     cached_languages = languages;
311                   }
312               }
313           }
314         cache_initialized = 1;
315       }
316     if (cached_languages != NULL)
317       return cached_languages;
318   }
319 #endif
320
321 #ifdef WIN32_NATIVE
322   {
323     /* Cache the preferences list, since computing it is expensive.  */
324     static const char *cached_languages;
325     static int cache_initialized;
326
327     /* Activate the new code only when the GETTEXT_MUI environment variable is
328        set, for the time being, since the new code is not well tested.  */
329     if (!cache_initialized && getenv ("GETTEXT_MUI") != NULL)
330       {
331         const char *languages = NULL;
332         HMODULE kernel32 = GetModuleHandle ("kernel32");
333
334         if (kernel32 != NULL)
335           languages = _nl_language_preferences_win32_mui (kernel32);
336
337         if (languages == NULL && kernel32 != NULL)
338           languages = _nl_language_preferences_win32_ME (kernel32);
339
340         if (languages == NULL)
341           languages = _nl_language_preferences_win32_95 ();
342
343         if (languages == NULL && kernel32 != NULL)
344           languages = _nl_language_preferences_win32_system (kernel32);
345
346         cached_languages = languages;
347         cache_initialized = 1;
348       }
349     if (cached_languages != NULL)
350       return cached_languages;
351   }
352 #endif
353
354   return NULL;
355 }