Back to index

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