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