]> git.ipfire.org Git - thirdparty/glibc.git/blob - intl/localealias.c
Update.
[thirdparty/glibc.git] / intl / localealias.c
1 /* Handle aliases for locale names.
2 Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
3
4 This file is part of the GNU C Library. Its master source is NOT part of
5 the C library, however.
6
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.
11
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.
16
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. */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <ctype.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29
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
46
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
57
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
74
75 #include "gettext.h"
76 #include "gettextP.h"
77
78 /* @@ end of prolog @@ */
79
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
86
87 /* We need locking here since we can be called from different palces. */
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 /* 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
105 {
106 void *address;
107 struct block_list *next;
108 };
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; \
118 } \
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); \
126 } \
127 } while (0)
128 # undef alloca
129 # define alloca(size) (malloc (size))
130 #endif /* have alloca */
131
132
133 struct alias_map
134 {
135 const char *alias;
136 const char *value;
137 };
138
139
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;
146
147
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));
154
155
156 const char *
157 _nl_expand_alias (name)
158 const char *name;
159 {
160 static const char *locale_alias_path = LOCALE_ALIAS_PATH;
161 struct alias_map *retval;
162 const char *result = NULL;
163 size_t added;
164
165 #ifdef _LIBC
166 __libc_lock_lock (lock);
167 #endif
168
169 do
170 {
171 struct alias_map item;
172
173 item.alias = name;
174
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;
183
184 /* We really found an alias. Return the value. */
185 if (retval != NULL)
186 {
187 result = retval->value;
188 break;
189 }
190
191 /* Perhaps we can find another alias file. */
192 added = 0;
193 while (added == 0 && locale_alias_path[0] != '\0')
194 {
195 const char *start;
196
197 while (locale_alias_path[0] == ':')
198 ++locale_alias_path;
199 start = locale_alias_path;
200
201 while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':')
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 #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";
231
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);
236
237 fp = fopen (full_fname, "r");
238 if (fp == NULL)
239 {
240 FREE_BLOCKS (block_list);
241 return 0;
242 }
243
244 added = 0;
245 while (!feof (fp))
246 {
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
251 */
252 char buf[BUFSIZ];
253 char *alias;
254 char *value;
255 char *cp;
256
257 if (fgets (buf, BUFSIZ, fp) == NULL)
258 /* EOF reached. */
259 break;
260
261 cp = buf;
262 /* Ignore leading white space. */
263 while (isspace (cp[0]))
264 ++cp;
265
266 /* A leading '#' signals a comment line. */
267 if (cp[0] != '\0' && cp[0] != '#')
268 {
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';
275
276 /* Now look for the beginning of the value. */
277 while (isspace (cp[0]))
278 ++cp;
279
280 if (cp[0] != '\0')
281 {
282 char *tp;
283 size_t alias_len;
284 size_t value_len;
285
286 value = cp++;
287 while (cp[0] != '\0' && !isspace (cp[0]))
288 ++cp;
289 /* Terminate value. */
290 if (cp[0] == '\n')
291 {
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';
297 }
298 else if (cp[0] != '\0')
299 *cp++ = '\0';
300
301 if (nmap >= maxmap)
302 extend_alias_table ();
303
304 alias_len = strlen (alias) + 1;
305 value_len = strlen (value) + 1;
306
307 if (string_space_act + alias_len + value_len > string_space_max)
308 {
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)
315 {
316 FREE_BLOCKS (block_list);
317 return added;
318 }
319 string_space = new_pool;
320 string_space_max = new_size;
321 }
322
323 map[nmap].alias = memcpy (&string_space[string_space_act],
324 alias, alias_len);
325 string_space_act += alias_len;
326
327 map[nmap].alias = memcpy (&string_space[string_space_act],
328 value, value_len);
329 string_space_act += value_len;
330
331 ++nmap;
332 ++added;
333 }
334 }
335
336 /* Possibly not the whole line fits into the buffer. Ignore
337 the rest of the line. */
338 while (strchr (cp, '\n') == NULL)
339 {
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';
345 }
346 }
347
348 /* Should we test for ferror()? I think we have to silently ignore
349 errors. --drepper */
350 fclose (fp);
351
352 if (added > 0)
353 qsort (map, nmap, sizeof (struct alias_map),
354 (int (*) PARAMS ((const void *, const void *))) alias_compare);
355
356 FREE_BLOCKS (block_list);
357 return added;
358 }
359
360
361 static void
362 extend_alias_table ()
363 {
364 size_t new_size;
365 struct alias_map *new_map;
366
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;
373
374 map = new_map;
375 maxmap = new_size;
376 }
377
378
379 #ifdef _LIBC
380 static void __attribute__ ((unused))
381 free_mem (void)
382 {
383 if (string_space != NULL)
384 free (string_space);
385 if (map != NULL)
386 free (map);
387 }
388 text_set_element (__libc_subfreeres, free_mem);
389 #endif
390
391
392 static int
393 alias_compare (map1, map2)
394 const struct alias_map *map1;
395 const struct alias_map *map2;
396 {
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;
403
404 if (p1 == p2)
405 return 0;
406
407 do
408 {
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;
417 }
418 while (c1 == c2);
419
420 return c1 - c2;
421 #endif
422 }