]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - intl/localealias.c
Sync move-if-change from Gnulib, updating copyright
[thirdparty/glibc.git] / intl / localealias.c
index d2b5ae8895f534a391bc0f6a9ca6662c2e9cb1c9..3ae360f40d202fc3ff3d1eca89e501742737bd0e 100644 (file)
@@ -1,20 +1,25 @@
 /* Handle aliases for locale names.
-   Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1995-2022 Free Software Foundation, Inc.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
 
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Tell glibc's <string.h> to provide a prototype for mempcpy().
+   This must come before <config.h> because <config.h> may include
+   <features.h>, and once <features.h> has been included, it's too late.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE    1
+#endif
 
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 
 #include <ctype.h>
 #include <stdio.h>
+#if defined _LIBC || defined HAVE___FSETLOCKING
+# include <stdio_ext.h>
+#endif
 #include <sys/types.h>
 
 #ifdef __GNUC__
+# undef alloca
 # define alloca __builtin_alloca
 # define HAVE_ALLOCA 1
 #else
-# if defined HAVE_ALLOCA_H || defined _LIBC
-#  include <alloca.h>
+# ifdef _MSC_VER
+#  include <malloc.h>
+#  define alloca _alloca
 # else
-#  ifdef _AIX
- #pragma alloca
+#  if defined HAVE_ALLOCA_H || defined _LIBC
+#   include <alloca.h>
 #  else
-#   ifndef alloca
+#   ifdef _AIX
+ #pragma alloca
+#   else
+#    ifndef alloca
 char *alloca ();
+#    endif
 #   endif
 #  endif
 # endif
 #endif
 
-#if defined STDC_HEADERS || defined _LIBC
-# include <stdlib.h>
-#else
-char *getenv ();
-# ifdef HAVE_MALLOC_H
-#  include <malloc.h>
-# else
-void free ();
-# endif
-#endif
+#include <stdlib.h>
+#include <string.h>
 
-#if defined HAVE_STRING_H || defined _LIBC
-# ifndef _GNU_SOURCE
-#  define _GNU_SOURCE  1
-# endif
-# include <string.h>
+#include "gettextP.h"
+
+#ifdef ENABLE_RELOCATABLE
+# include "relocatable.h"
 #else
-# include <strings.h>
-# ifndef memcpy
-#  define memcpy(Dst, Src, Num) bcopy (Src, Dst, Num)
-# endif
-#endif
-#if !HAVE_STRCHR && !defined _LIBC
-# ifndef strchr
-#  define strchr index
-# endif
+# define relocate(pathname) (pathname)
 #endif
 
-#include "gettext.h"
-#include "gettextP.h"
-
 /* @@ end of prolog @@ */
 
 #ifdef _LIBC
 /* Rename the non ANSI C functions.  This is required by the standard
    because some ANSI C functions will require linking with this object
    file and the name space must not be polluted.  */
-# define strcasecmp __strcasecmp
+# define strcasecmp(s1, s2) __strcasecmp_l (s1, s2, _nl_C_locobj_ptr)
 
 # ifndef mempcpy
 #  define mempcpy __mempcpy
 # endif
 # define HAVE_MEMPCPY  1
+# define HAVE___FSETLOCKING    1
+#endif
 
-/* We need locking here since we can be called from different places.  */
-# include <bits/libc-lock.h>
-
-__libc_lock_define_initialized (static, lock);
+/* Handle multi-threaded applications.  */
+#ifdef _LIBC
+# include <libc-lock.h>
+#else
+# include "lock.h"
 #endif
 
-#ifndef internal_function
-# define internal_function
+/* Some optimizations for glibc.  */
+#ifdef _LIBC
+# define FEOF(fp)              __feof_unlocked (fp)
+# define FGETS(buf, n, fp)     __fgets_unlocked (buf, n, fp)
+#else
+# define FEOF(fp)              feof (fp)
+# define FGETS(buf, n, fp)     fgets (buf, n, fp)
 #endif
 
-/* For those loosing systems which don't have `alloca' we have to add
+/* For those losing systems which don't have `alloca' we have to add
    some additional code emulating it.  */
 #ifdef HAVE_ALLOCA
