]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
newlocale: New module.
authorBruno Haible <bruno@clisp.org>
Thu, 13 Feb 2025 22:01:52 +0000 (23:01 +0100)
committerBruno Haible <bruno@clisp.org>
Fri, 14 Feb 2025 01:50:49 +0000 (02:50 +0100)
* lib/locale.in.h (newlocale): Consider GNULIB_NEWLOCALE. Declare if
not declared. Don't define HAVE_WORKING_NEWLOCALE.
* lib/newlocale.c: New file.
* m4/newlocale.m4: New file.
* m4/locale_h.m4 (gl_LOCALE_H_REQUIRE_DEFAULTS): Initialize
GNULIB_NEWLOCALE.
* modules/locale-h (Makefile.am): Substitute GNULIB_NEWLOCALE.
* modules/newlocale: New file.
* tests/test-locale-h-c++.cc: Check declaration of newlocale.
* tests/test-localename.c: Ignore HAVE_WORKING_NEWLOCALE.
* doc/posix-functions/newlocale.texi: Mention the new module.

ChangeLog
doc/posix-functions/newlocale.texi
lib/locale.in.h
lib/newlocale.c [new file with mode: 0644]
m4/locale_h.m4
m4/newlocale.m4 [new file with mode: 0644]
modules/locale-h
modules/newlocale [new file with mode: 0644]
tests/test-locale-h-c++.cc
tests/test-localename.c

index 6c4200a35fce052fa83059b683ed4c146a5fb992..0e53a6e5315345c47b2859bf6c20be9abfa271d2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2025-02-14  Bruno Haible  <bruno@clisp.org>
+
+       newlocale: New module.
+       * lib/locale.in.h (newlocale): Consider GNULIB_NEWLOCALE. Declare if
+       not declared. Don't define HAVE_WORKING_NEWLOCALE.
+       * lib/newlocale.c: New file.
+       * m4/newlocale.m4: New file.
+       * m4/locale_h.m4 (gl_LOCALE_H_REQUIRE_DEFAULTS): Initialize
+       GNULIB_NEWLOCALE.
+       * modules/locale-h (Makefile.am): Substitute GNULIB_NEWLOCALE.
+       * modules/newlocale: New file.
+       * tests/test-locale-h-c++.cc: Check declaration of newlocale.
+       * tests/test-localename.c: Ignore HAVE_WORKING_NEWLOCALE.
+       * doc/posix-functions/newlocale.texi: Mention the new module.
+
 2025-02-14  Bruno Haible  <bruno@clisp.org>
 
        localename-environ: New module.
index 762cf8c45e063ee6db81e28b943d0feb6512925e..a305c3821baf0f2d7e30e5883a2cfc930c12870b 100644 (file)
@@ -4,21 +4,22 @@
 
 POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9799919799/functions/newlocale.html}
 
-Gnulib module: ---
+Gnulib module: newlocale
+@mindex newlocale
 
 Portability problems fixed by Gnulib:
 @itemize
-@end itemize
-
-Portability problems not fixed by Gnulib:
-@itemize
 @item
 This function is missing on many platforms:
-FreeBSD 9.0, NetBSD 5.0, OpenBSD 6.1, Minix 3.1.8, AIX 6.1, HP-UX 11, Solaris 11.3, Cygwin 2.5.x, mingw, MSVC 14, Android 4.4.
+FreeBSD 9.0, NetBSD 6.1, OpenBSD 6.1, Minix 3.1.8, AIX 6.1, HP-UX 11, Solaris 11.3, Cygwin 2.5.x, mingw, MSVC 14, Android 4.4.
 @item
 This function is useless because the @code{locale_t} type is not defined
 on some platforms:
 z/OS.
+@end itemize
+
+Portability problems not fixed by Gnulib:
+@itemize
 @item
 This function is useless because the @code{locale_t} type contains basically
 no information on some platforms:
