~ubuntu-branches/ubuntu/lucid/texinfo/lucid

« back to all changes in this revision

Viewing changes to intl/localealias.c

  • Committer: Bazaar Package Importer
  • Author(s): Josip Rodin
  • Date: 2002-03-28 13:53:23 UTC
  • Revision ID: james.westby@ubuntu.com-20020328135323-bunuof07wv1fvx40
Tags: upstream-4.1
ImportĀ upstreamĀ versionĀ 4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Handle aliases for locale names.
 
2
   Copyright (C) 1995-1999, 2000, 2001 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
# define alloca __builtin_alloca
 
39
# define HAVE_ALLOCA 1
 
40
#else
 
41
# if defined HAVE_ALLOCA_H || defined _LIBC
 
42
#  include <alloca.h>
 
43
# else
 
44
#  ifdef _AIX
 
45
 #pragma alloca
 
46
#  else
 
47
#   ifndef alloca
 
48
char *alloca ();
 
49
#   endif
 
50
#  endif
 
51
# endif
 
52
#endif
 
53
 
 
54
#include <stdlib.h>
 
55
#include <string.h>
 
56
 
 
57
#include "gettextP.h"
 
58
 
 
59
/* @@ end of prolog @@ */
 
60
 
 
61
#ifdef _LIBC
 
62
/* Rename the non ANSI C functions.  This is required by the standard
 
63
   because some ANSI C functions will require linking with this object
 
64
   file and the name space must not be polluted.  */
 
65
# define strcasecmp __strcasecmp
 
66
 
 
67
# ifndef mempcpy
 
68
#  define mempcpy __mempcpy
 
69
# endif
 
70
# define HAVE_MEMPCPY   1
 
71
# define HAVE___FSETLOCKING     1
 
72
 
 
73
/* We need locking here since we can be called from different places.  */
 
74
# include <bits/libc-lock.h>
 
75
 
 
76
__libc_lock_define_initialized (static, lock);
 
77
#endif
 
78
 
 
79
#ifndef internal_function
 
80
# define internal_function
 
81
#endif
 
82
 
 
83
/* Some optimizations for glibc.  */
 
84
#ifdef _LIBC
 
85
# define FEOF(fp)               feof_unlocked (fp)
 
86
# define FGETS(buf, n, fp)      fgets_unlocked (buf, n, fp)
 
87
#else
 
88
# define FEOF(fp)               feof (fp)
 
89
# define FGETS(buf, n, fp)      fgets (buf, n, fp)
 
90
#endif
 
91
 
 
92
/* For those losing systems which don't have `alloca' we have to add
 
93
   some additional code emulating it.  */
 
94
#ifdef HAVE_ALLOCA
 
95
# define freea(p) /* nothing */
 
96
#else
 
97
# define alloca(n) malloc (n)
 
98
# define freea(p) free (p)
 
99
#endif
 
100
 
 
101
#if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED
 
102
# undef fgets
 
103
# define fgets(buf, len, s) fgets_unlocked (buf, len, s)
 
104
#endif
 
105
#if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED
 
106
# undef feof
 
107
# define feof(s) feof_unlocked (s)
 
108
#endif
 
109
 
 
110
 
 
111
struct alias_map
 
112
{
 
113
  const char *alias;
 
114
  const char *value;
 
115
};
 
116
 
 
117
 
 
118
static char *string_space;
 
119
static size_t string_space_act;
 
120
static size_t string_space_max;
 
121
static struct alias_map *map;
 
122
static size_t nmap;
 
123
static size_t maxmap;
 
124
 
 
125
 
 
126
/* Prototypes for local functions.  */
 
127
static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
 
128
     internal_function;
 
129
static int extend_alias_table PARAMS ((void));
 
130
static int alias_compare PARAMS ((const struct alias_map *map1,
 
131
                                  const struct alias_map *map2));
 
132
 
 
133
 
 
134
const char *
 
135
_nl_expand_alias (name)
 
136
    const char *name;
 
