Based on the portable 'locale_t' type, it is possible to write a portable
newlocale() function.


2025-02-14  Bruno Haible  <br...@clisp.org>

        newlocale: Add tests.
        * tests/test-newlocale.c: New file.
        * modules/newlocale-tests: New file.

        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  <br...@clisp.org>

        localename-environ: New module.
        * lib/localename-environ.c: New file, extracted from
        lib/localename-unsafe.c.
        * lib/localename-unsafe.c (gl_locale_name_environ): Remove function.
        * m4/localename.m4 (gl_LOCALENAME_ENVIRON): New macro.
        * modules/localename-environ: New file.
        * modules/localename-unsafe (Depends-on): Add localename-environ.
        * modules/setlocale (Depends-on): Likewise.

>From d3afa7cfedb4a621bcf4d4d48db3be76b9712c14 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 14 Feb 2025 02:02:58 +0100
Subject: [PATCH 1/5] localename-environ: New module.

* lib/localename-environ.c: New file, extracted from
lib/localename-unsafe.c.
* lib/localename-unsafe.c (gl_locale_name_environ): Remove function.
* m4/localename.m4 (gl_LOCALENAME_ENVIRON): New macro.
* modules/localename-environ: New file.
* modules/localename-unsafe (Depends-on): Add localename-environ.
* modules/setlocale (Depends-on): Likewise.
---
 ChangeLog                  | 11 ++++++++
 lib/localename-environ.c   | 58 ++++++++++++++++++++++++++++++++++++++
 lib/localename-unsafe.c    | 33 ----------------------
 m4/localename.m4           |  7 ++++-
 modules/localename-environ | 26 +++++++++++++++++
 modules/localename-unsafe  |  1 +
 modules/setlocale          |  5 ++--
 7 files changed, 105 insertions(+), 36 deletions(-)
 create mode 100644 lib/localename-environ.c
 create mode 100644 modules/localename-environ

diff --git a/ChangeLog b/ChangeLog
index dd083951c5..6c4200a35f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2025-02-14  Bruno Haible  <br...@clisp.org>
+
+	localename-environ: New module.
+	* lib/localename-environ.c: New file, extracted from
+	lib/localename-unsafe.c.
+	* lib/localename-unsafe.c (gl_locale_name_environ): Remove function.
+	* m4/localename.m4 (gl_LOCALENAME_ENVIRON): New macro.
+	* modules/localename-environ: New file.
+	* modules/localename-unsafe (Depends-on): Add localename-environ.
+	* modules/setlocale (Depends-on): Likewise.
+
 2025-02-13  Bruno Haible  <br...@clisp.org>
 
 	locale-h: Ensure locale_t type.
diff --git a/lib/localename-environ.c b/lib/localename-environ.c
new file mode 100644
index 0000000000..fea88fa88e
--- /dev/null
+++ b/lib/localename-environ.c
@@ -0,0 +1,58 @@
+/* Determine name of the currently selected locale.
+   Copyright (C) 1995-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 Ulrich Drepper <drep...@gnu.org>, 1995.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "localename.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+const char *
+gl_locale_name_environ (_GL_UNUSED int category, const char *categoryname)
+{
+  const char *retval;
+
+  /* Setting of LC_ALL overrides all other.  */
+  retval = getenv ("LC_ALL");
+  if (retval != NULL && retval[0] != '\0')
+    return retval;
+  /* Next comes the name of the desired category.  */
+  retval = getenv (categoryname);
+  if (retval != NULL && retval[0] != '\0')
+    return retval;
+  /* Last possibility is the LANG environment variable.  */
+  retval = getenv ("LANG");
+  if (retval != NULL && retval[0] != '\0')
+    {
+#if HAVE_CFPREFERENCESCOPYAPPVALUE
+      /* Mac OS X 10.2 or newer.
+         Ignore invalid LANG value set by the Terminal application.  */
+      if (strcmp (retval, "UTF-8") != 0)
+#endif
+#if defined __CYGWIN__
+      /* Cygwin.
+         Ignore dummy LANG value set by ~/.profile.  */
+      if (strcmp (retval, "C.UTF-8") != 0)
+#endif
+        return retval;
+    }
+
+  return NULL;
+}
diff --git a/lib/localename-unsafe.c b/lib/localename-unsafe.c
index b3160b5015..ebdf2e9491 100644
--- a/lib/localename-unsafe.c
+++ b/lib/localename-unsafe.c
@@ -3310,39 +3310,6 @@ gl_locale_name_posix_unsafe (int category, _GL_UNUSED const char *categoryname)
   }
 }
 
