ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-i18n.c
1 /*
2  * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
3  * All rights reserved.
4  *
5  * This file is part of the Gnome Library.
6  *
7  * The Gnome Library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * The Gnome Library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
19  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <config.h>
24 #include "gnome-vfs-i18n.h"
25
26 #include "gnome-vfs-private-utils.h"
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <glib/ghash.h>
30 #include <glib/gmessages.h>
31 #include <glib/gstrfuncs.h>
32 #include <glib/gutils.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <unistd.h>
41 #include <unistd.h>
42
43 static GHashTable *alias_table = NULL;
44 static GHashTable *category_table = NULL;
45
46 /*read an alias file for the locales*/
47 static void
48 read_aliases (char *file)
49 {
50   FILE *fp;
51   char buf[256];
52   
53   if (!alias_table)
54     alias_table = g_hash_table_new (g_str_hash, g_str_equal);
55   fp = fopen (file,"r");
56   if (!fp)
57     return;
58   while (fgets (buf, 256, fp))
59     {
60       char *p, *q;
61
62       g_strstrip (buf);
63
64       /* Line is a comment */
65       if ((buf[0] == '#') || (buf[0] == '\0'))
66         continue;
67
68       /* Reads first column */
69       for (p = buf, q = NULL; *p; p++) {
70         if ((*p == '\t') || (*p == ' ') || (*p == ':')) {
71           *p = '\0';
72           q = p+1;
73           while ((*q == '\t') || (*q == ' ')) {
74             q++;
75           }
76           break;
77         }
78       }
79       /* The line only had one column */
80       if (!q || *q == '\0')
81         continue;
82       
83       /* Read second column */
84       for (p = q; *p; p++) {
85         if ((*p == '\t') || (*p == ' ')) {
86           *p = '\0';
87           break;
88         }
89       }
90
91       /* Add to alias table if necessary */
92       if (!g_hash_table_lookup (alias_table, buf)) {
93         g_hash_table_insert (alias_table, g_strdup (buf), g_strdup (q));
94       }
95     }
96   fclose (fp);
97 }
98
99 static char *
100 unalias_lang (char *lang)
101 {
102   char *p;
103   int i;
104
105   if (!alias_table)
106     {
107       read_aliases ("/usr/share/locale/locale.alias");
108       read_aliases ("/usr/local/share/locale/locale.alias");
109       read_aliases ("/usr/lib/X11/locale/locale.alias");
110       read_aliases ("/usr/openwin/lib/locale/locale.alias");
111     }
112   i = 0;
113   while ((p = g_hash_table_lookup (alias_table, lang)) && (strcmp (p, lang) != 0))
114     {
115       lang = p;
116       if (i++ == 30)
117         {
118           static gboolean said_before = FALSE;
119           if (!said_before)
120             g_warning ("Too many alias levels for a locale, "
121                        "may indicate a loop");
122           said_before = TRUE;
123           return lang;
124         }
125     }
126   return lang;
127 }
128
129 /* Mask for components of locale spec. The ordering here is from
130  * least significant to most significant
131  */
132 enum
133 {
134   COMPONENT_CODESET =   1 << 0,
135   COMPONENT_TERRITORY = 1 << 1,
136   COMPONENT_MODIFIER =  1 << 2
137 };
138
139 /* Break an X/Open style locale specification into components
140  */
141 static guint
142 explode_locale (const gchar *locale,
143                 gchar **language, 
144                 gchar **territory, 
145                 gchar **codeset, 
146                 gchar **modifier)
147 {
148   const gchar *uscore_pos;
149   const gchar *at_pos;
150   const gchar *dot_pos;
151
152   guint mask = 0;
153
154   uscore_pos = strchr (locale, '_');
155   dot_pos = strchr (uscore_pos ? uscore_pos : locale, '.');
156   at_pos = strchr (dot_pos ? dot_pos : (uscore_pos ? uscore_pos : locale), '@');
157
158   if (at_pos)
159     {
160       mask |= COMPONENT_MODIFIER;
161       *modifier = g_strdup (at_pos);
162     }
163   else
164     at_pos = locale + strlen (locale);
165
166   if (dot_pos)
167     {
168       mask |= COMPONENT_CODESET;
169       *codeset = g_strndup (dot_pos, at_pos - dot_pos);
170     }
171   else
172     dot_pos = at_pos;
173
174   if (uscore_pos)
175     {
176       mask |= COMPONENT_TERRITORY;
177       *territory = g_strndup (uscore_pos, dot_pos - uscore_pos);
178     }
179   else
180     uscore_pos = dot_pos;
181
182   *language = g_strndup (locale, uscore_pos - locale);
183
184   return mask;
185 }
186
187 /*
188  * Compute all interesting variants for a given locale name -
189  * by stripping off different components of the value.
190  *
191  * For simplicity, we assume that the locale is in
192  * X/Open format: language[_territory][.codeset][@modifier]
193  *
194  * TODO: Extend this to handle the CEN format (see the GNUlibc docs)
195  *       as well. We could just copy the code from glibc wholesale
196  *       but it is big, ugly, and complicated, so I'm reluctant
197  *       to do so when this should handle 99% of the time...
198  */
199 static GList *
200 compute_locale_variants (const gchar *locale)
201 {
202   GList *retval = NULL;
203
204   gchar *language;
205   gchar *territory;
206   gchar *codeset;
207   gchar *modifier;
208
209   guint mask;
210   guint i;
211
212   g_return_val_if_fail (locale != NULL, NULL);
213
214   mask = explode_locale (locale, &language, &territory, &codeset, &modifier);
215
216   /* Iterate through all possible combinations, from least attractive
217    * to most attractive.
218    */
219   for (i = 0; i <= mask; i++)
220     if ((i & ~mask) == 0)
221       {
222         gchar *val = g_strconcat (language,
223                                   (i & COMPONENT_TERRITORY) ? territory : "",
224                                   (i & COMPONENT_CODESET) ? codeset : "",
225                                   (i & COMPONENT_MODIFIER) ? modifier : "",
226                                   NULL);
227         retval = g_list_prepend (retval, val);
228       }
229
230   g_free (language);
231   if (mask & COMPONENT_CODESET)
232     g_free (codeset);
233   if (mask & COMPONENT_TERRITORY)
234     g_free (territory);
235   if (mask & COMPONENT_MODIFIER)
236     g_free (modifier);
237
238   return retval;
239 }
240
241 /* The following is (partly) taken from the gettext package.
242    Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.  */
243
244 static const gchar *
245 guess_category_value (const gchar *categoryname)
246 {
247   const gchar *retval;
248
249   /* The highest priority value is the `LANGUAGE' environment
250      variable.  This is a GNU extension.  */
251   retval = g_getenv ("LANGUAGE");
252   if ((retval != NULL) && (retval[0] != '\0'))
253     return retval;
254
255   /* `LANGUAGE' is not set.  So we have to proceed with the POSIX
256      methods of looking to `LC_ALL', `LC_xxx', and `LANG'.  On some
257      systems this can be done by the `setlocale' function itself.  */
258
259   /* Setting of LC_ALL overwrites all other.  */
260   retval = g_getenv ("LC_ALL");  
261   if ((retval != NULL) && (retval[0] != '\0'))
262     return retval;
263
264   /* Next comes the name of the desired category.  */
265   retval = g_getenv (categoryname);
266   if ((retval != NULL) && (retval[0] != '\0'))
267     return retval;
268
269   /* Last possibility is the LANG environment variable.  */
270   retval = g_getenv ("LANG");
271   if ((retval != NULL) && (retval[0] != '\0'))
272     return retval;
273
274   return NULL;
275 }
276
277 /**
278  * gnome_vfs_i18n_get_language_list:
279  * @category_name: Name of category to look up, e.g. "LC_MESSAGES".
280  * 
281  * This computes a list of language strings.  It searches in the
282  * standard environment variables to find the list, which is sorted
283  * in order from most desirable to least desirable.  The `C' locale
284  * is appended to the list if it does not already appear (other
285  * routines depend on this behaviour).
286  * If @category_name is %NULL, then LC_ALL is assumed.
287  * 
288  * Return value: a copy of the list of languages (which you need to free).
289  **/
290 GList *
291 gnome_vfs_i18n_get_language_list (const gchar *category_name)
292 {
293   GList *list;
294
295   if (!category_name)
296     category_name = "LC_ALL";
297
298   if (category_table)
299     {
300       list = g_hash_table_lookup (category_table, (const gpointer) category_name);
301     }
302   else
303     {
304       category_table = g_hash_table_new (g_str_hash, g_str_equal);
305       list = NULL;
306     }
307
308   if (!list)
309     {
310       gint c_locale_defined = FALSE;
311   
312       const gchar *category_value;
313       gchar *category_memory, *orig_category_memory;
314
315       category_value = guess_category_value (category_name);
316       if (!category_value)
317         category_value = "C";
318       orig_category_memory = category_memory =
319         g_malloc (strlen (category_value)+1);
320       
321       while (category_value[0] != '\0')
322         {
323           while ((category_value[0] != '\0') && (category_value[0] == ':'))
324             ++category_value;
325           
326           if (category_value[0] != '\0')
327             {
328               char *cp = category_memory;
329               
330               while ((category_value[0] != '\0') && (category_value[0] != ':'))
331                 *category_memory++ = *category_value++;
332               
333               category_memory[0] = '\0'; 
334               category_memory++;
335               
336               cp = unalias_lang (cp);
337               
338               if (strcmp (cp, "C") == 0)
339                 c_locale_defined = TRUE;
340               
341               list = g_list_concat (list, compute_locale_variants (cp));
342             }
343         }
344
345       g_free (orig_category_memory);
346       
347       if (!c_locale_defined)
348         list= g_list_append (list, "C");
349
350       g_hash_table_insert (category_table, (gpointer) category_name, list);
351     }
352
353   return g_list_copy (list);
354 }