New module 'duplocale'.
authorBruno Haible <bruno@clisp.org>
Mon, 23 Nov 2009 00:46:00 +0000 (01:46 +0100)
committerBruno Haible <bruno@clisp.org>
Mon, 23 Nov 2009 00:48:36 +0000 (01:48 +0100)
ChangeLog
doc/posix-functions/duplocale.texi
lib/duplocale.c [new file with mode: 0644]
lib/locale.in.h
m4/duplocale.m4 [new file with mode: 0644]
m4/locale_h.m4
modules/duplocale [new file with mode: 0644]
modules/locale

index 394c56c..740085b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,20 @@
 2009-11-22  Bruno Haible  <bruno@clisp.org>
 
+       New module 'duplocale'.
+       * m4/duplocale.m4: New file.
+       * lib/locale.in.h (duplocale): New declaration.
+       * lib/duplocale.c: New file.
+       * m4/locale_h.m4 (gl_REPLACE_LOCALE_H, gl_LOCALE_MODULE_INDICATOR,
+       gl_LOCALE_H_DEFAULTS): New macros.
+       (gl_LOCALE_H): Require gl_LOCALE_H_DEFAULTS. Invoke
+       gl_CHECK_NEXT_HEADERS unconditionally. Invoke gl_REPLACE_LOCALE_H.
+       * modules/locale (Makefile.am): Substitute also GNULIB_DUPLOCALE,
+       REPLACE_DUPLOCALE.
+       * modules/duplocale: New file.
+       * doc/posix-functions/duplocale.texi: Mention the glibc bug.
+
+2009-11-22  Bruno Haible  <bruno@clisp.org>
+
        * modules/locale-tests (configure.ac): Test for newlocale function.
        * tests/test-locale.c: When the system has extended locale functions,
        verify that <locale.h> defines locale_t and LC_GLOBAL_LOCALE.
