Rocco Rutte:
[apps/madmutt.git] / intl / localealias.c
1 /* Handle aliases for locale names.
2    Copyright (C) 1995-1999, 2000, 2001 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17    USA.  */
18
19 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
20    This must come before <config.h> because <config.h> may include
21    <features.h>, and once <features.h> has been included, it's too late.  */
22 #ifndef _GNU_SOURCE
23 # define _GNU_SOURCE    1
24 #endif
25
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <sys/types.h>
33
34 #ifdef __GNUC__
35 # define alloca __builtin_alloca
36 # define HAVE_ALLOCA 1
37 #else
38 # if defined HAVE_ALLOCA_H || defined _LIBC
39 #  include <alloca.h>
40 # else
41 #  ifdef _AIX
42  #pragma alloca
43 #  else
44 #   ifndef alloca
45 char *alloca ();
46 #   endif
47 #  endif
48 # endif
49 #endif
50
51 #include <stdlib.h>
52
53 #include <string.h>
54 #if !HAVE_STRCHR && !defined _LIBC
55 # ifndef strchr
56 #  define strchr index
57 # endif
58 #endif
59
60 #include "gettextP.h"
61
62 /* @@ end of prolog @@ */
63
64 #ifdef _LIBC
65 /* Rename the non ANSI C functions.  This is required by the standard
66    because some ANSI C functions will require linking with this object
67    file and the name space must not be polluted.  */
68 # define strcasecmp __strcasecmp
69
70 # ifndef mempcpy
71 #  define mempcpy __mempcpy
72 # endif
73 # define HAVE_MEMPCPY   1
74
75 /* We need locking here since we can be called from different places.  */
76 # include <bits/libc-lock.h>
77
78 __libc_lock_define_initialized (static, lock);
79 #endif
80
81 #ifndef internal_function
82 # define internal_function
83 #endif
84
85 /* For those losing systems which don't have `alloca' we have to add
86    some additional code emulating it.  */
87 #ifdef HAVE_ALLOCA
88 # define freea(p) /* nothing */
89 #else
90 # define alloca(n) malloc (n)
91 # define freea(p) free (p)
92 #endif
93
94 #if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED
95 # undef fgets
96 # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
97 #endif
98 #if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED
99 # undef feof
100 # define feof(s) feof_unlocked (s)
101 #endif
102
103
104 struct alias_map
105 {
106   const char *alias;
107   const char *value;
108 };
109
110
111 static char *string_space;
112 static size_t string_space_act;
113 static size_t string_space_max;
114 static struct alias_map *map;
115 static size_t nmap;
116 static size_t maxmap;
117
118
119 /* Prototypes for local functions.  */
120 static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
121      internal_function;
122 static int extend_alias_table PARAMS ((void));
123 static int alias_compare PARAMS ((const struct alias_map *map1,
124                                   const struct alias_map *map2));
125
126
127 const char *
128 _nl_expand_alias (name)
129     const char *name;
130 {
131   static const char *locale_alias_path = LOCALE_ALIAS_PATH;
132   struct alias_map *retval;
133   const char *result = NULL;
134   size_t added;
135
136 #ifdef _LIBC
137   __libc_lock_lock (lock);
138 #endif
139
140   do
141     {
142       struct alias_map item;
143
144       item.alias = name;
145
146       if (nmap > 0)
147         retval = (struct alias_map *) bsearch (&item, map, nmap,
148                                                sizeof (struct alias_map),
149                                                (int (*) PARAMS ((const void *,
150                                                                  const void *))
151                                                 ) alias_compare);
152       else
153         retval = NULL;
154
155       /* We really found an alias.  Return the value.  */
156       if (retval != NULL)
157         {
158           result = retval->value;
159           break;
160         }
161
162       /* Perhaps we can find another alias file.  */
163       added = 0;
164       while (added == 0 && locale_alias_path[0] != '\0')
165         {
166           const char *start;
167
168           while (locale_alias_path[0] == PATH_SEPARATOR)
169             ++locale_alias_path;
170           start = locale_alias_path;
171
172           while (locale_alias_path[0] != '\0'
173                  && locale_alias_path[0] != PATH_SEPARATOR)
174             ++locale_alias_path;
175
176           if (start < locale_alias_path)
177             added = read_alias_file (start, locale_alias_path - start);
178         }
179     }
180   while (added != 0);
181
182 #ifdef _LIBC
183   __libc_lock_unlock (lock);
184 #endif
185
186   return result;
187 }
188
189
190 static size_t
191 internal_function
192 read_alias_file (fname, fname_len)
193      const char *fname;
194      int fname_len;
195 {
196   FILE *fp;
197   char *full_fname;
198   size_t added;
199   static const char aliasfile[] = "/locale.alias";
200
201   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
202 #ifdef HAVE_MEMPCPY
203   mempcpy (mempcpy (full_fname, fname, fname_len),
204            aliasfile, sizeof aliasfile);
205 #else
206   memcpy (full_fname, fname, fname_len);
207   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
208 #endif
209
210   fp = fopen (full_fname, "r");
211   freea (full_fname);
212   if (fp == NULL)
213     return 0;
214
215   added = 0;
216   while (!feof (fp))
217     {
218       /* It is a reasonable approach to use a fix buffer here because
219          a) we are only interested in the first two fields
220          b) these fields must be usable as file names and so must not
221             be that long
222        */
223       char buf[BUFSIZ];
224       char *alias;
225       char *value;
226       char *cp;
227
228       if (fgets (buf, sizeof buf, fp) == NULL)
229         /* EOF reached.  */
230         break;
231
232       /* Possibly not the whole line fits into the buffer.  Ignore
233          the rest of the line.  */
234       if (strchr (buf, '\n') == NULL)
235         {
236           char altbuf[BUFSIZ];
237           do
238             if (fgets (altbuf, sizeof altbuf, fp) == NULL)
239               /* Make sure the inner loop will be left.  The outer loop
240                  will exit at the `feof' test.  */
241               break;
242           while (strchr (altbuf, '\n') == NULL);
243         }
244
245       cp = buf;
246       /* Ignore leading white space.  */
247       while (isspace ((unsigned char) cp[0]))
248         ++cp;
249
250       /* A leading '#' signals a comment line.  */
251       if (cp[0] != '\0' && cp[0] != '#')
252         {
253           alias = cp++;
254           while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
255             ++cp;
256           /* Terminate alias name.  */
257           if (cp[0] != '\0')
258             *cp++ = '\0';
259
260           /* Now look for the beginning of the value.  */
261           while (isspace ((unsigned char) cp[0]))
262             ++cp;
263
264           if (cp[0] != '\0')
265             {
266               size_t alias_len;
267               size_t value_len;
268
269               value = cp++;
270               while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
271                 ++cp;
272               /* Terminate value.  */
273               if (cp[0] == '\n')
274                 {
275                   /* This has to be done to make the following test
276                      for the end of line possible.  We are looking for
277                      the terminating '\n' which do not overwrite here.  */
278                   *cp++ = '\0';
279                   *cp = '\n';
280                 }
281               else if (cp[0] != '\0')
282                 *cp++ = '\0';
283
284               if (nmap >= maxmap)
285                 if (__builtin_expect (extend_alias_table (), 0))
286                   return added;
287
288               alias_len = strlen (alias) + 1;
289               value_len = strlen (value) + 1;
290
291               if (string_space_act + alias_len + value_len > string_space_max)
292                 {
293                   /* Increase size of memory pool.  */
294                   size_t new_size = (string_space_max
295                                      + (alias_len + value_len > 1024
296                                         ? alias_len + value_len : 1024));
297                   char *new_pool = (char *) realloc (string_space, new_size);
298                   if (new_pool == NULL)
299                     return added;
300
301                   if (__builtin_expect (string_space != new_pool, 0))
302                     {
303                       size_t i;
304
305                       for (i = 0; i < nmap; i++)
306                         {
307                           map[i].alias += new_pool - string_space;
308                           map[i].value += new_pool - string_space;
309                         }
310                     }
311
312                   string_space = new_pool;
313                   string_space_max = new_size;
314                 }
315
316               map[nmap].alias = memcpy (&string_space[string_space_act],
317                                         alias, alias_len);
318               string_space_act += alias_len;
319
320               map[nmap].value = memcpy (&string_space[string_space_act],
321                                         value, value_len);
322               string_space_act += value_len;
323
324               ++nmap;
325               ++added;
326             }
327         }
328     }
329
330   /* Should we test for ferror()?  I think we have to silently ignore
331      errors.  --drepper  */
332   fclose (fp);
333
334   if (added > 0)
335     qsort (map, nmap, sizeof (struct alias_map),
336            (int (*) PARAMS ((const void *, const void *))) alias_compare);
337
338   return added;
339 }
340
341
342 static int
343 extend_alias_table ()
344 {
345   size_t new_size;
346   struct alias_map *new_map;
347
348   new_size = maxmap == 0 ? 100 : 2 * maxmap;
349   new_map = (struct alias_map *) realloc (map, (new_size
350                                                 * sizeof (struct alias_map)));
351   if (new_map == NULL)
352     /* Simply don't extend: we don't have any more core.  */
353     return -1;
354
355   map = new_map;
356   maxmap = new_size;
357   return 0;
358 }
359
360
361 #ifdef _LIBC
362 static void __attribute__ ((unused))
363 free_mem (void)
364 {
365   if (string_space != NULL)
366     free (string_space);
367   if (map != NULL)
368     free (map);
369 }
370 text_set_element (__libc_subfreeres, free_mem);
371 #endif
372
373
374 static int
375 alias_compare (map1, map2)
376      const struct alias_map *map1;
377      const struct alias_map *map2;
378 {
379 #if defined _LIBC || defined HAVE_STRCASECMP
380   return strcasecmp (map1->alias, map2->alias);
381 #else
382   const unsigned char *p1 = (const unsigned char *) map1->alias;
383   const unsigned char *p2 = (const unsigned char *) map2->alias;
384   unsigned char c1, c2;
385
386   if (p1 == p2)
387     return 0;
388
389   do
390     {
391       /* I know this seems to be odd but the tolower() function in
392          some systems libc cannot handle nonalpha characters.  */
393       c1 = isupper (*p1) ? tolower (*p1) : *p1;
394       c2 = isupper (*p2) ? tolower (*p2) : *p2;
395       if (c1 == '\0')
396         break;
397       ++p1;
398       ++p2;
399     }
400   while (c1 == c2);
401
402   return c1 - c2;
403 #endif
404 }