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