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