513cdc78e39c3de38f1c180f32dc1bfb83f4d4c1
[platform/upstream/gcc.git] / gcc / intl / localealias.c
1 /* Handle aliases for locale names.
2    Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
3    Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 # ifndef _GNU_SOURCE
24 #  define _GNU_SOURCE   1
25 # endif
26
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <sys/types.h>
30
31 #ifdef __GNUC__
32 # define alloca __builtin_alloca
33 # define HAVE_ALLOCA 1
34 #else
35 # if defined HAVE_ALLOCA_H || defined _LIBC
36 #  include <alloca.h>
37 # else
38 #  ifdef _AIX
39  #pragma alloca
40 #  else
41 #   ifndef alloca
42 char *alloca ();
43 #   endif
44 #  endif
45 # endif
46 #endif
47
48 #if defined STDC_HEADERS || defined _LIBC
49 # include <stdlib.h>
50 #else
51 char *getenv ();
52 # ifdef HAVE_MALLOC_H
53 #  include <malloc.h>
54 # else
55 void free ();
56 # endif
57 #endif
58
59 #if defined HAVE_STRING_H || defined _LIBC
60 # include <string.h>
61 #else
62 # include <strings.h>
63 # ifndef memcpy
64 #  define memcpy(Dst, Src, Num) bcopy (Src, Dst, Num)
65 # endif
66 #endif
67 #if !HAVE_STRCHR && !defined _LIBC
68 # ifndef strchr
69 #  define strchr index
70 # endif
71 #endif
72
73 #include "gettext.h"
74 #include "gettextP.h"
75
76 /* @@ end of prolog @@ */
77
78 #ifdef _LIBC
79 /* Rename the non ANSI C functions.  This is required by the standard
80    because some ANSI C functions will require linking with this object
81    file and the name space must not be polluted.  */
82 # define strcasecmp __strcasecmp
83
84 # define mempcpy __mempcpy
85 # define HAVE_MEMPCPY   1
86
87 /* We need locking here since we can be called from different places.  */
88 # include <bits/libc-lock.h>
89
90 __libc_lock_define_initialized (static, lock);
91 #endif
92
93
94 /* For those loosing systems which don't have `alloca' we have to add
95    some additional code emulating it.  */
96 #ifdef HAVE_ALLOCA
97 /* Nothing has to be done.  */
98 # define ADD_BLOCK(list, address) /* nothing */
99 # define FREE_BLOCKS(list) /* nothing */
100 #else
101 struct block_list
102 {
103   void *address;
104   struct block_list *next;
105 };
106 # define ADD_BLOCK(list, addr)                                                \
107   do {                                                                        \
108     struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
109     /* If we cannot get a free block we cannot add the new element to         \
110        the list.  */                                                          \
111     if (newp != NULL) {                                                       \
112       newp->address = (addr);                                                 \
113       newp->next = (list);                                                    \
114       (list) = newp;                                                          \
115     }                                                                         \
116   } while (0)
117 # define FREE_BLOCKS(list)                                                    \
118   do {                                                                        \
119     while (list != NULL) {                                                    \
120       struct block_list *old = list;                                          \
121       list = list->next;                                                      \
122       free (old);                                                             \
123     }                                                                         \
124   } while (0)
125 # undef alloca
126 # define alloca(size) (malloc (size))
127 #endif  /* have alloca */
128
129
130 struct alias_map
131 {
132   const char *alias;
133   const char *value;
134 };
135
136
137 static char *string_space = NULL;
138 static size_t string_space_act = 0;
139 static size_t string_space_max = 0;
140 static struct alias_map *map;
141 static size_t nmap = 0;
142 static size_t maxmap = 0;
143
144
145 /* Prototypes for local functions.  */
146 static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
147      internal_function;
148 static void extend_alias_table PARAMS ((void));
149 static int alias_compare PARAMS ((const struct alias_map *map1,
150                                   const struct alias_map *map2));
151
152
153 const char *
154 _nl_expand_alias (name)
155     const char *name;
156 {
157   static const char *locale_alias_path = LOCALE_ALIAS_PATH;
158   struct alias_map *retval;
159   const char *result = NULL;
160   size_t added;
161
162 #ifdef _LIBC
163   __libc_lock_lock (lock);
164 #endif
165
166   do
167     {
168       struct alias_map item;
169
170       item.alias = name;
171
172       if (nmap > 0)
173         retval = (struct alias_map *) bsearch (&item, map, nmap,
174                                                sizeof (struct alias_map),
175                                                (int (*) PARAMS ((const void *,
176                                                                  const void *))
177                                                 ) alias_compare);
178       else
179         retval = NULL;
180
181       /* We really found an alias.  Return the value.  */
182       if (retval != NULL)
183         {
184           result = retval->value;
185           break;
186         }
187
188       /* Perhaps we can find another alias file.  */
189       added = 0;
190       while (added == 0 && locale_alias_path[0] != '\0')
191         {
192           const char *start;
193
194           while (locale_alias_path[0] == ':')
195             ++locale_alias_path;
196           start = locale_alias_path;
197
198           while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':')
199             ++locale_alias_path;
200
201           if (start < locale_alias_path)
202             added = read_alias_file (start, locale_alias_path - start);
203         }
204     }
205   while (added != 0);
206
207 #ifdef _LIBC
208   __libc_lock_unlock (lock);
209 #endif
210
211   return result;
212 }
213
214
215 static size_t
216 internal_function
217 read_alias_file (fname, fname_len)
218      const char *fname;
219      int fname_len;
220 {
221 #ifndef HAVE_ALLOCA
222   struct block_list *block_list = NULL;
223 #endif
224   FILE *fp;
225   char *full_fname;
226   size_t added;
227   static const char aliasfile[] = "/locale.alias";
228
229   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
230   ADD_BLOCK (block_list, full_fname);
231   memcpy (full_fname, fname, fname_len);
232   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
233
234   fp = fopen (full_fname, "r");
235   if (fp == NULL)
236     {
237       FREE_BLOCKS (block_list);
238       return 0;
239     }
240
241   added = 0;
242   while (!feof (fp))
243     {
244       /* It is a reasonable approach to use a fix buffer here because
245          a) we are only interested in the first two fields
246          b) these fields must be usable as file names and so must not
247             be that long
248        */
249       unsigned char buf[BUFSIZ];
250       unsigned char *alias;
251       unsigned char *value;
252       unsigned char *cp;
253
254       if (fgets ((char *)buf, sizeof buf, fp) == NULL)
255         /* EOF reached.  */
256         break;
257
258       /* Possibly not the whole line fits into the buffer.  Ignore
259          the rest of the line.  */
260       if (strchr ((char *)buf, '\n') == NULL)
261         {
262           char altbuf[BUFSIZ];
263           do
264             if (fgets (altbuf, sizeof altbuf, fp) == NULL)
265               /* Make sure the inner loop will be left.  The outer loop
266                  will exit at the `feof' test.  */
267               break;
268           while (strchr (altbuf, '\n') == NULL);
269         }
270
271       cp = buf;
272       /* Ignore leading white space.  */
273       while (isspace (cp[0]))
274         ++cp;
275
276       /* A leading '#' signals a comment line.  */
277       if (cp[0] != '\0' && cp[0] != '#')
278         {
279           alias = cp++;
280           while (cp[0] != '\0' && !isspace (cp[0]))
281             ++cp;
282           /* Terminate alias name.  */
283           if (cp[0] != '\0')
284             *cp++ = '\0';
285
286           /* Now look for the beginning of the value.  */
287           while (isspace (cp[0]))
288             ++cp;
289
290           if (cp[0] != '\0')
291             {
292               size_t alias_len;
293               size_t value_len;
294
295               value = cp++;
296               while (cp[0] != '\0' && !isspace (cp[0]))
297                 ++cp;
298               /* Terminate value.  */
299               if (cp[0] == '\n')
300                 {
301                   /* This has to be done to make the following test
302                      for the end of line possible.  We are looking for
303                      the terminating '\n' which do not overwrite here.  */
304                   *cp++ = '\0';
305                   *cp = '\n';
306                 }
307               else if (cp[0] != '\0')
308                 *cp++ = '\0';
309
310               if (nmap >= maxmap)
311                 extend_alias_table ();
312
313               alias_len = strlen ((char *)alias) + 1;
314               value_len = strlen ((char *)value) + 1;
315
316               if (string_space_act + alias_len + value_len > string_space_max)
317                 {
318                   /* Increase size of memory pool.  */
319                   size_t new_size = (string_space_max
320                                      + (alias_len + value_len > 1024
321                                         ? alias_len + value_len : 1024));
322                   char *new_pool = (char *) realloc (string_space, new_size);
323                   if (new_pool == NULL)
324                     {
325                       FREE_BLOCKS (block_list);
326                       return added;
327                     }
328                   string_space = new_pool;
329                   string_space_max = new_size;
330                 }
331
332               memcpy (&string_space[string_space_act], alias, alias_len);
333               map[nmap].alias = &string_space[string_space_act];
334               string_space_act += alias_len;
335
336               memcpy (&string_space[string_space_act], value, value_len);
337               map[nmap].value = &string_space[string_space_act];
338               string_space_act += value_len;
339
340               ++nmap;
341               ++added;
342             }
343         }
344     }
345
346   /* Should we test for ferror()?  I think we have to silently ignore
347      errors.  --drepper  */
348   fclose (fp);
349
350   if (added > 0)
351     qsort (map, nmap, sizeof (struct alias_map),
352            (int (*) PARAMS ((const void *, const void *))) alias_compare);
353
354   FREE_BLOCKS (block_list);
355   return added;
356 }
357
358
359 static void
360 extend_alias_table ()
361 {
362   size_t new_size;
363   struct alias_map *new_map;
364
365   new_size = maxmap == 0 ? 100 : 2 * maxmap;
366   new_map = (struct alias_map *) realloc (map, (new_size
367                                                 * sizeof (struct alias_map)));
368   if (new_map == NULL)
369     /* Simply don't extend: we don't have any more core.  */
370     return;
371
372   map = new_map;
373   maxmap = new_size;
374 }
375
376
377 #ifdef _LIBC
378 static void __attribute__ ((unused))
379 free_mem (void)
380 {
381   if (string_space != NULL)
382     free (string_space);
383   if (map != NULL)
384     free (map);
385 }
386 text_set_element (__libc_subfreeres, free_mem);
387 #endif
388
389
390 static int
391 alias_compare (map1, map2)
392      const struct alias_map *map1;
393      const struct alias_map *map2;
394 {
395 #if defined _LIBC || defined HAVE_STRCASECMP
396   return strcasecmp (map1->alias, map2->alias);
397 #else
398   const unsigned char *p1 = (const unsigned char *) map1->alias;
399   const unsigned char *p2 = (const unsigned char *) map2->alias;
400   unsigned char c1, c2;
401
402   if (p1 == p2)
403     return 0;
404
405   do
406     {
407       /* I know this seems to be odd but the tolower() function in
408          some systems libc cannot handle nonalpha characters.  */
409       c1 = isupper (*p1) ? tolower (*p1) : *p1;
410       c2 = isupper (*p2) ? tolower (*p2) : *p2;
411       if (c1 == '\0')
412         break;
413       ++p1;
414       ++p2;
415     }
416   while (c1 == c2);
417
418   return c1 - c2;
419 #endif
420 }