-const char *
-gl_locale_name_environ (_GL_UNUSED int category, const char *categoryname)
-{
-  const char *retval;
-
-  /* Setting of LC_ALL overrides all other.  */
-  retval = getenv ("LC_ALL");
-  if (retval != NULL && retval[0] != '\0')
-    return retval;
-  /* Next comes the name of the desired category.  */
-  retval = getenv (categoryname);
-  if (retval != NULL && retval[0] != '\0')
-    return retval;
-  /* Last possibility is the LANG environment variable.  */
-  retval = getenv ("LANG");
-  if (retval != NULL && retval[0] != '\0')
-    {
-#if HAVE_CFPREFERENCESCOPYAPPVALUE
-      /* Mac OS X 10.2 or newer.
-         Ignore invalid LANG value set by the Terminal application.  */
-      if (strcmp (retval, "UTF-8") != 0)
-#endif
-#if defined __CYGWIN__
-      /* Cygwin.
-         Ignore dummy LANG value set by ~/.profile.  */
-      if (strcmp (retval, "C.UTF-8") != 0)
-#endif
-        return retval;
-    }
-
-  return NULL;
-}
-
 const char *
 gl_locale_name_default (void)
 {
diff --git a/m4/localename.m4 b/m4/localename.m4
index ee614fd943..af94411b23 100644
--- a/m4/localename.m4
+++ b/m4/localename.m4
@@ -1,5 +1,5 @@
 # localename.m4
-# serial 12
+# serial 13
 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,
@@ -61,3 +61,8 @@ AC_DEFUN([gl_LOCALENAME_UNSAFE_LIMITED]
   AC_REQUIRE([gt_LC_MESSAGES])
   AC_REQUIRE([gt_INTL_THREAD_LOCALE_NAME])
 ])
+
+AC_DEFUN([gl_LOCALENAME_ENVIRON],
+[
+  AC_REQUIRE([gt_INTL_MACOSX])
+])
diff --git a/modules/localename-environ b/modules/localename-environ
new file mode 100644
index 0000000000..dcfc47e468
--- /dev/null
+++ b/modules/localename-environ
@@ -0,0 +1,26 @@
+Description:
+Return current locale's name, as specified by environment variables.
+
+Files:
+lib/localename.h
+lib/localename-environ.c
+m4/localename.m4
+m4/intlmacosx.m4
+
+Depends-on:
+
+configure.ac:
+gl_LOCALENAME_ENVIRON
+gl_LOCALE_MODULE_INDICATOR([localename-environ])
+
+Makefile.am:
+lib_SOURCES += localename-environ.c
+
+Include:
+"localename.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
diff --git a/modules/localename-unsafe b/modules/localename-unsafe
index b0aea3409d..18fa5433bd 100644
--- a/modules/localename-unsafe
+++ b/modules/localename-unsafe
@@ -16,6 +16,7 @@ m4/musl.m4
 
 Depends-on:
 localename-unsafe-limited
+localename-environ
 extensions
 bool
 locale-h
diff --git a/modules/setlocale b/modules/setlocale
index c59ca1f41e..a7c52cdeef 100644
--- a/modules/setlocale
+++ b/modules/setlocale
@@ -7,8 +7,9 @@ m4/setlocale.m4
 
 Depends-on:
 locale-h
-localename      [test $NEED_SETLOCALE_IMPROVED = 1]
-setlocale-null  [test $NEED_SETLOCALE_MTSAFE = 1]
+localename         [test $NEED_SETLOCALE_IMPROVED = 1]
+localename-environ [test $NEED_SETLOCALE_IMPROVED = 1]
+setlocale-null     [test $NEED_SETLOCALE_MTSAFE = 1]
 
 configure.ac:
 gl_FUNC_SETLOCALE
-- 
2.43.0

>From f6af6db65754a7556c90672fd3abc1e20fc53e34 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Thu, 13 Feb 2025 23:01:52 +0100
Subject: [PATCH 2/5] 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.
---
 ChangeLog                          |  15 +++
 doc/posix-functions/newlocale.texi |  13 +-
 lib/locale.in.h                    |  16 +--
 lib/newlocale.c                    | 204 +++++++++++++++++++++++++++++
 m4/locale_h.m4                     |   3 +-
 m4/newlocale.m4                    |  22 ++++
 modules/locale-h                   |   1 +
 modules/newlocale                  |  32 +++++
 tests/test-locale-h-c++.cc         |   2 +-
 tests/test-localename.c            |   2 +-
 10 files changed, 292 insertions(+), 18 deletions(-)
 create mode 100644 lib/newlocale.c
 create mode 100644 m4/newlocale.m4
 create mode 100644 modules/newlocale

