Updated from ../gpl2lgpl.sed
[kopensolaris-gnu/glibc.git] / intl / localealias.c
1 /* localealias.c -- handle aliases for locale names
2    Copyright (C) 1995, 1996 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.  The master source lives in /gd/gnu/lib.
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
19 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
20 Cambridge, MA 02139, 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 # include <string.h>
60 #else
61 # include <strings.h>
62 #endif
63 #if !HAVE_STRCHR && !defined _LIBC
64 # ifndef strchr
65 #  define strchr index
66 # endif
67 #endif
68
69 #include "gettext.h"
70 #include "gettextP.h"
71
72 /* @@ end of prolog @@ */
73
74 #ifdef _LIBC
75 /* Rename the non ANSI C functions.  This is required by the standard
76    because some ANSI C functions will require linking with this object
77    file and the name space must not be polluted.  */
78 # define strcasecmp __strcasecmp
79 #endif
80
81
82 /* For those loosing systems which don't have `alloca' we have to add
83    some additional code emulating it.  */
84 #ifdef HAVE_ALLOCA
85 /* Nothing has to be done.  */
86 # define ADD_BLOCK(list, address) /* nothing */
87 # define FREE_BLOCKS(list) /* nothing */
88 #else
89 struct block_list
90 {
91   void *address;
92   struct block_list *next;
93 };
94 # define ADD_BLOCK(list, addr)                                                \
95   do {                                                                        \
96     struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
97     /* If we cannot get a free block we cannot add the new element to         \
98        the list.  */                                                          \
99     if (newp != NULL) {                                                       \
100       newp->address = (addr);                                                 \
101       newp->next = (list);                                                    \
102       (list) = newp;                                                          \
103     }                                                                         \
104   } while (0)
105 # define FREE_BLOCKS(list)                                                    \
106   do {                                                                        \
107     while (list != NULL) {                                                    \
108       struct block_list *old = list;                                          \
109       list = list->next;                                                      \
110       free (old);                                                             \
111     }                                                                         \
112   } while (0)
113 #endif  /* have alloca */
114
115
116 struct alias_map
117 {
118   const char *alias;
119   const char *value;
120 };
121
122
123 static struct alias_map *map;
124 static size_t nmap = 0;
125 static size_t maxmap = 0;
126
127
128 /* Prototypes for local functions.  */
129 static size_t read_alias_file PARAMS ((const char *fname, int fname_len));
130 static void 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   size_t added;
142
143   do
144     {
145       struct alias_map item;
146
147       item.alias = name;
148
149       if (nmap > 0)
150         retval = (struct alias_map *) bsearch (&item, map, nmap,
151                                                sizeof (struct alias_map),
152                                                (int (*) PARAMS ((const void *,
153                                                                  const void *))
154                                                 ) alias_compare);
155       else
156         retval = NULL;
157
158       /* We really found an alias.  Return the value.  */
159       if (retval != NULL)
160         return retval->value;
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] == ':')
169             ++locale_alias_path;
170           start = locale_alias_path;
171
172           while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':')
173             ++locale_alias_path;
174
175           if (start < locale_alias_path)
176             added = read_alias_file (start, locale_alias_path - start);
177         }
178     }
179   while (added != 0);
180
181   return NULL;
182 }
183
184
185 static size_t
186 read_alias_file (fname, fname_len)
187      const char *fname;
188      int fname_len;
189 {
190 #ifndef HAVE_ALLOCA
191   struct block_list *alloca_list = NULL;
192 #endif
193   FILE *fp;
194   char *full_fname;
195   size_t added;
196   static const char aliasfile[] = "/locale.alias";
197
198   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
199   ADD_BLOCK (block_list, full_fname);
200   memcpy (full_fname, fname, fname_len);
201   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
202
203   fp = fopen (full_fname, "r");
204   if (fp == NULL)
205     {
206       FREE_BLOCKS (block_list);
207       return 0;
208     }
209
210   added = 0;
211   while (!feof (fp))
212     {
213       /* It is a reasonable approach to use a fix buffer here because
214          a) we are only interested in the first two fields
215          b) these fields must be usable as file names and so must not
216             be that long
217        */
218       char buf[BUFSIZ];
219       char *alias;
220       char *value;
221       char *cp;
222
223       if (fgets (buf, BUFSIZ, fp) == NULL)
224         /* EOF reached.  */
225         break;
226
227       cp = buf;
228       /* Ignore leading white space.  */
229       while (isspace (cp[0]))
230         ++cp;
231
232       /* A leading '#' signals a comment line.  */
233       if (cp[0] != '\0' && cp[0] != '#')
234         {
235           alias = cp++;
236           while (cp[0] != '\0' && !isspace (cp[0]))
237             ++cp;
238           /* Terminate alias name.  */
239           if (cp[0] != '\0')
240             *cp++ = '\0';
241
242           /* Now look for the beginning of the value.  */
243           while (isspace (cp[0]))
244             ++cp;
245
246           if (cp[0] != '\0')
247             {
248               char *tp;
249               size_t len;
250
251               value = cp++;
252               while (cp[0] != '\0' && !isspace (cp[0]))
253                 ++cp;
254               /* Terminate value.  */
255               if (cp[0] == '\n')
256                 {
257                   /* This has to be done to make the following test
258                      for the end of line possible.  We are looking for
259                      the terminating '\n' which do not overwrite here.  */
260                   *cp++ = '\0';
261                   *cp = '\n';
262                 }
263               else if (cp[0] != '\0')
264                 *cp++ = '\0';
265
266               if (nmap >= maxmap)
267                 extend_alias_table ();
268
269               /* We cannot depend on strdup available in the libc.  Sigh!  */
270               len = strlen (alias) + 1;
271               tp = (char *) malloc (len);
272               if (tp == NULL)
273                 {
274                   FREE_BLOCKS (block_list);
275                   return added;
276                 }
277               memcpy (tp, alias, len);
278               map[nmap].alias = tp;
279
280               len = strlen (value) + 1;
281               tp = (char *) malloc (len);
282               if (tp == NULL)
283                 {
284                   FREE_BLOCKS (block_list);
285                   return added;
286                 }
287               memcpy (tp, value, len);
288               map[nmap].value = tp;
289
290               ++nmap;
291               ++added;
292             }
293         }
294
295       /* Possibily not the whole line fitted into the buffer.  Ignore
296          the rest of the line.  */
297       while (strchr (cp, '\n') == NULL)
298         {
299           cp = buf;
300           if (fgets (buf, BUFSIZ, fp) == NULL)
301             /* Make sure the inner loop will be left.  The outer loop
302                will exit at the `feof' test.  */
303             *cp = '\n';
304         }
305     }
306
307   /* Should we test for ferror()?  I think we have to silently ignore
308      errors.  --drepper  */
309   fclose (fp);
310
311   if (added > 0)
312     qsort (map, nmap, sizeof (struct alias_map),
313            (int (*) PARAMS ((const void *, const void *))) alias_compare);
314
315   FREE_BLOCKS (block_list);
316   return added;
317 }
318
319
320 static void
321 extend_alias_table ()
322 {
323   size_t new_size;
324   struct alias_map *new_map;
325
326   new_size = maxmap == 0 ? 100 : 2 * maxmap;
327   new_map = (struct alias_map *) malloc (new_size
328                                          * sizeof (struct alias_map));
329   if (new_map == NULL)
330     /* Simply don't extend: we don't have any more core.  */
331     return;
332
333   memcpy (new_map, map, nmap * sizeof (struct alias_map));
334
335   if (maxmap != 0)
336     free (map);
337
338   map = new_map;
339   maxmap = new_size;
340 }
341
342
343 static int
344 alias_compare (map1, map2)
345      const struct alias_map *map1;
346      const struct alias_map *map2;
347 {
348 #if defined _LIBC || defined HAVE_STRCASECMP
349   return strcasecmp (map1->alias, map2->alias);
350 #else
351   const unsigned char *p1 = (const unsigned char *) map1->alias;
352   const unsigned char *p2 = (const unsigned char *) map2->alias;
353   unsigned char c1, c2;
354
355   if (p1 == p2)
356     return 0;
357
358   do
359     {
360       /* I know this seems to be odd but the tolower() function in
361          some systems libc cannot handle nonalpha characters.  */
362       c1 = isupper (*p1) ? tolower (*p1) : *p1;
363       c2 = isupper (*p2) ? tolower (*p2) : *p2;
364       if (c1 == '\0')
365         break;
366       ++p1;
367       ++p2;
368     }
369   while (c1 == c2);
370
371   return c1 - c2;
372 #endif
373 }