137
{
 
138
  static const char *locale_alias_path;
 
139
  struct alias_map *retval;
 
140
  const char *result = NULL;
 
141
  size_t added;
 
142
 
 
143
#ifdef _LIBC
 
144
  __libc_lock_lock (lock);
 
145
#endif
 
146
 
 
147
  if (locale_alias_path == NULL)
 
148
    locale_alias_path = LOCALE_ALIAS_PATH;
 
149
 
 
150
  do
 
151
    {
 
152
      struct alias_map item;
 
153
 
 
154
      item.alias = name;
 
155
 
 
156
      if (nmap > 0)
 
157
        retval = (struct alias_map *) bsearch (&item, map, nmap,
 
158
                                               sizeof (struct alias_map),
 
159
                                               (int (*) PARAMS ((const void *,
 
160
                                                                 const void *))
 
161
                                                ) alias_compare);
 
162
      else
 
163
        retval = NULL;
 
164
 
 
165
      /* We really found an alias.  Return the value.  */
 
166
      if (retval != NULL)
 
167
        {
 
168
          result = retval->value;
 
169
          break;
 
170
        }
 
171
 
 
172
      /* Perhaps we can find another alias file.  */
 
173
      added = 0;
 
174
      while (added == 0 && locale_alias_path[0] != '\0')
 
175
        {
 
176
          const char *start;
 
177
 
 
178
          while (locale_alias_path[0] == PATH_SEPARATOR)
 
179
            ++locale_alias_path;
 
180
          start = locale_alias_path;
 
181
 
 
182
          while (locale_alias_path[0] != '\0'
 
183
                 && locale_alias_path[0] != PATH_SEPARATOR)
 
184
            ++locale_alias_path;
 
185
 
 
186
          if (start < locale_alias_path)
 
187
            added = read_alias_file (start, locale_alias_path - start);
 
188
        }
 
189
    }
 
190
  while (added != 0);
 
191
 
 
192
#ifdef _LIBC
 
193
  __libc_lock_unlock (lock);
 
194
#endif
 
195
 
 
196
  return result;
 
197
}
 
198
 
 
199
 
 
200
static size_t
 
201
internal_function
 
202
read_alias_file (fname, fname_len)
 
203
     const char *fname;
 
204
     int fname_len;
 
205
{
 
206
  FILE *fp;
 
207
  char *full_fname;
 
208
  size_t added;
 
209
  static const char aliasfile[] = "/locale.alias";
 
210
 
 
211
  full_fname = (char *) alloca (fname_len + sizeof aliasfile);
 
212
#ifdef HAVE_MEMPCPY
 
213
  mempcpy (mempcpy (full_fname, fname, fname_len),
 
214
           aliasfile, sizeof aliasfile);
 
215
#else
 
216
  memcpy (full_fname, fname, fname_len);
 
217
  memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
 
218
#endif
 
219
 
 
220
  fp = fopen (full_fname, "r");
 
221
  freea (full_fname);
 
222
  if (fp == NULL)
 
223
    return 0;
 
224
 
 
225
#ifdef HAVE___FSETLOCKING
 
226
  /* No threads present.  */
 
227
  __fsetlocking (fp, FSETLOCKING_BYCALLER);
 
228
#endif
 
229
 
 
230
  added = 0;
 
231
  while (!FEOF (fp))
 
232
    {
 
233
      /* It is a reasonable approach to use a fix buffer here because
 
234
         a) we are only interested in the first two fields
 
235
         b) these fields must be usable as file names and so must not
 
236
            be that long
 
237
       */
 
238
      char buf[BUFSIZ];
 
239
      char *alias;
 
240
      char *value;
 
241
      char *cp;
 
242
 
 
243
      if (FGETS (buf, sizeof buf, fp) == NULL)
 
244
        /* EOF reached.  */
 
245
        break;
 
246
 
 
247
      /* Possibly not the whole line fits into the buffer.  Ignore
 
248
         the rest of the line.  */
 
249
      if (strchr (buf, '\n') == NULL)
 
250
        {
 
251
          char altbuf[BUFSIZ];
 
252
          do
 
253
            if (FGETS (altbuf, sizeof altbuf, fp) == NULL)
 
254
              /* Make sure the inner loop will be left.  The outer loop
 
255
                 will exit at the `feof' test.  */
 
256
              break;
 
257
          while (strchr (altbuf, '\n') == NULL);
 
258
        }
 
259
 
 
260
      cp = buf;
 
261
      /* Ignore leading white space.  */
 
262
      while (isspace ((unsigned char) cp[0]))
 
263
        ++cp;
 
264
 
 
265
      /* A leading '#' signals a comment line.  */
 
266
      if (cp[0] != '\0' && cp[0] != '#')
 
267
        {
 
268
          alias = cp++;
 
269
          while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
 
270
            ++cp;
 
271
          /* Terminate alias name.  */
 
272
          if (cp[0] != '\0')
 
273
            *cp++ = '\0';
 
274
 
 
275
          /* Now look for the beginning of the value.  */
 
276
          while (isspace ((unsigned char) cp[0]))
 
277
            ++cp;
 
278
 
 
279
          if (cp[0] != '\0')
 
280
            {
 
281
              size_t alias_len;
 
282
              size_t value_len;
 
283
 
 
284
              value = cp++;
 
285
              while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
 
286
                ++cp;
 
287
              /* Terminate value.  */
 
288
              if (cp[0] == '\n')
 
289
                {
 
290
                  /* This has to be done to make the following test
 
291
                     for the end of line possible.  We are looking for
 
292
                     the terminating '\n' which do not overwrite here.  */
 
293
                  *cp++ = '\0';
 
294
                  *cp = '\n';
 
295
                }
 
296
              else if (cp[0] != '\0')
 
297
                *cp++ = '\0';
 
298
 
 
299
              if (nmap >= maxmap)
 
300
                if (__builtin_expect (extend_alias_table (), 0))
 
301
                  return added;
 
302
 
 
303
              alias_len = strlen (alias) + 1;
 
304
              value_len = strlen (value) + 1;
 
305
 
 
306
              if (string_space_act + alias_len + value_len > string_space_max)
 
307
                {
 
308
                  /* Increase size of memory pool.  */
 
309
                  size_t new_size = (string_space_max
 
310
                                     + (alias_len + value_len > 1024
 
311
                                        ? alias_len + value_len : 1024));
 
312
                  char *new_pool = (char *) realloc (string_space, new_size);
 
313
                  if (new_pool == NULL)
 
314
                    return added;
 
315
 
 
316
                  if (__builtin_expect (string_space != new_pool, 0))
 
317
                    {
 
318
                      size_t i;
 
319
 
 
320
                      for (i = 0; i < nmap; i++)
 
321
                        {
 
322
                          map[i].alias += new_pool - string_space;
 
323
                          map[i].value += new_pool - string_space;
 
324
                        }
 
325
                    }
 
326
 
 
327
                  string_space = new_pool;
 
328
                  string_space_max = new_size;
 
329
                }
 
330
 
 
331
              map[nmap].alias = memcpy (&string_space[string_space_act],
 
332
                                        alias, alias_len);
 
333
              string_space_act += alias_len;
 
334
 
 
335
              map[nmap].value = memcpy (&string_space[string_space_act],
 
336
                                        value, value_len);
 
337
              string_space_act += value_len;
 
338
 
 
339
              ++nmap;
 
340
              ++added;
 
341
            }
 
342
        }
 
343
    }
 
344
 
 
345
  /* Should we test for ferror()?  I think we have to silently ignore
 
346
     errors.  --drepper  */
 
347
  fclose (fp);
 
348
 
 
349
  if (added > 0)
 
350
    qsort (map, nmap, sizeof (struct alias_map),
 
351
           (int (*) PARAMS ((const void *, const void *))) alias_compare);
 
352
 
 
353
  return added;
 
354
}
 
