Back to index

cell-binutils  2.17cvs20070401
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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
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 || defined HAVE_FGETS_UNLOCKED
00114 # undef fgets
00115 # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
00116 #endif
00117 #if defined _LIBC_REENTRANT || defined HAVE_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 PARAMS ((const char *fname, int fname_len))
00144      internal_function;
00145 static int extend_alias_table PARAMS ((void));
00146 static int alias_compare PARAMS ((const struct alias_map *map1,
00147                               const struct alias_map *map2));
00148 
00149 
00150 const char *
00151 _nl_expand_alias (name)
00152     const char *name;
00153 {
00154   static const char *locale_alias_path;
00155   struct alias_map *retval;
00156   const char *result = NULL;
00157   size_t added;
00158 
00159 #ifdef _LIBC
00160   __libc_lock_lock (lock);
00161 #endif
00162 
00163   if (locale_alias_path == NULL)
00164     locale_alias_path = LOCALE_ALIAS_PATH;
00165 
00166   do
00167     {
00168       struct alias_map item;
00169 
00170       item.alias = name;
00171 
00172       if (nmap > 0)
00173        retval = (struct alias_map *) bsearch (&item, map, nmap,
00174                                           sizeof (struct alias_map),
00175                                           (int (*) PARAMS ((const void *,
00176                                                          const void *))
00177                                           ) alias_compare);
00178       else
00179        retval = NULL;
00180 
00181       /* We really found an alias.  Return the value.  */
00182       if (retval != NULL)
00183        {
00184          result = retval->value;
00185          break;
00186        }
00187 
00188       /* Perhaps we can find another alias file.  */
00189       added = 0;
00190       while (added == 0 && locale_alias_path[0] != '\0')
00191        {
00192          const char *start;
00193 
00194          while (locale_alias_path[0] == PATH_SEPARATOR)
00195            ++locale_alias_path;
00196          start = locale_alias_path;
00197 
00198          while (locale_alias_path[0] != '\0'
00199                && locale_alias_path[0] != PATH_SEPARATOR)
00200            ++locale_alias_path;
00201 
00202          if (start < locale_alias_path)
00203            added = read_alias_file (start, locale_alias_path - start);
00204        }
00205     }
00206   while (added != 0);
00207 
00208 #ifdef _LIBC
00209   __libc_lock_unlock (lock);
00210 #endif
00211 
00212   return result;
00213 }
00214 
00215 
00216 static size_t
00217 internal_function
00218 read_alias_file (fname, fname_len)
00219      const char *fname;
00220      int fname_len;
00221 {
00222   FILE *fp;
00223   char *full_fname;
00224   size_t added;
00225   static const char aliasfile[] = "/locale.alias";
00226 
00227   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
00228 #ifdef HAVE_MEMPCPY
00229   mempcpy (mempcpy (full_fname, fname, fname_len),
00230           aliasfile, sizeof aliasfile);
00231 #else
00232   memcpy (full_fname, fname, fname_len);
00233   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
00234 #endif
00235 
00236   fp = fopen (relocate (full_fname), "r");
00237   freea (full_fname);
00238   if (fp == NULL)
00239     return 0;
00240 
00241 #ifdef HAVE___FSETLOCKING
00242   /* No threads present.  */
00243   __fsetlocking (fp, FSETLOCKING_BYCALLER);
00244 #endif
00245 
00246   added = 0;
00247   while (!FEOF (fp))
00248     {
00249       /* It is a reasonable approach to use a fix buffer here because
00250         a) we are only interested in the first two fields
00251         b) these fields must be usable as file names and so must not
00252            be that long
00253         We avoid a multi-kilobyte buffer here since this would use up
00254         stack space which we might not have if the program ran out of
00255         memory.  */
00256       char buf[400];
00257       char *alias;
00258       char *value;
00259       char *cp;
00260 
00261       if (FGETS (buf, sizeof buf, fp) == NULL)
00262        /* EOF reached.  */
00263        break;
00264 
00265       cp = buf;
00266       /* Ignore leading white space.  */
00267       while (isspace ((unsigned char) cp[0]))
00268        ++cp;
00269 
00270       /* A leading '#' signals a comment line.  */
00271       if (cp[0] != '\0' && cp[0] != '#')
00272        {
00273          alias = cp++;
00274          while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
00275            ++cp;
00276          /* Terminate alias name.  */
00277          if (cp[0] != '\0')
00278            *cp++ = '\0';
00279 
00280          /* Now look for the beginning of the value.  */
00281          while (isspace ((unsigned char) cp[0]))
00282            ++cp;
00283 
00284          if (cp[0] != '\0')
00285            {
00286              size_t alias_len;
00287              size_t value_len;
00288 
00289              value = cp++;
00290              while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
00291               ++cp;
00292              /* Terminate value.  */
00293              if (cp[0] == '\n')
00294               {
00295                 /* This has to be done to make the following test
00296                    for the end of line possible.  We are looking for
00297                    the terminating '\n' which do not overwrite here.  */
00298                 *cp++ = '\0';
00299                 *cp = '\n';
00300               }
00301              else if (cp[0] != '\0')
00302               *cp++ = '\0';
00303 
00304              if (nmap >= maxmap)
00305               if (__builtin_expect (extend_alias_table (), 0))
00306                 return added;
00307 
00308              alias_len = strlen (alias) + 1;
00309              value_len = strlen (value) + 1;
00310 
00311              if (string_space_act + alias_len + value_len > string_space_max)
00312               {
00313                 /* Increase size of memory pool.  */
00314                 size_t new_size = (string_space_max
00315                                  + (alias_len + value_len > 1024
00316                                    ? alias_len + value_len : 1024));
00317                 char *new_pool = (char *) realloc (string_space, new_size);
00318                 if (new_pool == NULL)
00319                   return added;
00320 
00321                 if (__builtin_expect (string_space != new_pool, 0))
00322                   {
00323                     size_t i;
00324 
00325                     for (i = 0; i < nmap; i++)
00326                      {
00327                        map[i].alias += new_pool - string_space;
00328                        map[i].value += new_pool - string_space;
00329                      }
00330                   }
00331 
00332                 string_space = new_pool;
00333                 string_space_max = new_size;
00334               }
00335 
00336              map[nmap].alias = memcpy (&string_space[string_space_act],
00337                                    alias, alias_len);
00338              string_space_act += alias_len;
00339 
00340              map[nmap].value = memcpy (&string_space[string_space_act],
00341                                    value, value_len);
00342              string_space_act += value_len;
00343 
00344              ++nmap;
00345              ++added;
00346            }
00347        }
00348 
00349       /* Possibly not the whole line fits into the buffer.  Ignore
00350         the rest of the line.  */
00351       while (strchr (buf, '\n') == NULL)
00352        if (FGETS (buf, sizeof buf, fp) == NULL)
00353          /* Make sure the inner loop will be left.  The outer loop
00354             will exit at the `feof' test.  */
00355          break;
00356     }
00357 
00358   /* Should we test for ferror()?  I think we have to silently ignore
00359      errors.  --drepper  */
00360   fclose (fp);
00361 
00362   if (added > 0)
00363     qsort (map, nmap, sizeof (struct alias_map),
00364           (int (*) PARAMS ((const void *, const void *))) alias_compare);
00365 
00366   return added;
00367 }
00368 
00369 
00370 static int
00371 extend_alias_table ()
00372 {
00373   size_t new_size;
00374   struct alias_map *new_map;
00375 
00376   new_size = maxmap == 0 ? 100 : 2 * maxmap;
00377   new_map = (struct alias_map *) realloc (map, (new_size
00378                                           * sizeof (struct alias_map)));
00379   if (new_map == NULL)
00380     /* Simply don't extend: we don't have any more core.  */
00381     return -1;
00382 
00383   map = new_map;
00384   maxmap = new_size;
00385   return 0;
00386 }
00387 
00388 
00389 static int
00390 alias_compare (map1, map2)
00391      const struct alias_map *map1;
00392      const struct alias_map *map2;
00393 {
00394 #if defined _LIBC || defined HAVE_STRCASECMP
00395   return strcasecmp (map1->alias, map2->alias);
00396 #else
00397   const unsigned char *p1 = (const unsigned char *) map1->alias;
00398   const unsigned char *p2 = (const unsigned char *) map2->alias;
00399   unsigned char c1, c2;
00400 
00401   if (p1 == p2)
00402     return 0;
00403 
00404   do
00405     {
00406       /* I know this seems to be odd but the tolower() function in
00407         some systems libc cannot handle nonalpha characters.  */
00408       c1 = isupper (*p1) ? tolower (*p1) : *p1;
00409       c2 = isupper (*p2) ? tolower (*p2) : *p2;
00410       if (c1 == '\0')
00411        break;
00412       ++p1;
00413       ++p2;
00414     }
00415   while (c1 == c2);
00416 
00417   return c1 - c2;
00418 #endif
00419 }