Back to index

tetex-bin  3.0
localealias.c
Go to the documentation of this file.
00001 /* Handle aliases for locale names.
00002    Copyright (C) 1995-1999, 2000-2001, 2003 Free Software Foundation, Inc.
00003 
00004    This program is free software; you can redistribute it and/or modify it
00005    under the terms of the GNU Library General Public License as published
00006    by the Free Software Foundation; either version 2, or (at your option)
00007    any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public
00015    License along with this program; if not, write to the Free Software
00016    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
00017    USA.  */
00018 
00019 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
00020    This must come before <config.h> because <config.h> may include
00021    <features.h>, and once <features.h> has been included, it's too late.  */
00022 #ifndef _GNU_SOURCE
00023 # define _GNU_SOURCE    1
00024 #endif
00025 
00026 #ifdef HAVE_CONFIG_H
00027 # include <config.h>
00028 #endif
00029 
00030 #include <ctype.h>
00031 #include <stdio.h>
00032 #if defined _LIBC || defined HAVE___FSETLOCKING
00033 # include <stdio_ext.h>
00034 #endif
00035 #include <sys/types.h>
00036 
00037 #ifdef __GNUC__
00038 # undef alloca
00039 # define alloca __builtin_alloca
00040 # define HAVE_ALLOCA 1
00041 #else
00042 # ifdef _MSC_VER
00043 #  include <malloc.h>
00044 #  define alloca _alloca
00045 # else
00046 #  if defined HAVE_ALLOCA_H || defined _LIBC
00047 #   include <alloca.h>
00048 #  else
00049 #   ifdef _AIX
00050  #pragma alloca
00051 #   else
00052 #    ifndef alloca
00053 char *alloca ();
00054 #    endif
00055 #   endif
00056 #  endif
00057 # endif
00058 #endif
00059 
00060 #include <stdlib.h>
00061 #include <string.h>
00062 
00063 #include "gettextP.h"
00064 
00065 #if ENABLE_RELOCATABLE
00066 # include "relocatable.h"
00067 #else
00068 # define relocate(pathname) (pathname)
00069 #endif
00070 
00071 /* @@ end of prolog @@ */
00072 
00073 #ifdef _LIBC
00074 /* Rename the non ANSI C functions.  This is required by the standard
00075    because some ANSI C functions will require linking with this object
00076    file and the name space must not be polluted.  */
00077 # define strcasecmp __strcasecmp
00078 
00079 # ifndef mempcpy
00080 #  define mempcpy __mempcpy
00081 # endif
00082 # define HAVE_MEMPCPY       1
00083 # define HAVE___FSETLOCKING 1
00084 
00085 /* We need locking here since we can be called from different places.  */
00086 # include <bits/libc-lock.h>
00087 
00088 __libc_lock_define_initialized (static, lock);
00089 #endif
00090 
00091 #ifndef internal_function
00092 # define internal_function
00093 #endif
00094 
00095 /* Some optimizations for glibc.  */
00096 #ifdef _LIBC
00097 # define FEOF(fp)           feof_unlocked (fp)
00098 # define FGETS(buf, n, fp)  fgets_unlocked (buf, n, fp)
00099 #else
00100 # define FEOF(fp)           feof (fp)
00101 # define FGETS(buf, n, fp)  fgets (buf, n, fp)
00102 #endif
00103 
00104 /* For those losing systems which don't have `alloca' we have to add
00105    some additional code emulating it.  */
00106 #ifdef HAVE_ALLOCA
00107 # define freea(p) /* nothing */
00108 #else
00109 # define alloca(n) malloc (n)
00110 # define freea(p) free (p)
00111 #endif
00112 
00113 #if defined _LIBC_REENTRANT || HAVE_DECL_FGETS_UNLOCKED
00114 # undef fgets
00115 # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
00116 #endif
00117 #if defined _LIBC_REENTRANT || HAVE_DECL_FEOF_UNLOCKED
00118 # undef feof
00119 # define feof(s) feof_unlocked (s)
00120 #endif
00121 
00122 
00123 struct alias_map
00124 {
00125   const char *alias;
00126   const char *value;
00127 };
00128 
00129 
00130 #ifndef _LIBC
00131 # define libc_freeres_ptr(decl) decl
00132 #endif
00133 
00134 libc_freeres_ptr (static char *string_space);
00135 static size_t string_space_act;
00136 static size_t string_space_max;
00137 libc_freeres_ptr (static struct alias_map *map);
00138 static size_t nmap;
00139 static size_t maxmap;
00140 
00141 
00142 /* Prototypes for local functions.  */
00143 static size_t read_alias_file (const char *fname, int fname_len)
00144      internal_function;
00145 static int extend_alias_table (void);
00146 static int alias_compare (const struct alias_map *map1,
00147                        const struct alias_map *map2);
00148 
00149 
00150 const char *
00151 _nl_expand_alias (const char *name)
00152 {
00153   static const char *locale_alias_path;
00154   struct alias_map *retval;
00155   const char *result = NULL;
00156   size_t added;
00157 
00158 #ifdef _LIBC
00159   __libc_lock_lock (lock);
00160 #endif
00161 
00162   if (locale_alias_path == NULL)
00163     locale_alias_path = LOCALE_ALIAS_PATH;
00164 
00165   do
00166     {
00167       struct alias_map item;
00168 
00169       item.alias = name;
00170 
00171       if (nmap > 0)
00172        retval = (struct alias_map *) bsearch (&item, map, nmap,
00173                                           sizeof (struct alias_map),
00174                                           (int (*) (const void *,
00175                                                   const void *)
00176                                           ) alias_compare);
00177       else
00178        retval = NULL;
00179 
00180       /* We really found an alias.  Return the value.  */
00181       if (retval != NULL)
00182        {
00183          result = retval->value;
00184          break;
00185        }
00186 
00187       /* Perhaps we can find another alias file.  */
00188       added = 0;
00189       while (added == 0 && locale_alias_path[0] != '\0')
00190        {
00191          const char *start;
00192 
00193          while (locale_alias_path[0] == PATH_SEPARATOR)
00194            ++locale_alias_path;
00195          start = locale_alias_path;
00196 
00197          while (locale_alias_path[0] != '\0'
00198                && locale_alias_path[0] != PATH_SEPARATOR)
00199            ++locale_alias_path;
00200 
00201          if (start < locale_alias_path)
00202            added = read_alias_file (start, locale_alias_path - start);
00203        }
00204     }
00205   while (added != 0);
00206 
00207 #ifdef _LIBC
00208   __libc_lock_unlock (lock);
00209 #endif
00210 
00211   return result;
00212 }
00213 
00214 
00215 static size_t
00216 internal_function
00217 read_alias_file (const char *fname, int fname_len)
00218 {
00219   FILE *fp;
00220   char *full_fname;
00221   size_t added;
00222   static const char aliasfile[] = "/locale.alias";
00223 
00224   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
00225 #ifdef HAVE_MEMPCPY
00226   mempcpy (mempcpy (full_fname, fname, fname_len),
00227           aliasfile, sizeof aliasfile);
00228 #else
00229   memcpy (full_fname, fname, fname_len);
00230   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
00231 #endif
00232 
00233   fp = fopen (relocate (full_fname), "r");
00234   freea (full_fname);
00235   if (fp == NULL)
00236     return 0;
00237 
00238 #ifdef HAVE___FSETLOCKING
00239   /* No threads present.  */
00240   __fsetlocking (fp, FSETLOCKING_BYCALLER);
00241 #endif
00242 
00243   added = 0;
00244   while (!FEOF (fp))
00245     {
00246       /* It is a reasonable approach to use a fix buffer here because
00247         a) we are only interested in the first two fields
00248         b) these fields must be usable as file names and so must not
00249            be that long
00250         We avoid a multi-kilobyte buffer here since this would use up
00251         stack space which we might not have if the program ran out of
00252         memory.  */
00253       char buf[400];
00254       char *alias;
00255       char *value;
00256       char *cp;
00257 
00258       if (FGETS (buf, sizeof buf, fp) == NULL)
00259        /* EOF reached.  */
00260        break;
00261 
00262       cp = buf;
00263       /* Ignore leading white space.  */
00264       while (isspace ((unsigned char) cp[0]))
00265        ++cp;
00266 
00267       /* A leading '#' signals a comment line.  */
00268       if (cp[0] != '\0' && cp[0] != '#')
00269        {
00270          alias = cp++;
00271          while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
00272            ++cp;
00273          /* Terminate alias name.  */
00274          if (cp[0] != '\0')
00275            *cp++ = '\0';
00276 
00277          /* Now look for the beginning of the value.  */
00278          while (isspace ((unsigned char) cp[0]))
00279            ++cp;
00280 
00281          if (cp[0] != '\0')
00282            {
00283              size_t alias_len;
00284              size_t value_len;
00285 
00286              value = cp++;
00287              while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
00288               ++cp;
00289              /* Terminate value.  */
00290              if (cp[0] == '\n')
00291               {
00292                 /* This has to be done to make the following test
00293                    for the end of line possible.  We are looking for
00294                    the terminating '\n' which do not overwrite here.  */
00295                 *cp++ = '\0';
00296                 *cp = '\n';
00297               }
00298              else if (cp[0] != '\0')
00299               *cp++ = '\0';
00300 
00301              if (nmap >= maxmap)
00302               if (__builtin_expect (extend_alias_table (), 0))
00303                 return added;
00304 
00305              alias_len = strlen (alias) + 1;
00306              value_len = strlen (value) + 1;
00307 
00308              if (string_space_act + alias_len + value_len > string_space_max)
00309               {
00310                 /* Increase size of memory pool.  */
00311                 size_t new_size = (string_space_max
00312                                  + (alias_len + value_len > 1024
00313                                    ? alias_len + value_len : 1024));
00314                 char *new_pool = (char *) realloc (string_space, new_size);
00315                 if (new_pool == NULL)
00316                   return added;
00317 
00318                 if (__builtin_expect (string_space != new_pool, 0))
00319                   {
00320                     size_t i;
00321 
00322                     for (i = 0; i < nmap; i++)
00323                      {
00324                        map[i].alias += new_pool - string_space;
00325                        map[i].value += new_pool - string_space;
00326                      }
00327                   }
00328 
00329                 string_space = new_pool;
00330                 string_space_max = new_size;
00331               }
00332 
00333              map[nmap].alias = memcpy (&string_space[string_space_act],
00334                                    alias, alias_len);
00335              string_space_act += alias_len;
00336 
00337              map[nmap].value = memcpy (&string_space[string_space_act],
00338                                    value, value_len);
00339              string_space_act += value_len;
00340 
00341              ++nmap;
00342              ++added;
00343            }
00344        }
00345 
00346       /* Possibly not the whole line fits into the buffer.  Ignore
00347         the rest of the line.  */
00348       while (strchr (buf, '\n') == NULL)
00349        if (FGETS (buf, sizeof buf, fp) == NULL)
00350          /* Make sure the inner loop will be left.  The outer loop
00351             will exit at the `feof' test.  */
00352          break;
00353     }
00354 
00355   /* Should we test for ferror()?  I think we have to silently ignore
00356      errors.  --drepper  */
00357   fclose (fp);
00358 
00359   if (added > 0)
00360     qsort (map, nmap, sizeof (struct alias_map),
00361           (int (*) (const void *, const void *)) alias_compare);
00362 
00363   return added;
00364 }
00365 
00366 
00367 static int
00368 extend_alias_table ()
00369 {
00370   size_t new_size;
00371   struct alias_map *new_map;
00372 
00373   new_size = maxmap == 0 ? 100 : 2 * maxmap;
00374   new_map = (struct alias_map *) realloc (map, (new_size
00375                                           * sizeof (struct alias_map)));
00376   if (new_map == NULL)
00377     /* Simply don't extend: we don't have any more core.  */
00378     return -1;
00379 
00380   map = new_map;
00381   maxmap = new_size;
00382   return 0;
00383 }
00384 
00385 
00386 static int
00387 alias_compare (const struct alias_map *map1, const struct alias_map *map2)
00388 {
00389 #if defined _LIBC || defined HAVE_STRCASECMP
00390   return strcasecmp (map1->alias, map2->alias);
00391 #else
00392   const unsigned char *p1 = (const unsigned char *) map1->alias;
00393   const unsigned char *p2 = (const unsigned char *) map2->alias;
00394   unsigned char c1, c2;
00395 
00396   if (p1 == p2)
00397     return 0;
00398 
00399   do
00400     {
00401       /* I know this seems to be odd but the tolower() function in
00402         some systems libc cannot handle nonalpha characters.  */
00403       c1 = isupper (*p1) ? tolower (*p1) : *p1;
00404       c2 = isupper (*p2) ? tolower (*p2) : *p2;
00405       if (c1 == '\0')
00406        break;
00407       ++p1;
00408       ++p2;
00409     }
00410   while (c1 == c2);
00411 
00412   return c1 - c2;
00413 #endif
00414 }