(root)/
gettext-0.22.4/
gettext-runtime/
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
      31  # include <argz.h>
      32  #endif
      33  #include <ctype.h>
      34  #include <sys/types.h>
      35  #include <stdlib.h>
      36  #if defined _WIN32 && !defined __CYGWIN__
      37  # include <wchar.h>
      38  #endif
      39  
      40  #include "loadinfo.h"
      41  
      42  /* On some strange systems still no definition of NULL is found.  Sigh!  */
      43  #ifndef NULL
      44  # if defined __STDC__ && __STDC__
      45  #  define NULL ((void *) 0)
      46  # else
      47  #  define NULL 0
      48  # endif
      49  #endif
      50  
      51  /* @@ end of prolog @@ */
      52  
      53  #ifdef _LIBC
      54  /* Rename the non ANSI C functions.  This is required by the standard
      55     because some ANSI C functions will require linking with this object
      56     file and the name space must not be polluted.  */
      57  # ifndef stpcpy
      58  #  define stpcpy(dest, src) __stpcpy(dest, src)
      59  # endif
      60  #else
      61  # ifndef HAVE_STPCPY
      62  static char *stpcpy (char *dest, const char *src);
      63  # endif
      64  #endif
      65  
      66  #ifdef _LIBC
      67  # define IS_ABSOLUTE_FILE_NAME(P) ((P)[0] == '/')
      68  # define IS_RELATIVE_FILE_NAME(P) (! IS_ABSOLUTE_FILE_NAME (P))
      69  #else
      70  # include "filename.h"
      71  #endif
      72  
      73  /* Return number of bits set in X.  */
      74  #ifndef ARCH_POP
      75  static inline int
      76  pop (int x)
      77  {
      78    /* We assume that no more than 16 bits are used.  */
      79    x = ((x & ~0x5555) >> 1) + (x & 0x5555);
      80    x = ((x & ~0x3333) >> 2) + (x & 0x3333);
      81    x = ((x >> 4) + x) & 0x0f0f;
      82    x = ((x >> 8) + x) & 0xff;
      83  
      84    return x;
      85  }
      86  #endif
      87  
      88  
      89  struct loaded_l10nfile *
      90  _nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
      91  		    const char *dirlist, size_t dirlist_len,
      92  #if defined _WIN32 && !defined __CYGWIN__
      93  		    const wchar_t *wdirlist, size_t wdirlist_len,
      94  #endif
      95  		    int mask, const char *language, const char *territory,
      96  		    const char *codeset, const char *normalized_codeset,
      97  		    const char *modifier,
      98  		    const char *filename, int do_allocate)
      99  {
     100    char *abs_filename;
     101  #if defined _WIN32 && !defined __CYGWIN__
     102    wchar_t *abs_wfilename;
     103  #endif
     104    struct loaded_l10nfile **lastp;
     105    struct loaded_l10nfile *retval;
     106    size_t dirlist_count;
     107    size_t entries;
     108    int cnt;
     109  
     110    /* If LANGUAGE contains an absolute directory specification, we ignore
     111       DIRLIST and WDIRLIST.  */
     112    if (!IS_RELATIVE_FILE_NAME (language))
     113      {
     114        dirlist_len = 0;
     115  #if defined _WIN32 && !defined __CYGWIN__
     116        wdirlist_len = 0;
     117  #endif
     118      }
     119  
     120    /* Allocate room for the full file name.  */
     121    abs_filename = (char *) malloc (dirlist_len
     122  				  + strlen (language)
     123  				  + ((mask & XPG_TERRITORY) != 0
     124  				     ? strlen (territory) + 1 : 0)
     125  				  + ((mask & XPG_CODESET) != 0
     126  				     ? strlen (codeset) + 1 : 0)
     127  				  + ((mask & XPG_NORM_CODESET) != 0
     128  				     ? strlen (normalized_codeset) + 1 : 0)
     129  				  + ((mask & XPG_MODIFIER) != 0
     130  				     ? strlen (modifier) + 1 : 0)
     131  				  + 1 + strlen (filename) + 1);
     132  
     133    if (abs_filename == NULL)
     134      return NULL;
     135  
     136    /* Construct file name.  */
     137    {
     138      char *cp;
     139  
     140      cp = abs_filename;
     141      if (dirlist_len > 0)
     142        {
     143  	memcpy (cp, dirlist, dirlist_len);
     144  #ifdef _LIBC
     145  	__argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
     146  #endif
     147  	cp += dirlist_len;
     148  	cp[-1] = '/';
     149        }
     150  
     151      cp = stpcpy (cp, language);
     152  
     153      if ((mask & XPG_TERRITORY) != 0)
     154        {
     155  	*cp++ = '_';
     156  	cp = stpcpy (cp, territory);
     157        }
     158      if ((mask & XPG_CODESET) != 0)
     159        {
     160  	*cp++ = '.';
     161  	cp = stpcpy (cp, codeset);
     162        }
     163      if ((mask & XPG_NORM_CODESET) != 0)
     164        {
     165  	*cp++ = '.';
     166  	cp = stpcpy (cp, normalized_codeset);
     167        }
     168      if ((mask & XPG_MODIFIER) != 0)
     169        {
     170  	*cp++ = '@';
     171  	cp = stpcpy (cp, modifier);
     172        }
     173  
     174      *cp++ = '/';
     175      stpcpy (cp, filename);
     176    }
     177  
     178  #if defined _WIN32 && !defined __CYGWIN__
     179    /* Construct wide-char file name.  */
     180    if (wdirlist_len > 0)
     181      {
     182        /* Since dirlist_len == 0, just concatenate wdirlist and abs_filename.  */
     183        /* An upper bound for wcslen (mbstowcs (abs_filename)).  */
     184        size_t abs_filename_bound = mbstowcs (NULL, abs_filename, 0);
     185        if (abs_filename_bound == (size_t)-1)
     186  	{
     187  	  free (abs_filename);
     188  	  return NULL;
     189  	}
     190  
     191        /* Allocate and fill abs_wfilename.  */
     192        abs_wfilename =
     193  	(wchar_t *)
     194  	malloc ((wdirlist_len + abs_filename_bound + 1) * sizeof (wchar_t));
     195        if (abs_wfilename == NULL)
     196  	{
     197  	  free (abs_filename);
     198  	  return NULL;
     199  	}
     200        wmemcpy (abs_wfilename, wdirlist, wdirlist_len - 1);
     201        abs_wfilename[wdirlist_len - 1] = L'/';
     202        if (mbstowcs (abs_wfilename + wdirlist_len, abs_filename,
     203  		    abs_filename_bound + 1)
     204  	  > abs_filename_bound)
     205  	{
     206  	  free (abs_filename);
     207  	  free (abs_wfilename);
     208  	  return NULL;
     209  	}
     210  
     211        free (abs_filename);
     212        abs_filename = NULL;
     213      }
     214    else
     215      abs_wfilename = NULL;
     216  #endif
     217  
     218    /* Look in list of already loaded domains whether it is already
     219       available.  */
     220    lastp = l10nfile_list;
     221  #if defined _WIN32 && !defined __CYGWIN__
     222    if (abs_wfilename != NULL)
     223      {
     224        for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
     225  	{
     226  	  if (retval->wfilename != NULL)
     227  	    {
     228  	      int compare = wcscmp (retval->wfilename, abs_wfilename);
     229  	      if (compare == 0)
     230  		/* We found it!  */
     231  		break;
     232  	      if (compare < 0)
     233  		{
     234  		  /* It's not in the list, and we have found the place where it
     235  		     needs to be inserted: at *LASTP.  */
     236  		  retval = NULL;
     237  		  break;
     238  		}
     239  	    }
     240  	  lastp = &retval->next;
     241  	}
     242      }
     243    else
     244  #endif
     245      for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
     246        {
     247  #if defined _WIN32 && !defined __CYGWIN__
     248  	if (retval->filename != NULL)
     249  #endif
     250  	  {
     251  	    int compare = strcmp (retval->filename, abs_filename);
     252  	    if (compare == 0)
     253  	      /* We found it!  */
     254  	      break;
     255  	    if (compare < 0)
     256  	      {
     257  		/* It's not in the list, and we have found the place where it
     258  		   needs to be inserted: at *LASTP.  */
     259  		retval = NULL;
     260  	        break;
     261  	      }
     262  	  }
     263  	lastp = &retval->next;
     264        }
     265  
     266    if (retval != NULL || do_allocate == 0)
     267      {
     268        free (abs_filename);
     269  #if defined _WIN32 && !defined __CYGWIN__
     270        free (abs_wfilename);
     271  #endif
     272        return retval;
     273      }
     274  
     275  #ifdef _LIBC
     276    dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
     277  #else
     278    dirlist_count = 1;
     279  #endif
     280  
     281    /* Allocate a new loaded_l10nfile.  */
     282    retval =
     283      (struct loaded_l10nfile *)
     284      malloc (sizeof (*retval)
     285  	    + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
     286  	       * sizeof (struct loaded_l10nfile *)));
     287    if (retval == NULL)
     288      {
     289        free (abs_filename);
     290  #if defined _WIN32 && !defined __CYGWIN__
     291        free (abs_wfilename);
     292  #endif
     293        return NULL;
     294      }
     295  
     296    retval->filename = abs_filename;
     297  #if defined _WIN32 && !defined __CYGWIN__
     298    retval->wfilename = abs_wfilename;
     299  #endif
     300  
     301    /* We set retval->data to NULL here; it is filled in later.
     302       Setting retval->decided to 1 here means that retval does not
     303       correspond to a real file (dirlist_count > 1) or is not worth
     304       looking up (if an unnormalized codeset was specified).  */
     305    retval->decided = (dirlist_count > 1
     306  		     || ((mask & XPG_CODESET) != 0
     307  			 && (mask & XPG_NORM_CODESET) != 0));
     308    retval->data = NULL;
     309  
     310    retval->next = *lastp;
     311    *lastp = retval;
     312  
     313    entries = 0;
     314    /* Recurse to fill the inheritance list of RETVAL.
     315       If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
     316       entry does not correspond to a real file; retval->filename contains
     317       colons.  In this case we loop across all elements of DIRLIST and
     318       across all bit patterns dominated by MASK.
     319       If the DIRLIST is a single directory or entirely redundant (i.e.
     320       DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
     321       MASK, excluding MASK itself.
     322       In either case, we loop down from MASK to 0.  This has the effect
     323       that the extra bits in the locale name are dropped in this order:
     324       first the modifier, then the territory, then the codeset, then the
     325       normalized_codeset.  */
     326    for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
     327      if ((cnt & ~mask) == 0
     328  	&& !((cnt & XPG_CODESET) != 0 && (cnt & XPG_NORM_CODESET) != 0))
     329        {
     330  #ifdef _LIBC
     331  	if (dirlist_count > 1)
     332  	  {
     333  	    /* Iterate over all elements of the DIRLIST.  */
     334  	    char *dir = NULL;
     335  
     336  	    while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
     337  		   != NULL)
     338  	      retval->successor[entries++]
     339  		= _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
     340  				      cnt, language, territory, codeset,
     341  				      normalized_codeset, modifier, filename,
     342  				      1);
     343  	  }
     344  	else
     345  #endif
     346  	  retval->successor[entries++]
     347  	    = _nl_make_l10nflist (l10nfile_list,
     348  				  dirlist, dirlist_len,
     349  #if defined _WIN32 && !defined __CYGWIN__
     350  				  wdirlist, wdirlist_len,
     351  #endif
     352  				  cnt, language, territory, codeset,
     353  				  normalized_codeset, modifier, filename, 1);
     354        }
     355    retval->successor[entries] = NULL;
     356  
     357    return retval;
     358  }
     359  
     360  /* Normalize codeset name.  There is no standard for the codeset
     361     names.  Normalization allows the user to use any of the common
     362     names.  The return value is dynamically allocated and has to be
     363     freed by the caller.  */
     364  const char *
     365  _nl_normalize_codeset (const char *codeset, size_t name_len)
     366  {
     367    size_t len = 0;
     368    int only_digit = 1;
     369    char *retval;
     370    char *wp;
     371    size_t cnt;
     372  
     373    for (cnt = 0; cnt < name_len; ++cnt)
     374      if (isalnum ((unsigned char) codeset[cnt]))
     375        {
     376  	++len;
     377  
     378  	if (isalpha ((unsigned char) codeset[cnt]))
     379  	  only_digit = 0;
     380        }
     381  
     382    retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
     383  
     384    if (retval != NULL)
     385      {
     386        if (only_digit)
     387  	wp = stpcpy (retval, "iso");
     388        else
     389  	wp = retval;
     390  
     391        for (cnt = 0; cnt < name_len; ++cnt)
     392  	if (isalpha ((unsigned char) codeset[cnt]))
     393  	  *wp++ = tolower ((unsigned char) codeset[cnt]);
     394  	else if (isdigit ((unsigned char) codeset[cnt]))
     395  	  *wp++ = codeset[cnt];
     396  
     397        *wp = '\0';
     398      }
     399  
     400    return (const char *) retval;
     401  }
     402  
     403  
     404  /* @@ begin of epilog @@ */
     405  
     406  /* We don't want libintl.a to depend on any other library.  So we
     407     avoid the non-standard function stpcpy.  In GNU C Library this
     408     function is available, though.  Also allow the symbol HAVE_STPCPY
     409     to be defined.  */
     410  #if !_LIBC && !HAVE_STPCPY
     411  static char *
     412  stpcpy (char *dest, const char *src)
     413  {
     414    while ((*dest++ = *src++) != '\0')
     415      /* Do nothing. */ ;
     416    return dest - 1;
     417  }
     418  #endif