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