Added bigprefix1.exp.
[findutils.git] / intl / localealias.c
blobd62c0b2051cc4501989fd7d270739b4903d44aa0
1 /* Handle aliases for locale names.
2 Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
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
25 #ifdef HAVE_CONFIG_H
26 # include <config.h>
27 #endif
29 #include <ctype.h>
30 #include <stdio.h>
31 #include <sys/types.h>
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
50 #include <stdlib.h>
52 #include <string.h>
53 #if !HAVE_STRCHR && !defined _LIBC
54 # ifndef strchr
55 # define strchr index
56 # endif
57 #endif
59 #include "gettextP.h"
61 /* @@ end of prolog @@ */
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
69 # ifndef mempcpy
70 # define mempcpy __mempcpy
71 # endif
72 # define HAVE_MEMPCPY 1
74 /* We need locking here since we can be called from different places. */
75 # include <bits/libc-lock.h>
77 __libc_lock_define_initialized (static, lock);
78 #endif
80 #ifndef internal_function
81 # define internal_function
82 #endif
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
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
103 struct alias_map
105 const char *alias;
106 const char *value;
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;
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));
126 const char *
127 _nl_expand_alias (name)
128 const char *name;
130 static const char *locale_alias_path = LOCALE_ALIAS_PATH;
131 struct alias_map *retval;
132 const char *result = NULL;
133 size_t added;
135 #ifdef _LIBC
136 __libc_lock_lock (lock);
137 #endif
141 struct alias_map item;
143 item.alias = name;
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;
154 /* We really found an alias. Return the value. */
155 if (retval != NULL)
157 result = retval->value;
158 break;
161 /* Perhaps we can find another alias file. */
162 added = 0;
163 while (added == 0 && locale_alias_path[0] != '\0')
165 const char *start;
167 while (locale_alias_path[0] == PATH_SEPARATOR)
168 ++locale_alias_path;
169 start = locale_alias_path;
171 while (locale_alias_path[0] != '\0'
172 && locale_alias_path[0] != PATH_SEPARATOR)
173 ++locale_alias_path;
175 if (start < locale_alias_path)
176 added = read_alias_file (start, locale_alias_path - start);
179 while (added != 0);
181 #ifdef _LIBC
182 __libc_lock_unlock (lock);
183 #endif
185 return result;
189 static size_t
190 internal_function
191 read_alias_file (fname, fname_len)
192 const char *fname;
193 int fname_len;
195 FILE *fp;
196 char *full_fname;
197 size_t added;
198 static const char aliasfile[] = "/locale.alias";
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
209 fp = fopen (full_fname, "r");
210 freea (full_fname);
211 if (fp == NULL)
212 return 0;
214 added = 0;
215 while (!feof (fp))
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
222 char buf[BUFSIZ];
223 char *alias;
224 char *value;
225 char *cp;
227 if (fgets (buf, sizeof buf, fp) == NULL)
228 /* EOF reached. */
229 break;
231 /* Possibly not the whole line fits into the buffer. Ignore
232 the rest of the line. */
233 if (strchr (buf, '\n') == NULL)
235 char altbuf[BUFSIZ];
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);
244 cp = buf;
245 /* Ignore leading white space. */
246 while (isspace (cp[0]))
247 ++cp;
249 /* A leading '#' signals a comment line. */
250 if (cp[0] != '\0' && cp[0] != '#')
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';
259 /* Now look for the beginning of the value. */
260 while (isspace (cp[0]))
261 ++cp;
263 if (cp[0] != '\0')
265 size_t alias_len;
266 size_t value_len;
268 value = cp++;
269 while (cp[0] != '\0' && !isspace (cp[0]))
270 ++cp;
271 /* Terminate value. */
272 if (cp[0] == '\n')
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';
280 else if (cp[0] != '\0')
281 *cp++ = '\0';
283 if (nmap >= maxmap)
284 if (__builtin_expect (extend_alias_table (), 0))
285 return added;
287 alias_len = strlen (alias) + 1;
288 value_len = strlen (value) + 1;
290 if (string_space_act + alias_len + value_len > string_space_max)
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;
300 if (__builtin_expect (string_space != new_pool, 0))
302 size_t i;
304 for (i = 0; i < nmap; i++)
306 map[i].alias += new_pool - string_space;
307 map[i].value += new_pool - string_space;
311 string_space = new_pool;
312 string_space_max = new_size;
315 map[nmap].alias = memcpy (&string_space[string_space_act],
316 alias, alias_len);
317 string_space_act += alias_len;
319 map[nmap].value = memcpy (&string_space[string_space_act],
320 value, value_len);
321 string_space_act += value_len;
323 ++nmap;
324 ++added;
329 /* Should we test for ferror()? I think we have to silently ignore
330 errors. --drepper */
331 fclose (fp);
333 if (added > 0)
334 qsort (map, nmap, sizeof (struct alias_map),
335 (int (*) PARAMS ((const void *, const void *))) alias_compare);
337 return added;
341 static int
342 extend_alias_table ()
344 size_t new_size;
345 struct alias_map *new_map;
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;
354 map = new_map;
355 maxmap = new_size;
356 return 0;
360 #ifdef _LIBC
361 static void __attribute__ ((unused))
362 free_mem (void)
364 if (string_space != NULL)
365 free (string_space);
366 if (map != NULL)
367 free (map);
369 text_set_element (__libc_subfreeres, free_mem);
370 #endif
373 static int
374 alias_compare (map1, map2)
375 const struct alias_map *map1;
376 const struct alias_map *map2;
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;
385 if (p1 == p2)
386 return 0;
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;
399 while (c1 == c2);
401 return c1 - c2;
402 #endif