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