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