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