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