Home | History | Annotate | Download | only in intl
      1 /* Handle aliases for locale names.
      2    Copyright (C) 1995-1999, 2000-2001, 2003 Free Software Foundation, Inc.
      3 
      4    This program is free software; you can redistribute it and/or modify it
      5    under the terms of the GNU Library General Public License as published
      6    by the Free Software Foundation; either version 2, or (at your option)
      7    any later version.
      8 
      9    This program is distributed in the hope that it will be useful,
     10    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    Library General Public License for more details.
     13 
     14    You should have received a copy of the GNU Library General Public
     15    License along with this program; if not, write to the Free Software
     16    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
     17    USA.  */
     18 
     19 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
     20    This must come before <config.h> because <config.h> may include
     21    <features.h>, and once <features.h> has been included, it's too late.  */
     22 #ifndef _GNU_SOURCE
     23 # define _GNU_SOURCE    1
     24 #endif
     25 
     26 #ifdef HAVE_CONFIG_H
     27 # include <config.h>
     28 #endif
     29 
     30 #include <ctype.h>
     31 #include <stdio.h>
     32 #if defined _LIBC || defined HAVE___FSETLOCKING
     33 # include <stdio_ext.h>
     34 #endif
     35 #include <sys/types.h>
     36 
     37 #ifdef __GNUC__
     38 # undef alloca
     39 # define alloca __builtin_alloca
     40 # define HAVE_ALLOCA 1
     41 #else
     42 # ifdef _MSC_VER
     43 #  include <malloc.h>
     44 #  define alloca _alloca
     45 # else
     46 #  if defined HAVE_ALLOCA_H || defined _LIBC
     47 #   include <alloca.h>
     48 #  else
     49 #   ifdef _AIX
     50  #pragma alloca
     51 #   else
     52 #    ifndef alloca
     53 char *alloca ();
     54 #    endif
     55 #   endif
     56 #  endif
     57 # endif
     58 #endif
     59 
     60 #include <stdlib.h>
     61 #include <string.h>
     62 
     63 #include "gettextP.h"
     64 
     65 #if ENABLE_RELOCATABLE
     66 # include "relocatable.h"
     67 #else
     68 # define relocate(pathname) (pathname)
     69 #endif
     70 
     71 /* @@ end of prolog @@ */
     72 
     73 #ifdef _LIBC
     74 /* Rename the non ANSI C functions.  This is required by the standard
     75    because some ANSI C functions will require linking with this object
     76    file and the name space must not be polluted.  */
     77 # define strcasecmp __strcasecmp
     78 
     79 # ifndef mempcpy
     80 #  define mempcpy __mempcpy
     81 # endif
     82 # define HAVE_MEMPCPY	1
     83 # define HAVE___FSETLOCKING	1
     84 
     85 /* We need locking here since we can be called from different places.  */
     86 # include <bits/libc-lock.h>
     87 
     88 __libc_lock_define_initialized (static, lock);
     89 #endif
     90 
     91 #ifndef internal_function
     92 # define internal_function
     93 #endif
     94 
     95 /* Some optimizations for glibc.  */
     96 #ifdef _LIBC
     97 # define FEOF(fp)		feof_unlocked (fp)
     98 # define FGETS(buf, n, fp)	fgets_unlocked (buf, n, fp)
     99 #else
    100 # define FEOF(fp)		feof (fp)
    101 # define FGETS(buf, n, fp)	fgets (buf, n, fp)
    102 #endif
    103 
    104 /* For those losing systems which don't have `alloca' we have to add
    105    some additional code emulating it.  */
    106 #ifdef HAVE_ALLOCA
    107 # define freea(p) /* nothing */
    108 #else
    109 # define alloca(n) malloc (n)
    110 # define freea(p) free (p)
    111 #endif
    112 
    113 #if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED
    114 # undef fgets
    115 # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
    116 #endif
    117 #if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED
    118 # undef feof
    119 # define feof(s) feof_unlocked (s)
    120 #endif
    121 
    122 
    123 struct alias_map
    124 {
    125   const char *alias;
    126   const char *value;
    127 };
    128 
    129 
    130 #ifndef _LIBC
    131 # define libc_freeres_ptr(decl) decl
    132 #endif
    133 
    134 libc_freeres_ptr (static char *string_space);
    135 static size_t string_space_act;
    136 static size_t string_space_max;
    137 libc_freeres_ptr (static struct alias_map *map);
    138 static size_t nmap;
    139 static size_t maxmap;
    140 
    141 
    142 /* Prototypes for local functions.  */
    143 static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
    144      internal_function;
    145 static int extend_alias_table PARAMS ((void));
    146 static int alias_compare PARAMS ((const struct alias_map *map1,
    147 				  const struct alias_map *map2));
    148 
    149 
    150 const char *
    151 _nl_expand_alias (name)
    152     const char *name;
    153 {
    154   static const char *locale_alias_path;
    155   struct alias_map *retval;
    156   const char *result = NULL;
    157   size_t added;
    158 
    159 #ifdef _LIBC
    160   __libc_lock_lock (lock);
    161 #endif
    162 
    163   if (locale_alias_path == NULL)
    164     locale_alias_path = LOCALE_ALIAS_PATH;
    165 
    166   do
    167     {
    168       struct alias_map item;
    169 
    170       item.alias = name;
    171 
    172       if (nmap > 0)
    173 	retval = (struct alias_map *) bsearch (&item, map, nmap,
    174 					       sizeof (struct alias_map),
    175 					       (int (*) PARAMS ((const void *,
    176 								 const void *))
    177 						) alias_compare);
    178       else
    179 	retval = NULL;
    180 
    181       /* We really found an alias.  Return the value.  */
    182       if (retval != NULL)
    183 	{
    184 	  result = retval->value;
    185 	  break;
    186 	}
    187 
    188       /* Perhaps we can find another alias file.  */
    189       added = 0;
    190       while (added == 0 && locale_alias_path[0] != '\0')
    191 	{
    192 	  const char *start;
    193 
    194 	  while (locale_alias_path[0] == PATH_SEPARATOR)
    195 	    ++locale_alias_path;
    196 	  start = locale_alias_path;
    197 
    198 	  while (locale_alias_path[0] != '\0'
    199 		 && locale_alias_path[0] != PATH_SEPARATOR)
    200 	    ++locale_alias_path;
    201 
    202 	  if (start < locale_alias_path)
    203 	    added = read_alias_file (start, locale_alias_path - start);
    204 	}
    205     }
    206   while (added != 0);
    207 
    208 #ifdef _LIBC
    209   __libc_lock_unlock (lock);
    210 #endif
    211 
    212   return result;
    213 }
    214 
    215 
    216 static size_t
    217 internal_function
    218 read_alias_file (fname, fname_len)
    219      const char *fname;
    220      int fname_len;
    221 {
    222   FILE *fp;
    223   char *full_fname;
    224   size_t added;
    225   static const char aliasfile[] = "/locale.alias";
    226 
    227   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
    228 #ifdef HAVE_MEMPCPY
    229   mempcpy (mempcpy (full_fname, fname, fname_len),
    230 	   aliasfile, sizeof aliasfile);
    231 #else
    232   memcpy (full_fname, fname, fname_len);
    233   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
    234 #endif
    235 
    236   fp = fopen (relocate (full_fname), "r");
    237   freea (full_fname);
    238   if (fp == NULL)
    239     return 0;
    240 
    241 #ifdef HAVE___FSETLOCKING
    242   /* No threads present.  */
    243   __fsetlocking (fp, FSETLOCKING_BYCALLER);
    244 #endif
    245 
    246   added = 0;
    247   while (!FEOF (fp))
    248     {
    249       /* It is a reasonable approach to use a fix buffer here because
    250 	 a) we are only interested in the first two fields
    251 	 b) these fields must be usable as file names and so must not
    252 	    be that long
    253 	 We avoid a multi-kilobyte buffer here since this would use up
    254 	 stack space which we might not have if the program ran out of
    255 	 memory.  */
    256       char buf[400];
    257       char *alias;
    258       char *value;
    259       char *cp;
    260 
    261       if (FGETS (buf, sizeof buf, fp) == NULL)
    262 	/* EOF reached.  */
    263 	break;
    264 
    265       cp = buf;
    266       /* Ignore leading white space.  */
    267       while (isspace ((unsigned char) cp[0]))
    268 	++cp;
    269 
    270       /* A leading '#' signals a comment line.  */
    271       if (cp[0] != '\0' && cp[0] != '#')
    272 	{
    273 	  alias = cp++;
    274 	  while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
    275 	    ++cp;
    276 	  /* Terminate alias name.  */
    277 	  if (cp[0] != '\0')
    278 	    *cp++ = '\0';
    279 
    280 	  /* Now look for the beginning of the value.  */
    281 	  while (isspace ((unsigned char) cp[0]))
    282 	    ++cp;
    283 
    284 	  if (cp[0] != '\0')
    285 	    {
    286 	      size_t alias_len;
    287 	      size_t value_len;
    288 
    289 	      value = cp++;
    290 	      while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
    291 		++cp;
    292 	      /* Terminate value.  */
    293 	      if (cp[0] == '\n')
    294 		{
    295 		  /* This has to be done to make the following test
    296 		     for the end of line possible.  We are looking for
    297 		     the terminating '\n' which do not overwrite here.  */
    298 		  *cp++ = '\0';
    299 		  *cp = '\n';
    300 		}
    301 	      else if (cp[0] != '\0')
    302 		*cp++ = '\0';
    303 
    304 	      if (nmap >= maxmap)
    305 		if (__builtin_expect (extend_alias_table (), 0))
    306 		  return added;
    307 
    308 	      alias_len = strlen (alias) + 1;
    309 	      value_len = strlen (value) + 1;
    310 
    311 	      if (string_space_act + alias_len + value_len > string_space_max)
    312 		{
    313 		  /* Increase size of memory pool.  */
    314 		  size_t new_size = (string_space_max
    315 				     + (alias_len + value_len > 1024
    316 					? alias_len + value_len : 1024));
    317 		  char *new_pool = (char *) realloc (string_space, new_size);
    318 		  if (new_pool == NULL)
    319 		    return added;
    320 
    321 		  if (__builtin_expect (string_space != new_pool, 0))
    322 		    {
    323 		      size_t i;
    324 
    325 		      for (i = 0; i < nmap; i++)
    326 			{
    327 			  map[i].alias += new_pool - string_space;
    328 			  map[i].value += new_pool - string_space;
    329 			}
    330 		    }
    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       /* Possibly not the whole line fits into the buffer.  Ignore
    350 	 the rest of the line.  */
    351       while (strchr (buf, '\n') == NULL)
    352 	if (FGETS (buf, sizeof buf, fp) == NULL)
    353 	  /* Make sure the inner loop will be left.  The outer loop
    354 	     will exit at the `feof' test.  */
    355 	  break;
    356     }
    357 
    358   /* Should we test for ferror()?  I think we have to silently ignore
    359      errors.  --drepper  */
    360   fclose (fp);
    361 
    362   if (added > 0)
    363     qsort (map, nmap, sizeof (struct alias_map),
    364 	   (int (*) PARAMS ((const void *, const void *))) alias_compare);
    365 
    366   return added;
    367 }
    368 
    369 
    370 static int
    371 extend_alias_table ()
    372 {
    373   size_t new_size;
    374   struct alias_map *new_map;
    375 
    376   new_size = maxmap == 0 ? 100 : 2 * maxmap;
    377   new_map = (struct alias_map *) realloc (map, (new_size
    378 						* sizeof (struct alias_map)));
    379   if (new_map == NULL)
    380     /* Simply don't extend: we don't have any more core.  */
    381     return -1;
    382 
    383   map = new_map;
    384   maxmap = new_size;
    385   return 0;
    386 }
    387 
    388 
    389 static int
    390 alias_compare (map1, map2)
    391      const struct alias_map *map1;
    392      const struct alias_map *map2;
    393 {
    394 #if defined _LIBC || defined HAVE_STRCASECMP
    395   return strcasecmp (map1->alias, map2->alias);
    396 #else
    397   const unsigned char *p1 = (const unsigned char *) map1->alias;
    398   const unsigned char *p2 = (const unsigned char *) map2->alias;
    399   unsigned char c1, c2;
    400 
    401   if (p1 == p2)
    402     return 0;
    403 
    404   do
    405     {
    406       /* I know this seems to be odd but the tolower() function in
    407 	 some systems libc cannot handle nonalpha characters.  */
    408       c1 = isupper (*p1) ? tolower (*p1) : *p1;
    409       c2 = isupper (*p2) ? tolower (*p2) : *p2;
    410       if (c1 == '\0')
    411 	break;
    412       ++p1;
    413       ++p2;
    414     }
    415   while (c1 == c2);
    416 
    417   return c1 - c2;
    418 #endif
    419 }
    420