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