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