(root)/
glibc-2.38/
intl/
localealias.c
       1  /* Handle aliases for locale names.
       2     Copyright (C) 1995-2023 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  #endif
      68  
      69  /* @@ end of prolog @@ */
      70  
      71  #ifdef _LIBC
      72  /* Rename the non ANSI C functions.  This is required by the standard
      73     because some ANSI C functions will require linking with this object
      74     file and the name space must not be polluted.  */
      75  # define strcasecmp(s1, s2) __strcasecmp_l (s1, s2, _nl_C_locobj_ptr)
      76  
      77  # ifndef mempcpy
      78  #  define mempcpy __mempcpy
      79  # endif
      80  # define HAVE_MEMPCPY	1
      81  # define HAVE___FSETLOCKING	1
      82  #endif
      83  
      84  /* Handle multi-threaded applications.  */
      85  #ifdef _LIBC
      86  # include <libc-lock.h>
      87  #else
      88  # include "lock.h"
      89  #endif
      90  
      91  /* Some optimizations for glibc.  */
      92  #ifdef _LIBC
      93  # define FEOF(fp)		__feof_unlocked (fp)
      94  # define FGETS(buf, n, fp)	__fgets_unlocked (buf, n, fp)
      95  #else
      96  # define FEOF(fp)		feof (fp)
      97  # define FGETS(buf, n, fp)	fgets (buf, n, fp)
      98  #endif
      99  
     100  /* For those losing systems which don't have `alloca' we have to add
     101     some additional code emulating it.  */
     102  #ifdef HAVE_ALLOCA
     103  # define freea(p) /* nothing */
     104  #else
     105  # define alloca(n) malloc (n)
     106  # define freea(p) free (p)
     107  #endif
     108  
     109  #if defined _LIBC_REENTRANT || defined HAVE_DECL_FGETS_UNLOCKED
     110  # undef fgets
     111  # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
     112  #endif
     113  #if defined _LIBC_REENTRANT || defined HAVE_DECL_FEOF_UNLOCKED
     114  # undef feof
     115  # define feof(s) feof_unlocked (s)
     116  #endif
     117  
     118  
     119  __libc_lock_define_initialized (static, lock)
     120  
     121  
     122  struct alias_map
     123  {
     124    const char *alias;
     125    const char *value;
     126  };
     127  
     128  
     129  static char *string_space;
     130  static size_t string_space_act;
     131  static size_t string_space_max;
     132  static struct alias_map *map;
     133  static size_t nmap;
     134  static size_t maxmap;
     135  
     136  
     137  /* Prototypes for local functions.  */
     138  static size_t read_alias_file (const char *fname, int fname_len);
     139  static int extend_alias_table (void);
     140  static int alias_compare (const struct alias_map *map1,
     141  			  const struct alias_map *map2);
     142  
     143  
     144  const char *
     145  _nl_expand_alias (const char *name)
     146  {
     147    static const char *locale_alias_path;
     148    struct alias_map *retval;
     149    const char *result = NULL;
     150    size_t added;
     151  
     152    __libc_lock_lock (lock);
     153  
     154    if (locale_alias_path == NULL)
     155      locale_alias_path = LOCALE_ALIAS_PATH;
     156  
     157    do
     158      {
     159        struct alias_map item;
     160  
     161        item.alias = name;
     162  
     163        if (nmap > 0)
     164  	retval = (struct alias_map *) bsearch (&item, map, nmap,
     165  					       sizeof (struct alias_map),
     166  					       (int (*) (const void *,
     167  							 const void *)
     168  						) alias_compare);
     169        else
     170  	retval = NULL;
     171  
     172        /* We really found an alias.  Return the value.  */
     173        if (retval != NULL)
     174  	{
     175  	  result = retval->value;
     176  	  break;
     177  	}
     178  
     179        /* Perhaps we can find another alias file.  */
     180        added = 0;
     181        while (added == 0 && locale_alias_path[0] != '\0')
     182  	{
     183  	  const char *start;
     184  
     185  	  while (locale_alias_path[0] == PATH_SEPARATOR)
     186  	    ++locale_alias_path;
     187  	  start = locale_alias_path;
     188  
     189  	  while (locale_alias_path[0] != '\0'
     190  		 && locale_alias_path[0] != PATH_SEPARATOR)
     191  	    ++locale_alias_path;
     192  
     193  	  if (start < locale_alias_path)
     194  	    added = read_alias_file (start, locale_alias_path - start);
     195  	}
     196      }
     197    while (added != 0);
     198  
     199    __libc_lock_unlock (lock);
     200  
     201    return result;
     202  }
     203  
     204  
     205  static size_t
     206  read_alias_file (const char *fname, 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  #ifdef _LIBC
     223    /* Note the file is opened with cancellation in the I/O functions
     224       disabled.  */
     225    fp = fopen (relocate (full_fname), "rce");
     226  #else
     227    fp = fopen (relocate (full_fname), "r");
     228  #endif
     229    freea (full_fname);
     230    if (fp == NULL)
     231      return 0;
     232  
     233  #ifdef HAVE___FSETLOCKING
     234    /* No threads present.  */
     235    __fsetlocking (fp, FSETLOCKING_BYCALLER);
     236  #endif
     237  
     238    added = 0;
     239    while (!FEOF (fp))
     240      {
     241        /* It is a reasonable approach to use a fix buffer here because
     242  	 a) we are only interested in the first two fields
     243  	 b) these fields must be usable as file names and so must not
     244  	    be that long
     245  	 We avoid a multi-kilobyte buffer here since this would use up
     246  	 stack space which we might not have if the program ran out of
     247  	 memory.  */
     248        char buf[400];
     249        char *alias;
     250        char *value;
     251        char *cp;
     252        int complete_line;
     253  
     254        if (FGETS (buf, sizeof buf, fp) == NULL)
     255  	/* EOF reached.  */
     256  	break;
     257  
     258        /* Determine whether the line is complete.  */
     259        complete_line = strchr (buf, '\n') != NULL;
     260  
     261        cp = buf;
     262        /* Ignore leading white space.  */
     263        while (isspace ((unsigned char) cp[0]))
     264  	++cp;
     265  
     266        /* A leading '#' signals a comment line.  */
     267        if (cp[0] != '\0' && cp[0] != '#')
     268  	{
     269  	  alias = cp++;
     270  	  while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
     271  	    ++cp;
     272  	  /* Terminate alias name.  */
     273  	  if (cp[0] != '\0')
     274  	    *cp++ = '\0';
     275  
     276  	  /* Now look for the beginning of the value.  */
     277  	  while (isspace ((unsigned char) cp[0]))
     278  	    ++cp;
     279  
     280  	  if (cp[0] != '\0')
     281  	    {
     282  	      value = cp++;
     283  	      while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
     284  		++cp;
     285  	      /* Terminate value.  */
     286  	      if (cp[0] == '\n')
     287  		{
     288  		  /* This has to be done to make the following test
     289  		     for the end of line possible.  We are looking for
     290  		     the terminating '\n' which do not overwrite here.  */
     291  		  *cp++ = '\0';
     292  		  *cp = '\n';
     293  		}
     294  	      else if (cp[0] != '\0')
     295  		*cp++ = '\0';
     296  
     297  #ifdef IN_LIBGLOCALE
     298  	      /* glibc's locale.alias contains entries for ja_JP and ko_KR
     299  		 that make it impossible to use a Japanese or Korean UTF-8
     300  		 locale under the name "ja_JP" or "ko_KR".  Ignore these
     301  		 entries.  */
     302  	      if (strchr (alias, '_') == NULL)
     303  #endif
     304  		{
     305  		  size_t alias_len;
     306  		  size_t value_len;
     307  
     308  		  if (nmap >= maxmap)
     309  		    if (__builtin_expect (extend_alias_table (), 0))
     310  		      goto out;
     311  
     312  		  alias_len = strlen (alias) + 1;
     313  		  value_len = strlen (value) + 1;
     314  
     315  		  if (string_space_act + alias_len + value_len > string_space_max)
     316  		    {
     317  #pragma GCC diagnostic push
     318  
     319  #if defined __GNUC__ && __GNUC__ >= 12
     320    /* Suppress the valid GCC 12 warning until the code below is changed
     321       to avoid using pointers to the reallocated block.  */
     322  #  pragma GCC diagnostic ignored "-Wuse-after-free"
     323  #endif
     324  
     325  		    /* Increase size of memory pool.  */
     326  		      size_t new_size = (string_space_max
     327  					 + (alias_len + value_len > 1024
     328  					    ? alias_len + value_len : 1024));
     329  		      char *new_pool = (char *) realloc (string_space, new_size);
     330  		      if (new_pool == NULL)
     331  			goto out;
     332  
     333  		      if (__builtin_expect (string_space != new_pool, 0))
     334  			{
     335  			  size_t i;
     336  
     337  			  for (i = 0; i < nmap; i++)
     338  			    {
     339  			      map[i].alias += new_pool - string_space;
     340  			      map[i].value += new_pool - string_space;
     341  			    }
     342  			}
     343  
     344  		      string_space = new_pool;
     345  		      string_space_max = new_size;
     346  		    }
     347  
     348  		  map[nmap].alias =
     349  		    (const char *) memcpy (&string_space[string_space_act],
     350  					   alias, alias_len);
     351  		  string_space_act += alias_len;
     352  
     353  		  map[nmap].value =
     354  		    (const char *) memcpy (&string_space[string_space_act],
     355  					   value, value_len);
     356  		  string_space_act += value_len;
     357  
     358  #pragma GCC diagnostic pop
     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 (void)
     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  
     439  void
     440  __libc_localealias_freemem (void)
     441  {
     442    free (string_space);
     443    free (map);
     444  }