Back to index

courier  0.68.2
localealias.c
Go to the documentation of this file.
00001 /* Handle aliases for locale names.
00002    Copyright (C) 1995-1999, 2000-2001, 2003, 2005-2006 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 #endif
00085 
00086 /* Handle multi-threaded applications.  */
00087 #ifdef _LIBC
00088 # include <bits/libc-lock.h>
00089 #else
00090 # include "lock.h"
00091 #endif
00092 
00093 #ifndef internal_function
00094 # define internal_function
00095 #endif
00096 
00097 /* Some optimizations for glibc.  */
00098 #ifdef _LIBC
00099 # define FEOF(fp)           feof_unlocked (fp)
00100 # define FGETS(buf, n, fp)  fgets_unlocked (buf, n, fp)
00101 #else
00102 # define FEOF(fp)           feof (fp)
00103 # define FGETS(buf, n, fp)  fgets (buf, n, fp)
00104 #endif
00105 
00106 /* For those losing systems which don't have `alloca' we have to add
00107    some additional code emulating it.  */
00108 #ifdef HAVE_ALLOCA
00109 # define freea(p) /* nothing */
00110 #else
00111 # define alloca(n) malloc (n)
00112 # define freea(p) free (p)
00113 #endif
00114 
00115 #if defined _LIBC_REENTRANT || HAVE_DECL_FGETS_UNLOCKED
00116 # undef fgets
00117 # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
00118 #endif
00119 #if defined _LIBC_REENTRANT || HAVE_DECL_FEOF_UNLOCKED
00120 # undef feof
00121 # define feof(s) feof_unlocked (s)
00122 #endif
00123 
00124 
00125 __libc_lock_define_initialized (static, lock)
00126 
00127 
00128 struct alias_map
00129 {
00130   const char *alias;
00131   const char *value;
00132 };
00133 
00134 
00135 #ifndef _LIBC
00136 # define libc_freeres_ptr(decl) decl
00137 #endif
00138 
00139 libc_freeres_ptr (static char *string_space);
00140 static size_t string_space_act;
00141 static size_t string_space_max;
00142 libc_freeres_ptr (static struct alias_map *map);
00143 static size_t nmap;
00144 static size_t maxmap;
00145 
00146 
00147 /* Prototypes for local functions.  */
00148 static size_t read_alias_file (const char *fname, int fname_len)
00149      internal_function;
00150 static int extend_alias_table (void);
00151 static int alias_compare (const struct alias_map *map1,
00152                        const struct alias_map *map2);
00153 
00154 
00155 const char *
00156 _nl_expand_alias (const char *name)
00157 {
00158   static const char *locale_alias_path;
00159   struct alias_map *retval;
00160   const char *result = NULL;
00161   size_t added;
00162 
00163   __libc_lock_lock (lock);
00164 
00165   if (locale_alias_path == NULL)
00166     locale_alias_path = LOCALE_ALIAS_PATH;
00167 
00168   do
00169     {
00170       struct alias_map item;
00171 
00172       item.alias = name;
00173 
00174       if (nmap > 0)
00175        retval = (struct alias_map *) bsearch (&item, map, nmap,
00176                                           sizeof (struct alias_map),
00177                                           (int (*) (const void *,
00178                                                   const void *)
00179                                           ) alias_compare);
00180       else
00181        retval = NULL;
00182 
00183       /* We really found an alias.  Return the value.  */
00184       if (retval != NULL)
00185        {
00186          result = retval->value;
00187          break;
00188        }
00189 
00190       /* Perhaps we can find another alias file.  */
00191       added = 0;
00192       while (added == 0 && locale_alias_path[0] != '\0')
00193        {
00194          const char *start;
00195 
00196          while (locale_alias_path[0] == PATH_SEPARATOR)
00197            ++locale_alias_path;
00198          start = locale_alias_path;
00199 
00200          while (locale_alias_path[0] != '\0'
00201                && locale_alias_path[0] != PATH_SEPARATOR)
00202            ++locale_alias_path;
00203 
00204          if (start < locale_alias_path)
00205            added = read_alias_file (start, locale_alias_path - start);
00206        }
00207     }
00208   while (added != 0);
00209 
00210   __libc_lock_unlock (lock);
00211 
00212   return result;
00213 }
00214 
00215 
00216 static size_t
00217 internal_function
00218 read_alias_file (const char *fname, int fname_len)
00219 {
00220   FILE *fp;
00221   char *full_fname;
00222   size_t added;
00223   static const char aliasfile[] = "/locale.alias";
00224 
00225   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
00226 #ifdef HAVE_MEMPCPY
00227   mempcpy (mempcpy (full_fname, fname, fname_len),
00228           aliasfile, sizeof aliasfile);
00229 #else
00230   memcpy (full_fname, fname, fname_len);
00231   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
00232 #endif
00233 
00234 #ifdef _LIBC
00235   /* Note the file is opened with cancellation in the I/O functions
00236      disabled.  */
00237   fp = fopen (relocate (full_fname), "rc");
00238 #else
00239   fp = fopen (relocate (full_fname), "r");
00240 #endif
00241   freea (full_fname);
00242   if (fp == NULL)
00243     return 0;
00244 
00245 #ifdef HAVE___FSETLOCKING
00246   /* No threads present.  */
00247   __fsetlocking (fp, FSETLOCKING_BYCALLER);
00248 #endif
00249 
00250   added = 0;
00251   while (!FEOF (fp))
00252     {
00253       /* It is a reasonable approach to use a fix buffer here because
00254         a) we are only interested in the first two fields
00255         b) these fields must be usable as file names and so must not
00256            be that long
00257         We avoid a multi-kilobyte buffer here since this would use up
00258         stack space which we might not have if the program ran out of
00259         memory.  */
00260       char buf[400];
00261       char *alias;
00262       char *value;
00263       char *cp;
00264       int complete_line;
00265 
00266       if (FGETS (buf, sizeof buf, fp) == NULL)
00267        /* EOF reached.  */
00268        break;
00269 
00270       /* Determine whether the line is complete.  */
00271       complete_line = strchr (buf, '\n') != NULL;
00272 
00273       cp = buf;
00274       /* Ignore leading white space.  */
00275       while (isspace ((unsigned char) cp[0]))
00276        ++cp;
00277 
00278       /* A leading '#' signals a comment line.  */
00279       if (cp[0] != '\0' && cp[0] != '#')
00280        {
00281          alias = cp++;
00282          while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
00283            ++cp;
00284          /* Terminate alias name.  */
00285          if (cp[0] != '\0')
00286            *cp++ = '\0';
00287 
00288          /* Now look for the beginning of the value.  */
00289          while (isspace ((unsigned char) cp[0]))
00290            ++cp;
00291 
00292          if (cp[0] != '\0')
00293            {
00294              value = cp++;
00295              while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
00296               ++cp;
00297              /* Terminate value.  */
00298              if (cp[0] == '\n')
00299               {
00300                 /* This has to be done to make the following test
00301                    for the end of line possible.  We are looking for
00302                    the terminating '\n' which do not overwrite here.  */
00303                 *cp++ = '\0';
00304                 *cp = '\n';
00305               }
00306              else if (cp[0] != '\0')
00307               *cp++ = '\0';
00308 
00309 #ifdef IN_LIBGLOCALE
00310              /* glibc's locale.alias contains entries for ja_JP and ko_KR
00311                that make it impossible to use a Japanese or Korean UTF-8
00312                locale under the name "ja_JP" or "ko_KR".  Ignore these
00313                entries.  */
00314              if (strchr (alias, '_') == NULL)
00315 #endif
00316               {
00317                 size_t alias_len;
00318                 size_t value_len;
00319 
00320                 if (nmap >= maxmap)
00321                   if (__builtin_expect (extend_alias_table (), 0))
00322                     goto out;
00323 
00324                 alias_len = strlen (alias) + 1;
00325                 value_len = strlen (value) + 1;
00326 
00327                 if (string_space_act + alias_len + value_len > string_space_max)
00328                   {
00329                     /* Increase size of memory pool.  */
00330                     size_t new_size = (string_space_max
00331                                     + (alias_len + value_len > 1024
00332                                        ? alias_len + value_len : 1024));
00333                     char *new_pool = (char *) realloc (string_space, new_size);
00334                     if (new_pool == NULL)
00335                      goto out;
00336 
00337                     if (__builtin_expect (string_space != new_pool, 0))
00338                      {
00339                        size_t i;
00340 
00341                        for (i = 0; i < nmap; i++)
00342                          {
00343                            map[i].alias += new_pool - string_space;
00344                            map[i].value += new_pool - string_space;
00345                          }
00346                      }
00347 
00348                     string_space = new_pool;
00349                     string_space_max = new_size;
00350                   }
00351 
00352                 map[nmap].alias =
00353                   (const char *) memcpy (&string_space[string_space_act],
00354                                       alias, alias_len);
00355                 string_space_act += alias_len;
00356 
00357                 map[nmap].value =
00358                   (const char *) memcpy (&string_space[string_space_act],
00359                                       value, value_len);
00360                 string_space_act += value_len;
00361 
00362                 ++nmap;
00363                 ++added;
00364               }
00365            }
00366        }
00367 
00368       /* Possibly not the whole line fits into the buffer.  Ignore
00369         the rest of the line.  */
00370       if (! complete_line)
00371        do
00372          if (FGETS (buf, sizeof buf, fp) == NULL)
00373            /* Make sure the inner loop will be left.  The outer loop
00374               will exit at the `feof' test.  */
00375            break;
00376        while (strchr (buf, '\n') == NULL);
00377     }
00378 
00379  out:
00380   /* Should we test for ferror()?  I think we have to silently ignore
00381      errors.  --drepper  */
00382   fclose (fp);
00383 
00384   if (added > 0)
00385     qsort (map, nmap, sizeof (struct alias_map),
00386           (int (*) (const void *, const void *)) alias_compare);
00387 
00388   return added;
00389 }
00390 
00391 
00392 static int
00393 extend_alias_table ()
00394 {
00395   size_t new_size;
00396   struct alias_map *new_map;
00397 
00398   new_size = maxmap == 0 ? 100 : 2 * maxmap;
00399   new_map = (struct alias_map *) realloc (map, (new_size
00400                                           * sizeof (struct alias_map)));
00401   if (new_map == NULL)
00402     /* Simply don't extend: we don't have any more core.  */
00403     return -1;
00404 
00405   map = new_map;
00406   maxmap = new_size;
00407   return 0;
00408 }
00409 
00410 
00411 static int
00412 alias_compare (const struct alias_map *map1, const struct alias_map *map2)
00413 {
00414 #if defined _LIBC || defined HAVE_STRCASECMP
00415   return strcasecmp (map1->alias, map2->alias);
00416 #else
00417   const unsigned char *p1 = (const unsigned char *) map1->alias;
00418   const unsigned char *p2 = (const unsigned char *) map2->alias;
00419   unsigned char c1, c2;
00420 
00421   if (p1 == p2)
00422     return 0;
00423 
00424   do
00425     {
00426       /* I know this seems to be odd but the tolower() function in
00427         some systems libc cannot handle nonalpha characters.  */
00428       c1 = isupper (*p1) ? tolower (*p1) : *p1;
00429       c2 = isupper (*p2) ? tolower (*p2) : *p2;
00430       if (c1 == '\0')
00431        break;
00432       ++p1;
00433       ++p2;
00434     }
00435   while (c1 == c2);
00436 
00437   return c1 - c2;
00438 #endif
00439 }