Hi,

Running a gnulib testdir on NetBSD, I'm seeing a link error for test-duplocale.
This is due to the fact that the NetBSD people implemented duplocale() without
uselocale().
  https://mail-index.netbsd.org/tech-userlevel/2013/04/23/msg007714.html
Which is pretty senseless, since it forces programs to use functions that take
an explicit locale argument (fprintf_l, strfmon_l, and similar). The point
of standardizing duplocale(), newlocale(), uselocale() was to *eliminate*
the need for these non-standard *_l functions.

And on top of that, in NetBSD 7.0,
  - strfmon_l is not implemented,
  - duplocale(LC_GLOBAL_LOCALE) is broken.

I'm committing these workarounds:
  1) Extend the duplocale unit test so that it does meaningful tests even when
     uselocale() is not defined. Verified to work on glibc and macOS systems.
  2) Extend the duplocale autoconf test to recognize the broken behaviour on
     NetBSD.

@ Jörg Sonnenberger: Find attached a program (netbsd7-bug.c) that exhibits
the bug. Its exit code on NetBSD 7.0 is 2. The correct exit code is 0.

Bruno

>From a71158abf562cd6ed3fe384b0d99cba0de2034c2 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 15 Aug 2017 20:23:00 +0200
Subject: [PATCH 1/2] duplocale tests: Verify use with *_l functions.

* modules/duplocale-tests (configure.ac): Test for uselocale and
some *_l functions.
* tests/test-duplocale.c (test_with_uselocale): New function, extracted
from main.
(get_locale_dependent_values_from, test_with_locale_parameter): New
functions.
(main): Test both test_with_uselocale and test_with_locale_parameter.
---
 ChangeLog               |  11 +++++
 modules/duplocale-tests |   2 +-
 tests/test-duplocale.c  | 111 +++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 117 insertions(+), 7 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index b23b295..5791694 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2017-08-15  Bruno Haible  <br...@clisp.org>
 
+	duplocale tests: Verify use with *_l functions.
+	* modules/duplocale-tests (configure.ac): Test for uselocale and
+	some *_l functions.
+	* tests/test-duplocale.c (test_with_uselocale): New function, extracted
+	from main.
+	(get_locale_dependent_values_from, test_with_locale_parameter): New
+	functions.
+	(main): Test both test_with_uselocale and test_with_locale_parameter.
+
+2017-08-15  Bruno Haible  <br...@clisp.org>
+
 	extensions: Enable NetBSD specific extensions.
 	* m4/extensions.m4 (AC_USE_SYSTEM_EXTENSIONS): Define _NETBSD_SOURCE.
 
diff --git a/modules/duplocale-tests b/modules/duplocale-tests
index 9cd9d44..3420abf 100644
--- a/modules/duplocale-tests
+++ b/modules/duplocale-tests
@@ -7,7 +7,7 @@ Depends-on:
 langinfo
 
 configure.ac:
-AC_CHECK_FUNCS_ONCE([duplocale])
+AC_CHECK_FUNCS_ONCE([duplocale uselocale strfmon_l snprintf_l nl_langinfo_l])
 AC_CHECK_HEADERS([monetary.h])
 
 Makefile.am:
diff --git a/tests/test-duplocale.c b/tests/test-duplocale.c
index 0db1513..ba3ab09 100644
--- a/tests/test-duplocale.c
+++ b/tests/test-duplocale.c
@@ -52,8 +52,10 @@ get_locale_dependent_values (struct locale_dependent_values *result)
   /* result->time is usually "janvier" */
 }
 
-int
-main ()
+#if HAVE_USELOCALE
+
+static int
+test_with_uselocale (void)
 {
   struct locale_dependent_values expected_results;
   locale_t mixed1;
@@ -73,10 +75,7 @@ main ()
   /* Use a per-thread locale.  */
   perthread = newlocale (LC_ALL_MASK, "es_ES.UTF-8", NULL);
   if (perthread == NULL)
-    {
-      fprintf (stderr, "Skipping test: Spanish Unicode locale is not installed\n");
-      return 77;
-    }
+    return 1;
   uselocale (perthread);
 
   /* Save the locale in a locale_t object again.  */
@@ -122,6 +121,106 @@ main ()
   return 0;
 }
 
