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