+2025-02-13 Bruno Haible <bruno@clisp.org>
+
+ locale-h: Ensure locale_t type.
+ * lib/locale.in.h (gl_log2_lc_mask, gl_log2_lcmask_to_index,
+ gl_index_to_log2_lcmask): New macros.
+ (LC_COLLATE_MASK, LC_CTYPE_MASK, LC_MESSAGES_MASK, LC_MONETARY_MASK,
+ LC_NUMERIC_MASK, LC_TIME_MASK, LC_ALL_MASK): New macros.
+ (struct gl_locale_category_t, struct gl_locale_t, locale_t): New types.
+ (LC_GLOBAL_LOCALE, GNULIB_defined_locale_t): New macros.
+ * m4/locale_h.m4 (gl_LOCALE_H): Set and define HAVE_WINDOWS_LOCALE_T.
+ (gl_LOCALE_T): Prepare for substituting HAVE_LOCALE_T.
+ * modules/locale-h (Depends-on): Add bool.
+ (Makefile.am): Substitute HAVE_LOCALE_T, HAVE_WINDOWS_LOCALE_T.
+ * tests/test-locale-h.c: Check that the LC_*_MASK macros and locale_t
+ are defined.
+ * doc/posix-headers/locale.texi: Document the change.
+
2025-02-13 Bruno Haible <bruno@clisp.org>
duplocale: Relicense under LGPLv2+.
@item
The @code{locale_t} type is not defined on some platforms:
-glibc 2.11, macOS 11.1.
+glibc 2.11, macOS 11.1, FreeBSD 9.0, NetBSD 6.1, OpenBSD 6.1, Solaris 11.3, mingw, MSVC 14, Android 4.4.
@item
The @code{struct lconv} type does not contain any members on some platforms:
# define LC_MESSAGES 1729
#endif
+#if !@HAVE_LOCALE_T@
+# if !defined GNULIB_defined_locale_t
+/* The values of the POSIX-standardized LC_* macros are:
+
+ LC_COLLATE LC_CTYPE LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME
+
+ glibc, Solaris, 3 0 5 4 1 2
+ Android
+ macOS, *BSD 1 2 6 3 4 5
+ native Windows 1 2 1729 3 4 5
+
+ We map these to the log2(LC_*_MASK) values, chosen to be compatible with
+ later releases of the same operating system. */
+# if defined __APPLE__ && defined __MACH__ /* macOS */
+/* LC_COLLATE LC_CTYPE LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME
+
+ category 1 2 6 3 4 5
+ log2(LC_*_MASK) 0 1 2 3 4 5
+ */
+# define gl_log2_lc_mask(category) ((0x2543100 >> (4 * (category))) & 0xf)
+# elif defined __FreeBSD__ || defined __DragonFly__ /* FreeBSD */
+/* LC_COLLATE LC_CTYPE LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME
+
+ category 1 2 6 3 4 5
+ log2(LC_*_MASK) 0 1 5 2 3 4
+ */
+# define gl_log2_lc_mask(category) ((category) - 1)
+# elif defined _WIN32 && !defined __CYGWIN__ /* native Windows */
+# define gl_log2_lc_mask(category) \
+ ((category) == LC_MESSAGES ? 0 : (category))
+# else /* glibc, Solaris, Android, NetBSD, OpenBSD */
+# define gl_log2_lc_mask(category) (category)
+# endif
+/* From there we map them to array indices 0..5. */
+# if (gl_log2_lc_mask (LC_COLLATE) == 0 || gl_log2_lc_mask (LC_CTYPE) == 0 \
+ || gl_log2_lc_mask (LC_MESSAGES) == 0)
+ /* glibc, Solaris, Android, macOS, FreeBSD, native Windows */
+# define gl_log2_lcmask_to_index(c) (c)
+# define gl_index_to_log2_lcmask(i) (i)
+# else
+ /* NetBSD, OpenBSD */
+# define gl_log2_lcmask_to_index(c) ((c) - 1)
+# define gl_index_to_log2_lcmask(i) ((i) + 1)
+# endif
+/* Define the LC_*_MASK macros. */
+# define LC_COLLATE_MASK (1 << gl_log2_lc_mask (LC_COLLATE))
+# define LC_CTYPE_MASK (1 << gl_log2_lc_mask (LC_CTYPE))
+# define LC_MESSAGES_MASK (1 << gl_log2_lc_mask (LC_MESSAGES))
+# define LC_MONETARY_MASK (1 << gl_log2_lc_mask (LC_MONETARY))
+# define LC_NUMERIC_MASK (1 << gl_log2_lc_mask (LC_NUMERIC))
+# define LC_TIME_MASK (1 << gl_log2_lc_mask (LC_TIME))
+# define LC_ALL_MASK \
+ (LC_COLLATE_MASK | LC_CTYPE_MASK | LC_MESSAGES_MASK | LC_MONETARY_MASK \
+ | LC_NUMERIC_MASK | LC_TIME_MASK)
+/* Now define the locale_t type. */
+struct gl_locale_category_t
+{
+ char *name;
+ bool is_c_locale;
+# if @HAVE_WINDOWS_LOCALE_T@
+ /* Use the native Windows '_locale_t' type.
+ Documentation:
+ <https://learn.microsoft.com/en-us/cpp/c-runtime-library/locale>
+ This field is NULL if is_c_locale is true. But don't use this NULL value,
+ since for the native Windows *_l functions a null _locale_t means to use
+ the global locale. */
+ _locale_t system_locale;
+# endif
+};
+struct gl_locale_t
+{
+ struct gl_locale_category_t category[6];
+};
+typedef struct gl_locale_t *locale_t;
+# define LC_GLOBAL_LOCALE ((locale_t)(-1))
+# define GNULIB_defined_locale_t 1
+# endif
+#endif
+
/* On native Windows with MSVC, 'struct lconv' lacks the members int_p_* and
int_n_*. Instead of overriding 'struct lconv', merely define these member
names as macros. This avoids trouble in C++ mode. */
# locale_h.m4
-# serial 31
+# serial 32
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,
AC_REQUIRE([gl_STDDEF_H])
AC_REQUIRE([gl_LOCALE_T])
+ dnl On native Windows, there is a type '_locale_t' that can be used to
+ dnl define locale_t.
+ AC_CACHE_CHECK([whether locale.h defines _locale_t],
+ [gl_cv_header_locale_has_windows_locale_t],
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <locale.h>
+ _locale_t x;]],
+ [[]])],
+ [gl_cv_header_locale_has_windows_locale_t=yes],
+ [gl_cv_header_locale_has_windows_locale_t=no])
+ ])
+ if test $gl_cv_header_locale_has_windows_locale_t = yes; then
+ HAVE_WINDOWS_LOCALE_T=1
+ AC_DEFINE([HAVE_WINDOWS_LOCALE_T], [1],
+ [Define to 1 if <locale.h> defines the _locale_t type.])
+ else
+ HAVE_WINDOWS_LOCALE_T=0
+ fi
+ AC_SUBST([HAVE_WINDOWS_LOCALE_T])
dnl Solaris 11.0 defines the int_p_*, int_n_* members of 'struct lconv'
dnl only if _LCONV_C99 is defined.
fi
fi
AC_SUBST([HAVE_XLOCALE_H])
+ AC_SUBST([HAVE_LOCALE_T])
])
# gl_LOCALE_MODULE_INDICATOR([modulename])
snippet/c++defs
snippet/warn-on-use
stddef-h
+bool
configure.ac:
gl_LOCALE_H
-e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
-e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
-e 's|@''NEXT_LOCALE_H''@|$(NEXT_LOCALE_H)|g' \
+ -e 's|@''HAVE_LOCALE_T''@|$(HAVE_LOCALE_T)|g' \
+ -e 's|@''HAVE_WINDOWS_LOCALE_T''@|$(HAVE_WINDOWS_LOCALE_T)|g' \
-e 's/@''GNULIB_LOCALECONV''@/$(GNULIB_LOCALECONV)/g' \
-e 's/@''GNULIB_SETLOCALE''@/$(GNULIB_SETLOCALE)/g' \
-e 's/@''GNULIB_SETLOCALE_NULL''@/$(GNULIB_SETLOCALE_NULL)/g' \
LC_NUMERIC,
LC_TIME
};
+int m[] =
+ {
+ LC_ALL_MASK,
+ LC_COLLATE_MASK,
+ LC_CTYPE_MASK,
+ LC_MESSAGES_MASK,
+ LC_MONETARY_MASK,
+ LC_NUMERIC_MASK,
+ LC_TIME_MASK
+ };
/* Check that the 'struct lconv' type is defined. */
struct lconv l;
+/* Check that the 'locale_t' type is defined. */
+locale_t lt;
int ls;
/* Check that NULL can be passed through varargs as a pointer type,
int
main ()
{
-#if HAVE_WORKING_NEWLOCALE
/* Check that the locale_t type and the LC_GLOBAL_LOCALE macro are defined. */
locale_t b = LC_GLOBAL_LOCALE;
(void) b;
-#endif
/* Check that 'struct lconv' has the ISO C and POSIX specified members. */
ls += sizeof (*l.decimal_point);