-/* Nothing has to be done.  */
-# define ADD_BLOCK(list, address) /* nothing */
-# define FREE_BLOCKS(list) /* nothing */
+# define freea(p) /* nothing */
 #else
-struct block_list
-{
-  void *address;
-  struct block_list *next;
-};
-# define ADD_BLOCK(list, addr)                                               \
-  do {                                                                       \
-    struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
-    /* If we cannot get a free block we cannot add the new element to        \
-       the list.  */                                                         \
-    if (newp != NULL) {                                                              \
-      newp->address = (addr);                                                \
-      newp->next = (list);                                                   \
-      (list) = newp;                                                         \
-    }                                                                        \
-  } while (0)
-# define FREE_BLOCKS(list)                                                   \
-  do {                                                                       \
-    while (list != NULL) {                                                   \
-      struct block_list *old = list;                                         \
-      list = list->next;                                                     \
-      free (old);                                                            \
-    }                                                                        \
-  } while (0)
-# undef alloca
-# define alloca(size) (malloc (size))
-#endif /* have alloca */
+# define alloca(n) malloc (n)
+# define freea(p) free (p)
+#endif
 
-#if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED
+#if defined _LIBC_REENTRANT || defined HAVE_DECL_FGETS_UNLOCKED
 # undef fgets
 # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
 #endif
-#if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED
+#if defined _LIBC_REENTRANT || defined HAVE_DECL_FEOF_UNLOCKED
 # undef feof
 # define feof(s) feof_unlocked (s)
 #endif
 
 
+__libc_lock_define_initialized (static, lock)
+
+
 struct alias_map
 {
   const char *alias;
@@ -147,34 +126,37 @@ struct alias_map
 };
 
 
-static char *string_space;
+#ifndef _LIBC
+# define libc_freeres_ptr(decl) decl
+#endif
+
+libc_freeres_ptr (static char *string_space);
 static size_t string_space_act;
 static size_t string_space_max;
-static struct alias_map *map;
+libc_freeres_ptr (static struct alias_map *map);
 static size_t nmap;
 static size_t maxmap;
 
 
 /* Prototypes for local functions.  */
-static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
-     internal_function;
-static void extend_alias_table PARAMS ((void));
-static int alias_compare PARAMS ((const struct alias_map *map1,
-                                 const struct alias_map *map2));
+static size_t read_alias_file (const char *fname, int fname_len);
+static int extend_alias_table (void);
+static int alias_compare (const struct alias_map *map1,
+                         const struct alias_map *map2);
 
 
 const char *
