Update.
[glibc.git] / intl / localealias.c
blob2226897f58a51addbeb6ad395c71dd9604d515bc
1 /* Handle aliases for locale names.
2 Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library 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 GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
20 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
21 This must come before <config.h> because <config.h> may include
22 <features.h>, and once <features.h> has been included, it's too late. */
23 #ifndef _GNU_SOURCE
24 # define _GNU_SOURCE 1
25 #endif
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
31 #include <ctype.h>
32 #include <stdio.h>
33 #if defined _LIBC || defined HAVE___FSETLOCKING
34 # include <stdio_ext.h>
35 #endif
36 #include <sys/types.h>
38 #ifdef __GNUC__
39 # define alloca __builtin_alloca
40 # define HAVE_ALLOCA 1
41 #else
42 # if defined HAVE_ALLOCA_H || defined _LIBC
43 # include <alloca.h>
44 # else
45 # ifdef _AIX
46 #pragma alloca
47 # else
48 # ifndef alloca
49 char *alloca ();
50 # endif
51 # endif
52 # endif
53 #endif
55 #include <stdlib.h>
56 #include <string.h>
58 #include "gettextP.h"
60 /* @@ end of prolog @@ */
62 #ifdef _LIBC
63 /* Rename the non ANSI C functions. This is required by the standard
64 because some ANSI C functions will require linking with this object
65 file and the name space must not be polluted. */
66 # define strcasecmp __strcasecmp
68 # ifndef mempcpy
69 # define mempcpy __mempcpy
70 # endif
71 # define HAVE_MEMPCPY 1
72 # define HAVE___FSETLOCKING 1
74 /* We need locking here since we can be called from different places. */
75 # include <bits/libc-lock.h>
77 __libc_lock_define_initialized (static, lock);
78 #endif
80 #ifndef internal_function
81 # define internal_function
82 #endif
84 /* Some optimizations for glibc. */
85 #ifdef _LIBC
86 # define FEOF(fp) feof_unlocked (fp)
87 # define FGETS(buf, n, fp) fgets_unlocked (buf, n, fp)
88 #else
89 # define FEOF(fp) feof (fp)
90 # define FGETS(buf, n, fp) fgets (buf, n, fp)
91 #endif
93 /* For those losing systems which don't have `alloca' we have to add
94 some additional code emulating it. */
95 #ifdef HAVE_ALLOCA
96 # define freea(p) /* nothing */
97 #else
98 # define alloca(n) malloc (n)
99 # define freea(p) free (p)
100 #endif
102 #if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED
103 # undef fgets
104 # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
105 #endif
106 #if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED
107 # undef feof
108 # define feof(s) feof_unlocked (s)
109 #endif
112 struct alias_map
114 const char *alias;
115 const char *value;
119 static char *string_space;
120 static size_t string_space_act;
121 static size_t string_space_max;
122 static struct alias_map *map;
123 static size_t nmap;
124 static size_t maxmap;
127 /* Prototypes for local functions. */
128 static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
129 internal_function;
130 static int extend_alias_table PARAMS ((void));
131 static int alias_compare PARAMS ((const struct alias_map *map1,
132 const struct alias_map *map2));
135 const char *
136 _nl_expand_alias (name)
137 const char *name;
139 static const char *locale_alias_path = LOCALE_ALIAS_PATH;
140 struct alias_map *retval;
141 const char *result = NULL;
142 size_t added;
144 #ifdef _LIBC
145 __libc_lock_lock (lock);
146 #endif
150 struct alias_map item;
152 item.alias = name;
154 if (nmap > 0)
155 retval = (struct alias_map *) bsearch (&item, map, nmap,
156 sizeof (struct alias_map),
157 (int (*) PARAMS ((const void *,
158 const void *))
159 ) alias_compare);
160 else
161 retval = NULL;
163 /* We really found an alias. Return the value. */
164 if (retval != NULL)
166 result = retval->value;
167 break;
170 /* Perhaps we can find another alias file. */
171 added = 0;
172 while (added == 0 && locale_alias_path[0] != '\0')
174 const char *start;
176 while (locale_alias_path[0] == ':')
177 ++locale_alias_path;
178 start = locale_alias_path;
180 while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':')
181 ++locale_alias_path;
183 if (start < locale_alias_path)
184 added = read_alias_file (start, locale_alias_path - start);
187 while (added != 0);
189 #ifdef _LIBC
190 __libc_lock_unlock (lock);
191 #endif
193 return result;
197 static size_t
198 internal_function
199 read_alias_file (fname, fname_len)
200 const char *fname;
201 int fname_len;
203 FILE *fp;
204 char *full_fname;
205 size_t added;
206 static const char aliasfile[] = "/locale.alias";
208 full_fname = (char *) alloca (fname_len + sizeof aliasfile);
209 #ifdef HAVE_MEMPCPY
210 mempcpy (mempcpy (full_fname, fname, fname_len),
211 aliasfile, sizeof aliasfile);
212 #else
213 memcpy (full_fname, fname, fname_len);
214 memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
215 #endif
217 fp = fopen (full_fname, "r");
218 freea (full_fname);
219 if (fp == NULL)
220 return 0;
222 #ifdef HAVE___FSETLOCKING
223 /* No threads present. */
224 __fsetlocking (fp, FSETLOCKING_BYCALLER);
225 #endif
227 added = 0;
228 while (!FEOF (fp))
230 /* It is a reasonable approach to use a fix buffer here because
231 a) we are only interested in the first two fields
232 b) these fields must be usable as file names and so must not
233 be that long
235 char buf[BUFSIZ];
236 char *alias;
237 char *value;
238 char *cp;
240 if (FGETS (buf, sizeof buf, fp) == NULL)
241 /* EOF reached. */
242 break;
244 /* Possibly not the whole line fits into the buffer. Ignore
245 the rest of the line. */
246 if (strchr (buf, '\n') == NULL)
248 char altbuf[BUFSIZ];
250 if (FGETS (altbuf, sizeof altbuf, fp) == NULL)
251 /* Make sure the inner loop will be left. The outer loop
252 will exit at the `feof' test. */
253 break;
254 while (strchr (altbuf, '\n') == NULL);
257 cp = buf;
258 /* Ignore leading white space. */
259 while (isspace ((unsigned char) cp[0]))
260 ++cp;
262 /* A leading '#' signals a comment line. */
263 if (cp[0] != '\0' && cp[0] != '#')
265 alias = cp++;
266 while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
267 ++cp;
268 /* Terminate alias name. */
269 if (cp[0] != '\0')
270 *cp++ = '\0';
272 /* Now look for the beginning of the value. */
273 while (isspace ((unsigned char) cp[0]))
274 ++cp;
276 if (cp[0] != '\0')
278 size_t alias_len;
279 size_t value_len;
281 value = cp++;
282 while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
283 ++cp;
284 /* Terminate value. */
285 if (cp[0] == '\n')
287 /* This has to be done to make the following test
288 for the end of line possible. We are looking for
289 the terminating '\n' which do not overwrite here. */
290 *cp++ = '\0';
291 *cp = '\n';
293 else if (cp[0] != '\0')
294 *cp++ = '\0';
296 if (nmap >= maxmap)
297 if (__builtin_expect (extend_alias_table (), 0))
298 return added;
300 alias_len = strlen (alias) + 1;
301 value_len = strlen (value) + 1;
303 if (string_space_act + alias_len + value_len > string_space_max)
305 /* Increase size of memory pool. */
306 size_t new_size = (string_space_max
307 + (alias_len + value_len > 1024
308 ? alias_len + value_len : 1024));
309 char *new_pool = (char *) realloc (string_space, new_size);
310 if (new_pool == NULL)
311 return added;
313 if (__builtin_expect (string_space != new_pool, 0))
315 size_t i;
317 for (i = 0; i < nmap; i++)
319 map[i].alias += new_pool - string_space;
320 map[i].value += new_pool - string_space;
324 string_space = new_pool;
325 string_space_max = new_size;
328 map[nmap].alias = memcpy (&string_space[string_space_act],
329 alias, alias_len);
330 string_space_act += alias_len;
332 map[nmap].value = memcpy (&string_space[string_space_act],
333 value, value_len);
334 string_space_act += value_len;
336 ++nmap;
337 ++added;
342 /* Should we test for ferror()? I think we have to silently ignore
343 errors. --drepper */
344 fclose (fp);
346 if (added > 0)
347 qsort (map, nmap, sizeof (struct alias_map),
348 (int (*) PARAMS ((const void *, const void *))) alias_compare);
350 return added;
354 static int
355 extend_alias_table ()
357 size_t new_size;
358 struct alias_map *new_map;
360 new_size = maxmap == 0 ? 100 : 2 * maxmap;
361 new_map = (struct alias_map *) realloc (map, (new_size
362 * sizeof (struct alias_map)));
363 if (new_map == NULL)
364 /* Simply don't extend: we don't have any more core. */
365 return -1;
367 map = new_map;
368 maxmap = new_size;
369 return 0;
373 #ifdef _LIBC
374 static void __attribute__ ((unused))
375 free_mem (void)
377 if (string_space != NULL)
378 free (string_space);
379 if (map != NULL)
380 free (map);
382 text_set_element (__libc_subfreeres, free_mem);
383 #endif
386 static int
387 alias_compare (map1, map2)
388 const struct alias_map *map1;
389 const struct alias_map *map2;
391 #if defined _LIBC || defined HAVE_STRCASECMP
392 return strcasecmp (map1->alias, map2->alias);
393 #else
394 const unsigned char *p1 = (const unsigned char *) map1->alias;
395 const unsigned char *p2 = (const unsigned char *) map2->alias;
396 unsigned char c1, c2;
398 if (p1 == p2)
399 return 0;
403 /* I know this seems to be odd but the tolower() function in
404 some systems libc cannot handle nonalpha characters. */
405 c1 = isupper (*p1) ? tolower (*p1) : *p1;
406 c2 = isupper (*p2) ? tolower (*p2) : *p2;
407 if (c1 == '\0')
408 break;
409 ++p1;
410 ++p2;
412 while (c1 == c2);
414 return c1 - c2;
415 #endif