source: trunk/third/librep/intl/localealias.c @ 15283

Revision 15283, 9.9 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15282, which included commits to RCS files with non-trunk default branches.
Line 
1/* Handle aliases for locale names.
2   Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
3   Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2, or (at your option)
8   any later version.
9
10   This program 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
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software Foundation,
17   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23#include <ctype.h>
24#include <stdio.h>
25#include <sys/types.h>
26
27#ifdef __GNUC__
28# define alloca __builtin_alloca
29# define HAVE_ALLOCA 1
30#else
31# if defined HAVE_ALLOCA_H || defined _LIBC
32#  include <alloca.h>
33# else
34#  ifdef _AIX
35 #pragma alloca
36#  else
37#   ifndef alloca
38char *alloca ();
39#   endif
40#  endif
41# endif
42#endif
43
44#if defined STDC_HEADERS || defined _LIBC
45# include <stdlib.h>
46#else
47char *getenv ();
48# ifdef HAVE_MALLOC_H
49#  include <malloc.h>
50# else
51void free ();
52# endif
53#endif
54
55#if defined HAVE_STRING_H || defined _LIBC
56# ifndef _GNU_SOURCE
57#  define _GNU_SOURCE   1
58# endif
59# include <string.h>
60#else
61# include <strings.h>
62# ifndef memcpy
63#  define memcpy(Dst, Src, Num) bcopy (Src, Dst, Num)
64# endif
65#endif
66#if !HAVE_STRCHR && !defined _LIBC
67# ifndef strchr
68#  define strchr index
69# endif
70#endif
71
72#include "gettext.h"
73#include "gettextP.h"
74
75/* @@ end of prolog @@ */
76
77#ifdef _LIBC
78/* Rename the non ANSI C functions.  This is required by the standard
79   because some ANSI C functions will require linking with this object
80   file and the name space must not be polluted.  */
81# define strcasecmp __strcasecmp
82
83# define mempcpy __mempcpy
84# define HAVE_MEMPCPY   1
85
86/* We need locking here since we can be called from different places.  */
87# include <bits/libc-lock.h>
88
89__libc_lock_define_initialized (static, lock);
90#endif
91
92
93/* For those loosing systems which don't have `alloca' we have to add
94   some additional code emulating it.  */
95#ifdef HAVE_ALLOCA
96/* Nothing has to be done.  */
97# define ADD_BLOCK(list, address) /* nothing */
98# define FREE_BLOCKS(list) /* nothing */
99#else
100struct block_list
101{
102  void *address;
103  struct block_list *next;
104};
105# define ADD_BLOCK(list, addr)                                                \
106  do {                                                                        \
107    struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
108    /* If we cannot get a free block we cannot add the new element to         \
109       the list.  */                                                          \
110    if (newp != NULL) {                                                       \
111      newp->address = (addr);                                                 \
112      newp->next = (list);                                                    \
113      (list) = newp;                                                          \
114    }                                                                         \
115  } while (0)
116# define FREE_BLOCKS(list)                                                    \
117  do {                                                                        \
118    while (list != NULL) {                                                    \
119      struct block_list *old = list;                                          \
120      list = list->next;                                                      \
121      free (old);                                                             \
122    }                                                                         \
123  } while (0)
124# undef alloca
125# define alloca(size) (malloc (size))
126#endif  /* have alloca */
127
128
129struct alias_map
130{
131  const char *alias;
132  const char *value;
133};
134
135
136static char *string_space = NULL;
137static size_t string_space_act = 0;
138static size_t string_space_max = 0;
139static struct alias_map *map;
140static size_t nmap = 0;
141static size_t maxmap = 0;
142
143
144/* Prototypes for local functions.  */
145static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
146     internal_function;
147static void extend_alias_table PARAMS ((void));
148static int alias_compare PARAMS ((const struct alias_map *map1,
149                                  const struct alias_map *map2));
150
151
152const char *
153_nl_expand_alias (name)
154    const char *name;
155{
156  static const char *locale_alias_path = 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  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 (*) PARAMS ((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] == ':')
194            ++locale_alias_path;
195          start = locale_alias_path;
196
197          while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':')
198            ++locale_alias_path;
199
200          if (start < locale_alias_path)
201            added = read_alias_file (start, locale_alias_path - start);
202        }
203    }
204  while (added != 0);
205
206#ifdef _LIBC
207  __libc_lock_unlock (lock);
208#endif
209
210  return result;
211}
212
213
214static size_t
215internal_function
216read_alias_file (fname, fname_len)
217     const char *fname;
218     int fname_len;
219{
220#ifndef HAVE_ALLOCA
221  struct block_list *block_list = NULL;
222#endif
223  FILE *fp;
224  char *full_fname;
225  size_t added;
226  static const char aliasfile[] = "/locale.alias";
227
228  full_fname = (char *) alloca (fname_len + sizeof aliasfile);
229  ADD_BLOCK (block_list, full_fname);
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 (full_fname, "r");
239  if (fp == NULL)
240    {
241      FREE_BLOCKS (block_list);
242      return 0;
243    }
244
245  added = 0;
246  while (!feof (fp))
247    {
248      /* It is a reasonable approach to use a fix buffer here because
249         a) we are only interested in the first two fields
250         b) these fields must be usable as file names and so must not
251            be that long
252       */
253      unsigned char buf[BUFSIZ];
254      unsigned char *alias;
255      unsigned char *value;
256      unsigned char *cp;
257
258      if (fgets (buf, sizeof buf, fp) == NULL)
259        /* EOF reached.  */
260        break;
261
262      /* Possibly not the whole line fits into the buffer.  Ignore
263         the rest of the line.  */
264      if (strchr (buf, '\n') == NULL)
265        {
266          char altbuf[BUFSIZ];
267          do
268            if (fgets (altbuf, sizeof altbuf, fp) == NULL)
269              /* Make sure the inner loop will be left.  The outer loop
270                 will exit at the `feof' test.  */
271              break;
272          while (strchr (altbuf, '\n') == NULL);
273        }
274
275      cp = buf;
276      /* Ignore leading white space.  */
277      while (isspace (cp[0]))
278        ++cp;
279
280      /* A leading '#' signals a comment line.  */
281      if (cp[0] != '\0' && cp[0] != '#')
282        {
283          alias = cp++;
284          while (cp[0] != '\0' && !isspace (cp[0]))
285            ++cp;
286          /* Terminate alias name.  */
287          if (cp[0] != '\0')
288            *cp++ = '\0';
289
290          /* Now look for the beginning of the value.  */
291          while (isspace (cp[0]))
292            ++cp;
293
294          if (cp[0] != '\0')
295            {
296              size_t alias_len;
297              size_t value_len;
298
299              value = cp++;
300              while (cp[0] != '\0' && !isspace (cp[0]))
301                ++cp;
302              /* Terminate value.  */
303              if (cp[0] == '\n')
304                {
305                  /* This has to be done to make the following test
306                     for the end of line possible.  We are looking for
307                     the terminating '\n' which do not overwrite here.  */
308                  *cp++ = '\0';
309                  *cp = '\n';
310                }
311              else if (cp[0] != '\0')
312                *cp++ = '\0';
313
314              if (nmap >= maxmap)
315                extend_alias_table ();
316
317              alias_len = strlen (alias) + 1;
318              value_len = strlen (value) + 1;
319
320              if (string_space_act + alias_len + value_len > string_space_max)
321                {
322                  /* Increase size of memory pool.  */
323                  size_t new_size = (string_space_max
324                                     + (alias_len + value_len > 1024
325                                        ? alias_len + value_len : 1024));
326                  char *new_pool = (char *) realloc (string_space, new_size);
327                  if (new_pool == NULL)
328                    {
329                      FREE_BLOCKS (block_list);
330                      return added;
331                    }
332                  string_space = new_pool;
333                  string_space_max = new_size;
334                }
335
336              map[nmap].alias = memcpy (&string_space[string_space_act],
337                                        alias, alias_len);
338              string_space_act += alias_len;
339
340              map[nmap].value = memcpy (&string_space[string_space_act],
341                                        value, value_len);
342              string_space_act += value_len;
343
344              ++nmap;
345              ++added;
346            }
347        }
348    }
349
350  /* Should we test for ferror()?  I think we have to silently ignore
351     errors.  --drepper  */
352  fclose (fp);
353
354  if (added > 0)
355    qsort (map, nmap, sizeof (struct alias_map),
356           (int (*) PARAMS ((const void *, const void *))) alias_compare);
357
358  FREE_BLOCKS (block_list);
359  return added;
360}
361
362
363static void
364extend_alias_table ()
365{
366  size_t new_size;
367  struct alias_map *new_map;
368
369  new_size = maxmap == 0 ? 100 : 2 * maxmap;
370  new_map = (struct alias_map *) realloc (map, (new_size
371                                                * sizeof (struct alias_map)));
372  if (new_map == NULL)
373    /* Simply don't extend: we don't have any more core.  */
374    return;
375
376  map = new_map;
377  maxmap = new_size;
378}
379
380
381#ifdef _LIBC
382static void __attribute__ ((unused))
383free_mem (void)
384{
385  if (string_space != NULL)
386    free (string_space);
387  if (map != NULL)
388    free (map);
389}
390text_set_element (__libc_subfreeres, free_mem);
391#endif
392
393
394static int
395alias_compare (map1, map2)
396     const struct alias_map *map1;
397     const struct alias_map *map2;
398{
399#if defined _LIBC || defined HAVE_STRCASECMP
400  return strcasecmp (map1->alias, map2->alias);
401#else
402  const unsigned char *p1 = (const unsigned char *) map1->alias;
403  const unsigned char *p2 = (const unsigned char *) map2->alias;
404  unsigned char c1, c2;
405
406  if (p1 == p2)
407    return 0;
408
409  do
410    {
411      /* I know this seems to be odd but the tolower() function in
412         some systems libc cannot handle nonalpha characters.  */
413      c1 = isupper (*p1) ? tolower (*p1) : *p1;
414      c2 = isupper (*p2) ? tolower (*p2) : *p2;
415      if (c1 == '\0')
416        break;
417      ++p1;
418      ++p2;
419    }
420  while (c1 == c2);
421
422  return c1 - c2;
423#endif
424}
Note: See TracBrowser for help on using the repository browser.