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