Update.
[glibc.git] / intl / localealias.c
blob05832f0e368b428dc0c5dbfd1531a6d05c023fce
1 /* Handle aliases for locale names.
2 Copyright (C) 1995, 1996, 1997 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
85 # define strdup __strdup
87 /* We need locking here since we can be called from different palces. */
88 # include <bits/libc-lock.h>
90 __libc_lock_define_initialized (static, lock);
91 #endif
93 #ifndef internal_function
94 # define internal_function
95 #endif
97 /* For those loosing systems which don't have `alloca' we have to add
98 some additional code emulating it. */
99 #ifdef HAVE_ALLOCA
100 /* Nothing has to be done. */
101 # define ADD_BLOCK(list, address) /* nothing */
102 # define FREE_BLOCKS(list) /* nothing */
103 #else
104 struct block_list
106 void *address;
107 struct block_list *next;
109 # define ADD_BLOCK(list, addr) \
110 do { \
111 struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \
112 /* If we cannot get a free block we cannot add the new element to \
113 the list. */ \
114 if (newp != NULL) { \
115 newp->address = (addr); \
116 newp->next = (list); \
117 (list) = newp; \
119 } while (0)
120 # define FREE_BLOCKS(list) \
121 do { \
122 while (list != NULL) { \
123 struct block_list *old = list; \
124 list = list->next; \
125 free (old); \
127 } while (0)
128 # undef alloca
129 # define alloca(size) (malloc (size))
130 #endif /* have alloca */
133 struct alias_map
135 const char *alias;
136 const char *value;
140 static char *string_space = NULL;
141 static size_t string_space_act = 0;
142 static size_t string_space_max = 0;
143 static struct alias_map *map;
144 static size_t nmap = 0;
145 static size_t maxmap = 0;
148 /* Prototypes for local functions. */
149 static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
150 internal_function;
151 static void extend_alias_table PARAMS ((void));
152 static int alias_compare PARAMS ((const struct alias_map *map1,
153 const struct alias_map *map2));
156 const char *
157 _nl_expand_alias (name)
158 const char *name;
160 static const char *locale_alias_path = LOCALE_ALIAS_PATH;
161 struct alias_map *retval;
162 const char *result = NULL;
163 size_t added;
165 #ifdef _LIBC
166 __libc_lock_lock (lock);
167 #endif
171 struct alias_map item;
173 item.alias = name;
175 if (nmap > 0)
176 retval = (struct alias_map *) bsearch (&item, map, nmap,
177 sizeof (struct alias_map),
178 (int (*) PARAMS ((const void *,
179 const void *))
180 ) alias_compare);
181 else
182 retval = NULL;
184 /* We really found an alias. Return the value. */
185 if (retval != NULL)
187 result = retval->value;
188 break;
191 /* Perhaps we can find another alias file. */
192 added = 0;
193 while (added == 0 && locale_alias_path[0] != '\0')
195 const char *start;
197 while (locale_alias_path[0] == ':')
198 ++locale_alias_path;
199 start = locale_alias_path;
201 while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':')
202 ++locale_alias_path;
204 if (start < locale_alias_path)
205 added = read_alias_file (start, locale_alias_path - start);
208 while (added != 0);
210 #ifdef _LIBC
211 __libc_lock_unlock (lock);
212 #endif
214 return result;
218 static size_t
219 internal_function
220 read_alias_file (fname, fname_len)
221 const char *fname;
222 int fname_len;
224 #ifndef HAVE_ALLOCA
225 struct block_list *block_list = NULL;
226 #endif
227 FILE *fp;
228 char *full_fname;
229 size_t added;
230 static const char aliasfile[] = "/locale.alias";
232 full_fname = (char *) alloca (fname_len + sizeof aliasfile);
233 ADD_BLOCK (block_list, full_fname);
234 memcpy (full_fname, fname, fname_len);
235 memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
237 fp = fopen (full_fname, "r");
238 if (fp == NULL)
240 FREE_BLOCKS (block_list);
241 return 0;
244 added = 0;
245 while (!feof (fp))
247 /* It is a reasonable approach to use a fix buffer here because
248 a) we are only interested in the first two fields
249 b) these fields must be usable as file names and so must not
250 be that long
252 char buf[BUFSIZ];
253 char *alias;
254 char *value;
255 char *cp;
257 if (fgets (buf, BUFSIZ, fp) == NULL)
258 /* EOF reached. */
259 break;
261 cp = buf;
262 /* Ignore leading white space. */
263 while (isspace (cp[0]))
264 ++cp;
266 /* A leading '#' signals a comment line. */
267 if (cp[0] != '\0' && cp[0] != '#')
269 alias = cp++;
270 while (cp[0] != '\0' && !isspace (cp[0]))
271 ++cp;
272 /* Terminate alias name. */
273 if (cp[0] != '\0')
274 *cp++ = '\0';
276 /* Now look for the beginning of the value. */
277 while (isspace (cp[0]))
278 ++cp;
280 if (cp[0] != '\0')
282 char *tp;
283 size_t alias_len;
284 size_t value_len;
286 value = cp++;
287 while (cp[0] != '\0' && !isspace (cp[0]))
288 ++cp;
289 /* Terminate value. */
290 if (cp[0] == '\n')
292 /* This has to be done to make the following test
293 for the end of line possible. We are looking for
294 the terminating '\n' which do not overwrite here. */
295 *cp++ = '\0';
296 *cp = '\n';
298 else if (cp[0] != '\0')
299 *cp++ = '\0';
301 if (nmap >= maxmap)
302 extend_alias_table ();
304 alias_len = strlen (alias) + 1;
305 value_len = strlen (value) + 1;
307 if (string_space_act + alias_len + value_len > string_space_max)
309 /* Increase size of memory pool. */
310 size_t new_size = (string_space_max
311 + (alias_len + value_len > 1024
312 ? alias_len + value_len : 1024));
313 char *new_pool = (char *) realloc (string_space, new_size);
314 if (new_pool == NULL)
316 FREE_BLOCKS (block_list);
317 return added;
319 string_space = new_pool;
320 string_space_max = new_size;
323 map[nmap].alias = memcpy (&string_space[string_space_act],
324 alias, alias_len);
325 string_space_act += alias_len;
327 map[nmap].alias = memcpy (&string_space[string_space_act],
328 value, value_len);
329 string_space_act += value_len;
331 ++nmap;
332 ++added;
336 /* Possibly not the whole line fits into the buffer. Ignore
337 the rest of the line. */
338 while (strchr (cp, '\n') == NULL)
340 cp = buf;
341 if (fgets (buf, BUFSIZ, fp) == NULL)
342 /* Make sure the inner loop will be left. The outer loop
343 will exit at the `feof' test. */
344 *cp = '\n';
348 /* Should we test for ferror()? I think we have to silently ignore
349 errors. --drepper */
350 fclose (fp);
352 if (added > 0)
353 qsort (map, nmap, sizeof (struct alias_map),
354 (int (*) PARAMS ((const void *, const void *))) alias_compare);
356 FREE_BLOCKS (block_list);
357 return added;
361 static void
362 extend_alias_table ()
364 size_t new_size;
365 struct alias_map *new_map;
367 new_size = maxmap == 0 ? 100 : 2 * maxmap;
368 new_map = (struct alias_map *) realloc (map, (new_size
369 * sizeof (struct alias_map)));
370 if (new_map == NULL)
371 /* Simply don't extend: we don't have any more core. */
372 return;
374 map = new_map;
375 maxmap = new_size;
379 #ifdef _LIBC
380 static void __attribute__ ((unused))
381 free_mem (void)
383 if (string_space != NULL)
384 free (string_space);
385 if (map != NULL)
386 free (map);
388 text_set_element (__libc_subfreeres, free_mem);
389 #endif
392 static int
393 alias_compare (map1, map2)
394 const struct alias_map *map1;
395 const struct alias_map *map2;
397 #if defined _LIBC || defined HAVE_STRCASECMP
398 return strcasecmp (map1->alias, map2->alias);
399 #else
400 const unsigned char *p1 = (const unsigned char *) map1->alias;
401 const unsigned char *p2 = (const unsigned char *) map2->alias;
402 unsigned char c1, c2;
404 if (p1 == p2)
405 return 0;
409 /* I know this seems to be odd but the tolower() function in
410 some systems libc cannot handle nonalpha characters. */
411 c1 = isupper (*p1) ? tolower (*p1) : *p1;
412 c2 = isupper (*p2) ? tolower (*p2) : *p2;
413 if (c1 == '\0')
414 break;
415 ++p1;
416 ++p2;
418 while (c1 == c2);
420 return c1 - c2;
421 #endif