(root)/
glibc-2.38/
intl/
l10nflist.c
       1  /* Copyright (C) 1995-2023 Free Software Foundation, Inc.
       2     Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
       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 stpcpy().
      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 <string.h>
      29  
      30  #if defined _LIBC || defined HAVE_ARGZ_H
      31  # include <argz.h>
      32  #endif
      33  #include <ctype.h>
      34  #include <sys/types.h>
      35  #include <stdlib.h>
      36  
      37  #include "loadinfo.h"
      38  
      39  /* On some strange systems still no definition of NULL is found.  Sigh!  */
      40  #ifndef NULL
      41  # if defined __STDC__ && __STDC__
      42  #  define NULL ((void *) 0)
      43  # else
      44  #  define NULL 0
      45  # endif
      46  #endif
      47  
      48  /* @@ end of prolog @@ */
      49  
      50  #ifdef _LIBC
      51  /* Rename the non ANSI C functions.  This is required by the standard
      52     because some ANSI C functions will require linking with this object
      53     file and the name space must not be polluted.  */
      54  # ifndef stpcpy
      55  #  define stpcpy(dest, src) __stpcpy(dest, src)
      56  # endif
      57  #else
      58  # ifndef HAVE_STPCPY
      59  static char *stpcpy (char *dest, const char *src);
      60  # endif
      61  #endif
      62  
      63  /* Define function which are usually not available.  */
      64  
      65  #if defined HAVE_ARGZ_COUNT
      66  # undef __argz_count
      67  # define __argz_count argz_count
      68  #else
      69  /* Returns the number of strings in ARGZ.  */
      70  static size_t
      71  argz_count__ (const char *argz, size_t len)
      72  {
      73    size_t count = 0;
      74    while (len > 0)
      75      {
      76        size_t part_len = strlen (argz);
      77        argz += part_len + 1;
      78        len -= part_len + 1;
      79        count++;
      80      }
      81    return count;
      82  }
      83  # undef __argz_count
      84  # define __argz_count(argz, len) argz_count__ (argz, len)
      85  #endif	/* !_LIBC && !HAVE_ARGZ_COUNT */
      86  
      87  #if defined HAVE_ARGZ_STRINGIFY
      88  # undef __argz_stringify
      89  # define __argz_stringify argz_stringify
      90  #else
      91  /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
      92     except the last into the character SEP.  */
      93  static void
      94  argz_stringify__ (char *argz, size_t len, int sep)
      95  {
      96    while (len > 0)
      97      {
      98        size_t part_len = strlen (argz);
      99        argz += part_len;
     100        len -= part_len + 1;
     101        if (len > 0)
     102  	*argz++ = sep;
     103      }
     104  }
     105  # undef __argz_stringify
     106  # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
     107  #endif	/* !_LIBC && !HAVE_ARGZ_STRINGIFY */
     108  
     109  #ifdef _LIBC
     110  #elif defined HAVE_ARGZ_NEXT
     111  # undef __argz_next
     112  # define __argz_next argz_next
     113  #else
     114  static char *
     115  argz_next__ (char *argz, size_t argz_len, const char *entry)
     116  {
     117    if (entry)
     118      {
     119        if (entry < argz + argz_len)
     120          entry = strchr (entry, '\0') + 1;
     121  
     122        return entry >= argz + argz_len ? NULL : (char *) entry;
     123      }
     124    else
     125      if (argz_len > 0)
     126        return argz;
     127      else
     128        return 0;
     129  }
     130  # undef __argz_next
     131  # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
     132  #endif	/* !_LIBC && !HAVE_ARGZ_NEXT */
     133  
     134  /* Return number of bits set in X.  */
     135  #ifndef ARCH_POP
     136  static inline int
     137  pop (int x)
     138  {
     139    /* We assume that no more than 16 bits are used.  */
     140    x = ((x & ~0x5555) >> 1) + (x & 0x5555);
     141    x = ((x & ~0x3333) >> 2) + (x & 0x3333);
     142    x = ((x >> 4) + x) & 0x0f0f;
     143    x = ((x >> 8) + x) & 0xff;
     144  
     145    return x;
     146  }
     147  #endif
     148  
     149  
     150  struct loaded_l10nfile *
     151  _nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
     152  		    const char *dirlist, size_t dirlist_len,
     153  		    int mask, const char *language, const char *territory,
     154  		    const char *codeset, const char *normalized_codeset,
     155  		    const char *modifier,
     156  		    const char *filename, int do_allocate)
     157  {
     158    char *abs_filename;
     159    struct loaded_l10nfile *last = NULL;
     160    struct loaded_l10nfile *retval;
     161    char *cp;
     162    size_t entries;
     163    int cnt;
     164  
     165    /* Allocate room for the full file name.  */
     166    abs_filename = (char *) malloc (dirlist_len
     167  				  + strlen (language)
     168  				  + ((mask & XPG_TERRITORY) != 0
     169  				     ? strlen (territory) + 1 : 0)
     170  				  + ((mask & XPG_CODESET) != 0
     171  				     ? strlen (codeset) + 1 : 0)
     172  				  + ((mask & XPG_NORM_CODESET) != 0
     173  				     ? strlen (normalized_codeset) + 1 : 0)
     174  				  + ((mask & XPG_MODIFIER) != 0
     175  				     ? strlen (modifier) + 1 : 0)
     176  				  + 1 + strlen (filename) + 1);
     177  
     178    if (abs_filename == NULL)
     179      return NULL;
     180  
     181    retval = NULL;
     182    last = NULL;
     183  
     184    /* Construct file name.  */
     185    memcpy (abs_filename, dirlist, dirlist_len);
     186    __argz_stringify (abs_filename, dirlist_len, ':');
     187    cp = abs_filename + (dirlist_len - 1);
     188    *cp++ = '/';
     189    cp = stpcpy (cp, language);
     190  
     191    if ((mask & XPG_TERRITORY) != 0)
     192      {
     193        *cp++ = '_';
     194        cp = stpcpy (cp, territory);
     195      }
     196    if ((mask & XPG_CODESET) != 0)
     197      {
     198        *cp++ = '.';
     199        cp = stpcpy (cp, codeset);
     200      }
     201    if ((mask & XPG_NORM_CODESET) != 0)
     202      {
     203        *cp++ = '.';
     204        cp = stpcpy (cp, normalized_codeset);
     205      }
     206    if ((mask & XPG_MODIFIER) != 0)
     207      {
     208        *cp++ = '@';
     209        cp = stpcpy (cp, modifier);
     210      }
     211  
     212    *cp++ = '/';
     213    stpcpy (cp, filename);
     214  
     215    /* Look in list of already loaded domains whether it is already
     216       available.  */
     217    last = NULL;
     218    for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
     219      if (retval->filename != NULL)
     220        {
     221  	int compare = strcmp (retval->filename, abs_filename);
     222  	if (compare == 0)
     223  	  /* We found it!  */
     224  	  break;
     225  	if (compare < 0)
     226  	  {
     227  	    /* It's not in the list.  */
     228  	    retval = NULL;
     229  	    break;
     230  	  }
     231  
     232  	last = retval;
     233        }
     234  
     235    if (retval != NULL || do_allocate == 0)
     236      {
     237        free (abs_filename);
     238        return retval;
     239      }
     240  
     241    retval = (struct loaded_l10nfile *)
     242      malloc (sizeof (*retval) + (__argz_count (dirlist, dirlist_len)
     243  				* (1 << pop (mask))
     244  				* sizeof (struct loaded_l10nfile *)));
     245    if (retval == NULL)
     246      {
     247        free (abs_filename);
     248        return NULL;
     249      }
     250  
     251    retval->filename = abs_filename;
     252    /* If more than one directory is in the list this is a pseudo-entry
     253       which just references others.  We do not try to load data for it,
     254       ever.  */
     255    retval->decided = (__argz_count (dirlist, dirlist_len) != 1
     256  		     || ((mask & XPG_CODESET) != 0
     257  			 && (mask & XPG_NORM_CODESET) != 0));
     258    retval->data = NULL;
     259  
     260    if (last == NULL)
     261      {
     262        retval->next = *l10nfile_list;
     263        *l10nfile_list = retval;
     264      }
     265    else
     266      {
     267        retval->next = last->next;
     268        last->next = retval;
     269      }
     270  
     271    entries = 0;
     272    /* If the DIRLIST is a real list the RETVAL entry corresponds not to
     273       a real file.  So we have to use the DIRLIST separation mechanism
     274       of the inner loop.  */
     275    cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
     276    for (; cnt >= 0; --cnt)
     277      if ((cnt & ~mask) == 0)
     278        {
     279  	/* Iterate over all elements of the DIRLIST.  */
     280  	char *dir = NULL;
     281  
     282  	while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
     283  	       != NULL)
     284  	  retval->successor[entries++]
     285  	    = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
     286  				  language, territory, codeset,
     287  				  normalized_codeset, modifier, filename, 1);
     288        }
     289    retval->successor[entries] = NULL;
     290  
     291    return retval;
     292  }
     293  
     294  /* Normalize codeset name.  There is no standard for the codeset
     295     names.  Normalization allows the user to use any of the common
     296     names.  The return value is dynamically allocated and has to be
     297     freed by the caller.  */
     298  const char *
     299  _nl_normalize_codeset (const char *codeset, size_t name_len)
     300  {
     301    size_t len = 0;
     302    int only_digit = 1;
     303    char *retval;
     304    char *wp;
     305    size_t cnt;
     306  #if !IS_IN (libc)
     307    locale_t locale = newlocale (0, "C", NULL);
     308  #else
     309  # define locale _nl_C_locobj_ptr
     310  #endif
     311  
     312    for (cnt = 0; cnt < name_len; ++cnt)
     313      if (__isalnum_l ((unsigned char) codeset[cnt], locale))
     314        {
     315  	++len;
     316  
     317  	if (! __isdigit_l ((unsigned char) codeset[cnt], locale))
     318  	  only_digit = 0;
     319        }
     320  
     321    retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
     322  
     323    if (retval != NULL)
     324      {
     325        if (only_digit)
     326  	wp = stpcpy (retval, "iso");
     327        else
     328  	wp = retval;
     329  
     330        for (cnt = 0; cnt < name_len; ++cnt)
     331  	if (__isalpha_l ((unsigned char) codeset[cnt], locale))
     332  	  *wp++ = __tolower_l ((unsigned char) codeset[cnt], locale);
     333  	else if (__isdigit_l ((unsigned char) codeset[cnt], locale))
     334  	  *wp++ = codeset[cnt];
     335  
     336        *wp = '\0';
     337      }
     338  
     339    return (const char *) retval;
     340  }
     341  
     342  
     343  /* @@ begin of epilog @@ */
     344  
     345  /* We don't want libintl.a to depend on any other library.  So we
     346     avoid the non-standard function stpcpy.  In GNU C Library this
     347     function is available, though.  Also allow the symbol HAVE_STPCPY
     348     to be defined.  */
     349  #if !_LIBC && !HAVE_STPCPY
     350  static char *
     351  stpcpy (char *dest, const char *src)
     352  {
     353    while ((*dest++ = *src++) != '\0')
     354      /* Do nothing. */ ;
     355    return dest - 1;
     356  }
     357  #endif