+#endif
+
+#if HAVE_STRFMON_L || HAVE_SNPRINTF_L || HAVE_NL_LANGINFO_L
+
+static void
+get_locale_dependent_values_from (struct locale_dependent_values *result, locale_t locale)
+{
+#if HAVE_STRFMON_L
+  strfmon_l (result->monetary, sizeof (result->monetary), locale,
+             "%n", 123.75);
+  /* result->monetary is usually "$123.75" */
+#endif
+#if HAVE_SNPRINTF_L
+  snprintf_l (result->numeric, sizeof (result->numeric), locale,
+              "%g", 3.5);
+  /* result->numeric is usually "3,5" */
+#endif
+#if HAVE_NL_LANGINFO_L
+  strcpy (result->time, nl_langinfo_l (MON_1, locale));
+  /* result->time is usually "janvier" */
+#endif
+}
+
+static int
+test_with_locale_parameter (void)
+{
+  struct locale_dependent_values expected_results;
+  locale_t mixed1;
+  locale_t mixed2;
+
+  /* Set up a locale which is a mix between different system locales.  */
+  setlocale (LC_ALL, "en_US.UTF-8");
+  setlocale (LC_NUMERIC, "de_DE.UTF-8");
+  setlocale (LC_TIME, "fr_FR.UTF-8");
+  get_locale_dependent_values (&expected_results);
+
+  /* Save the locale in a locale_t object.  */
+  mixed1 = duplocale (LC_GLOBAL_LOCALE);
+  ASSERT (mixed1 != NULL);
+
+  /* Create another locale_t object.  */
+  mixed2 = newlocale (LC_ALL_MASK, "es_ES.UTF-8", NULL);
+  if (mixed2 == NULL)
+    return 1;
+
+  /* Set up a default locale.  */
+  setlocale (LC_ALL, "C");
+  {
+    struct locale_dependent_values c_results;
+    get_locale_dependent_values (&c_results);
+  }
+
+  /* Now use the saved locale mixed2.  */
+  {
+    struct locale_dependent_values results;
+    get_locale_dependent_values_from (&results, mixed2);
+  }
+
+  /* Now use the saved locale mixed1 again.  */
+  {
+    struct locale_dependent_values results;
+    get_locale_dependent_values_from (&results, mixed1);
+#if HAVE_STRFMON_L
+    ASSERT (strcmp (results.monetary, expected_results.monetary) == 0);
+#endif
+#if HAVE_SNPRINTF_L
+    ASSERT (strcmp (results.numeric, expected_results.numeric) == 0);
+#endif
+#if HAVE_NL_LANGINFO_L
+    ASSERT (strcmp (results.time, expected_results.time) == 0);
+#endif
+  }
+
+  freelocale (mixed1);
+  freelocale (mixed2);
+  return 0;
+}
+
+#endif
+
+int
+main ()
+{
+  int skipped = 0;
+#if HAVE_USELOCALE
+  skipped |= test_with_uselocale ();
+#endif
+#if HAVE_STRFMON_L || HAVE_SNPRINTF_L || HAVE_NL_LANGINFO_L
+  skipped |= test_with_locale_parameter ();
+#endif
+
+  if (skipped)
+    {
+      fprintf (stderr, "Skipping test: Spanish Unicode locale is not installed\n");
+      return 77;
+    }
+
+  return 0;
+}
+
 #else
 
 #include <stdio.h>
-- 
2.7.4

>From a1971db1d4fe4430a48475a751602f861812503e Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 15 Aug 2017 21:18:44 +0200
Subject: [PATCH 2/2] duplocale: Work around NetBSD 7.0 bug.

