+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.
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:
# 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
_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
--- /dev/null
+/* 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;
+}
# 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,
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])
])
--- /dev/null
+# 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],
+[
+ :
+])
-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' \
--- /dev/null
+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
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
#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