diff --git a/ChangeLog b/ChangeLog
index 6c4200a35f..0e53a6e531 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2025-02-14  Bruno Haible  <br...@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  <br...@clisp.org>
 
 	localename-environ: New module.
diff --git a/doc/posix-functions/newlocale.texi b/doc/posix-functions/newlocale.texi
index 762cf8c45e..a305c3821b 100644
--- a/doc/posix-functions/newlocale.texi
+++ b/doc/posix-functions/newlocale.texi
@@ -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:
diff --git a/lib/locale.in.h b/lib/locale.in.h
index 389ff26124..7fa426859f 100644
--- a/lib/locale.in.h
+++ b/lib/locale.in.h
@@ -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
index 0000000000..01bc2f3953
--- /dev/null
+++ b/lib/newlocale.c
@@ -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 <br...@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;
+}
diff --git a/m4/locale_h.m4 b/m4/locale_h.m4
index 9300062194..0635a4c3bb 100644
--- a/m4/locale_h.m4
+++ b/m4/locale_h.m4
@@ -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
index 0000000000..b2475eb16f
--- /dev/null
+++ b/m4/newlocale.m4
@@ -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],
+[
+  :
+])
diff --git a/modules/locale-h b/modules/locale-h
index 5dab6ab42a..a5317cf9c9 100644
--- a/modules/locale-h
+++ b/modules/locale-h
@@ -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
index 0000000000..78a10436fa
--- /dev/null
+++ b/modules/newlocale
@@ -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
diff --git a/tests/test-locale-h-c++.cc b/tests/test-locale-h-c++.cc
index 1a68f2f28a..ad66ae4871 100644
--- a/tests/test-locale-h-c++.cc
+++ b/tests/test-locale-h-c++.cc
@@ -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
 
diff --git a/tests/test-localename.c b/tests/test-localename.c
index 3089b56c62..0bbc2c76d9 100644
--- a/tests/test-localename.c
+++ b/tests/test-localename.c
@@ -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
 
-- 
2.43.0

>From d08fa12b6eea6d3a8643d07501018cd93d45fa6b Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 14 Feb 2025 02:52:01 +0100
Subject: [PATCH 3/5] newlocale: Add tests.

* tests/test-newlocale.c: New file.
* modules/newlocale-tests: New file.
---
 ChangeLog               |  4 +++
 modules/newlocale-tests | 12 +++++++++
 tests/test-newlocale.c  | 56 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+)
 create mode 100644 modules/newlocale-tests
 create mode 100644 tests/test-newlocale.c

diff --git a/ChangeLog b/ChangeLog
index 0e53a6e531..1a73e832db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2025-02-14  Bruno Haible  <br...@clisp.org>
 
+	newlocale: Add tests.
+	* tests/test-newlocale.c: New file.
+	* modules/newlocale-tests: New file.
+
 	newlocale: New module.
 	* lib/locale.in.h (newlocale): Consider GNULIB_NEWLOCALE. Declare if
 	not declared. Don't define HAVE_WORKING_NEWLOCALE.
diff --git a/modules/newlocale-tests b/modules/newlocale-tests
new file mode 100644
index 0000000000..706003b4f2
--- /dev/null
+++ b/modules/newlocale-tests
@@ -0,0 +1,12 @@
+Files:
+tests/test-newlocale.c
+tests/signature.h
+tests/macros.h
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-newlocale
+check_PROGRAMS += test-newlocale
diff --git a/tests/test-newlocale.c b/tests/test-newlocale.c
new file mode 100644
index 0000000000..fa21f8b697
--- /dev/null
+++ b/tests/test-newlocale.c
@@ -0,0 +1,56 @@
+/* Test of creating a locale object.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <br...@clisp.org>, 2025.  */
+
+#include <config.h>
+
+#include <locale.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (newlocale, locale_t, (int, const char *, locale_t));
+
+#include "macros.h"
+
+#if defined _WIN32 && !defined __CYGWIN__
+
+# define ENGLISH  "English_United States"
+# define FRENCH   "French_France"
+# define GERMAN   "German_Germany"
+# define ENCODING ".1252"
+
+# define LOCALE1 ENGLISH ENCODING
+# define LOCALE2 FRENCH ENCODING
+# define LOCALE3 GERMAN ENCODING
+
+#else
+
+# define LOCALE1 "en_US.UTF-8"
+# define LOCALE2 "fr_FR.UTF-8"
+# define LOCALE3 "de_DE.UTF-8"
+
+#endif
+
+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;
+
+  return test_exit_status;
+}
-- 
2.43.0

Reply via email to