-_nl_expand_alias (name)
-    const char *name;
+_nl_expand_alias (const char *name)
 {
-  static const char *locale_alias_path = LOCALE_ALIAS_PATH;
+  static const char *locale_alias_path;
   struct alias_map *retval;
   const char *result = NULL;
   size_t added;
 
-#ifdef _LIBC
   __libc_lock_lock (lock);
-#endif
+
+  if (locale_alias_path == NULL)
+    locale_alias_path = LOCALE_ALIAS_PATH;
 
   do
     {
@@ -185,8 +167,8 @@ _nl_expand_alias (name)
       if (nmap > 0)
        retval = (struct alias_map *) bsearch (&item, map, nmap,
                                               sizeof (struct alias_map),
-                                              (int (*) PARAMS ((const void *,
-                                                                const void *))
+                                              (int (*) (const void *,
+                                                        const void *)
                                                ) alias_compare);
       else
        retval = NULL;
@@ -204,11 +186,12 @@ _nl_expand_alias (name)
        {
          const char *start;
 
-         while (locale_alias_path[0] == ':')
+         while (locale_alias_path[0] == PATH_SEPARATOR)
            ++locale_alias_path;
          start = locale_alias_path;
 
-         while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':')
+         while (locale_alias_path[0] != '\0'
+                && locale_alias_path[0] != PATH_SEPARATOR)
            ++locale_alias_path;
 
          if (start < locale_alias_path)
@@ -217,30 +200,21 @@ _nl_expand_alias (name)
     }
   while (added != 0);
 
-#ifdef _LIBC
   __libc_lock_unlock (lock);
-#endif
 
   return result;
 }
 
 
 static size_t
-internal_function
-read_alias_file (fname, fname_len)
-     const char *fname;
-     int fname_len;
+read_alias_file (const char *fname, int fname_len)
 {
-#ifndef HAVE_ALLOCA
-  struct block_list *block_list = NULL;
-#endif
   FILE *fp;
   char *full_fname;
   size_t added;
   static const char aliasfile[] = "/locale.alias";
 
   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
-  ADD_BLOCK (block_list, full_fname);
 #ifdef HAVE_MEMPCPY
   mempcpy (mempcpy (full_fname, fname, fname_len),
           aliasfile, sizeof aliasfile);
@@ -249,69 +223,68 @@ read_alias_file (fname, fname_len)
   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
 #endif
 
-  fp = fopen (full_fname, "r");
+#ifdef _LIBC
+  /* Note the file is opened with cancellation in the I/O functions
+     disabled.  */
+  fp = fopen (relocate (full_fname), "rce");
+#else
+  fp = fopen (relocate (full_fname), "r");
+#endif
+  freea (full_fname);
   if (fp == NULL)
-    {
-      FREE_BLOCKS (block_list);
-      return 0;
-    }
+    return 0;
+
+#ifdef HAVE___FSETLOCKING
+  /* No threads present.  */
+  __fsetlocking (fp, FSETLOCKING_BYCALLER);
+#endif
 
   added = 0;
-  while (!feof (fp))
+  while (!FEOF (fp))
     {
       /* It is a reasonable approach to use a fix buffer here because
         a) we are only interested in the first two fields
         b) these fields must be usable as file names and so must not
            be that long
-       */
-      char buf[BUFSIZ];
+        We avoid a multi-kilobyte buffer here since this would use up
+        stack space which we might not have if the program ran out of
+        memory.  */
+      char buf[400];
       char *alias;
       char *value;
       char *cp;
+      int complete_line;
 
-      if (fgets (buf, sizeof buf, fp) == NULL)
+      if (FGETS (buf, sizeof buf, fp) == NULL)
        /* EOF reached.  */
        break;
 
-      /* Possibly not the whole line fits into the buffer.  Ignore
-        the rest of the line.  */
-      if (strchr (buf, '\n') == NULL)
-       {
-         char altbuf[BUFSIZ];
-         do
-           if (fgets (altbuf, sizeof altbuf, fp) == NULL)
-             /* Make sure the inner loop will be left.  The outer loop
-                will exit at the `feof' test.  */
-             break;
-         while (strchr (altbuf, '\n') == NULL);
-       }
+      /* Determine whether the line is complete.  */
+      complete_line = strchr (buf, '\n') != NULL;
 
       cp = buf;
       /* Ignore leading white space.  */
-      while (isspace (cp[0]))
+      while (isspace ((unsigned char) cp[0]))
        ++cp;
 
       /* A leading '#' signals a comment line.  */
       if (cp[0] != '\0' && cp[0] != '#')
        {
          alias = cp++;
-         while (cp[0] != '\0' && !isspace (cp[0]))
+         while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
            ++cp;
          /* Terminate alias name.  */
          if (cp[0] != '\0')
            *cp++ = '\0';
 
          /* Now look for the beginning of the value.  */
-         while (isspace (cp[0]))
+         while (isspace ((unsigned char) cp[0]))
            ++cp;
 
          if (cp[0] != '\0')
            {
-             size_t alias_len;
-             size_t value_len;
-
              value = cp++;
-             while (cp[0] != '\0' && !isspace (cp[0]))
+             while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
                ++cp;
              /* Terminate value.  */
              if (cp[0] == '\n')
@@ -325,57 +298,91 @@ read_alias_file (fname, fname_len)
              else if (cp[0] != '\0')
                *cp++ = '\0';
 
-             if (nmap >= maxmap)
-               extend_alias_table ();
+#ifdef IN_LIBGLOCALE
+             /* glibc's locale.alias contains entries for ja_JP and ko_KR
+                that make it impossible to use a Japanese or Korean UTF-8
+                locale under the name "ja_JP" or "ko_KR".  Ignore these
+                entries.  */
+             if (strchr (alias, '_') == NULL)
+#endif
+               {
+                 size_t alias_len;
+                 size_t value_len;
 
-             alias_len = strlen (alias) + 1;
-             value_len = strlen (value) + 1;
+                 if (nmap >= maxmap)
+                   if (__builtin_expect (extend_alias_table (), 0))
+                     goto out;
 
-             if (string_space_act + alias_len + value_len > string_space_max)
-               {
-                 /* Increase size of memory pool.  */
-                 size_t new_size = (string_space_max
-                                    + (alias_len + value_len > 1024
-                                       ? alias_len + value_len : 1024));
-                 char *new_pool = (char *) realloc (string_space, new_size);
-                 if (new_pool == NULL)
+                 alias_len = strlen (alias) + 1;
+                 value_len = strlen (value) + 1;
+
+                 if (string_space_act + alias_len + value_len > string_space_max)
                    {
-                     FREE_BLOCKS (block_list);
-                     return added;
+                     /* Increase size of memory pool.  */
+                     size_t new_size = (string_space_max
+                                        + (alias_len + value_len > 1024
+                                           ? alias_len + value_len : 1024));
+                     char *new_pool = (char *) realloc (string_space, new_size);
+                     if (new_pool == NULL)
+                       goto out;
+
+                     if (__builtin_expect (string_space != new_pool, 0))
+                       {
+                         size_t i;
+
+                         for (i = 0; i < nmap; i++)
+                           {
+                             map[i].alias += new_pool - string_space;
+                             map[i].value += new_pool - string_space;
+                           }
+                       }
+
+                     string_space = new_pool;
+                     string_space_max = new_size;
                    }
-                 string_space = new_pool;
-                 string_space_max = new_size;
-               }
 
-             map[nmap].alias = memcpy (&string_space[string_space_act],
-                                       alias, alias_len);
-             string_space_act += alias_len;
+                 map[nmap].alias =
+                   (const char *) memcpy (&string_space[string_space_act],
+                                          alias, alias_len);
+                 string_space_act += alias_len;
 
-             map[nmap].value = memcpy (&string_space[string_space_act],
-                                       value, value_len);
-             string_space_act += value_len;
+                 map[nmap].value =
+                   (const char *) memcpy (&string_space[string_space_act],
+                                          value, value_len);
+                 string_space_act += value_len;
 
-             ++nmap;
-             ++added;
+                 ++nmap;
+                 ++added;
+               }
            }
        }
+
+      /* Possibly not the whole line fits into the buffer.  Ignore
+        the rest of the line.  */
+      if (! complete_line)
+       do
+         if (FGETS (buf, sizeof buf, fp) == NULL)
+           /* Make sure the inner loop will be left.  The outer loop
+              will exit at the `feof' test.  */
+           break;
+       while (strchr (buf, '\n') == NULL);
     }
 
+ out:
   /* Should we test for ferror()?  I think we have to silently ignore
      errors.  --drepper  */
   fclose (fp);
 
   if (added > 0)
     qsort (map, nmap, sizeof (struct alias_map),
-          (int (*) PARAMS ((const void *, const void *))) alias_compare);
+          (int (*) (const void *, const void *)) alias_compare);
 
-  FREE_BLOCKS (block_list);
   return added;
 }
 
 
-static void
-extend_alias_table ()
+static int
+extend_alias_table (void)
 {
   size_t new_size;
   struct alias_map *new_map;
@@ -385,30 +392,16 @@ extend_alias_table ()
                                                * sizeof (struct alias_map)));
   if (new_map == NULL)
     /* Simply don't extend: we don't have any more core.  */
-    return;
+    return -1;
 
   map = new_map;
   maxmap = new_size;
+  return 0;
 }
 
 
-#ifdef _LIBC
-static void __attribute__ ((unused))
-free_mem (void)
-{
-  if (string_space != NULL)
-    free (string_space);
-  if (map != NULL)
-    free (map);
-}
-text_set_element (__libc_subfreeres, free_mem);
-#endif
-
-
 static int
-alias_compare (map1, map2)
-     const struct alias_map *map1;
-     const struct alias_map *map2;
+alias_compare (const struct alias_map *map1, const struct alias_map *map2)
 {
 #if defined _LIBC || defined HAVE_STRCASECMP
   return strcasecmp (map1->alias, map2->alias);