From: Bruno Haible Date: Thu, 13 Feb 2025 21:16:13 +0000 (+0100) Subject: locale-h: Ensure locale_t type. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6790a26c04f8e59b3476bc319c1ecd21e4120504;p=thirdparty%2Fgnulib.git 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. --- diff --git a/ChangeLog b/ChangeLog index f91cac7ba4..dd083951c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2025-02-13 Bruno Haible + + 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 duplocale: Relicense under LGPLv2+. diff --git a/doc/posix-headers/locale.texi b/doc/posix-headers/locale.texi index 0ba1826832..4a99931c27 100644 --- a/doc/posix-headers/locale.texi +++ b/doc/posix-headers/locale.texi @@ -14,7 +14,7 @@ mingw, MSVC 14. @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: diff --git a/lib/locale.in.h b/lib/locale.in.h index 000f382831..389ff26124 100644 --- a/lib/locale.in.h +++ b/lib/locale.in.h @@ -69,6 +69,85 @@ # 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: + + 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. */ diff --git a/m4/locale_h.m4 b/m4/locale_h.m4 index 486b5a1396..9300062194 100644 --- a/m4/locale_h.m4 +++ b/m4/locale_h.m4 @@ -1,5 +1,5 @@ # 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, @@ -20,6 +20,26 @@ AC_DEFUN_ONCE([gl_LOCALE_H], 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_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 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. @@ -131,6 +151,7 @@ AC_DEFUN([gl_LOCALE_T], fi fi AC_SUBST([HAVE_XLOCALE_H]) + AC_SUBST([HAVE_LOCALE_T]) ]) # gl_LOCALE_MODULE_INDICATOR([modulename]) diff --git a/modules/locale-h b/modules/locale-h index c81bd908d7..5dab6ab42a 100644 --- a/modules/locale-h +++ b/modules/locale-h @@ -13,6 +13,7 @@ snippet/arg-nonnull snippet/c++defs snippet/warn-on-use stddef-h +bool configure.ac: gl_LOCALE_H @@ -32,6 +33,8 @@ locale.h: locale.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_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' \ diff --git a/tests/test-locale-h.c b/tests/test-locale-h.c index ba5bdf0ef1..201cd12525 100644 --- a/tests/test-locale-h.c +++ b/tests/test-locale-h.c @@ -30,9 +30,21 @@ int a[] = 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, @@ -42,11 +54,9 @@ static_assert (sizeof NULL == sizeof (void *)); 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);