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