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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     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 || HAVE_DECL_FGETS_UNLOCKED
    114 # undef fgets
    115 # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
    116 #endif
    117 #if defined _LIBC_REENTRANT || HAVE_DECL_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 (const char *fname, int fname_len)
    144      internal_function;
    145 static int extend_alias_table (void);
    146 static int alias_compare (const struct alias_map *map1,
    147 			  const struct alias_map *map2);
    148 
    149 
    150 const char *
    151 _nl_expand_alias (const char *name)
    152 {
    153   static const char *locale_alias_path;
    154   struct alias_map *retval;
    155   const char *result = NULL;
    156   size_t added;
    157 
    158 #ifdef _LIBC
    159   __libc_lock_lock (lock);
    160 #endif
    161 
    162   if (locale_alias_path == NULL)
    163     locale_alias_path = LOCALE_ALIAS_PATH;
    164 
    165   do
    166     {
    167       struct alias_map item;
    168 
    169       item.alias = name;
    170 
    171       if (nmap > 0)
    172 	retval = (struct alias_map *) bsearch (&item, map, nmap,
    173 					       sizeof (struct alias_map),
    174 					       (int (*) (const void *,
    175 							 const void *)
    176 						) alias_compare);
    177       else
    178 	retval = NULL;
    179 
    180       /* We really found an alias.  Return the value.  */
    181       if (retval != NULL)
    182 	{
    183 	  result = retval->value;
    184 	  break;
    185 	}
    186 
    187       /* Perhaps we can find another alias file.  */
    188       added = 0;
    189       while (added == 0 && locale_alias_path[0] != '\0')
    190 	{
    191 	  const char *start;
    192 
    193 	  while (locale_alias_path[0] == PATH_SEPARATOR)
    194 	    ++locale_alias_path;
    195 	  start = locale_alias_path;
    196 
    197 	  while (locale_alias_path[0] != '\0'
    198 		 && locale_alias_path[0] != PATH_SEPARATOR)
    199 	    ++locale_alias_path;
    200 
    201 	  if (start < locale_alias_path)
    202 	    added = read_alias_file (start, locale_alias_path - start);
    203 	}
    204     }
    205   while (added != 0);
    206 
    207 #ifdef _LIBC
    208   __libc_lock_unlock (lock);
    209 #endif
    210 
    211   return result;
    212 }
    213 
    214 
    215 static size_t
    216 internal_function
    217 read_alias_file (const char *fname, int fname_len)
    218 {
    219   FILE *fp;
    220   char *full_fname;
    221   size_t added;
    222   static const char aliasfile[] = "/locale.alias";
    223 
    224   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
    225 #ifdef HAVE_MEMPCPY
    226   mempcpy (mempcpy (full_fname, fname, fname_len),
    227 	   aliasfile, sizeof aliasfile);
    228 #else
    229   memcpy (full_fname, fname, fname_len);
    230   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
    231 #endif
    232 
    233   fp = fopen (relocate (full_fname), "r");
    234   freea (full_fname);
    235   if (fp == NULL)
    236     return 0;
    237 
    238 #ifdef HAVE___FSETLOCKING
    239   /* No threads present.  */
    240   __fsetlocking (fp, FSETLOCKING_BYCALLER);
    241 #endif
    242 
    243   added = 0;
    244   while (!FEOF (fp))
    245     {
    246       /* It is a reasonable approach to use a fix buffer here because
    247 	 a) we are only interested in the first two fields
    248 	 b) these fields must be usable as file names and so must not
    249 	    be that long
    250 	 We avoid a multi-kilobyte buffer here since this would use up
    251 	 stack space which we might not have if the program ran out of
    252 	 memory.  */
    253       char buf[400];
    254       char *alias;
    255       char *value;
    256       char *cp;
    257 
    258       if (FGETS (buf, sizeof buf, fp) == NULL)
    259 	/* EOF reached.  */
    260 	break;
    261 
    262       cp = buf;
    263       /* Ignore leading white space.  */
    264       while (isspace ((unsigned char) cp[0]))
    265 	++cp;
    266 
    267       /* A leading '#' signals a comment line.  */
    268       if (cp[0] != '\0' && cp[0] != '#')
    269 	{
    270 	  alias = cp++;
    271 	  while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
    272 	    ++cp;
    273 	  /* Terminate alias name.  */
    274 	  if (cp[0] != '\0')
    275 	    *cp++ = '\0';
    276 
    277 	  /* Now look for the beginning of the value.  */
    278 	  while (isspace ((unsigned char) cp[0]))
    279 	    ++cp;
    280 
    281 	  if (cp[0] != '\0')
    282 	    {
    283 	      size_t alias_len;
    284 	      size_t value_len;
    285 
    286 	      value = cp++;
    287 	      while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
    288 		++cp;
    289 	      /* Terminate value.  */
    290 	      if (cp[0] == '\n')
    291 		{
    292 		  /* This has to be done to make the following test
    293 		     for the end of line possible.  We are looking for
    294 		     the terminating '\n' which do not overwrite here.  */
    295 		  *cp++ = '\0';
    296 		  *cp = '\n';
    297 		}
    298 	      else if (cp[0] != '\0')
    299 		*cp++ = '\0';
    300 
    301 	      if (nmap >= maxmap)
    302 		if (__builtin_expect (extend_alias_table (), 0))
    303 		  return added;
    304 
    305 	      alias_len = strlen (alias) + 1;
    306 	      value_len = strlen (value) + 1;
    307 
    308 	      if (string_space_act + alias_len + value_len > string_space_max)
    309 		{
    310 		  /* Increase size of memory pool.  */
    311 		  size_t new_size = (string_space_max
    312 				     + (alias_len + value_len > 1024
    313 					? alias_len + value_len : 1024));
    314 		  char *new_pool = (char *) realloc (string_space, new_size);
    315 		  if (new_pool == NULL)
    316 		    return added;
    317 
    318 		  if (__builtin_expect (string_space != new_pool, 0))
    319 		    {
    320 		      size_t i;
    321 
    322 		      for (i = 0; i < nmap; i++)
    323 			{
    324 			  map[i].alias += new_pool - string_space;
    325 			  map[i].value += new_pool - string_space;
    326 			}
    327 		    }
    328 
    329 		  string_space = new_pool;
    330 		  string_space_max = new_size;
    331 		}
    332 
    333 	      map[nmap].alias = memcpy (&string_space[string_space_act],
    334 					alias, alias_len);
    335 	      string_space_act += alias_len;
    336 
    337 	      map[nmap].value = memcpy (&string_space[string_space_act],
    338 					value, value_len);
    339 	      string_space_act += value_len;
    340 
    341 	      ++nmap;
    342 	      ++added;
    343 	    }
    344 	}
    345 
    346       /* Possibly not the whole line fits into the buffer.  Ignore
    347 	 the rest of the line.  */
    348       while (strchr (buf, '\n') == NULL)
    349 	if (FGETS (buf, sizeof buf, fp) == NULL)
    350 	  /* Make sure the inner loop will be left.  The outer loop
    351 	     will exit at the `feof' test.  */
    352 	  break;
    353     }
    354 
    355   /* Should we test for ferror()?  I think we have to silently ignore
    356      errors.  --drepper  */
    357   fclose (fp);
    358 
    359   if (added > 0)
    360     qsort (map, nmap, sizeof (struct alias_map),
    361 	   (int (*) (const void *, const void *)) alias_compare);
    362 
    363   return added;
    364 }
    365 
    366 
    367 static int
    368 extend_alias_table ()
    369 {
    370   size_t new_size;
    371   struct alias_map *new_map;
    372 
    373   new_size = maxmap == 0 ? 100 : 2 * maxmap;
    374   new_map = (struct alias_map *) realloc (map, (new_size
    375 						* sizeof (struct alias_map)));
    376   if (new_map == NULL)
    377     /* Simply don't extend: we don't have any more core.  */
    378     return -1;
    379 
    380   map = new_map;
    381   maxmap = new_size;
    382   return 0;
    383 }
    384 
    385 
    386 static int
    387 alias_compare (const struct alias_map *map1, const struct alias_map *map2)
    388 {
    389 #if defined _LIBC || defined HAVE_STRCASECMP
    390   return strcasecmp (map1->alias, map2->alias);
    391 #else
    392   const unsigned char *p1 = (const unsigned char *) map1->alias;
    393   const unsigned char *p2 = (const unsigned char *) map2->alias;
    394   unsigned char c1, c2;
    395 
    396   if (p1 == p2)
    397     return 0;
    398 
    399   do
    400     {
    401       /* I know this seems to be odd but the tolower() function in
    402 	 some systems libc cannot handle nonalpha characters.  */
    403       c1 = isupper (*p1) ? tolower (*p1) : *p1;
    404       c2 = isupper (*p2) ? tolower (*p2) : *p2;
    405       if (c1 == '\0')
    406 	break;
    407       ++p1;
    408       ++p2;
    409     }
    410   while (c1 == c2);
    411 
    412   return c1 - c2;
    413 #endif
    414 }
    415