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