index 389ff26124755afa0c6406b34892b45b435439c9..7fa426859f91f7b9bd4bd0a14ffba994e0e59b3b 100644 (file)
@@ -300,7 +300,7 @@ _GL_WARN_ON_USE (setlocale, "setlocale works differently on native Windows - "
 # include "setlocale_null.h"
 #endif
 
-#if /*@GNULIB_NEWLOCALE@ ||*/ (@GNULIB_LOCALENAME_UNSAFE@ && @LOCALENAME_ENHANCE_LOCALE_FUNCS@ && @HAVE_NEWLOCALE@)
+#if @GNULIB_NEWLOCALE@ || (@GNULIB_LOCALENAME_UNSAFE@ && @LOCALENAME_ENHANCE_LOCALE_FUNCS@ && @HAVE_NEWLOCALE@)
 # if @REPLACE_NEWLOCALE@
 #  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
 #   undef newlocale
@@ -313,19 +313,17 @@ _GL_FUNCDECL_RPL (newlocale, locale_t,
 _GL_CXXALIAS_RPL (newlocale, locale_t,
                   (int category_mask, const char *name, locale_t base));
 # else
-#  if @HAVE_NEWLOCALE@
+#  if !@HAVE_NEWLOCALE@
+_GL_FUNCDECL_SYS (newlocale, locale_t,
+                  (int category_mask, const char *name, locale_t base),
+                  _GL_ARG_NONNULL ((2)));
+#  endif
 _GL_CXXALIAS_SYS (newlocale, locale_t,
                   (int category_mask, const char *name, locale_t base));
-#  endif
 # endif
-# if __GLIBC__ >= 2 && @HAVE_NEWLOCALE@
+# if __GLIBC__ >= 2
 _GL_CXXALIASWARN (newlocale);
 # endif
-# if @HAVE_NEWLOCALE@ || @REPLACE_NEWLOCALE@
-#  ifndef HAVE_WORKING_NEWLOCALE
-#   define HAVE_WORKING_NEWLOCALE 1
-#  endif
-# endif
 #elif defined GNULIB_POSIXCHECK
 # undef newlocale
 # if HAVE_RAW_DECL_NEWLOCALE
diff --git a/lib/newlocale.c b/lib/newlocale.c
new file mode 100644 (file)
index 0000000..01bc2f3
--- /dev/null
@@ -0,0 +1,204 @@
+/* Create a locale object.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+
+   This file 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.
+
+   This file 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 Lesser General Public License for more details.
+
+   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/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2025.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <locale.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "localename.h"
+
+locale_t
+newlocale (int category_mask, const char *name, locale_t base)
+{
+  if ((category_mask & ~LC_ALL_MASK) != 0)
+    {
+      errno = EINVAL;
+      return NULL;
+    }
+
+  struct gl_locale_t tmp;
+  struct gl_locale_t *result;
+  if (base != NULL)
+    {
+      /* Work on tmp, so as not to destroy BASE until we're successful.  */
+      result = &tmp;
+    }
+  else
+    {
+      result = (struct gl_locale_t *) malloc (sizeof (struct gl_locale_t));
+      if (result == NULL)
+        {
+          errno = ENOMEM;
+          return NULL;
+        }
+    }
+
+  /* Canonicalize the name.  */
+  if (strcmp (name, "POSIX") == 0)
+    name = "C";
+
+#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)
+    {
+      errno = ENOENT;
+      return NULL;
+    }
+#endif
+
+  int i;
+  int err;
+  for (i = 0; i < 6; i++)
+    {
+      int log2_lcmask = gl_index_to_log2_lcmask (i);
+
+      if ((category_mask & (1 << log2_lcmask)) != 0)
+        {
+          const char *lcname;
+          if (name[0] == '\0')
+            {
+              /* name == "" means to look at the environment variables.  */
+              static struct { int cat; char cat_name[11 + 1]; } const categories[6] =
+                {
+                  [gl_log2_lcmask_to_index (gl_log2_lc_mask (LC_COLLATE))]  =
+                    { LC_COLLATE,  "LC_COLLATE" },
+                  [gl_log2_lcmask_to_index (gl_log2_lc_mask (LC_CTYPE))]    =
+                    { LC_CTYPE,    "LC_CTYPE" },
+                  [gl_log2_lcmask_to_index (gl_log2_lc_mask (LC_MESSAGES))] =
+                    { LC_MESSAGES, "LC_MESSAGES" },
+                  [gl_log2_lcmask_to_index (gl_log2_lc_mask (LC_MONETARY))] =
+                    { LC_MONETARY, "LC_MONETARY" },
+                  [gl_log2_lcmask_to_index (gl_log2_lc_mask (LC_NUMERIC))]  =
+                    { LC_NUMERIC,  "LC_NUMERIC" },
+                  [gl_log2_lcmask_to_index (gl_log2_lc_mask (LC_TIME))]     =
+                    { LC_TIME,     "LC_TIME" }
+                };
+              lcname = gl_locale_name_environ (categories[i].cat,
+                                               categories[i].cat_name);
+              if (lcname == NULL)
+                lcname = "C";
+            }
+          else
+            lcname = name;
+
+          result->category[i].name = strdup (lcname);
+          if (result->category[i].name == NULL)
+            {
+              err = ENOMEM;
+              goto fail_with_err;
+            }
+          if (strcmp (lcname, "C") == 0)
+            {
+              result->category[i].is_c_locale = true;
+#if HAVE_WINDOWS_LOCALE_T
+              /* Just to initialize it.  */
+              result->category[i].system_locale = NULL;
+#endif
+            }
+          else
+            {
+              result->category[i].is_c_locale = false;
+#if HAVE_WINDOWS_LOCALE_T
+              if (log2_lcmask == gl_log2_lc_mask (LC_MESSAGES))
+                result->category[i].system_locale = NULL;
+              else
+                {
+                  int cat = log2_lcmask;
+                  (void) cat;
+                  /* Documentation:
+                     <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/create-locale-wcreate-locale>  */
+                  result->category[i].system_locale =
+                    _create_locale (LC_ALL /* or cat */, lcname);
+                  if (result->category[i].system_locale == NULL)
+                    {
+                      free (result->category[i].name);
+                      err = ENOENT;
+                      goto fail_with_err;
+                    }
+                }
+#endif
+            }
+        }
+      else
+        {
+          if (base == NULL)
+            {
+              result->category[i].name = strdup ("C");
+              if (result->category[i].name == NULL)
+                {
+                  err = ENOMEM;
+                  goto fail_with_err;
+                }
+              result->category[i].is_c_locale = true;
+#if HAVE_WINDOWS_LOCALE_T
+              /* Just to initialize it.  */
+              result->category[i].system_locale = NULL;
+#endif
+            }
+        }
+    }
+
+  /* Success.  */
+  if (base != NULL)
+    {
+      /* Copy the modified entries from RESULT to BASE.  */
+      for (i = 0; i < 6; i++)
+        {
+          int log2_lcmask = gl_index_to_log2_lcmask (i);
+          if ((category_mask & (1 << log2_lcmask)) != 0)
+            {
+#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
+              free (base->category[i].name);
+
+              base->category[i] = result->category[i];
+            }
+        }
+      return base;
+    }
+  else
+    return result;
+
+ fail_with_err:
+  while (--i >= 0)
+    {
+#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
+      free (result->category[i].name);
+    }
+  if (base == NULL)
+    free (result);
+  errno = err;
+  return NULL;
+}
index 9300062194486a478ab5fd0d03b5996859d6c399..0635a4c3bb5aeaa519ce9eab27d85c55223bfb02 100644 (file)
@@ -1,5 +1,5 @@
 # locale_h.m4