index 6bae698..3abd73b 100644 (file)
@@ -4,10 +4,13 @@
 
 POSIX specification: @url{http://www.opengroup.org/onlinepubs/9699919799/functions/duplocale.html}
 
-Gnulib module: ---
+Gnulib module: duplocale
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+The argument @code{LC_GLOBAL_LOCALE} is not supported on some platforms:
+glibc 2.11.
 @end itemize
 
 Portability problems not fixed by Gnulib:
diff --git a/lib/duplocale.c b/lib/duplocale.c
new file mode 100644 (file)
index 0000000..63d00d9
--- /dev/null
@@ -0,0 +1,105 @@
+/* Duplicate a locale object.
+   Copyright (C) 2009 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 <http://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <locale.h>
+
+#include <errno.h>
+#include <langinfo.h>
+#include <string.h>
+
+/* Work around an incorrect definition of the _NL_LOCALE_NAME macro in
+   glibc < 2.12.
+   See <http://sourceware.org/bugzilla/show_bug.cgi?id=10968>.  */
+#undef _NL_LOCALE_NAME
+#define _NL_LOCALE_NAME(category) _NL_ITEM ((category), _NL_ITEM_INDEX (-1))
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+#undef duplocale
+
+locale_t
+rpl_duplocale (locale_t locale)
+{
+  /* Work around crash in the duplocale function in glibc < 2.12.
+     See <http://sourceware.org/bugzilla/show_bug.cgi?id=10969>.  */
+  if (locale == LC_GLOBAL_LOCALE)
+    {
+      /* Create a copy of the locale by fetching the name of each locale
+        category, starting with LC_CTYPE.  */
+      static struct { int cat; int mask; } categories[] =
+       {
+           { LC_NUMERIC,        LC_NUMERIC_MASK },
+           { LC_TIME,           LC_TIME_MASK },
+           { LC_COLLATE,        LC_COLLATE_MASK },
+           { LC_MONETARY,       LC_MONETARY_MASK },
+           { LC_MESSAGES,       LC_MESSAGES_MASK }
+#ifdef LC_PAPER
+         , { LC_PAPER,          LC_PAPER_MASK }
+#endif
+#ifdef LC_NAME
+         , { LC_NAME,           LC_NAME_MASK }
+#endif
+#ifdef LC_ADDRESS
+         , { LC_ADDRESS,        LC_ADDRESS_MASK }
+#endif
+#ifdef LC_TELEPHONE
+         , { LC_TELEPHONE,      LC_TELEPHONE_MASK }
+#endif
+#ifdef LC_MEASUREMENT
+         , { LC_MEASUREMENT,    LC_MEASUREMENT_MASK }
+#endif
+#ifdef LC_IDENTIFICATION
+         , { LC_IDENTIFICATION, LC_IDENTIFICATION_MASK }
+#endif
+       };
+      const char *base_name = nl_langinfo (_NL_LOCALE_NAME (LC_CTYPE));
+      locale_t base_copy = newlocale (LC_ALL_MASK, base_name, NULL);
+      unsigned int i;
+
+      if (base_copy == NULL)
+       return NULL;
+
+      for (i = 0; i < SIZEOF (categories); i++)
+       {
+         int category = categories[i].cat;
+         int category_mask = categories[i].mask;
+         const char *name = nl_langinfo (_NL_LOCALE_NAME (category));
+         if (strcmp (name, base_name) != 0)
+           {
+             locale_t copy = newlocale (category_mask, name, base_copy);
+             if (copy == NULL)
+               {
+                 int saved_errno = errno;
+                 freelocale (base_copy);
+                 errno = saved_errno;
+                 return NULL;
+               }
+             /* No need to call freelocale (base_copy) if copy != base_copy;
+                the newlocale function already takes care of doing it.  */
+             base_copy = copy;
+           }
+       }
+
+      return base_copy;
+    }
+
+  return duplocale (locale);
+}
index 49835c5..08d0574 100644 (file)
 # define LC_MESSAGES 1729
 #endif
 
+#if @GNULIB_DUPLOCALE@
+# if @REPLACE_DUPLOCALE@
+#  undef duplocale
+#  define duplocale rpl_duplocale
+extern locale_t duplocale (locale_t locale);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef duplocale
+# define duplocale(l) \
+   (GL_LINK_WARNING ("duplocale is buggy on some glibc systems - " \
+                     "use gnulib module duplocale for portability"), \
+    duplocale (l))
+#endif
+
 #endif /* _GL_LOCALE_H */
 #endif /* _GL_LOCALE_H */
diff --git a/m4/duplocale.m4 b/m4/duplocale.m4
new file mode 100644 (file)
index 0000000..7e0a071
--- /dev/null
@@ -0,0 +1,56 @@
+# duplocale.m4 serial 1
+dnl Copyright (C) 2009 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.
+
+AC_DEFUN([gl_FUNC_DUPLOCALE],
+[
+  AC_REQUIRE([gl_LOCALE_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  AC_CHECK_FUNCS_ONCE([duplocale])
+  if test $ac_cv_func_duplocale = yes; then
+    dnl Check against glibc bug where duplocale crashes.
+    dnl See <http://sourceware.org/bugzilla/show_bug.cgi?id=10969>.
+    AC_REQUIRE([gl_LOCALE_H])
+    AC_CACHE_CHECK([whether duplocale(LC_GLOBAL_LOCALE) works],
+      [gl_cv_func_duplocale_works],
+      [AC_TRY_RUN([
+#include <locale.h>
+#if HAVE_XLOCALE_H
+# include <xlocale.h>
+#endif
+int main ()
+{
+  (void) duplocale (LC_GLOBAL_LOCALE);
+  return 0;
+}], [gl_cv_func_duplocale_works=yes], [gl_cv_func_duplocale_works=no],
+         [dnl Guess it works except on glibc < 2.12.
+          AC_EGREP_CPP([Unlucky GNU user], [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ == 2 && __GLIBC_MINOR__ < 12)
+  Unlucky GNU user
+ #endif
+#endif
+            ],
+            [gl_cv_func_duplocale_works="guessing no"],
+            [gl_cv_func_duplocale_works="guessing yes"])
+         ])
+      ])
+    case "$gl_cv_func_duplocale_works" in
+      *no) REPLACE_DUPLOCALE=1 ;;
+    esac
+  fi
+  if test $REPLACE_DUPLOCALE = 1; then
+    gl_REPLACE_LOCALE_H
+    AC_LIBOBJ([duplocale])
+    gl_PREREQ_DUPLOCALE
+  fi
+])
+
+# Prerequisites of lib/duplocale.c.
+AC_DEFUN([gl_PREREQ_DUPLOCALE],
+[
+  :
+])
index 913a200..35b8b32 100644 (file)
@@ -1,4 +1,4 @@
-# locale_h.m4 serial 4
+# locale_h.m4 serial 5
 dnl Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -6,6 +6,10 @@ dnl with or without modifications, as long as this notice is preserved.
 
 AC_DEFUN([gl_LOCALE_H],
 [
+  dnl Use AC_REQUIRE here, so that the default behavior below is expanded
+  dnl once only, before all statements that occur in other macros.
+  AC_REQUIRE([gl_LOCALE_H_DEFAULTS])
+
   dnl Persuade glibc <locale.h> to define locale_t.
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
 
@@ -44,13 +48,35 @@ locale_t x;], [],
   fi
   AC_SUBST([HAVE_XLOCALE_H])
 
-  if test -z "$STDDEF_H" \
-     && test $gl_cv_header_locale_h_posix2001 = yes \
-     && test $gl_cv_header_locale_h_needs_xlocale_h = no; then
-    LOCALE_H=
-  else
-    gl_CHECK_NEXT_HEADERS([locale.h])
-    LOCALE_H=locale.h
+  dnl Execute this unconditionally, because LOCALE_H may be set by other
+  dnl modules, after this code is executed.
+  gl_CHECK_NEXT_HEADERS([locale.h])
+
+  if test -n "$STDDEF_H" \
+     || test $gl_cv_header_locale_h_posix2001 = no \
+     || test $gl_cv_header_locale_h_needs_xlocale_h = yes; then
+    gl_REPLACE_LOCALE_H
   fi
-  AC_SUBST([LOCALE_H])
+])
+
+dnl Unconditionally enables the replacement of <locale.h>.
+AC_DEFUN([gl_REPLACE_LOCALE_H],
+[
+  AC_REQUIRE([gl_LOCALE_H_DEFAULTS])
+  LOCALE_H=locale.h
+])
+
+AC_DEFUN([gl_LOCALE_MODULE_INDICATOR],
+[
+  dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
+  AC_REQUIRE([gl_LOCALE_H_DEFAULTS])
+  GNULIB_[]m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./-],[ABCDEFGHIJKLMNOPQRSTUVWXYZ___])=1
+])
+
+AC_DEFUN([gl_LOCALE_H_DEFAULTS],
+[
+  GNULIB_DUPLOCALE=0;  AC_SUBST([GNULIB_DUPLOCALE])
+  dnl Assume proper GNU behavior unless another module says otherwise.
+  REPLACE_DUPLOCALE=0; AC_SUBST([REPLACE_DUPLOCALE])
+  LOCALE_H='';         AC_SUBST([LOCALE_H])
 ])
