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