* m4/duplocale.m4 (gl_FUNC_DUPLOCALE): Test against the NetBSD 7.0 bug.
* lib/duplocale.c: Add comment about NetBSD problem.
* doc/posix-functions/duplocale.texi: Mention the NetBSD problem.
---
 ChangeLog                          |  7 ++++++
 doc/posix-functions/duplocale.texi |  4 ++++
 lib/duplocale.c                    |  4 +++-
 m4/duplocale.m4                    | 49 ++++++++++++++++++++++++++++++++++----
 4 files changed, 59 insertions(+), 5 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 5791694..a9896aa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2017-08-15  Bruno Haible  <br...@clisp.org>
 
+	duplocale: Work around NetBSD 7.0 bug.
+	* m4/duplocale.m4 (gl_FUNC_DUPLOCALE): Test against the NetBSD 7.0 bug.
+	* lib/duplocale.c: Add comment about NetBSD problem.
+	* doc/posix-functions/duplocale.texi: Mention the NetBSD problem.
+
+2017-08-15  Bruno Haible  <br...@clisp.org>
+
 	duplocale tests: Verify use with *_l functions.
 	* modules/duplocale-tests (configure.ac): Test for uselocale and
 	some *_l functions.
diff --git a/doc/posix-functions/duplocale.texi b/doc/posix-functions/duplocale.texi
index 08ac855..876c768 100644
--- a/doc/posix-functions/duplocale.texi
+++ b/doc/posix-functions/duplocale.texi
@@ -11,6 +11,10 @@ Portability problems fixed by Gnulib:
 @item
 The argument @code{LC_GLOBAL_LOCALE} is not supported on some platforms:
 glibc 2.11, AIX 7.1.
+@item
+With the argument @code{LC_GLOBAL_LOCALE}, this function returns a wrong result
+on some platforms:
+NetBSD 7.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
diff --git a/lib/duplocale.c b/lib/duplocale.c
index ac7ec14..9775533 100644
--- a/lib/duplocale.c
+++ b/lib/duplocale.c
@@ -34,7 +34,9 @@ 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>.
      Also, on AIX 7.1, duplocale(LC_GLOBAL_LOCALE) returns (locale_t)0 with
