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