Commit | Line | Data |
---|---|---|
7421c9f1 TT |
1 | /* Handle aliases for locale names |
2 | Copyright (C) 1995, 1996, 1997 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 | ||
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 | ||
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 | # 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 | #endif | |
83 | ||
84 | ||
85 | /* For those loosing systems which don't have `alloca' we have to add | |
86 | some additional code emulating it. */ | |
87 | #ifdef HAVE_ALLOCA | |
88 | /* Nothing has to be done. */ | |
89 | # define ADD_BLOCK(list, address) /* nothing */ | |
90 | # define FREE_BLOCKS(list) /* nothing */ | |
91 | #else | |
92 | struct block_list | |
93 | { | |
94 | void *address; | |
95 | struct block_list *next; | |
96 | }; | |
97 | # define ADD_BLOCK(list, addr) \ | |
98 | do { \ | |
99 | struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \ | |
100 | /* If we cannot get a free block we cannot add the new element to \ | |
101 | the list. */ \ | |
102 | if (newp != NULL) { \ | |
103 | newp->address = (addr); \ | |
104 | newp->next = (list); \ | |
105 | (list) = newp; \ | |
106 | } \ | |
107 | } while (0) | |
108 | # define FREE_BLOCKS(list) \ | |
109 | do { \ | |
110 | while (list != NULL) { \ | |
111 | struct block_list *old = list; \ | |
112 | list = list->next; \ | |
113 | free (old); \ | |
114 | } \ | |
115 | } while (0) | |
116 | # undef alloca | |
117 | # define alloca(size) (malloc (size)) | |
118 | #endif /* have alloca */ | |
119 | ||
120 | ||
121 | struct alias_map | |
122 | { | |
123 | const char *alias; | |
124 | const char *value; | |
125 | }; | |
126 | ||
127 | ||
128 | static struct alias_map *map; | |
129 | static size_t nmap = 0; | |
130 | static size_t maxmap = 0; | |
131 | ||
132 | ||
133 | /* Prototypes for local functions. */ | |
134 | static size_t read_alias_file PARAMS ((const char *fname, int fname_len)); | |
135 | static void extend_alias_table PARAMS ((void)); | |
136 | static int alias_compare PARAMS ((const struct alias_map *map1, | |
137 | const struct alias_map *map2)); | |
138 | ||
139 | ||
140 | const char * | |
141 | _nl_expand_alias (name) | |
142 | const char *name; | |
143 | { | |
144 | static const char *locale_alias_path = LOCALE_ALIAS_PATH; | |
145 | struct alias_map *retval; | |
146 | size_t added; | |
147 | ||
148 | do | |
149 | { | |
150 | struct alias_map item; | |
151 | ||
152 | item.alias = name; | |
153 | ||
154 | if (nmap > 0) | |
155 | retval = (struct alias_map *) bsearch (&item, map, nmap, | |
156 | sizeof (struct alias_map), | |
157 | (int (*) PARAMS ((const void *, | |
158 | const void *)) | |
159 | ) alias_compare); | |
160 | else | |
161 | retval = NULL; | |
162 | ||
163 | /* We really found an alias. Return the value. */ | |
164 | if (retval != NULL) | |
165 | return retval->value; | |
166 | ||
167 | /* Perhaps we can find another alias file. */ | |
168 | added = 0; | |
169 | while (added == 0 && locale_alias_path[0] != '\0') | |
170 | { | |
171 | const char *start; | |
172 | ||
173 | while (locale_alias_path[0] == ':') | |
174 | ++locale_alias_path; | |
175 | start = locale_alias_path; | |
176 | ||
177 | while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':') | |
178 | ++locale_alias_path; | |
179 | ||
180 | if (start < locale_alias_path) | |
181 | added = read_alias_file (start, locale_alias_path - start); | |
182 | } | |
183 | } | |
184 | while (added != 0); | |
185 | ||
186 | return NULL; | |
187 | } | |
188 | ||
189 | ||
190 | static size_t | |
191 | read_alias_file (fname, fname_len) | |
192 | const char *fname; | |
193 | int fname_len; | |
194 | { | |
195 | #ifndef HAVE_ALLOCA | |
196 | struct block_list *block_list = NULL; | |
197 | #endif | |
198 | FILE *fp; | |
199 | char *full_fname; | |
200 | size_t added; | |
201 | static const char aliasfile[] = "/locale.alias"; | |
202 | ||
203 | full_fname = (char *) alloca (fname_len + sizeof aliasfile); | |
204 | ADD_BLOCK (block_list, full_fname); | |
205 | memcpy (full_fname, fname, fname_len); | |
206 | memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile); | |
207 | ||
208 | fp = fopen (full_fname, "r"); | |
209 | if (fp == NULL) | |
210 | { | |
211 | FREE_BLOCKS (block_list); | |
212 | return 0; | |
213 | } | |
214 | ||
215 | added = 0; | |
216 | while (!feof (fp)) | |
217 | { | |
218 | /* It is a reasonable approach to use a fix buffer here because | |
219 | a) we are only interested in the first two fields | |
220 | b) these fields must be usable as file names and so must not | |
221 | be that long | |
222 | */ | |
223 | char buf[BUFSIZ]; | |
224 | char *alias; | |
225 | char *value; | |
226 | char *cp; | |
227 | ||
228 | if (fgets (buf, BUFSIZ, fp) == NULL) | |
229 | /* EOF reached. */ | |
230 | break; | |
231 | ||
232 | cp = buf; | |
233 | /* Ignore leading white space. */ | |
234 | while (isspace (cp[0])) | |
235 | ++cp; | |
236 | ||
237 | /* A leading '#' signals a comment line. */ | |
238 | if (cp[0] != '\0' && cp[0] != '#') | |
239 | { | |
240 | alias = cp++; | |
241 | while (cp[0] != '\0' && !isspace (cp[0])) | |
242 | ++cp; | |
243 | /* Terminate alias name. */ | |
244 | if (cp[0] != '\0') | |
245 | *cp++ = '\0'; | |
246 | ||
247 | /* Now look for the beginning of the value. */ | |
248 | while (isspace (cp[0])) | |
249 | ++cp; | |
250 | ||
251 | if (cp[0] != '\0') | |
252 | { | |
253 | char *tp; | |
254 | size_t len; | |
255 | ||
256 | value = cp++; | |
257 | while (cp[0] != '\0' && !isspace (cp[0])) | |
258 | ++cp; | |
259 | /* Terminate value. */ | |
260 | if (cp[0] == '\n') | |
261 | { | |
262 | /* This has to be done to make the following test | |
263 | for the end of line possible. We are looking for | |
264 | the terminating '\n' which do not overwrite here. */ | |
265 | *cp++ = '\0'; | |
266 | *cp = '\n'; | |
267 | } | |
268 | else if (cp[0] != '\0') | |
269 | *cp++ = '\0'; | |
270 | ||
271 | if (nmap >= maxmap) | |
272 | extend_alias_table (); | |
273 | ||
274 | /* We cannot depend on strdup available in the libc. Sigh! */ | |
275 | len = strlen (alias) + 1; | |
276 | tp = (char *) malloc (len); | |
277 | if (tp == NULL) | |
278 | { | |
279 | FREE_BLOCKS (block_list); | |
280 | return added; | |
281 | } | |
282 | memcpy (tp, alias, len); | |
283 | map[nmap].alias = tp; | |
284 | ||
285 | len = strlen (value) + 1; | |
286 | tp = (char *) malloc (len); | |
287 | if (tp == NULL) | |
288 | { | |
289 | FREE_BLOCKS (block_list); | |
290 | return added; | |
291 | } | |
292 | memcpy (tp, value, len); | |
293 | map[nmap].value = tp; | |
294 | ||
295 | ++nmap; | |
296 | ++added; | |
297 | } | |
298 | } | |
299 | ||
300 | /* Possibly not the whole line fits into the buffer. Ignore | |
301 | the rest of the line. */ | |
302 | while (strchr (cp, '\n') == NULL) | |
303 | { | |
304 | cp = buf; | |
305 | if (fgets (buf, BUFSIZ, fp) == NULL) | |
306 | /* Make sure the inner loop will be left. The outer loop | |
307 | will exit at the `feof' test. */ | |
308 | *cp = '\n'; | |
309 | } | |
310 | } | |
311 | ||
312 | /* Should we test for ferror()? I think we have to silently ignore | |
313 | errors. --drepper */ | |
314 | fclose (fp); | |
315 | ||
316 | if (added > 0) | |
317 | qsort (map, nmap, sizeof (struct alias_map), | |
318 | (int (*) PARAMS ((const void *, const void *))) alias_compare); | |
319 | ||
320 | FREE_BLOCKS (block_list); | |
321 | return added; | |
322 | } | |
323 | ||
324 | ||
325 | static void | |
326 | extend_alias_table () | |
327 | { | |
328 | size_t new_size; | |
329 | struct alias_map *new_map; | |
330 | ||
331 | new_size = maxmap == 0 ? 100 : 2 * maxmap; | |
332 | new_map = (struct alias_map *) malloc (new_size | |
333 | * sizeof (struct alias_map)); | |
334 | if (new_map == NULL) | |
335 | /* Simply don't extend: we don't have any more core. */ | |
336 | return; | |
337 | ||
338 | memcpy (new_map, map, nmap * sizeof (struct alias_map)); | |
339 | ||
340 | if (maxmap != 0) | |
341 | free (map); | |
342 | ||
343 | map = new_map; | |
344 | maxmap = new_size; | |
345 | } | |
346 | ||
347 | ||
348 | static int | |
349 | alias_compare (map1, map2) | |
350 | const struct alias_map *map1; | |
351 | const struct alias_map *map2; | |
352 | { | |
353 | #if defined _LIBC || defined HAVE_STRCASECMP | |
354 | return strcasecmp (map1->alias, map2->alias); | |
355 | #else | |
356 | const unsigned char *p1 = (const unsigned char *) map1->alias; | |
357 | const unsigned char *p2 = (const unsigned char *) map2->alias; | |
358 | unsigned char c1, c2; | |
359 | ||
360 | if (p1 == p2) | |
361 | return 0; | |
362 | ||
363 | do | |
364 | { | |
365 | /* I know this seems to be odd but the tolower() function in | |
366 | some systems libc cannot handle nonalpha characters. */ | |
367 | c1 = isupper (*p1) ? tolower (*p1) : *p1; | |
368 | c2 = isupper (*p2) ? tolower (*p2) : *p2; | |
369 | if (c1 == '\0') | |
370 | break; | |
371 | ++p1; | |
372 | ++p2; | |
373 | } | |
374 | while (c1 == c2); | |
375 | ||
376 | return c1 - c2; | |
377 | #endif | |
378 | } |