diff --git a/modules/duplocale b/modules/duplocale
new file mode 100644 (file)
index 0000000..00852fd
--- /dev/null
@@ -0,0 +1,24 @@
+Description:
+duplocale() function: duplicate a locale object.
+
+Files:
+lib/duplocale.c
+m4/duplocale.m4
+
+Depends-on:
+locale
+
+configure.ac:
+gl_FUNC_DUPLOCALE
+gl_LOCALE_MODULE_INDICATOR([duplocale])
+
+Makefile.am:
+
+Include:
+<locale.h>
+
+License:
+LGPL
+
+Maintainer:
+Bruno Haible
index 953237d..8b4b3b6 100644 (file)
@@ -24,7 +24,9 @@ locale.h: locale.in.h
          sed -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
              -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
              -e 's|@''NEXT_LOCALE_H''@|$(NEXT_LOCALE_H)|g' \
+             -e 's|@''GNULIB_DUPLOCALE''@|$(GNULIB_DUPLOCALE)|g' \
              -e 's|@''HAVE_XLOCALE_H''@|$(HAVE_XLOCALE_H)|g' \
+             -e 's|@''REPLACE_DUPLOCALE''@|$(REPLACE_DUPLOCALE)|g' \
              < $(srcdir)/locale.in.h; \
        } > $@-t && \
        mv $@-t $@