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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, 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 
83 # define mempcpy __mempcpy
84 # define HAVE_MEMPCPY	1
85 
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   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 
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 static int alias_compare PARAMS ((const struct alias_map *map1,
149 				  const struct alias_map *map2));
150 
151 
152 const char *
_nl_expand_alias(name)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       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 	{
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 }
212 
213 
214 static size_t
215 internal_function
read_alias_file(fname,fname_len)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 	   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       unsigned char buf[BUFSIZ];
254       unsigned char *alias;
255       unsigned 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 
275       cp = 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 = 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 	    {
296 	      size_t alias_len;
297 	      size_t value_len;
298 
299 	      value = 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 
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 					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   FREE_BLOCKS (block_list);
359   return added;
360 }
361 
362 
363 static void
extend_alias_table()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 
380 
381 #ifdef _LIBC
382 static void __attribute__ ((unused))
free_mem(void)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
alias_compare(map1,map2)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   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 
422   return c1 - c2;
423 #endif
424 }
425