-# serial 32
+# serial 33
 dnl Copyright (C) 2007, 2009-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,
@@ -176,6 +176,7 @@ AC_DEFUN([gl_LOCALE_H_REQUIRE_DEFAULTS],
     gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOCALECONV])
     gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SETLOCALE])
     gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SETLOCALE_NULL])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_NEWLOCALE])
     gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_DUPLOCALE])
     gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOCALENAME_UNSAFE])
   ])
diff --git a/m4/newlocale.m4 b/m4/newlocale.m4
new file mode 100644 (file)
index 0000000..b2475eb
--- /dev/null
@@ -0,0 +1,22 @@
+# newlocale.m4
+# serial 1
+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,
+dnl with or without modifications, as long as this notice is preserved.
+dnl This file is offered as-is, without any warranty.
+
+AC_DEFUN([gl_FUNC_NEWLOCALE],
+[
+  AC_REQUIRE([gl_LOCALE_H_DEFAULTS])
+  gl_CHECK_FUNCS_ANDROID([newlocale], [[#include <locale.h>]])
+  if test $ac_cv_func_newlocale = no; then
+    HAVE_NEWLOCALE=0
+  fi
+])
+
+# Prerequisites of lib/newlocale.c.
+AC_DEFUN([gl_PREREQ_NEWLOCALE],
+[
+  :
+])
index 5dab6ab42ac52c13779c45da553683e6c8ab1caf..a5317cf9c9db414b8b0e534b5097ebca99d16234 100644 (file)
@@ -38,6 +38,7 @@ locale.h: locale.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H
              -e 's/@''GNULIB_LOCALECONV''@/$(GNULIB_LOCALECONV)/g' \
              -e 's/@''GNULIB_SETLOCALE''@/$(GNULIB_SETLOCALE)/g' \
              -e 's/@''GNULIB_SETLOCALE_NULL''@/$(GNULIB_SETLOCALE_NULL)/g' \
+             -e 's/@''GNULIB_NEWLOCALE''@/$(GNULIB_NEWLOCALE)/g' \
              -e 's/@''GNULIB_DUPLOCALE''@/$(GNULIB_DUPLOCALE)/g' \
              -e 's/@''GNULIB_LOCALENAME_UNSAFE''@/$(GNULIB_LOCALENAME_UNSAFE)/g' \
              -e 's|@''HAVE_NEWLOCALE''@|$(HAVE_NEWLOCALE)|g' \
diff --git a/modules/newlocale b/modules/newlocale
new file mode 100644 (file)
index 0000000..78a1043
--- /dev/null
@@ -0,0 +1,32 @@
+Description:
+newlocale() function: create a locale object.
+
+Files:
+lib/newlocale.c
+m4/newlocale.m4
+
+Depends-on:
+locale-h
+localename-environ
+
+configure.ac:
+gl_FUNC_NEWLOCALE
+gl_CONDITIONAL([GL_COND_OBJ_NEWLOCALE], [test $HAVE_LOCALE_T = 0])
+AM_COND_IF([GL_COND_OBJ_NEWLOCALE], [
+  gl_PREREQ_NEWLOCALE
+])
+gl_LOCALE_MODULE_INDICATOR([newlocale])
+
+Makefile.am:
+if GL_COND_OBJ_NEWLOCALE
+lib_SOURCES += newlocale.c
+endif
+
+Include:
+<locale.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+all
index 1a68f2f28a8fedd9b10f361af7851fed8d5d3e3a..ad66ae4871ff2bc8db46432cb726071b5ccd5a7b 100644 (file)
@@ -32,7 +32,7 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::localeconv, struct lconv *, (void));
 SIGNATURE_CHECK (GNULIB_NAMESPACE::setlocale, char *, (int, const char *));
 #endif
 
-#if 0
+#if GNULIB_TEST_NEWLOCALE
 SIGNATURE_CHECK (GNULIB_NAMESPACE::newlocale, locale_t, (int, const char *, locale_t));
 #endif
 
index 3089b56c62e3e1140c3176af6ca4d77c7ed06e3b..0bbc2c76d984acdab4e2fbbcacd6442e1b28facb 100644 (file)
@@ -26,7 +26,7 @@
 
 #include "macros.h"
 
-#if HAVE_WORKING_NEWLOCALE && HAVE_WORKING_USELOCALE && !HAVE_FAKE_LOCALES
+#if HAVE_WORKING_USELOCALE && !HAVE_FAKE_LOCALES
 # define HAVE_GOOD_USELOCALE 1
 #endif