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