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