8f4f03946122387706277285bf51e909661c7e50
[platform/upstream/glib.git] / glib / gcharset.c
1 /* gcharset.c - Charset information
2  *
3  * Copyright (C) 2011 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "config.h"
20
21 #include "gcharset.h"
22 #include "gcharsetprivate.h"
23
24 #include "garray.h"
25 #include "genviron.h"
26 #include "ghash.h"
27 #include "gmessages.h"
28 #include "gstrfuncs.h"
29 #include "gthread.h"
30 #include "gthreadprivate.h"
31 #ifdef G_OS_WIN32
32 #include "gwin32.h"
33 #endif
34
35 #include "libcharset/libcharset.h"
36
37 #include <string.h>
38 #include <stdio.h>
39
40 G_LOCK_DEFINE_STATIC (aliases);
41
42 static GHashTable *
43 get_alias_hash (void)
44 {
45   static GHashTable *alias_hash = NULL;
46   const char *aliases;
47
48   G_LOCK (aliases);
49
50   if (!alias_hash)
51     {
52       alias_hash = g_hash_table_new (g_str_hash, g_str_equal);
53
54       aliases = _g_locale_get_charset_aliases ();
55       while (*aliases != '\0')
56         {
57           const char *canonical;
58           const char *alias;
59           const char **alias_array;
60           int count = 0;
61
62           alias = aliases;
63           aliases += strlen (aliases) + 1;
64           canonical = aliases;
65           aliases += strlen (aliases) + 1;
66
67           alias_array = g_hash_table_lookup (alias_hash, canonical);
68           if (alias_array)
69             {
70               while (alias_array[count])
71                 count++;
72             }
73
74           alias_array = g_renew (const char *, alias_array, count + 2);
75           alias_array[count] = alias;
76           alias_array[count + 1] = NULL;
77
78           g_hash_table_insert (alias_hash, (char *)canonical, alias_array);
79         }
80     }
81
82   G_UNLOCK (aliases);
83
84   return alias_hash;
85 }
86
87 /* As an abuse of the alias table, the following routines gets
88  * the charsets that are aliases for the canonical name.
89  */
90 const char **
91 _g_charset_get_aliases (const char *canonical_name)
92 {
93   GHashTable *alias_hash = get_alias_hash ();
94
95   return g_hash_table_lookup (alias_hash, canonical_name);
96 }
97
98 static gboolean
99 g_utf8_get_charset_internal (const char  *raw_data,
100                              const char **a)
101 {
102   const char *charset = g_getenv ("CHARSET");
103
104   if (charset && *charset)
105     {
106       *a = charset;
107
108       if (charset && strstr (charset, "UTF-8"))
109         return TRUE;
110       else
111         return FALSE;
112     }
113
114   /* The libcharset code tries to be thread-safe without
115    * a lock, but has a memory leak and a missing memory
116    * barrier, so we lock for it
117    */
118   G_LOCK (aliases);
119   charset = _g_locale_charset_unalias (raw_data);
120   G_UNLOCK (aliases);
121
122   if (charset && *charset)
123     {
124       *a = charset;
125
126       if (charset && strstr (charset, "UTF-8"))
127         return TRUE;
128       else
129         return FALSE;
130     }
131
132   /* Assume this for compatibility at present.  */
133   *a = "US-ASCII";
134
135   return FALSE;
136 }
137
138 typedef struct _GCharsetCache GCharsetCache;
139
140 struct _GCharsetCache {
141   gboolean is_utf8;
142   gchar *raw;
143   gchar *charset;
144 };
145
146 static void
147 charset_cache_free (gpointer data)
148 {
149   GCharsetCache *cache = data;
150   g_free (cache->raw);
151   g_free (cache->charset);
152   g_free (cache);
153 }
154
155 /**
156  * g_get_charset:
157  * @charset: (out) (optional) (transfer none): return location for character set
158  *   name, or %NULL.
159  *
160  * Obtains the character set for the [current locale][setlocale]; you
161  * might use this character set as an argument to g_convert(), to convert
162  * from the current locale's encoding to some other encoding. (Frequently
163  * g_locale_to_utf8() and g_locale_from_utf8() are nice shortcuts, though.)
164  *
165  * On Windows the character set returned by this function is the
166  * so-called system default ANSI code-page. That is the character set
167  * used by the "narrow" versions of C library and Win32 functions that
168  * handle file names. It might be different from the character set
169  * used by the C library's current locale.
170  *
171  * On Linux, the character set is found by consulting nl_langinfo() if
172  * available. If not, the environment variables `LC_ALL`, `LC_CTYPE`, `LANG`
173  * and `CHARSET` are queried in order.
174  *
175  * The return value is %TRUE if the locale's encoding is UTF-8, in that
176  * case you can perhaps avoid calling g_convert().
177  *
178  * The string returned in @charset is not allocated, and should not be
179  * freed.
180  *
181  * Returns: %TRUE if the returned charset is UTF-8
182  */
183 gboolean
184 g_get_charset (const char **charset)
185 {
186   static GPrivate cache_private = G_PRIVATE_INIT (charset_cache_free);
187   GCharsetCache *cache = g_private_get (&cache_private);
188   const gchar *raw;
189
190   if (!cache)
191     cache = g_private_set_alloc0 (&cache_private, sizeof (GCharsetCache));
192
193   G_LOCK (aliases);
194   raw = _g_locale_charset_raw ();
195   G_UNLOCK (aliases);
196
197   if (!(cache->raw && strcmp (cache->raw, raw) == 0))
198     {
199       const gchar *new_charset;
200
201       g_free (cache->raw);
202       g_free (cache->charset);
203       cache->raw = g_strdup (raw);
204       cache->is_utf8 = g_utf8_get_charset_internal (raw, &new_charset);
205       cache->charset = g_strdup (new_charset);
206     }
207
208   if (charset)
209     *charset = cache->charset;
210
211   return cache->is_utf8;
212 }
213
214 /**
215  * g_get_codeset:
216  *
217  * Gets the character set for the current locale.
218  *
219  * Returns: a newly allocated string containing the name
220  *     of the character set. This string must be freed with g_free().
221  */
222 gchar *
223 g_get_codeset (void)
224 {
225   const gchar *charset;
226
227   g_get_charset (&charset);
228
229   return g_strdup (charset);
230 }
231
232 #ifndef G_OS_WIN32
233
234 /* read an alias file for the locales */
235 static void
236 read_aliases (const gchar *file,
237               GHashTable  *alias_table)
238 {
239   FILE *fp;
240   char buf[256];
241
242   fp = fopen (file,"r");
243   if (!fp)
244     return;
245   while (fgets (buf, 256, fp))
246     {
247       char *p, *q;
248
249       g_strstrip (buf);
250
251       /* Line is a comment */
252       if ((buf[0] == '#') || (buf[0] == '\0'))
253         continue;
254
255       /* Reads first column */
256       for (p = buf, q = NULL; *p; p++) {
257         if ((*p == '\t') || (*p == ' ') || (*p == ':')) {
258           *p = '\0';
259           q = p+1;
260           while ((*q == '\t') || (*q == ' ')) {
261             q++;
262           }
263           break;
264         }
265       }
266       /* The line only had one column */
267       if (!q || *q == '\0')
268         continue;
269
270       /* Read second column */
271       for (p = q; *p; p++) {
272         if ((*p == '\t') || (*p == ' ')) {
273           *p = '\0';
274           break;
275         }
276       }
277
278       /* Add to alias table if necessary */
279       if (!g_hash_table_lookup (alias_table, buf)) {
280         g_hash_table_insert (alias_table, g_strdup (buf), g_strdup (q));
281       }
282     }
283   fclose (fp);
284 }
285
286 #endif
287
288 static char *
289 unalias_lang (char *lang)
290 {
291 #ifndef G_OS_WIN32
292   static GHashTable *alias_table = NULL;
293   char *p;
294   int i;
295
296   if (g_once_init_enter (&alias_table))
297     {
298       GHashTable *table = g_hash_table_new (g_str_hash, g_str_equal);
299       read_aliases ("/usr/share/locale/locale.alias", table);
300       g_once_init_leave (&alias_table, table);
301     }
302
303   i = 0;
304   while ((p = g_hash_table_lookup (alias_table, lang)) && (strcmp (p, lang) != 0))
305     {
306       lang = p;
307       if (i++ == 30)
308         {
309           static gboolean said_before = FALSE;
310           if (!said_before)
311             g_warning ("Too many alias levels for a locale, "
312                        "may indicate a loop");
313           said_before = TRUE;
314           return lang;
315         }
316     }
317 #endif
318   return lang;
319 }
320
321 /* Mask for components of locale spec. The ordering here is from
322  * least significant to most significant
323  */
324 enum
325 {
326   COMPONENT_CODESET =   1 << 0,
327   COMPONENT_TERRITORY = 1 << 1,
328   COMPONENT_MODIFIER =  1 << 2
329 };
330
331 /* Break an X/Open style locale specification into components
332  */
333 static guint
334 explode_locale (const gchar *locale,
335                 gchar      **language,
336                 gchar      **territory,
337                 gchar      **codeset,
338                 gchar      **modifier)
339 {
340   const gchar *uscore_pos;
341   const gchar *at_pos;
342   const gchar *dot_pos;
343
344   guint mask = 0;
345
346   uscore_pos = strchr (locale, '_');
347   dot_pos = strchr (uscore_pos ? uscore_pos : locale, '.');
348   at_pos = strchr (dot_pos ? dot_pos : (uscore_pos ? uscore_pos : locale), '@');
349
350   if (at_pos)
351     {
352       mask |= COMPONENT_MODIFIER;
353       *modifier = g_strdup (at_pos);
354     }
355   else
356     at_pos = locale + strlen (locale);
357
358   if (dot_pos)
359     {
360       mask |= COMPONENT_CODESET;
361       *codeset = g_strndup (dot_pos, at_pos - dot_pos);
362     }
363   else
364     dot_pos = at_pos;
365
366   if (uscore_pos)
367     {
368       mask |= COMPONENT_TERRITORY;
369       *territory = g_strndup (uscore_pos, dot_pos - uscore_pos);
370     }
371   else
372     uscore_pos = dot_pos;
373
374   *language = g_strndup (locale, uscore_pos - locale);
375
376   return mask;
377 }
378
379 /*
380  * Compute all interesting variants for a given locale name -
381  * by stripping off different components of the value.
382  *
383  * For simplicity, we assume that the locale is in
384  * X/Open format: language[_territory][.codeset][@modifier]
385  *
386  * TODO: Extend this to handle the CEN format (see the GNUlibc docs)
387  *       as well. We could just copy the code from glibc wholesale
388  *       but it is big, ugly, and complicated, so I'm reluctant
389  *       to do so when this should handle 99% of the time...
390  */
391 static void
392 append_locale_variants (GPtrArray *array,
393                         const gchar *locale)
394 {
395   gchar *language = NULL;
396   gchar *territory = NULL;
397   gchar *codeset = NULL;
398   gchar *modifier = NULL;
399
400   guint mask;
401   guint i, j;
402
403   g_return_if_fail (locale != NULL);
404
405   mask = explode_locale (locale, &language, &territory, &codeset, &modifier);
406
407   /* Iterate through all possible combinations, from least attractive
408    * to most attractive.
409    */
410   for (j = 0; j <= mask; ++j)
411     {
412       i = mask - j;
413
414       if ((i & ~mask) == 0)
415         {
416           gchar *val = g_strconcat (language,
417                                     (i & COMPONENT_TERRITORY) ? territory : "",
418                                     (i & COMPONENT_CODESET) ? codeset : "",
419                                     (i & COMPONENT_MODIFIER) ? modifier : "",
420                                     NULL);
421           g_ptr_array_add (array, val);
422         }
423     }
424
425   g_free (language);
426   if (mask & COMPONENT_CODESET)
427     g_free (codeset);
428   if (mask & COMPONENT_TERRITORY)
429     g_free (territory);
430   if (mask & COMPONENT_MODIFIER)
431     g_free (modifier);
432 }
433
434 /**
435  * g_get_locale_variants:
436  * @locale: a locale identifier
437  *
438  * Returns a list of derived variants of @locale, which can be used to
439  * e.g. construct locale-dependent filenames or search paths. The returned
440  * list is sorted from most desirable to least desirable.
441  * This function handles territory, charset and extra locale modifiers.
442  *
443  * For example, if @locale is "fr_BE", then the returned list
444  * is "fr_BE", "fr".
445  *
446  * If you need the list of variants for the current locale,
447  * use g_get_language_names().
448  *
449  * Returns: (transfer full) (array zero-terminated=1) (element-type utf8): a newly
450  *   allocated array of newly allocated strings with the locale variants. Free with
451  *   g_strfreev().
452  *
453  * Since: 2.28
454  */
455 gchar **
456 g_get_locale_variants (const gchar *locale)
457 {
458   GPtrArray *array;
459
460   g_return_val_if_fail (locale != NULL, NULL);
461
462   array = g_ptr_array_sized_new (8);
463   append_locale_variants (array, locale);
464   g_ptr_array_add (array, NULL);
465
466   return (gchar **) g_ptr_array_free (array, FALSE);
467 }
468
469 /* The following is (partly) taken from the gettext package.
470    Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.  */
471
472 static const gchar *
473 guess_category_value (const gchar *category_name)
474 {
475   const gchar *retval;
476
477   /* The highest priority value is the 'LANGUAGE' environment
478      variable.  This is a GNU extension.  */
479   retval = g_getenv ("LANGUAGE");
480   if ((retval != NULL) && (retval[0] != '\0'))
481     return retval;
482
483   /* 'LANGUAGE' is not set.  So we have to proceed with the POSIX
484      methods of looking to 'LC_ALL', 'LC_xxx', and 'LANG'.  On some
485      systems this can be done by the 'setlocale' function itself.  */
486
487   /* Setting of LC_ALL overwrites all other.  */
488   retval = g_getenv ("LC_ALL");
489   if ((retval != NULL) && (retval[0] != '\0'))
490     return retval;
491
492   /* Next comes the name of the desired category.  */
493   retval = g_getenv (category_name);
494   if ((retval != NULL) && (retval[0] != '\0'))
495     return retval;
496
497   /* Last possibility is the LANG environment variable.  */
498   retval = g_getenv ("LANG");
499   if ((retval != NULL) && (retval[0] != '\0'))
500     return retval;
501
502 #ifdef G_PLATFORM_WIN32
503   /* g_win32_getlocale() first checks for LC_ALL, LC_MESSAGES and
504    * LANG, which we already did above. Oh well. The main point of
505    * calling g_win32_getlocale() is to get the thread's locale as used
506    * by Windows and the Microsoft C runtime (in the "English_United
507    * States" format) translated into the Unixish format.
508    */
509   {
510     char *locale = g_win32_getlocale ();
511     retval = g_intern_string (locale);
512     g_free (locale);
513     return retval;
514   }
515 #endif
516
517   return NULL;
518 }
519
520 typedef struct _GLanguageNamesCache GLanguageNamesCache;
521
522 struct _GLanguageNamesCache {
523   gchar *languages;
524   gchar **language_names;
525 };
526
527 static void
528 language_names_cache_free (gpointer data)
529 {
530   GLanguageNamesCache *cache = data;
531   g_free (cache->languages);
532   g_strfreev (cache->language_names);
533   g_free (cache);
534 }
535
536 /**
537  * g_get_language_names:
538  *
539  * Computes a list of applicable locale names, which can be used to
540  * e.g. construct locale-dependent filenames or search paths. The returned
541  * list is sorted from most desirable to least desirable and always contains
542  * the default locale "C".
543  *
544  * For example, if LANGUAGE=de:en_US, then the returned list is
545  * "de", "en_US", "en", "C".
546  *
547  * This function consults the environment variables `LANGUAGE`, `LC_ALL`,
548  * `LC_MESSAGES` and `LANG` to find the list of locales specified by the
549  * user.
550  *
551  * Returns: (array zero-terminated=1) (transfer none): a %NULL-terminated array of strings owned by GLib
552  *    that must not be modified or freed.
553  *
554  * Since: 2.6
555  */
556 const gchar * const *
557 g_get_language_names (void)
558 {
559   return g_get_language_names_with_category ("LC_MESSAGES");
560 }
561
562 /**
563  * g_get_language_names_with_category:
564  * @category_name: a locale category name
565  *
566  * Computes a list of applicable locale names with a locale category name,
567  * which can be used to construct the fallback locale-dependent filenames
568  * or search paths. The returned list is sorted from most desirable to
569  * least desirable and always contains the default locale "C".
570  *
571  * This function consults the environment variables `LANGUAGE`, `LC_ALL`,
572  * @category_name, and `LANG` to find the list of locales specified by the
573  * user.
574  *
575  * g_get_language_names() returns g_get_language_names_with_category("LC_MESSAGES").
576  *
577  * Returns: (array zero-terminated=1) (transfer none): a %NULL-terminated array of strings owned by
578  *    the thread g_get_language_names_with_category was called from.
579  *    It must not be modified or freed. It must be copied if planned to be used in another thread.
580  *
581  * Since: 2.58
582  */
583 const gchar * const *
584 g_get_language_names_with_category (const gchar *category_name)
585 {
586   static GPrivate cache_private = G_PRIVATE_INIT ((void (*)(gpointer)) g_hash_table_unref);
587   GHashTable *cache = g_private_get (&cache_private);
588   const gchar *languages;
589   GLanguageNamesCache *name_cache;
590
591   g_return_val_if_fail (category_name != NULL, NULL);
592
593   if (!cache)
594     {
595       cache = g_hash_table_new_full (g_str_hash, g_str_equal,
596                                      g_free, language_names_cache_free);
597       g_private_set (&cache_private, cache);
598     }
599
600   languages = guess_category_value (category_name);
601   if (!languages)
602     languages = "C";
603
604   name_cache = (GLanguageNamesCache *) g_hash_table_lookup (cache, category_name);
605   if (!(name_cache && name_cache->languages &&
606         strcmp (name_cache->languages, languages) == 0))
607     {
608       GPtrArray *array;
609       gchar **alist, **a;
610
611       g_hash_table_remove (cache, category_name);
612
613       array = g_ptr_array_sized_new (8);
614
615       alist = g_strsplit (languages, ":", 0);
616       for (a = alist; *a; a++)
617         append_locale_variants (array, unalias_lang (*a));
618       g_strfreev (alist);
619       g_ptr_array_add (array, g_strdup ("C"));
620       g_ptr_array_add (array, NULL);
621
622       name_cache = g_new0 (GLanguageNamesCache, 1);
623       name_cache->languages = g_strdup (languages);
624       name_cache->language_names = (gchar **) g_ptr_array_free (array, FALSE);
625       g_hash_table_insert (cache, g_strdup (category_name), name_cache);
626     }
627
628   return (const gchar * const *) name_cache->language_names;
629 }