Updated from /gd4/gnu/gettext-0.9.12/intl/loadmsgcat.c
[kopensolaris-gnu/glibc.git] / intl / localealias.c
1 /* localealias.c -- handle aliases for locale names
2    Copyright (C) 1995 Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25
26 #ifdef __GNUC__
27 # define alloca __builtin_alloca
28 #else
29 # if defined HAVE_ALLOCA_H || defined _LIBC
30 #  include <alloca.h>
31 # else
32 #  ifdef _AIX
33  #pragma alloca
34 #  else
35 #   ifndef alloca
36 char *alloca ();
37 #   endif
38 #  endif
39 # endif
40 #endif
41
42 #if defined STDC_HEADERS || defined _LIBC
43 # include <stdlib.h>
44 #else
45 char *getenv ();
46 # ifdef HAVE_MALLOC_H
47 #  include <malloc.h>
48 # else
49 void free ();
50 # endif
51 #endif
52
53 #if defined HAVE_STRING_H || defined _LIBC
54 # include <string.h>
55 #else
56 # include <strings.h>
57 #endif
58 #if !HAVE_STRCHR && !defined _LIBC
59 # ifndef strchr
60 #  define strchr index
61 # endif
62 #endif
63
64 #include "gettext.h"
65 #include "gettextP.h"
66
67 /* @@ end of prolog @@ */
68
69 #ifdef _LIBC
70 /* Rename the non ANSI C functions.  This is required by the standard
71    because some ANSI C functions will require linking with this object
72    file and the name space must not be polluted.  */
73 # define strcasecmp __strcasecmp
74 #endif
75
76 struct alias_map
77 {
78   const char *alias;
79   const char *value;
80 };
81
82
83 static struct alias_map *map;
84 static size_t nmap = 0;
85 static size_t maxmap = 0;
86
87
88 /* Prototypes for local functions.  */
89 static size_t read_alias_file __P ((const char *fname, int fname_len));
90 static void extend_alias_table __P ((void));
91 static int alias_compare __P ((const struct alias_map *map1,
92                                const struct alias_map *map2));
93
94
95 const char *
96 _nl_expand_alias (name)
97     const char *name;
98 {
99   static const char *locale_alias_path = LOCALE_ALIAS_PATH;
100   struct alias_map *retval;
101   size_t added;
102
103   do
104     {
105       struct alias_map item;
106
107       item.alias = name;
108
109       if (nmap > 0)
110         retval = (struct alias_map *) bsearch (&item, map, nmap,
111                                                sizeof (struct alias_map),
112                                                (int (*) (const void *,
113                                                          const void *))
114                                                  alias_compare);
115       else
116         retval = NULL;
117
118       /* We really found an alias.  Return the value.  */
119       if (retval != NULL)
120         return retval->value;
121
122       /* Perhaps we can find another alias file.  */
123       added = 0;
124       while (added == 0 && locale_alias_path[0] != '\0')
125         {
126           const char *start;
127
128           while (locale_alias_path[0] != '\0' && locale_alias_path[0] == ':')
129             ++locale_alias_path;
130           start = locale_alias_path;
131
132           while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':')
133             ++locale_alias_path;
134
135           if (start < locale_alias_path)
136             added = read_alias_file (start, locale_alias_path - start);
137         }
138     }
139   while (added != 0);
140
141   return NULL;
142 }
143
144
145 static size_t
146 read_alias_file (fname, fname_len)
147      const char *fname;
148      int fname_len;
149 {
150   FILE *fp;
151   char *full_fname;
152   size_t added;
153   static const char aliasfile[] = "/locale.alias";
154
155   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
156   memcpy (full_fname, fname, fname_len);
157   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
158
159   fp = fopen (full_fname, "r");
160   if (fp == NULL)
161     return 0;
162
163   added = 0;
164   while (!feof (fp))
165     {
166       /* It is a reasonable approach to use a fix buffer here because
167          a) we are only interested in the first two fields
168          b) these fields must be usable as file names and so must not
169             be that long
170        */
171       char buf[BUFSIZ];
172       char *alias;
173       char *value;
174       char *cp;
175
176       if (fgets (buf, BUFSIZ, fp) == NULL)
177         /* EOF reached.  */
178         break;
179
180       cp = buf;
181       /* Ignore leading white space.  */
182       while (isspace (cp[0]))
183         ++cp;
184
185       /* A leading '#' signals a comment line.  */
186       if (cp[0] != '\0' && cp[0] != '#')
187         {
188           alias = cp++;
189           while (cp[0] != '\0' && !isspace (cp[0]))
190             ++cp;
191           /* Terminate alias name.  */
192           if (cp[0] != '\0')
193             *cp++ = '\0';
194
195           /* Now look for the beginning of the value.  */
196           while (isspace (cp[0]))
197             ++cp;
198
199           if (cp[0] != '\0')
200             {
201               char *tp;
202               size_t len;
203
204               value = cp++;
205               while (cp[0] != '\0' && !isspace (cp[0]))
206                 ++cp;
207               /* Terminate value.  */
208               if (cp[0] == '\n')
209                 {
210                   /* This has to be done to make the following test
211                      for the end of line possible.  We are looking for
212                      the terminating '\n' which do not overwrite here.  */
213                   *cp++ = '\0';
214                   *cp = '\n';
215                 }
216               else if (cp[0] != '\0')
217                 *cp++ = '\0';
218
219               if (nmap >= maxmap)
220                 extend_alias_table ();
221
222               /* We cannot depend on strdup available in the libc.  Sigh!  */
223               len = strlen (alias) + 1;
224               tp = (char *) malloc (len);
225               if (tp == NULL)
226                 return added;
227               memcpy (tp, alias, len);
228               map[nmap].alias = tp;
229
230               len = strlen (value) + 1;
231               tp = (char *) malloc (len);
232               if (tp == NULL)
233                 return added;
234               memcpy (tp, value, len);
235               map[nmap].value = tp;
236
237               ++nmap;
238               ++added;
239             }
240         }
241
242       /* Possibily not the whole line fits into the buffer.  Ignore
243          the rest of the line.  */
244       while (strchr (cp, '\n') == NULL)
245         {
246           cp = buf;
247           if (fgets (buf, BUFSIZ, fp) == NULL)
248             /* Make sure the inner loop will be left.  The outer loop
249                will exit at the `feof' test.  */
250             *cp = '\n';
251         }
252     }
253
254   /* Should we test for ferror()?  I think we have to silently ignore
255      errors.  --drepper  */
256   fclose (fp);
257
258   if (added > 0)
259     qsort (map, nmap, sizeof (struct alias_map),
260            (int (*) (const void *, const void *)) alias_compare);
261
262   return added;
263 }
264
265
266 static void
267 extend_alias_table ()
268 {
269   size_t new_size;
270   struct alias_map *new_map;
271
272   new_size = maxmap == 0 ? 100 : 2 * maxmap;
273   new_map = (struct alias_map *) malloc (new_size
274                                          * sizeof (struct alias_map));
275   if (new_map == NULL)
276     /* Simply don't extend: we don't have any more core.  */
277     return;
278
279   memcpy (new_map, map, nmap * sizeof (struct alias_map));
280
281   if (maxmap != 0)
282     free (map);
283
284   map = new_map;
285   maxmap = new_size;
286 }
287
288
289 static int
290 alias_compare (map1, map2)
291      const struct alias_map *map1;
292      const struct alias_map *map2;
293 {
294 #if defined _LIBC || defined HAVE_STRCASECMP
295   return strcasecmp (map1->alias, map2->alias);
296 #else
297   const unsigned char *p1 = (const unsigned char *) map1->alias;
298   const unsigned char *p2 = (const unsigned char *) map2->alias;
299   unsigned char c1, c2;
300
301   if (p1 == p2)
302     return 0;
303
304   do
305     {
306       /* I know this seems to be odd but the tolower() function in
307          some systems libc cannot handle nonalpha characters.  */
308       c1 = isalpha (*p1) ? tolower (*p1) : *p1;
309       c2 = isalpha (*p2) ? tolower (*p2) : *p2;
310       if (c1 == '\0')
311         break;
312     }
313   while (c1 == c2);
314
315   return c1 - c2;
316 #endif
317 }