Statistics
| Revision:

root / intl / localealias.c @ 1

History | View | Annotate | Download (9.72 KB)

1
/* Handle aliases for locale names.
2
   Copyright (C) 1995-1999, 2000-2001, 2003 Free Software Foundation, Inc.
3

4
   This program is free software; you can redistribute it and/or modify it
5
   under the terms of the GNU Library General Public License as published
6
   by 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 GNU
12
   Library General Public License for more details.
13

14
   You should have received a copy of the GNU Library General Public
15
   License along with this program; if not, write to the Free Software
16
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17
   USA.  */
18

    
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

    
26
#ifdef HAVE_CONFIG_H
27
# include <config.h>
28
#endif
29

    
30
#include <ctype.h>
31
#include <stdio.h>
32
#if defined _LIBC || defined HAVE___FSETLOCKING
33
# include <stdio_ext.h>
34
#endif
35
#include <sys/types.h>
36

    
37
#ifdef __GNUC__
38
# undef alloca
39
# define alloca __builtin_alloca
40
# define HAVE_ALLOCA 1
41
#else
42
# ifdef _MSC_VER
43
#  include <malloc.h>
44
#  define alloca _alloca
45
# else
46
#  if defined HAVE_ALLOCA_H || defined _LIBC
47
#   include <alloca.h>
48
#  else
49
#   ifdef _AIX
50
 #pragma alloca
51
#   else
52
#    ifndef alloca
53
char *alloca ();
54
#    endif
55
#   endif
56
#  endif
57
# endif
58
#endif
59

    
60
#include <stdlib.h>
61
#include <string.h>
62

    
63
#include "gettextP.h"
64

    
65
#if ENABLE_RELOCATABLE
66
# include "relocatable.h"
67
#else
68
# define relocate(pathname) (pathname)
69
#endif
70

    
71
/* @@ end of prolog @@ */
72

    
73
#ifdef _LIBC
74
/* Rename the non ANSI C functions.  This is required by the standard
75
   because some ANSI C functions will require linking with this object
76
   file and the name space must not be polluted.  */
77
# define strcasecmp __strcasecmp
78

    
79
# ifndef mempcpy
80
#  define mempcpy __mempcpy
81
# endif
82
# define HAVE_MEMPCPY        1
83
# define HAVE___FSETLOCKING        1
84

    
85
/* We need locking here since we can be called from different places.  */
86
# include <bits/libc-lock.h>
87

    
88
__libc_lock_define_initialized (static, lock);
89
#endif
90

    
91
#ifndef internal_function
92
# define internal_function
93
#endif
94

    
95
/* Some optimizations for glibc.  */
96
#ifdef _LIBC
97
# define FEOF(fp)                feof_unlocked (fp)
98
# define FGETS(buf, n, fp)        fgets_unlocked (buf, n, fp)
99
#else
100
# define FEOF(fp)                feof (fp)
101
# define FGETS(buf, n, fp)        fgets (buf, n, fp)
102
#endif
103

    
104
/* For those losing systems which don't have `alloca' we have to add
105
   some additional code emulating it.  */
106
#ifdef HAVE_ALLOCA
107
# define freea(p) /* nothing */
108
#else
109
# define alloca(n) malloc (n)
110
# define freea(p) free (p)
111
#endif
112

    
113
#if defined _LIBC_REENTRANT || HAVE_DECL_FGETS_UNLOCKED
114
# undef fgets
115
# define fgets(buf, len, s) fgets_unlocked (buf, len, s)
116
#endif
117
#if defined _LIBC_REENTRANT || HAVE_DECL_FEOF_UNLOCKED
118
# undef feof
119
# define feof(s) feof_unlocked (s)
120
#endif
121

    
122

    
123
struct alias_map
124
{
125
  const char *alias;
126
  const char *value;
127
};
128

    
129

    
130
#ifndef _LIBC
131
# define libc_freeres_ptr(decl) decl
132
#endif
133

    
134
libc_freeres_ptr (static char *string_space);
135
static size_t string_space_act;
136
static size_t string_space_max;
137
libc_freeres_ptr (static struct alias_map *map);
138
static size_t nmap;
139
static size_t maxmap;
140

    
141

    
142
/* Prototypes for local functions.  */
143
static size_t read_alias_file (const char *fname, int fname_len)
144
     internal_function;
