]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
newlocale: Work around macOS, NetBSD, Solaris 11 OpenIndiana bug.
authorBruno Haible <bruno@clisp.org>
Fri, 14 Feb 2025 14:24:54 +0000 (15:24 +0100)
committerBruno Haible <bruno@clisp.org>
Fri, 14 Feb 2025 14:43:39 +0000 (15:43 +0100)
* m4/newlocale.m4 (gl_FUNC_NEWLOCALE): Test for the "null base" bug.
Set REPLACE_NEWLOCALE to 1 if it has the bug.
* lib/newlocale.c (newlocale): Add alternative implementation that uses
the system's newlocale().
* modules/newlocale (configure.ac): Consider REPLACE_NEWLOCALE.
* tests/test-newlocale.c: Include <langinfo.h>.
(main): Verify fix for the "null base" bug.
* modules/newlocale-tests (configure.ac): Test for nl_langinfo_l.
* doc/posix-functions/newlocale.texi: Mention the "null base" bug.

ChangeLog
doc/posix-functions/newlocale.texi
lib/newlocale.c
m4/newlocale.m4
modules/newlocale
modules/newlocale-tests
tests/test-newlocale.c

index 03cc2df88b773158e0d8b09e09d3515a661dba17..5c52985015962b71fecf84fb137733463ec550f5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2025-02-14  Bruno Haible  <bruno@clisp.org>
+
+       newlocale: Work around macOS, NetBSD, Solaris 11 OpenIndiana bug.
+       * m4/newlocale.m4 (gl_FUNC_NEWLOCALE): Test for the "null base" bug.
+       Set REPLACE_NEWLOCALE to 1 if it has the bug.
+       * lib/newlocale.c (newlocale): Add alternative implementation that uses
+       the system's newlocale().
+       * modules/newlocale (configure.ac): Consider REPLACE_NEWLOCALE.
+       * tests/test-newlocale.c: Include <langinfo.h>.
+       (main): Verify fix for the "null base" bug.
+       * modules/newlocale-tests (configure.ac): Test for nl_langinfo_l.
+       * doc/posix-functions/newlocale.texi: Mention the "null base" bug.
+
 2025-02-14  Bruno Haible  <bruno@clisp.org>
 
        newlocale, freelocale: Tweak configuration.
index a305c3821baf0f2d7e30e5883a2cfc930c12870b..b4dc3661bd26d5a7bdf9f894304ea8ac9e501ab3 100644 (file)
@@ -16,6 +16,10 @@ FreeBSD 9.0, NetBSD 6.1, OpenBSD 6.1, Minix 3.1.8, AIX 6.1, HP-UX 11, Solaris 11
 This function is useless because the @code{locale_t} type is not defined
 on some platforms:
 z/OS.
+@item
+When the third argument is NULL, this function uses locale category data
+from the current locale instead of from the "C" locale on some platforms:
+macOS, NetBSD 10.0, Solaris 11 OpenIndiana.
 @end itemize
 
 Portability problems not fixed by Gnulib:
index 01bc2f3953c515ff8587c85e746b700eb83b6b2c..d5f902b8fbac729ff233e1b22bf416f820cfd199 100644 (file)
 #include <locale.h>
 
 #include <errno.h>
-#include <stdlib.h>
-#include <string.h>
 
-#include "localename.h"
+#if HAVE_NEWLOCALE
+/* Only provide workarounds.  */
+
+locale_t
+newlocale (int category_mask, const char *name, locale_t base)
+# undef newlocale
+{
+  if ((category_mask & ~LC_ALL_MASK) != 0)
+    {
+      errno = EINVAL;
+      return NULL;
+    }
+
+  if (category_mask != LC_ALL_MASK && base == NULL)
+    base = newlocale (LC_ALL_MASK, "C", NULL);
+
+  return newlocale (category_mask, name, base);
+}
+
+#else
+/* Implement from scratch.  */
+
+# include <stdlib.h>
+# include <string.h>
+
+# include "localename.h"
 
 locale_t
 newlocale (int category_mask, const char *name, locale_t base)
@@ -57,7 +80,7 @@ newlocale (int category_mask, const char *name, locale_t base)
   if (strcmp (name, "POSIX") == 0)
     name = "C";
 
-#if !HAVE_WINDOWS_LOCALE_T
+# if !HAVE_WINDOWS_LOCALE_T
   /* In this case, the only NAMEs that we support are "C" and (equivalently)
      "POSIX".  */
   if (category_mask != 0 && strcmp (name, "C") != 0)
@@ -65,7 +88,7 @@ newlocale (int category_mask, const char *name, locale_t base)
       errno = ENOENT;
       return NULL;
     }
