Bash-4.3 distribution sources and documentation
[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                   {
309                     fclose (fp);
310                     return added;
311                   }
312
313               alias_len = strlen (alias) + 1;
314               value_len = strlen (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                     return added;
325
326                   if (__builtin_expect (string_space != new_pool, 0))
327                     {
328                       size_t i;
329
330                       for (i = 0; i < nmap; i++)
331                         {
332                           map[i].alias += new_pool - string_space;
333                           map[i].value += new_pool - string_space;
334                         }
335                     }
336
337                   string_space = new_pool;
338                   string_space_max = new_size;
339                 }
340
341               map[nmap].alias = memcpy (&string_space[string_space_act],
342                                         alias, alias_len);
343               string_space_act += alias_len;
344
345               map[nmap].value = memcpy (&string_space[string_space_act],
346                                         value, value_len);
347               string_space_act += value_len;
348
349               ++nmap;
350               ++added;
351             }
352         }
353
354       /* Possibly not the whole line fits into the buffer.  Ignore
355          the rest of the line.  */
356       while (strchr (buf, '\n') == NULL)
357         if (FGETS (buf, sizeof buf, fp) == NULL)
358           /* Make sure the inner loop will be left.  The outer loop
359              will exit at the `feof' test.  */
360           break;
361     }
362
363   /* Should we test for ferror()?  I think we have to silently ignore
364      errors.  --drepper  */
365   fclose (fp);
366
367   if (added > 0)
368     qsort (map, nmap, sizeof (struct alias_map),
369            (int (*) PARAMS ((const void *, const void *))) alias_compare);
370
371   return added;
372 }
373
374
375 static int
376 extend_alias_table ()
377 {
378   size_t new_size;
379   struct alias_map *new_map;
380
381   new_size = maxmap == 0 ? 100 : 2 * maxmap;
382   new_map = (struct alias_map *) realloc (map, (new_size
383                                                 * sizeof (struct alias_map)));
384   if (new_map == NULL)
385     /* Simply don't extend: we don't have any more core.  */
386     return -1;
387
388   map = new_map;
389   maxmap = new_size;
390   return 0;
391 }
392
393
394 static int
395 alias_compare (map1, map2)
396      const struct alias_map *map1;
397      const struct alias_map *map2;
398 {
399 #if defined _LIBC || defined HAVE_STRCASECMP
400   return strcasecmp (map1->alias, map2->alias);
401 #else
402   const unsigned char *p1 = (const unsigned char *) map1->alias;
403   const unsigned char *p2 = (const unsigned char *) map2->alias;
404   unsigned char c1, c2;
405
406   if (p1 == p2)
407     return 0;
408
409   do
410     {
411       /* I know this seems to be odd but the tolower() function in
412          some systems libc cannot handle nonalpha characters.  */
413       c1 = isupper (*p1) ? tolower (*p1) : *p1;
414       c2 = isupper (*p2) ? tolower (*p2) : *p2;
415       if (c1 == '\0')
416         break;
417       ++p1;
418       ++p2;
419     }
420   while (c1 == c2);
421
422   return c1 - c2;
423 #endif
424 }