145
static int extend_alias_table (void);
146
static int alias_compare (const struct alias_map *map1,
147
                          const struct alias_map *map2);
148

    
149

    
150
const char *
151
_nl_expand_alias (const char *name)
152
{
153
  static const char *locale_alias_path;
154
  struct alias_map *retval;
155
  const char *result = NULL;
156
  size_t added;
157

    
158
#ifdef _LIBC
159
  __libc_lock_lock (lock);
160
#endif
161

    
162
  if (locale_alias_path == NULL)
163
    locale_alias_path = LOCALE_ALIAS_PATH;
164

    
165
  do
166
    {
167
      struct alias_map item;
168

    
169
      item.alias = name;
170

    
171
      if (nmap > 0)
172
        retval = (struct alias_map *) bsearch (&item, map, nmap,
173
                                               sizeof (struct alias_map),
174
                                               (int (*) (const void *,
175
                                                         const void *)
176
                                                ) alias_compare);
177
      else
178
        retval = NULL;
179

    
180
      /* We really found an alias.  Return the value.  */
181
      if (retval != NULL)
182
        {
183
          result = retval->value;
184
          break;
185
        }
186

    
187
      /* Perhaps we can find another alias file.  */
188
      added = 0;
189
      while (added == 0 && locale_alias_path[0] != '\0')
190
        {
191
          const char *start;
192

    
193
          while (locale_alias_path[0] == PATH_SEPARATOR)
194
            ++locale_alias_path;
195
          start = locale_alias_path;
196

    
197
          while (locale_alias_path[0] != '\0'
198
                 && locale_alias_path[0] != PATH_SEPARATOR)
199
            ++locale_alias_path;
200

    
201
          if (start < locale_alias_path)
202
            added = read_alias_file (start, locale_alias_path - start);
203
        }
204
    }
205
  while (added != 0);
206

    
207
#ifdef _LIBC
208
  __libc_lock_unlock (lock);
209
#endif
210

    
211
  return result;
212
}
213

    
214

    
215
static size_t
216
internal_function
217
read_alias_file (const char *fname, int fname_len)
218
{
219
  FILE *fp;
220
  char *full_fname;
221
  size_t added;
222
  static const char aliasfile[] = "/locale.alias";
223

    
224
  full_fname = (char *) alloca (fname_len + sizeof aliasfile);
225
#ifdef HAVE_MEMPCPY
226
  mempcpy (mempcpy (full_fname, fname, fname_len),
227
           aliasfile, sizeof aliasfile);
228
#else
229
  memcpy (full_fname, fname, fname_len);
230
  memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
231
#endif
232

    
233
  fp = fopen (relocate (full_fname), "r");
234
  freea (full_fname);
235
  if (fp == NULL)
236
    return 0;
237

    
238
#ifdef HAVE___FSETLOCKING
239
  /* No threads present.  */
240
  __fsetlocking (fp, FSETLOCKING_BYCALLER);
241
#endif
242

    
243
  added = 0;
244
  while (!FEOF (fp))
245
    {
246
      /* It is a reasonable approach to use a fix buffer here because
247
         a) we are only interested in the first two fields
248
         b) these fields must be usable as file names and so must not
249
            be that long
250
         We avoid a multi-kilobyte buffer here since this would use up
251
         stack space which we might not have if the program ran out of
252
         memory.  */
253
      char buf[400];
254
      char *alias;
255
      char *value;
256
      char *cp;
257

    
258
      if (FGETS (buf, sizeof buf, fp) == NULL)
259
        /* EOF reached.  */
260
        break;
261

    
262
      cp = buf;
263
      /* Ignore leading white space.  */
264
      while (isspace ((unsigned char) cp[0]))
265
        ++cp;
266

    
267
      /* A leading '#' signals a comment line.  */
268
      if (cp[0] != '\0' && cp[0] != '#')
269
        {
270
          alias = cp++;
271
          while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
272
            ++cp;
273
          /* Terminate alias name.  */
274
          if (cp[0] != '\0')
275
            *cp++ = '\0';
276

    
277
          /* Now look for the beginning of the value.  */
278
          while (isspace ((unsigned char) cp[0]))
279
            ++cp;
280

    
281
          if (cp[0] != '\0')
282
            {
283
              size_t alias_len;
284
              size_t value_len;
285

    
286
              value = cp++;
287
              while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
288
                ++cp;
289
              /* Terminate value.  */
290
              if (cp[0] == '\n')
291
                {
292
                  /* This has to be done to make the following test
293
                     for the end of line possible.  We are looking for
294
                     the terminating '\n' which do not overwrite here.  */
295
                  *cp++ = '\0';
296
                  *cp = '\n';
297
                }
298
              else if (cp[0] != '\0')
299
                *cp++ = '\0';
300

    
301
              if (nmap >= maxmap)
302
                if (__builtin_expect (extend_alias_table (), 0))
303
                  return added;
304

    
305
              alias_len = strlen (alias) + 1;
306
              value_len = strlen (value) + 1;
307

    
308
              if (string_space_act + alias_len + value_len > string_space_max)
309
                {
310
                  /* Increase size of memory pool.  */
311
                  size_t new_size = (string_space_max
312
                                     + (alias_len + value_len > 1024
313
                                        ? alias_len + value_len : 1024));
314
                  char *new_pool = (char *) realloc (string_space, new_size);
315
                  if (new_pool == NULL)
316
                    return added;
317

    
318
                  if (__builtin_expect (string_space != new_pool, 0))
319
                    {
320
                      size_t i;
321

    
322
                      for (i = 0; i < nmap; i++)
323
                        {
324
                          map[i].alias += new_pool - string_space;
325
                          map[i].value += new_pool - string_space;
326
                        }
327
                    }
328

    
329
                  string_space = new_pool;
330
                  string_space_max = new_size;
331
                }
332

    
333
              map[nmap].alias = memcpy (&string_space[string_space_act],
334
                                        alias, alias_len);
335
              string_space_act += alias_len;
336

    
337
              map[nmap].value = memcpy (&string_space[string_space_act],
338
                                        value, value_len);
339
              string_space_act += value_len;
340

    
341
              ++nmap;
342
              ++added;
343
            }
344
        }
345

    
346
      /* Possibly not the whole line fits into the buffer.  Ignore
347
         the rest of the line.  */
348
      while (strchr (buf, '\n') == NULL)
349
        if (FGETS (buf, sizeof buf, fp) == NULL)
350
          /* Make sure the inner loop will be left.  The outer loop
351
             will exit at the `feof' test.  */
352
          break;
353
    }
354

    
355
  /* Should we test for ferror()?  I think we have to silently ignore
356
     errors.  --drepper  */
357
  fclose (fp);
358

    
359
  if (added > 0)
360
    qsort (map, nmap, sizeof (struct alias_map),
361
           (int (*) (const void *, const void *)) alias_compare);
362

    
363
  return added;
364
}
365

    
366

    
367
static int
368
extend_alias_table ()
369
{
370
  size_t new_size;
371
  struct alias_map *new_map;
372

    
373
  new_size = maxmap == 0 ? 100 : 2 * maxmap;
374
  new_map = (struct alias_map *) realloc (map, (new_size
375
                                                * sizeof (struct alias_map)));
376
  if (new_map == NULL)
377
    /* Simply don't extend: we don't have any more core.  */
378
    return -1;
379

    
380
  map = new_map;
381
  maxmap = new_size;
382
  return 0;
383
}
384

    
385

    
386
static int
387
alias_compare (const struct alias_map *map1, const struct alias_map *map2)
388
{
389
#if defined _LIBC || defined HAVE_STRCASECMP
390
  return strcasecmp (map1->alias, map2->alias);
391
#else
392
  const unsigned char *p1 = (const unsigned char *) map1->alias;
393
  const unsigned char *p2 = (const unsigned char *) map2->alias;
394
  unsigned char c1, c2;
395

    
396
  if (p1 == p2)
397
    return 0;
398

    
399
  do
400
    {
401
      /* I know this seems to be odd but the tolower() function in
402
         some systems libc cannot handle nonalpha characters.  */
403
      c1 = isupper (*p1) ? tolower (*p1) : *p1;
404
      c2 = isupper (*p2) ? tolower (*p2) : *p2;
405
      if (c1 == '\0')
406
        break;
407
      ++p1;
408
      ++p2;
409
    }
410
  while (c1 == c2);
411

    
412
  return c1 - c2;
413
#endif
414
}