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