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