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