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