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