Home | History | Annotate | Line # | Download | only in intl
localealias.c revision 1.1.1.1
      1 /* Handle aliases for locale names.
      2    Copyright (C) 1995-1999, 2000-2001, 2003, 2005 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 #endif
     85 
     86 /* Handle multi-threaded applications.  */
     87 #ifdef _LIBC
     88 # include <bits/libc-lock.h>
     89 #else
     90 # include "lock.h"
     91 #endif
     92 
     93 #ifndef internal_function
     94 # define internal_function
     95 #endif
     96 
     97 /* Some optimizations for glibc.  */
     98 #ifdef _LIBC
     99 # define FEOF(fp)		feof_unlocked (fp)
    100 # define FGETS(buf, n, fp)	fgets_unlocked (buf, n, fp)
    101 #else
    102 # define FEOF(fp)		feof (fp)
    103 # define FGETS(buf, n, fp)	fgets (buf, n, fp)
    104 #endif
    105 
    106 /* For those losing systems which don't have `alloca' we have to add
    107    some additional code emulating it.  */
    108 #ifdef HAVE_ALLOCA
    109 # define freea(p) /* nothing */
    110 #else
    111 # define alloca(n) malloc (n)
    112 # define freea(p) free (p)
    113 #endif
    114 
    115 #if defined _LIBC_REENTRANT || HAVE_DECL_FGETS_UNLOCKED
    116 # undef fgets
    117 # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
    118 #endif
    119 #if defined _LIBC_REENTRANT || HAVE_DECL_FEOF_UNLOCKED
    120 # undef feof
    121 # define feof(s) feof_unlocked (s)
    122 #endif
    123 
    124 
    125 __libc_lock_define_initialized (static, lock)
    126 
    127 
    128 struct alias_map
    129 {
    130   const char *alias;
    131   const char *value;
    132 };
    133 
    134 
    135 #ifndef _LIBC
    136 # define libc_freeres_ptr(decl) decl
    137 #endif
    138 
    139 libc_freeres_ptr (static char *string_space);
    140 static size_t string_space_act;
    141 static size_t string_space_max;
    142 libc_freeres_ptr (static struct alias_map *map);
    143 static size_t nmap;
    144 static size_t maxmap;
    145 
    146 
    147 /* Prototypes for local functions.  */
    148 static size_t read_alias_file (const char *fname, int fname_len)
    149      internal_function;
    150 static int extend_alias_table (void);
    151 static int alias_compare (const struct alias_map *map1,
    152 			  const struct alias_map *map2);
    153 
    154 
    155 const char *
    156 _nl_expand_alias (const char *name)
    157 {
    158   static const char *locale_alias_path;
    159   struct alias_map *retval;
    160   const char *result = NULL;
    161   size_t added;
    162 
    163   __libc_lock_lock (lock);
    164 
    165   if (locale_alias_path == NULL)
    166     locale_alias_path = LOCALE_ALIAS_PATH;
    167 
    168   do
    169     {
    170       struct alias_map item;
    171 
    172       item.alias = name;
    173 
    174       if (nmap > 0)
    175 	retval = (struct alias_map *) bsearch (&item, map, nmap,
    176 					       sizeof (struct alias_map),
    177 					       (int (*) (const void *,
    178 							 const void *)
    179 						) alias_compare);
    180       else
    181 	retval = NULL;
    182 
    183       /* We really found an alias.  Return the value.  */
    184       if (retval != NULL)
    185 	{
    186 	  result = retval->value;
    187 	  break;
    188 	}
    189 
    190       /* Perhaps we can find another alias file.  */
    191       added = 0;
    192       while (added == 0 && locale_alias_path[0] != '\0')
    193 	{
    194 	  const char *start;
    195 
    196 	  while (locale_alias_path[0] == PATH_SEPARATOR)
    197 	    ++locale_alias_path;
    198 	  start = locale_alias_path;
    199 
    200 	  while (locale_alias_path[0] != '\0'
    201 		 && locale_alias_path[0] != PATH_SEPARATOR)
    202 	    ++locale_alias_path;
    203 
    204 	  if (start < locale_alias_path)
    205 	    added = read_alias_file (start, locale_alias_path - start);
    206 	}
    207     }
    208   while (added != 0);
    209 
    210   __libc_lock_unlock (lock);
    211 
    212   return result;
    213 }
    214 
    215 
    216 static size_t
    217 internal_function
    218 read_alias_file (const char *fname, int fname_len)
    219 {
    220   FILE *fp;
    221   char *full_fname;
    222   size_t added;
    223   static const char aliasfile[] = "/locale.alias";
    224 
    225   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
    226 #ifdef HAVE_MEMPCPY
    227   mempcpy (mempcpy (full_fname, fname, fname_len),
    228 	   aliasfile, sizeof aliasfile);
    229 #else
    230   memcpy (full_fname, fname, fname_len);
    231   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
    232 #endif
    233 
    234 #ifdef _LIBC
    235   /* Note the file is opened with cancellation in the I/O functions
    236      disabled.  */
    237   fp = fopen (relocate (full_fname), "rc");
    238 #else
    239   fp = fopen (relocate (full_fname), "r");
    240 #endif
    241   freea (full_fname);
    242   if (fp == NULL)
    243     return 0;
    244 
    245 #ifdef HAVE___FSETLOCKING
    246   /* No threads present.  */
    247   __fsetlocking (fp, FSETLOCKING_BYCALLER);
    248 #endif
    249 
    250   added = 0;
    251   while (!FEOF (fp))
    252     {
    253       /* It is a reasonable approach to use a fix buffer here because
    254 	 a) we are only interested in the first two fields
    255 	 b) these fields must be usable as file names and so must not
    256 	    be that long
    257 	 We avoid a multi-kilobyte buffer here since this would use up
    258 	 stack space which we might not have if the program ran out of
    259 	 memory.  */
    260       char buf[400];
    261       char *alias;
    262       char *value;
    263       char *cp;
    264       int complete_line;
    265 
    266       if (FGETS (buf, sizeof buf, fp) == NULL)
    267 	/* EOF reached.  */
    268 	break;
    269 
    270       /* Determine whether the line is complete.  */
    271       complete_line = strchr (buf, '\n') != NULL;
    272 
    273       cp = buf;
    274       /* Ignore leading white space.  */
    275       while (isspace ((unsigned char) cp[0]))
    276 	++cp;
    277 
    278       /* A leading '#' signals a comment line.  */
    279       if (cp[0] != '\0' && cp[0] != '#')
    280 	{
    281 	  alias = cp++;
    282 	  while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
    283 	    ++cp;
    284 	  /* Terminate alias name.  */
    285 	  if (cp[0] != '\0')
    286 	    *cp++ = '\0';
    287 
    288 	  /* Now look for the beginning of the value.  */
    289 	  while (isspace ((unsigned char) cp[0]))
    290 	    ++cp;
    291 
    292 	  if (cp[0] != '\0')
    293 	    {
    294 	      value = cp++;
    295 	      while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
    296 		++cp;
    297 	      /* Terminate value.  */
    298 	      if (cp[0] == '\n')
    299 		{
    300 		  /* This has to be done to make the following test
    301 		     for the end of line possible.  We are looking for
    302 		     the terminating '\n' which do not overwrite here.  */
    303 		  *cp++ = '\0';
    304 		  *cp = '\n';
    305 		}
    306 	      else if (cp[0] != '\0')
    307 		*cp++ = '\0';
    308 
    309 #ifdef IN_LIBGLOCALE
    310 	      /* glibc's locale.alias contains entries for ja_JP and ko_KR
    311 		 that make it impossible to use a Japanese or Korean UTF-8
    312 		 locale under the name "ja_JP" or "ko_KR".  Ignore these
    313 		 entries.  */
    314 	      if (strchr (alias, '_') == NULL)
    315 #endif
    316 		{
    317 		  size_t alias_len;
    318 		  size_t value_len;
    319 
    320 		  if (nmap >= maxmap)
    321 		    if (__builtin_expect (extend_alias_table (), 0))
    322 		      goto out;
    323 
    324 		  alias_len = strlen (alias) + 1;
    325 		  value_len = strlen (value) + 1;
    326 
    327 		  if (string_space_act + alias_len + value_len > string_space_max)
    328 		    {
    329 		      /* Increase size of memory pool.  */
    330 		      size_t new_size = (string_space_max
    331 					 + (alias_len + value_len > 1024
    332 					    ? alias_len + value_len : 1024));
    333 		      char *new_pool = (char *) realloc (string_space, new_size);
    334 		      if (new_pool == NULL)
    335 			goto out;
    336 
    337 		      if (__builtin_expect (string_space != new_pool, 0))
    338 			{
    339 			  size_t i;
    340 
    341 			  for (i = 0; i < nmap; i++)
    342 			    {
    343 			      map[i].alias += new_pool - string_space;
    344 			      map[i].value += new_pool - string_space;
    345 			    }
    346 			}
    347 
    348 		      string_space = new_pool;
    349 		      string_space_max = new_size;
    350 		    }
    351 
    352 		  map[nmap].alias = memcpy (&string_space[string_space_act],
    353 					    alias, alias_len);
    354 		  string_space_act += alias_len;
    355 
    356 		  map[nmap].value = memcpy (&string_space[string_space_act],
    357 					    value, value_len);
    358 		  string_space_act += value_len;
    359 
    360 		  ++nmap;
    361 		  ++added;
    362 		}
    363 	    }
    364 	}
    365 
    366       /* Possibly not the whole line fits into the buffer.  Ignore
    367 	 the rest of the line.  */
    368       if (! complete_line)
    369 	do
    370 	  if (FGETS (buf, sizeof buf, fp) == NULL)
    371 	    /* Make sure the inner loop will be left.  The outer loop
    372 	       will exit at the `feof' test.  */
    373 	    break;
    374 	while (strchr (buf, '\n') == NULL);
    375     }
    376 
    377  out:
    378   /* Should we test for ferror()?  I think we have to silently ignore
    379      errors.  --drepper  */
    380   fclose (fp);
    381 
    382   if (added > 0)
    383     qsort (map, nmap, sizeof (struct alias_map),
    384 	   (int (*) (const void *, const void *)) alias_compare);
    385 
    386   return added;
    387 }
    388 
    389 
    390 static int
    391 extend_alias_table ()
    392 {
    393   size_t new_size;
    394   struct alias_map *new_map;
    395 
    396   new_size = maxmap == 0 ? 100 : 2 * maxmap;
    397   new_map = (struct alias_map *) realloc (map, (new_size
    398 						* sizeof (struct alias_map)));
    399   if (new_map == NULL)
    400     /* Simply don't extend: we don't have any more core.  */
    401     return -1;
    402 
    403   map = new_map;
    404   maxmap = new_size;
    405   return 0;
    406 }
    407 
    408 
    409 static int
    410 alias_compare (const struct alias_map *map1, const struct alias_map *map2)
    411 {
    412 #if defined _LIBC || defined HAVE_STRCASECMP
    413   return strcasecmp (map1->alias, map2->alias);
    414 #else
    415   const unsigned char *p1 = (const unsigned char *) map1->alias;
    416   const unsigned char *p2 = (const unsigned char *) map2->alias;
    417   unsigned char c1, c2;
    418 
    419   if (p1 == p2)
    420     return 0;
    421 
    422   do
    423     {
    424       /* I know this seems to be odd but the tolower() function in
    425 	 some systems libc cannot handle nonalpha characters.  */
    426       c1 = isupper (*p1) ? tolower (*p1) : *p1;
    427       c2 = isupper (*p2) ? tolower (*p2) : *p2;
    428       if (c1 == '\0')
    429 	break;
    430       ++p1;
    431       ++p2;
    432     }
    433   while (c1 == c2);
    434 
    435   return c1 - c2;
    436 #endif
    437 }
    438