-#endif
+# endif
 
   int i;
   int err;
@@ -111,15 +134,15 @@ newlocale (int category_mask, const char *name, locale_t base)
           if (strcmp (lcname, "C") == 0)
             {
               result->category[i].is_c_locale = true;
-#if HAVE_WINDOWS_LOCALE_T
+# if HAVE_WINDOWS_LOCALE_T
               /* Just to initialize it.  */
               result->category[i].system_locale = NULL;
-#endif
+# endif
             }
           else
             {
               result->category[i].is_c_locale = false;
-#if HAVE_WINDOWS_LOCALE_T
+# if HAVE_WINDOWS_LOCALE_T
               if (log2_lcmask == gl_log2_lc_mask (LC_MESSAGES))
                 result->category[i].system_locale = NULL;
               else
@@ -137,7 +160,7 @@ newlocale (int category_mask, const char *name, locale_t base)
                       goto fail_with_err;
                     }
                 }
-#endif
+# endif
             }
         }
       else
@@ -151,10 +174,10 @@ newlocale (int category_mask, const char *name, locale_t base)
                   goto fail_with_err;
                 }
               result->category[i].is_c_locale = true;
-#if HAVE_WINDOWS_LOCALE_T
+# if HAVE_WINDOWS_LOCALE_T
               /* Just to initialize it.  */
               result->category[i].system_locale = NULL;
-#endif
+# endif
             }
         }
     }
@@ -168,13 +191,13 @@ newlocale (int category_mask, const char *name, locale_t base)
           int log2_lcmask = gl_index_to_log2_lcmask (i);
           if ((category_mask & (1 << log2_lcmask)) != 0)
             {
-#if HAVE_WINDOWS_LOCALE_T
+# if HAVE_WINDOWS_LOCALE_T
               if (!(i == gl_log2_lcmask_to_index (gl_log2_lc_mask (LC_MESSAGES))
                     || base->category[i].is_c_locale))
                 /* Documentation:
                    <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/free-locale>  */
                 _free_locale (base->category[i].system_locale);
-#endif
+# endif
               free (base->category[i].name);
 
               base->category[i] = result->category[i];
@@ -188,13 +211,13 @@ newlocale (int category_mask, const char *name, locale_t base)
  fail_with_err:
   while (--i >= 0)
     {
-#if HAVE_WINDOWS_LOCALE_T
+# if HAVE_WINDOWS_LOCALE_T
       if (!(i == gl_log2_lcmask_to_index (gl_log2_lc_mask (LC_MESSAGES))
             || result->category[i].is_c_locale))
         /* Documentation:
            <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/free-locale>  */
         _free_locale (result->category[i].system_locale);
-#endif
+# endif
       free (result->category[i].name);
     }
   if (base == NULL)
@@ -202,3 +225,5 @@ newlocale (int category_mask, const char *name, locale_t base)
   errno = err;
   return NULL;
 }
+
+#endif
index 13df13808ecfa25135300513e2aa5e55b716550c..2775609ee6e41f25bdf0e5ffc4081d578ff95dba 100644 (file)
@@ -1,5 +1,5 @@
 # newlocale.m4
-# serial 2
+# serial 3
 dnl Copyright (C) 2025 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -19,7 +19,57 @@ AC_DEFUN([gl_FUNC_NEWLOCALE],
     gl_cv_onwards_func_newlocale='future OS version'
     gl_func_newlocale=no
   fi
-  if test $gl_func_newlocale != yes; then
+  if test $gl_func_newlocale = yes; then
+    dnl Check against the macOS, NetBSD, Solaris 11 OpenIndiana bug:
+    dnl When the third argument is NULL, newlocale() uses locale category data
+    dnl from the current locale instead of from the "C" locale.
+    gl_CHECK_FUNCS_ANDROID([nl_langinfo_l], [[#include <langinfo.h>]])
+    if test $ac_cv_func_nl_langinfo_l = yes; then
+      AC_REQUIRE([AC_CANONICAL_HOST])
+      AC_CACHE_CHECK([whether newlocale with a null base works],
+        [gl_cv_func_newlocale_works],
+        [dnl Prepare a guess, used when cross-compiling or when specific locales
+         dnl are not available.
+         case "$host_os" in
+           darwin* | netbsd* | solaris*)
+             gl_cv_func_newlocale_works="guessing no" ;;
+           *)
+             gl_cv_func_newlocale_works="guessing yes" ;;
+         esac
+         AC_RUN_IFELSE(
+           [AC_LANG_SOURCE([[
+#include <locale.h>
+#if HAVE_XLOCALE_H
+# include <xlocale.h>
+#endif
+#include <langinfo.h>
+int main ()
+{
+  locale_t l1 = newlocale (LC_TIME_MASK, "en_US.UTF-8", NULL);
+  if (l1 != NULL)
+    {
+      if (setlocale (LC_ALL, "fr_FR.UTF-8") != NULL)
+        {
+          locale_t l1a = newlocale (LC_TIME_MASK, "en_US.UTF-8", NULL);
+          const char *radixchar1a = nl_langinfo_l (RADIXCHAR, l1a);
+          return (*radixchar1a != '.');
+        }
+    }
+  return 2;
+}]])],
+           [gl_cv_func_newlocale_works=yes],
+           [if test $? = 1; then
+              gl_cv_func_newlocale_works=no
+            fi
+           ],
+           [])
+        ])
+      case "$gl_cv_func_newlocale_works" in
+        *yes) ;;
+        *) REPLACE_NEWLOCALE=1 ;;
+      esac
+    fi
+  else
     HAVE_NEWLOCALE=0
     case "$gl_cv_onwards_func_newlocale" in
       future*) REPLACE_NEWLOCALE=1 ;;
