> Thanks, this change looks fine to me.

Thanks. But before I do the rewrite, let me add a unit test first.

It's a bit tricky only on musl libc, NetBSD, and OpenBSD. Other
than that, there's a test failure on Android 4.3, where
  - setlocale (category, NULL) always returns NULL.
  - hard_locale of the initial locale returns true, but should be false.


2019-12-18  Bruno Haible  <br...@clisp.org>

        hard-locale: Add test.
        * tests/test-hard-locale.c: New file.
        * tests/locale.c: New file.
        * modules/hard-locale-tests: New file.

>From 2d1b7501b00dce53481b58b3808313f264c720ea Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Wed, 18 Dec 2019 09:41:31 +0100
Subject: [PATCH] hard-locale: Add test.

* tests/test-hard-locale.c: New file.
* tests/locale.c: New file.
* modules/hard-locale-tests: New file.
---
 ChangeLog                 |   7 +++
 modules/hard-locale-tests |  18 ++++++++
 tests/locale.c            |  81 ++++++++++++++++++++++++++++++++++
 tests/test-hard-locale.c  | 109 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 215 insertions(+)
 create mode 100644 modules/hard-locale-tests
 create mode 100644 tests/locale.c
 create mode 100644 tests/test-hard-locale.c

diff --git a/ChangeLog b/ChangeLog
index 7988bec..2fffaf2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2019-12-18  Bruno Haible  <br...@clisp.org>
+
+	hard-locale: Add test.
+	* tests/test-hard-locale.c: New file.
+	* tests/locale.c: New file.
+	* modules/hard-locale-tests: New file.
+
 2019-12-17  Paul Eggert  <egg...@cs.ucla.edu>
 
 	dfa: do not match invalid UTF-8