355
 
 
356
 
 
357
static int
 
358
extend_alias_table ()
 
359
{
 
360
  size_t new_size;
 
361
  struct alias_map *new_map;
 
362
 
 
363
  new_size = maxmap == 0 ? 100 : 2 * maxmap;
 
364
  new_map = (struct alias_map *) realloc (map, (new_size
 
365
                                                * sizeof (struct alias_map)));
 
366
  if (new_map == NULL)
 
367
    /* Simply don't extend: we don't have any more core.  */
 
368
    return -1;
 
369
 
 
370
  map = new_map;
 
371
  maxmap = new_size;
 
372
  return 0;
 
373
}
 
374
 
 
375
 
 
376
#ifdef _LIBC
 
377
static void __attribute__ ((unused))
 
378
free_mem (void)
 
379
{
 
380
  if (string_space != NULL)
 
381
    free (string_space);
 
382
  if (map != NULL)
 
383
    free (map);
 
384
}
 
385
text_set_element (__libc_subfreeres, free_mem);
 
386
#endif
 
387
 
 
388
 
 
389
static int
 
390
alias_compare (map1, map2)
 
391
     const struct alias_map *map1;
 
392
     const struct alias_map *map2;
 
393
{
 
394
#if defined _LIBC || defined HAVE_STRCASECMP
 
395
  return strcasecmp (map1->alias, map2->alias);
 
396
#else
 
397
  const unsigned char *p1 = (const unsigned char *) map1->alias;
 
398
  const unsigned char *p2 = (const unsigned char *) map2->alias;
 
399
  unsigned char c1, c2;
 
400
 
 
401
  if (p1 == p2)
 
402
    return 0;
 
403
 
 
404
  do
 
405
    {
 
406
      /* I know this seems to be odd but the tolower() function in
 
407
         some systems libc cannot handle nonalpha characters.  */
 
408
      c1 = isupper (*p1) ? tolower (*p1) : *p1;
 
409
      c2 = isupper (*p2) ? tolower (*p2) : *p2;
 
410
      if (c1 == '\0')
 
411
        break;
 
412
      ++p1;
 
413
      ++p2;
 
414
    }
 
415
  while (c1 == c2);
 
416
 
 
417
  return c1 - c2;
 
418
#endif
 
419
}