index 78a10436fa35d955bc03b60a32370c64bae88ac4..04f6df28f9d74658de23c2dc0fbd3fc931a64d30 100644 (file)
@@ -11,7 +11,8 @@ localename-environ
 
 configure.ac:
 gl_FUNC_NEWLOCALE
-gl_CONDITIONAL([GL_COND_OBJ_NEWLOCALE], [test $HAVE_LOCALE_T = 0])
+gl_CONDITIONAL([GL_COND_OBJ_NEWLOCALE],
+               [test $HAVE_LOCALE_T = 0 || { test $REPLACE_NEWLOCALE = 1 && test "$gt_localename_enhances_locale_funcs" != yes; }])
 AM_COND_IF([GL_COND_OBJ_NEWLOCALE], [
   gl_PREREQ_NEWLOCALE
 ])
index 706003b4f2c3f08f942bd9d761df08a73bfe7028..68764567846190cfe998d7251e8874d9e2126366 100644 (file)
@@ -6,6 +6,7 @@ tests/macros.h
 Depends-on:
 
 configure.ac:
+gl_CHECK_FUNCS_ANDROID([nl_langinfo_l], [[#include <langinfo.h>]])
 
 Makefile.am:
 TESTS += test-newlocale
index fa21f8b69754d94a5beac1dfff7884a84770e682..a6829d7154ebf7de8e7a8bc4d050f4e2d17bcac4 100644 (file)
 #include "signature.h"
 SIGNATURE_CHECK (newlocale, locale_t, (int, const char *, locale_t));
 
+#if HAVE_NL_LANGINFO_L
+# include <langinfo.h>
+#endif
+
 #include "macros.h"
 
 #if defined _WIN32 && !defined __CYGWIN__
@@ -47,10 +51,33 @@ SIGNATURE_CHECK (newlocale, locale_t, (int, const char *, locale_t));
 int
 main ()
 {
-  locale_t l1 = newlocale (LC_TIME_MASK, LOCALE1, NULL);
-  locale_t l2 = newlocale (LC_MESSAGES_MASK, LOCALE2, l1);
-  locale_t l3 = newlocale (LC_TIME_MASK, LOCALE3, l2);
-  (void) l3;
+  {
+    locale_t l1 = newlocale (LC_TIME_MASK, LOCALE1, NULL);
+    locale_t l2 = newlocale (LC_MESSAGES_MASK, LOCALE2, l1);
+    locale_t l3 = newlocale (LC_TIME_MASK, LOCALE3, l2);
+    (void) l3;
+  }
+
+#if HAVE_NL_LANGINFO_L
+  /* Verify that when the base argument is NULL, "the data for all sections
+     not requested by category_mask shall be taken from the POSIX locale".  */
+  {
+    locale_t l1 = newlocale (LC_TIME_MASK, LOCALE1, NULL);
+    if (l1 != NULL)
+      {
+        const char *radixchar1 = nl_langinfo_l (RADIXCHAR, l1);
+        ASSERT (*radixchar1 == '.');
+        if (setlocale (LC_ALL, LOCALE2) != NULL)
+          {
+            radixchar1 = nl_langinfo_l (RADIXCHAR, l1);
+            ASSERT (*radixchar1 == '.');
+            locale_t l1a = newlocale (LC_TIME_MASK, LOCALE1, NULL);
+            const char *radixchar1a = nl_langinfo_l (RADIXCHAR, l1a);
+            ASSERT (*radixchar1a == '.');
+          }
+      }
+  }
+#endif
 
   return test_exit_status;
 }