diff --git a/modules/hard-locale-tests b/modules/hard-locale-tests
new file mode 100644
index 0000000..cdc084e
--- /dev/null
+++ b/modules/hard-locale-tests
@@ -0,0 +1,18 @@
+Files:
+tests/test-hard-locale.c
+tests/locale.c
+
+Depends-on:
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+case "$host_os" in
+  *-musl*) AC_DEFINE([MUSL_LIBC], [1], [Define to 1 on musl libc.]) ;;
+esac
+dnl Distinguish OpenBSD >= 6.2 from OpenBSD < 6.2.
+AC_CHECK_FUNCS_ONCE([duplocale])
+
+Makefile.am:
+TESTS += test-hard-locale
+check_PROGRAMS += test-hard-locale
+noinst_PROGRAMS += locale
diff --git a/tests/locale.c b/tests/locale.c
new file mode 100644
index 0000000..ac587ad
--- /dev/null
+++ b/tests/locale.c
@@ -0,0 +1,81 @@
+/* Program that prints the names of the categories of the current locale.
+   Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <br...@clisp.org>, 2019.  */
+
+#include <config.h>
+
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Specification:
+   <https://pubs.opengroup.org/onlinepubs/9699919799/utilities/locale.html>
+   Here we implement only the invocation without any command-line options.  */
+
+static const char *
+defaulted_getenv (const char *variable)
+{
+  const char *value = getenv (variable);
+  return (value != NULL ? value : "");
+}
+
+static void
+print_category (int category, const char *variable)
+{
+  const char *value = defaulted_getenv (variable);
+  if (value[0] != '\0' && defaulted_getenv ("LC_ALL")[0] == '\0')
+    /* The variable is set in the environment and not overridden by LC_ALL.  */
+    printf ("%s=%s\n", variable, value);
+  else
+    printf ("%s=\"%s\"\n", variable, setlocale (category, NULL));
+}
+
+int
+main ()
+{
+  setlocale (LC_ALL, "");
+
+  printf ("LANG=%s\n", defaulted_getenv ("LANG"));
+  print_category (LC_CTYPE, "LC_CTYPE");
+  print_category (LC_NUMERIC, "LC_NUMERIC");
+  print_category (LC_TIME, "LC_TIME");
+  print_category (LC_COLLATE, "LC_COLLATE");
+  print_category (LC_MONETARY, "LC_MONETARY");
+  print_category (LC_MESSAGES, "LC_MESSAGES");
+#ifdef LC_PAPER
+  print_category (LC_PAPER, "LC_PAPER");
+#endif
+#ifdef LC_NAME
+  print_category (LC_NAME, "LC_NAME");
+#endif
+#ifdef LC_ADDRESS
+  print_category (LC_ADDRESS, "LC_ADDRESS");
+#endif
+#ifdef LC_TELEPHONE
+  print_category (LC_TELEPHONE, "LC_TELEPHONE");
+#endif
+#ifdef LC_MEASUREMENT
+  print_category (LC_MEASUREMENT, "LC_MEASUREMENT");
+#endif
+#ifdef LC_IDENTIFICATION
+  print_category (LC_IDENTIFICATION, "LC_IDENTIFICATION");
+#endif
+
+  printf ("LC_ALL=%s\n", defaulted_getenv ("LC_ALL"));
+
+  return 0;
+}
diff --git a/tests/test-hard-locale.c b/tests/test-hard-locale.c
new file mode 100644
index 0000000..f83dc27
--- /dev/null
+++ b/tests/test-hard-locale.c
@@ -0,0 +1,109 @@
+/* Test of determination whether a locale is different from the "C" locale.
+   Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <br...@clisp.org>, 2019.  */
+
+#include <config.h>
+
+#include "hard-locale.h"
+
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+/* True if all locale names are accepted and all locales are trivial.
+   This is the case e.g. on OpenBSD 3.8.  */
+static bool all_trivial;
+
+static int
+test_one (const char *name, int failure_bitmask)
+{
+  if (setlocale (LC_ALL, name) != NULL)
+    {
+      bool expected;
+
+      /* musl libc has special code for the C.UTF-8 locale; other than that,
+         all locale names are accepted and all locales are trivial.
+         OpenBSD returns the locale name that was set, but we don't know how it
+         behaves under the hood.  */
+#if defined MUSL_LIBC || defined __OpenBSD__
+      expected = true;
+#else
+      expected = !all_trivial;
+#endif
+      if (hard_locale (LC_CTYPE) != expected)
+        {
+          if (expected)
+            fprintf (stderr, "Unexpected: The category LC_CTYPE of the locale '%s' is not equivalent to C or POSIX.\n",
+                     name);
+          else
+            fprintf (stderr, "Unexpected: The category LC_CTYPE of the locale '%s' is equivalent to C or POSIX.\n",
+                     name);
+          return failure_bitmask;
+        }
+
+      /* On NetBSD 7.0, some locales such as de_DE.ISO8859-1 and de_DE.UTF-8
+         have the LC_COLLATE category set to "C".
+         Similarly, on musl libc, with the C.UTF-8 locale.  */
+#if defined __NetBSD__
+      expected = false;
+#elif defined MUSL_LIBC
+      expected = strcmp (name, "C.UTF-8") != 0;
+#elif defined __OpenBSD__ && HAVE_DUPLOCALE /* OpenBSD >= 6.2 */
+      expected = true;
+#else
+      expected = !all_trivial;
+#endif
+      if (hard_locale (LC_COLLATE) != expected)
+        {
+          if (expected)
+            fprintf (stderr, "Unexpected: The category LC_COLLATE of the locale '%s' is not equivalent to C or POSIX.\n",
+                     name);
+          else
+            fprintf (stderr, "Unexpected: The category LC_COLLATE of the locale '%s' is equivalent to C or POSIX.\n",
+                     name);
+          return failure_bitmask;
+        }
+    }
+  return 0;
+}
+
+int
+main ()
+{
+  int fail = 0;
+
+  /* The initial locale is the "C" or "POSIX" locale.  */
+  if (hard_locale (LC_CTYPE) || hard_locale (LC_COLLATE))
+    {
+      fprintf (stderr, "The initial locale should not be hard!\n");
+      fail |= 1;
+    }
+
+  all_trivial = (setlocale (LC_ALL, "foobar") != NULL);
+
+  fail |= test_one ("de", 2);
+  fail |= test_one ("de_DE", 4);
+  fail |= test_one ("de_DE.ISO8859-1", 8);
+  fail |= test_one ("de_DE.iso88591", 8);
+  fail |= test_one ("de_DE.UTF-8", 16);
+  fail |= test_one ("de_DE.utf8", 16);
+  fail |= test_one ("german", 32);
+  fail |= test_one ("C.UTF-8", 64);
+
+  return fail;
+}
-- 
2.7.4

Reply via email to