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