]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
locale-h: Ensure locale_t type.
authorBruno Haible <bruno@clisp.org>
Thu, 13 Feb 2025 21:16:13 +0000 (22:16 +0100)
committerBruno Haible <bruno@clisp.org>
Thu, 13 Feb 2025 21:23:33 +0000 (22:23 +0100)
* 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.

ChangeLog
doc/posix-headers/locale.texi
lib/locale.in.h
m4/locale_h.m4
modules/locale-h
tests/test-locale-h.c

index f91cac7ba4837d9d5797771fabc800bb4363e730..dd083951c524416ca4079b76693f4f2a41c3a228 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+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+.
index 0ba18268320842fcd7711bff7e1c60141b9119c6..4a99931c27af59c9912c8f8af55eac331b7085bf 100644 (file)
@@ -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:
index 000f382831a2d94bb06041e4d5aae402c0a9ad3b..389ff26124755afa0c6406b34892b45b435439c9 100644 (file)
 # 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.  */
index 486b5a1396ba2597e5b912ef8e49009bc2228a59..9300062194486a478ab5fd0d03b5996859d6c399 100644 (file)
@@ -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.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.
@@ -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])
index c81bd908d74d99123ca6779b7230d4f48b745843..5dab6ab42ac52c13779c45da553683e6c8ab1caf 100644 (file)
@@ -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' \
index ba5bdf0ef115e0dbe58a7760dcb6411e86ec673b..201cd12525d3dfd5a6f4fe5a2ebac0a16329a166 100644 (file)
@@ -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);