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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17    USA.  */
18 
19 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
20    This must come before <autoconf.h> because <autoconf.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 <autoconf.h>
28 #endif
29 
30 #undef freea
31 
32 /* see AC_FUNC_ALLOCA macro */
33 #ifdef __GNUC__
34 # define alloca __builtin_alloca
35 #else
36 # ifdef _MSC_VER
37 #  include <malloc.h>
38 #  define alloca _alloca
39 # else
40 #  if HAVE_ALLOCA_H
41 #   include <alloca.h>
42 #  else
43 #   ifdef _AIX
44  #pragma alloca
45 #   else
46 #    ifndef alloca /* predefined by HP cc +Olibcalls */
47 char *alloca ();
48 #    else
49 #     define freea(n) free(n)
50 #    endif
51 #   endif
52 #  endif
53 # endif
54 #endif
55 
56 #ifndef freea
57 #define freea(n) /* nothing */
58 #endif
59 
60 #include <ctype.h>
61 #include <stdio.h>
62 #include <sys/types.h>
63 
64 #include <stdlib.h>
65 
66 #include <string.h>
67 #if !HAVE_STRCHR && !defined _LIBC
68 # ifndef strchr
69 #  define strchr index
70 # endif
71 #endif
72 
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 # ifndef mempcpy
84 #  define mempcpy __mempcpy
85 # endif
86 # define HAVE_MEMPCPY	1
87 
88 /* We need locking here since we can be called from different places.  */
89 # include <bits/libc-lock.h>
90 
91 __libc_lock_define_initialized (static, lock);
92 #endif
93 
94 #ifndef internal_function
95 # define internal_function
96 #endif
97 
98 #if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED
99 # undef fgets
100 # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
101 #endif
102 #if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED
103 # undef feof
104 # define feof(s) feof_unlocked (s)
105 #endif
106 
107 
108 struct alias_map
109 {
110   const char *alias;
111   const char *value;
112 };
113 
114 
115 static char *string_space;
116 static size_t string_space_act;
117 static size_t string_space_max;
118 static struct alias_map *map;
119 static size_t nmap;
120 static size_t maxmap;
121 
122 
123 /* Prototypes for local functions.  */
124 static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
125      internal_function;
126 static int extend_alias_table PARAMS ((void));
127 static int alias_compare PARAMS ((const struct alias_map *map1,
128 				  const struct alias_map *map2));
129 
130 
131 const char *
_nl_expand_alias(name)132 _nl_expand_alias (name)
133     const char *name;
134 {
135   static const char *locale_alias_path = LOCALE_ALIAS_PATH;
136   struct alias_map *retval;
137   const char *result = NULL;
138   size_t added;
139 
140 #ifdef _LIBC
141   __libc_lock_lock (lock);
142 #endif
143 
144   do
145     {
146       struct alias_map item;
147 
148       item.alias = name;
149 
150       if (nmap > 0)
151 	retval = (struct alias_map *) bsearch (&item, map, nmap,
152 					       sizeof (struct alias_map),
153 					       (int (*) PARAMS ((const void *,
154 								 const void *))
155 						) alias_compare);
156       else
157 	retval = NULL;
158 
159       /* We really found an alias.  Return the value.  */
160       if (retval != NULL)
161 	{
162 	  result = retval->value;
163 	  break;
164 	}
165 
166       /* Perhaps we can find another alias file.  */
167       added = 0;
168       while (added == 0 && locale_alias_path[0] != '\0')
169 	{
170 	  const char *start;
171 
172 	  while (locale_alias_path[0] == PATH_SEPARATOR)
173 	    ++locale_alias_path;
174 	  start = locale_alias_path;
175 
176 	  while (locale_alias_path[0] != '\0'
177 		 && locale_alias_path[0] != PATH_SEPARATOR)
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 #ifdef _LIBC
187   __libc_lock_unlock (lock);
188 #endif
189 
190   return result;
191 }
192 
193 
194 static size_t
195 internal_function
read_alias_file(fname,fname_len)196 read_alias_file (fname, fname_len)
197      const char *fname;
198      int fname_len;
199 {
200   FILE *fp;
201   char *full_fname;
202   size_t added;
203   static const char aliasfile[] = "/locale.alias";
204 
205   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
206 #ifdef HAVE_MEMPCPY
207   mempcpy (mempcpy (full_fname, fname, fname_len),
208 	   aliasfile, sizeof aliasfile);
209 #else
210   memcpy (full_fname, fname, fname_len);
211   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
212 #endif
213 
214   fp = fopen (full_fname, "r");
215   freea (full_fname);
216   if (fp == NULL)
217     return 0;
218 
219   added = 0;
220   while (!feof (fp))
221     {
222       /* It is a reasonable approach to use a fix buffer here because
223 	 a) we are only interested in the first two fields
224 	 b) these fields must be usable as file names and so must not
225 	    be that long
226        */
227       char buf[BUFSIZ];
228       char *alias;
229       char *value;
230       char *cp;
231 
232       if (fgets (buf, sizeof buf, fp) == NULL)
233 	/* EOF reached.  */
234 	break;
235 
236       /* Possibly not the whole line fits into the buffer.  Ignore
237 	 the rest of the line.  */
238       if (strchr (buf, '\n') == NULL)
239 	{
240 	  char altbuf[BUFSIZ];
241 	  do
242 	    if (fgets (altbuf, sizeof altbuf, fp) == NULL)
243 	      /* Make sure the inner loop will be left.  The outer loop
244 		 will exit at the `feof' test.  */
245 	      break;
246 	  while (strchr (altbuf, '\n') == NULL);
247 	}
248 
249       cp = buf;
250       /* Ignore leading white space.  */
251       while (isspace (cp[0]))
252 	++cp;
253 
254       /* A leading '#' signals a comment line.  */
255       if (cp[0] != '\0' && cp[0] != '#')
256 	{
257 	  alias = cp++;
258 	  while (cp[0] != '\0' && !isspace (cp[0]))
259 	    ++cp;
260 	  /* Terminate alias name.  */
261 	  if (cp[0] != '\0')
262 	    *cp++ = '\0';
263 
264 	  /* Now look for the beginning of the value.  */
265 	  while (isspace (cp[0]))
266 	    ++cp;
267 
268 	  if (cp[0] != '\0')
269 	    {
270 	      size_t alias_len;
271 	      size_t value_len;
272 
273 	      value = cp++;
274 	      while (cp[0] != '\0' && !isspace (cp[0]))
275 		++cp;
276 	      /* Terminate value.  */
277 	      if (cp[0] == '\n')
278 		{
279 		  /* This has to be done to make the following test
280 		     for the end of line possible.  We are looking for
281 		     the terminating '\n' which do not overwrite here.  */
282 		  *cp++ = '\0';
283 		  *cp = '\n';
284 		}
285 	      else if (cp[0] != '\0')
286 		*cp++ = '\0';
287 
288 	      if (nmap >= maxmap)
289 		if (__builtin_expect (extend_alias_table (), 0))
290 		  return added;
291 
292 	      alias_len = strlen (alias) + 1;
293 	      value_len = strlen (value) + 1;
294 
295 	      if (string_space_act + alias_len + value_len > string_space_max)
296 		{
297 		  /* Increase size of memory pool.  */
298 		  size_t new_size = (string_space_max
299 				     + (alias_len + value_len > 1024
300 					? alias_len + value_len : 1024));
301 		  char *new_pool = (char *) realloc (string_space, new_size);
302 		  if (new_pool == NULL)
303 		    return added;
304 
305 		  if (__builtin_expect (string_space != new_pool, 0))
306 		    {
307 		      size_t i;
308 
309 		      for (i = 0; i < nmap; i++)
310 			{
311 			  map[i].alias += new_pool - string_space;
312 			  map[i].value += new_pool - string_space;
313 			}
314 		    }
315 
316 		  string_space = new_pool;
317 		  string_space_max = new_size;
318 		}
319 
320 	      map[nmap].alias = memcpy (&string_space[string_space_act],
321 					alias, alias_len);
322 	      string_space_act += alias_len;
323 
324 	      map[nmap].value = memcpy (&string_space[string_space_act],
325 					value, value_len);
326 	      string_space_act += value_len;
327 
328 	      ++nmap;
329 	      ++added;
330 	    }
331 	}
332     }
333 
334   /* Should we test for ferror()?  I think we have to silently ignore
335      errors.  --drepper  */
336   fclose (fp);
337 
338   if (added > 0)
339     qsort (map, nmap, sizeof (struct alias_map),
340 	   (int (*) PARAMS ((const void *, const void *))) alias_compare);
341 
342   return added;
343 }
344 
345 
346 static int
extend_alias_table()347 extend_alias_table ()
348 {
349   size_t new_size;
350   struct alias_map *new_map;
351 
352   new_size = maxmap == 0 ? 100 : 2 * maxmap;
353   new_map = (struct alias_map *) realloc (map, (new_size
354 						* sizeof (struct alias_map)));
355   if (new_map == NULL)
356     /* Simply don't extend: we don't have any more core.  */
357     return -1;
358 
359   map = new_map;
360   maxmap = new_size;
361   return 0;
362 }
363 
364 
365 #ifdef _LIBC
366 static void __attribute__ ((unused))
free_mem(void)367 free_mem (void)
368 {
369   if (string_space != NULL)
370     free (string_space);
371   if (map != NULL)
372     free (map);
373 }
374 text_set_element (__libc_subfreeres, free_mem);
375 #endif
376 
377 
378 static int
alias_compare(map1,map2)379 alias_compare (map1, map2)
380      const struct alias_map *map1;
381      const struct alias_map *map2;
382 {
383 #if defined _LIBC || defined HAVE_STRCASECMP
384   return strcasecmp (map1->alias, map2->alias);
385 #else
386   const unsigned char *p1 = (const unsigned char *) map1->alias;
387   const unsigned char *p2 = (const unsigned char *) map2->alias;
388   unsigned char c1, c2;
389 
390   if (p1 == p2)
391     return 0;
392 
393   do
394     {
395       /* I know this seems to be odd but the tolower() function in
396 	 some systems libc cannot handle nonalpha characters.  */
397       c1 = isupper (*p1) ? tolower (*p1) : *p1;
398       c2 = isupper (*p2) ? tolower (*p2) : *p2;
399       if (c1 == '\0')
400 	break;
401       ++p1;
402       ++p2;
403     }
404   while (c1 == c2);
405 
406   return c1 - c2;
407 #endif
408 }
409