]> git.ipfire.org Git - thirdparty/glibc.git/blame - intl/localealias.c
Fix localealias.c fgets_unlocked namespace (bug 17589).
[thirdparty/glibc.git] / intl / localealias.c
CommitLineData
c84142e8 1/* Handle aliases for locale names.
d4697bc9 2 Copyright (C) 1995-2014 Free Software Foundation, Inc.
41bdb6e2 3 This file is part of the GNU C Library.
24906b43 4
c84142e8 5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
0393dfd6 9
c84142e8
UD
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 13 Lesser General Public License for more details.
24906b43 14
41bdb6e2 15 You should have received a copy of the GNU Lesser General Public
59ba27a6
PE
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
24906b43 18
17c389fc
UD
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
24906b43
RM
26#ifdef HAVE_CONFIG_H
27# include <config.h>
28#endif
29
30#include <ctype.h>
31#include <stdio.h>
2706ee38
UD
32#if defined _LIBC || defined HAVE___FSETLOCKING
33# include <stdio_ext.h>
34#endif
24906b43
RM
35#include <sys/types.h>
36
37#ifdef __GNUC__
0f283ffc 38# undef alloca
24906b43 39# define alloca __builtin_alloca
fa00327f 40# define HAVE_ALLOCA 1
24906b43
RM
41#else
42# if defined HAVE_ALLOCA_H || defined _LIBC
43# include <alloca.h>
44# else
45# ifdef _AIX
46 #pragma alloca
47# else
48# ifndef alloca
49char *alloca ();
50# endif
51# endif
52# endif
53#endif
54
0555fcce
UD
55#include <stdlib.h>
56#include <string.h>
24906b43 57
24906b43
RM
58#include "gettextP.h"
59
60/* @@ end of prolog @@ */
61
62#ifdef _LIBC
63/* Rename the non ANSI C functions. This is required by the standard
64 because some ANSI C functions will require linking with this object
65 file and the name space must not be polluted. */
5f078c32 66# define strcasecmp(s1, s2) __strcasecmp_l (s1, s2, _nl_C_locobj_ptr)
40a55d20 67
e7c5513d
UD
68# ifndef mempcpy
69# define mempcpy __mempcpy
70# endif
1618c590 71# define HAVE_MEMPCPY 1
2706ee38 72# define HAVE___FSETLOCKING 1
1618c590 73
838e5ffe 74/* We need locking here since we can be called from different places. */
f41c8091 75# include <bits/libc-lock.h>
40a55d20
UD
76
77__libc_lock_define_initialized (static, lock);
24906b43
RM
78#endif
79
dfd2257a
UD
80#ifndef internal_function
81# define internal_function
82#endif
5f2eab42 83
2706ee38
UD
84/* Some optimizations for glibc. */
85#ifdef _LIBC
86# define FEOF(fp) feof_unlocked (fp)
cc67478e 87# define FGETS(buf, n, fp) __fgets_unlocked (buf, n, fp)
2706ee38
UD
88#else
89# define FEOF(fp) feof (fp)
90# define FGETS(buf, n, fp) fgets (buf, n, fp)
91#endif
92
4a4d50f3 93/* For those losing systems which don't have `alloca' we have to add
5f2eab42
RM
94 some additional code emulating it. */
95#ifdef HAVE_ALLOCA
4a4d50f3 96# define freea(p) /* nothing */
5f2eab42 97#else
4a4d50f3
UD
98# define alloca(n) malloc (n)
99# define freea(p) free (p)
100#endif
5f2eab42 101
628a0aa1 102#if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED
77ccaba1
UD
103# undef fgets
104# define fgets(buf, len, s) fgets_unlocked (buf, len, s)
105#endif
628a0aa1 106#if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED
77ccaba1
UD
107# undef feof
108# define feof(s) feof_unlocked (s)
109#endif
110
5f2eab42 111
24906b43
RM
112struct alias_map
113{
114 const char *alias;
115 const char *value;
116};
117
118
c877418f
RM
119#ifndef _LIBC
120# define libc_freeres_ptr(decl) decl
121#endif
122
123libc_freeres_ptr (static char *string_space);
390500b1
UD
124static size_t string_space_act;
125static size_t string_space_max;
c877418f 126libc_freeres_ptr (static struct alias_map *map);
390500b1
UD
127static size_t nmap;
128static size_t maxmap;
24906b43
RM
129
130
131/* Prototypes for local functions. */
dfd2257a
UD
132static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
133 internal_function;
17c389fc 134static int extend_alias_table PARAMS ((void));
be10a868
RM
135static int alias_compare PARAMS ((const struct alias_map *map1,
136 const struct alias_map *map2));
24906b43
RM
137
138
139const char *
140_nl_expand_alias (name)
141 const char *name;
142{
143 static const char *locale_alias_path = LOCALE_ALIAS_PATH;
144 struct alias_map *retval;
40a55d20 145 const char *result = NULL;
24906b43
RM
146 size_t added;
147
40a55d20
UD
148#ifdef _LIBC
149 __libc_lock_lock (lock);
150#endif
151
24906b43
RM
152 do
153 {
154 struct alias_map item;
155
156 item.alias = name;
157
158 if (nmap > 0)
159 retval = (struct alias_map *) bsearch (&item, map, nmap,
160 sizeof (struct alias_map),
be10a868
RM
161 (int (*) PARAMS ((const void *,
162 const void *))
163 ) alias_compare);
24906b43
RM
164 else
165 retval = NULL;
166
167 /* We really found an alias. Return the value. */
168 if (retval != NULL)
40a55d20
UD
169 {
170 result = retval->value;
171 break;
172 }
24906b43
RM
173
174 /* Perhaps we can find another alias file. */
175 added = 0;
176 while (added == 0 && locale_alias_path[0] != '\0')
177 {
178 const char *start;
179
75914335 180 while (locale_alias_path[0] == ':')
24906b43
RM
181 ++locale_alias_path;
182 start = locale_alias_path;
183
184 while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':')
185 ++locale_alias_path;
186
187 if (start < locale_alias_path)
188 added = read_alias_file (start, locale_alias_path - start);
189 }
190 }
191 while (added != 0);
192
40a55d20
UD
193#ifdef _LIBC
194 __libc_lock_unlock (lock);
195#endif
196
197 return result;
24906b43
RM
198}
199
200
201static size_t
dfd2257a 202internal_function
24906b43
RM
203read_alias_file (fname, fname_len)
204 const char *fname;
205 int fname_len;
206{
207 FILE *fp;
208 char *full_fname;
209 size_t added;
86d2c878 210 static const char aliasfile[] = "/locale.alias";
24906b43 211
86d2c878 212 full_fname = (char *) alloca (fname_len + sizeof aliasfile);
1618c590
UD
213#ifdef HAVE_MEMPCPY
214 mempcpy (mempcpy (full_fname, fname, fname_len),
215 aliasfile, sizeof aliasfile);
216#else
86d2c878
RM
217 memcpy (full_fname, fname, fname_len);
218 memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
1618c590 219#endif
24906b43 220
ee8449f7
UD
221 /* Note the file is opened with cancellation in the I/O functions
222 disabled. */
312be3f9 223 fp = fopen (full_fname, "rce");
4a4d50f3 224 freea (full_fname);
24906b43 225 if (fp == NULL)
4a4d50f3 226 return 0;
24906b43 227
2706ee38
UD
228#ifdef HAVE___FSETLOCKING
229 /* No threads present. */
230 __fsetlocking (fp, FSETLOCKING_BYCALLER);
231#endif
232
24906b43 233 added = 0;
2706ee38 234 while (!FEOF (fp))
24906b43
RM
235 {
236 /* It is a reasonable approach to use a fix buffer here because
237 a) we are only interested in the first two fields
238 b) these fields must be usable as file names and so must not
239 be that long
51b3c8f6
UD
240 We avoid a multi-kilobyte buffer here since this would use up
241 stack space which we might not have if the program ran out of
242 memory. */
243 char buf[400];
8cb569b7
UD
244 char *alias;
245 char *value;
246 char *cp;
fae49c62 247 int complete_line;
24906b43 248
2706ee38 249 if (FGETS (buf, sizeof buf, fp) == NULL)
24906b43
RM
250 /* EOF reached. */
251 break;
252
fae49c62
UD
253 /* Determine whether the line is complete. */
254 complete_line = strchr (buf, '\n') != NULL;
255
24906b43
RM
256 cp = buf;
257 /* Ignore leading white space. */
0555fcce 258 while (isspace ((unsigned char) cp[0]))
24906b43
RM
259 ++cp;
260
261 /* A leading '#' signals a comment line. */
262 if (cp[0] != '\0' && cp[0] != '#')
263 {
264 alias = cp++;
0555fcce 265 while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
24906b43
RM
266 ++cp;
267 /* Terminate alias name. */
268 if (cp[0] != '\0')
269 *cp++ = '\0';
270
271 /* Now look for the beginning of the value. */
0555fcce 272 while (isspace ((unsigned char) cp[0]))
24906b43
RM
273 ++cp;
274
275 if (cp[0] != '\0')
276 {
a5a0310d
UD
277 size_t alias_len;
278 size_t value_len;
24906b43
RM
279
280 value = cp++;
0555fcce 281 while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
24906b43
RM
282 ++cp;
283 /* Terminate value. */
284 if (cp[0] == '\n')
285 {
286 /* This has to be done to make the following test
287 for the end of line possible. We are looking for
288 the terminating '\n' which do not overwrite here. */
289 *cp++ = '\0';
290 *cp = '\n';
291 }
292 else if (cp[0] != '\0')
293 *cp++ = '\0';
294
295 if (nmap >= maxmap)
a1ffb40e 296 if (__glibc_unlikely (extend_alias_table ()))
d6c7294e 297 goto out;
24906b43 298
a5a0310d
UD
299 alias_len = strlen (alias) + 1;
300 value_len = strlen (value) + 1;
24906b43 301
a5a0310d 302 if (string_space_act + alias_len + value_len > string_space_max)
5f2eab42 303 {
a5a0310d
UD
304 /* Increase size of memory pool. */
305 size_t new_size = (string_space_max
306 + (alias_len + value_len > 1024
307 ? alias_len + value_len : 1024));
308 char *new_pool = (char *) realloc (string_space, new_size);
309 if (new_pool == NULL)
d6c7294e 310 goto out;
42be70d4 311
a1ffb40e 312 if (__glibc_unlikely (string_space != new_pool))
42be70d4
UD
313 {
314 size_t i;
315
316 for (i = 0; i < nmap; i++)
317 {
318 map[i].alias += new_pool - string_space;
319 map[i].value += new_pool - string_space;
320 }
321 }
322
a5a0310d
UD
323 string_space = new_pool;
324 string_space_max = new_size;
5f2eab42 325 }
a5a0310d
UD
326
327 map[nmap].alias = memcpy (&string_space[string_space_act],
328 alias, alias_len);
329 string_space_act += alias_len;
330
1618c590 331 map[nmap].value = memcpy (&string_space[string_space_act],
a5a0310d
UD
332 value, value_len);
333 string_space_act += value_len;
24906b43
RM
334
335 ++nmap;
336 ++added;
337 }
338 }
51b3c8f6
UD
339
340 /* Possibly not the whole line fits into the buffer. Ignore
341 the rest of the line. */
fae49c62
UD
342 if (! complete_line)
343 do
344 if (FGETS (buf, sizeof buf, fp) == NULL)
345 /* Make sure the inner loop will be left. The outer loop
346 will exit at the `feof' test. */
347 break;
f6c93bd9 348 while (strchr (buf, '\n') == NULL);
24906b43
RM
349 }
350
d6c7294e 351out:
24906b43
RM
352 /* Should we test for ferror()? I think we have to silently ignore
353 errors. --drepper */
354 fclose (fp);
355
356 if (added > 0)
357 qsort (map, nmap, sizeof (struct alias_map),
be10a868 358 (int (*) PARAMS ((const void *, const void *))) alias_compare);
24906b43
RM
359
360 return added;
361}
362
363
17c389fc 364static int
60d2f8f3 365extend_alias_table (void)
24906b43
RM
366{
367 size_t new_size;
368 struct alias_map *new_map;
369
370 new_size = maxmap == 0 ? 100 : 2 * maxmap;
a5a0310d
UD
371 new_map = (struct alias_map *) realloc (map, (new_size
372 * sizeof (struct alias_map)));
24906b43
RM
373 if (new_map == NULL)
374 /* Simply don't extend: we don't have any more core. */
17c389fc 375 return -1;
24906b43 376
24906b43
RM
377 map = new_map;
378 maxmap = new_size;
17c389fc 379 return 0;
24906b43
RM
380}
381
382
383static int
384alias_compare (map1, map2)
385 const struct alias_map *map1;
386 const struct alias_map *map2;
387{
388#if defined _LIBC || defined HAVE_STRCASECMP
389 return strcasecmp (map1->alias, map2->alias);
390#else
391 const unsigned char *p1 = (const unsigned char *) map1->alias;
392 const unsigned char *p2 = (const unsigned char *) map2->alias;
393 unsigned char c1, c2;
394
395 if (p1 == p2)
396 return 0;
397
398 do
399 {
400 /* I know this seems to be odd but the tolower() function in
401 some systems libc cannot handle nonalpha characters. */
75914335
RM
402 c1 = isupper (*p1) ? tolower (*p1) : *p1;
403 c2 = isupper (*p2) ? tolower (*p2) : *p2;
24906b43
RM
404 if (c1 == '\0')
405 break;
be10a868
RM
406 ++p1;
407 ++p2;
24906b43
RM
408 }
409 while (c1 == c2);
410
411 return c1 - c2;
412#endif
413}