-     errno set to EINVAL.  */
+     errno set to EINVAL.
+     Also, on NetBSD 7.0, duplocale(LC_GLOBAL_LOCALE) returns a locale that
+     corresponds to the C locale.  */
   if (locale == LC_GLOBAL_LOCALE)
     {
       /* Create a copy of the locale by fetching the name of each locale
diff --git a/m4/duplocale.m4 b/m4/duplocale.m4
index b5efd24..4167422 100644
--- a/m4/duplocale.m4
+++ b/m4/duplocale.m4
@@ -1,4 +1,4 @@
-# duplocale.m4 serial 8
+# duplocale.m4 serial 9
 dnl Copyright (C) 2009-2017 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -14,7 +14,10 @@ AC_DEFUN([gl_FUNC_DUPLOCALE],
     dnl See <http://sourceware.org/bugzilla/show_bug.cgi?id=10969>.
     dnl Also, on AIX 7.1, duplocale(LC_GLOBAL_LOCALE) returns (locale_t)0 with
     dnl errno set to EINVAL.
+    dnl Also, on NetBSD 7.0, duplocale(LC_GLOBAL_LOCALE) returns a locale that
+    dnl corresponds to the C locale.
     AC_REQUIRE([gl_LOCALE_H])
+    AC_CHECK_FUNCS_ONCE([snprintf_l nl_langinfo_l])
     AC_CACHE_CHECK([whether duplocale(LC_GLOBAL_LOCALE) works],
       [gl_cv_func_duplocale_works],
       [AC_RUN_IFELSE(
@@ -23,19 +26,57 @@ AC_DEFUN([gl_FUNC_DUPLOCALE],
 #if HAVE_XLOCALE_H
 # include <xlocale.h>
 #endif
+#if HAVE_SNPRINTF_L
+# include <stdio.h>
+#endif
+#if HAVE_NL_LANGINFO_L
+# include <langinfo.h>
+#endif
+#include <string.h>
+struct locale_dependent_values
+{
+  char numeric[100];
+  char time[100];
+};
 int main ()
 {
-  locale_t loc = duplocale (LC_GLOBAL_LOCALE);
+  struct locale_dependent_values expected_result;
+  struct locale_dependent_values result;
+  locale_t loc;
+  setlocale (LC_ALL, "en_US.UTF-8");
+  setlocale (LC_NUMERIC, "de_DE.UTF-8");
+  setlocale (LC_TIME, "fr_FR.UTF-8");
+#if HAVE_SNPRINTF_L
+  snprintf (expected_result.numeric, sizeof (expected_result.numeric), "%g", 3.5);
+#endif
+#if HAVE_NL_LANGINFO_L
+  strcpy (expected_result.time, nl_langinfo (MON_1));
+#endif
+  loc = duplocale (LC_GLOBAL_LOCALE);
   if (!loc)
     return 1;
+#if HAVE_SNPRINTF_L
+  snprintf_l (result.numeric, sizeof (result.numeric), loc, "%g", 3.5);
+#endif
+#if HAVE_NL_LANGINFO_L
+  strcpy (result.time, nl_langinfo_l (MON_1, loc));
+#endif
+#if HAVE_SNPRINTF_L
+  if (strcmp (result.numeric, expected_result.numeric) != 0)
+    return 2;
+#endif
+#if HAVE_NL_LANGINFO_L
+  if (strcmp (result.time, expected_result.time) != 0)
+    return 3;
+#endif
   freelocale (loc);
   return 0;
 }]])],
          [gl_cv_func_duplocale_works=yes],
          [gl_cv_func_duplocale_works=no],
-         [dnl Guess it works except on glibc < 2.12, uClibc, and AIX.
+         [dnl Guess it works except on glibc < 2.12, uClibc, AIX, and NetBSD.
           case "$host_os" in
-            aix*) gl_cv_func_duplocale_works="guessing no";;
+            aix* | netbsd*) gl_cv_func_duplocale_works="guessing no";;
             *-gnu*)
               AC_EGREP_CPP([Unlucky], [
 #include <features.h>
-- 
2.7.4

#include <locale.h>
#include <langinfo.h>
#include <stdio.h>
#include <string.h>

struct locale_dependent_values
{
  char numeric[100];
  char time[100];
};

int
main ()
{
  struct locale_dependent_values expected_results;
  struct locale_dependent_values results;
  locale_t mixed1;

  /* Set up a locale which is a mix between different system locales.  */
  setlocale (LC_ALL, "en_US.UTF-8");
  setlocale (LC_NUMERIC, "de_DE.UTF-8");
  setlocale (LC_TIME, "fr_FR.UTF-8");
  snprintf (expected_result.numeric, sizeof (expected_result.numeric),
            "%g", 3.5);
  /* expected_result.numeric is usually "3,5" */
  strcpy (expected_result.time, nl_langinfo (MON_1));
  /* expected_result.time is usually "janvier" */

  /* Save the locale in a locale_t object.  */
  mixed1 = duplocale (LC_GLOBAL_LOCALE);
  if (mixed1 == NULL)
    return 1;

  /* Now use the saved locale mixed1.  */
  snprintf_l (result.numeric, sizeof (result.numeric), mixed1,
              "%g", 3.5);
  /* result.numeric is usually "3,5" */
  strcpy (result.time, nl_langinfo_l (MON_1, mixed1));
  /* result.time is usually "janvier" */
  if (strcmp (results.numeric, expected_results.numeric) != 0)
    return 2;
  if (strcmp (results.time, expected_results.time) != 0)
    return 3;

  return 0;
}

Reply via email to