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