I don't believe it. I just don't believe it.
Solaris, with its fame of being seminal regarding internationalization,
ships a buggy strcasecmp() function. The bug is reproducible in Solaris 10,
Solaris 11.4, Solaris 11 OpenIndiana, Solaris 11 OmniOS.
========================= foo.c ========================
#include <stdio.h>
#include <ctype.h>
#include <locale.h>
#include <strings.h>
int main ()
{
if (setlocale (LC_ALL, "fr_FR.ISO8859-1") == NULL)
return 1;
int c1 = (unsigned char) '\311';
int c2 = (unsigned char) '\351';
printf ("0x%02X -> 0x%02X, 0x%02X\n", c1, tolower (c1), toupper (c1));
printf ("0x%02X -> 0x%02X, 0x%02X\n", c2, tolower (c2), toupper (c2));
printf ("strcasecmp -> %d\n", strcasecmp ("Fej\311r", "Fej\351r"));
printf ("strncasecmp -> %d\n", strncasecmp ("Fej\311r", "Fej\351r", 5));
}
=======================================================================
$ gcc -Wall foo.c
$ ./a
Expected output:
0xC9 -> 0xE9, 0xC9
0xE9 -> 0xE9, 0xC9
strcasecmp -> 0
strncasecmp -> 0
Actual output:
0xC9 -> 0xE9, 0xC9
0xE9 -> 0xE9, 0xC9
strcasecmp -> -32
strncasecmp -> -32
I'm committing these workarounds.
2025-02-16 Bruno Haible <[email protected]>
strncasecmp: Add tests.
* tests/test-strncasecmp-1.sh: New file.
* tests/test-strncasecmp-2.sh: New file.
* tests/test-strncasecmp.c: New file.
* modules/strncasecmp-tests: New file.
strncasecmp: Work around Solaris, Cygwin bug.
* lib/strings.in.h (strncasecmp): Consider REPLACE_STRNCASECMP. Use the
usual idioms.
* m4/strings_h.m4 (gl_STRINGS_H_DEFAULTS): Initialize HAVE_STRNCASECMP,
REPLACE_STRNCASECMP.
* m4/strncasecmp.m4 (gl_FUNC_STRNCASECMP): Invoke gl_STRNCASECMP_WORKS.
Set REPLACE_STRNCASECMP. Assume that HAVE_STRNCASECMP is initialized.
* modules/strncasecmp (Files): Add m4/strcasecmp.m4.
(configure.ac): Consider REPLACE_STRNCASECMP.
* modules/strings-h (Makefile.am): Substitute HAVE_STRNCASECMP,
REPLACE_STRNCASECMP.
* doc/posix-functions/strncasecmp.texi: Mention the Solaris, Cygwin bug.
2025-02-16 Bruno Haible <[email protected]>
strcasecmp: Add tests.
* tests/test-strcasecmp-1.sh: New file.
* tests/test-strcasecmp-2.sh: New file.
* tests/test-strcasecmp.c: New file.
* modules/strcasecmp-tests: New file.
strcasecmp: Work around Solaris, Cygwin bug.
* lib/strings.in.h (strcasecmp): Consider REPLACE_STRCASECMP. Use the
usual idioms.
* m4/strings_h.m4 (gl_STRINGS_H_DEFAULTS): Initialize
REPLACE_STRCASECMP.
* m4/strcasecmp.m4 (gl_STRCASECMP_WORKS): New macro.
(gl_FUNC_STRCASECMP): Invoke it. Set REPLACE_STRCASECMP.
* modules/strcasecmp (configure.ac): Consider REPLACE_STRCASECMP.
* modules/strings-h (Makefile.am): Substitute REPLACE_STRCASECMP.
* doc/posix-functions/strcasecmp.texi: Mention the Solaris, Cygwin bug.
2025-02-16 Bruno Haible <[email protected]>
strcasecmp, strncasecmp: Fix header reference.
* lib/strcasecmp.c: Include <strings.h>, not <string.h>.
* lib/strncasecmp.c: Likewise.
>From 6abed08f2cffaba927c105fc931a68984d2a84a6 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Sun, 16 Feb 2025 14:12:26 +0100
Subject: [PATCH 1/5] strcasecmp, strncasecmp: Fix header reference.
* lib/strcasecmp.c: Include <strings.h>, not <string.h>.
* lib/strncasecmp.c: Likewise.
---
ChangeLog | 6 ++++++
lib/strcasecmp.c | 2 +-
lib/strncasecmp.c | 2 +-
3 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 549db282db..9669e2210d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2025-02-16 Bruno Haible <[email protected]>
+
+ strcasecmp, strncasecmp: Fix header reference.
+ * lib/strcasecmp.c: Include <strings.h>, not <string.h>.
+ * lib/strncasecmp.c: Likewise.
+
2025-02-16 Bruno Haible <[email protected]>
realloc: Fix link error in C++ mode on CentOS 5 (regression 2024-11-04).
diff --git a/lib/strcasecmp.c b/lib/strcasecmp.c
index fe21a6d5b3..16626d4d09 100644
--- a/lib/strcasecmp.c
+++ b/lib/strcasecmp.c
@@ -17,7 +17,7 @@
#include <config.h>
/* Specification. */
-#include <string.h>
+#include <strings.h>
#include <ctype.h>
#include <limits.h>
diff --git a/lib/strncasecmp.c b/lib/strncasecmp.c
index 7a0115d050..7d7c5b7f04 100644
--- a/lib/strncasecmp.c
+++ b/lib/strncasecmp.c
@@ -17,7 +17,7 @@
#include <config.h>
/* Specification. */
-#include <string.h>
+#include <strings.h>
#include <ctype.h>
#include <limits.h>
--
2.43.0
>From 9980b9e526d3436524fb148fc8ed8ebbd15e76dc Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Sun, 16 Feb 2025 17:59:48 +0100
Subject: [PATCH 2/5] strcasecmp: Work around Solaris, Cygwin bug.
* lib/strings.in.h (strcasecmp): Consider REPLACE_STRCASECMP. Use the
usual idioms.
* m4/strings_h.m4 (gl_STRINGS_H_DEFAULTS): Initialize
REPLACE_STRCASECMP.
* m4/strcasecmp.m4 (gl_STRCASECMP_WORKS): New macro.
(gl_FUNC_STRCASECMP): Invoke it. Set REPLACE_STRCASECMP.
* modules/strcasecmp (configure.ac): Consider REPLACE_STRCASECMP.
* modules/strings-h (Makefile.am): Substitute REPLACE_STRCASECMP.
* doc/posix-functions/strcasecmp.texi: Mention the Solaris, Cygwin bug.
---
ChangeLog | 13 ++++++++
doc/posix-functions/strcasecmp.texi | 5 +++
lib/strings.in.h | 22 ++++++++++---
m4/strcasecmp.m4 | 50 +++++++++++++++++++++++++++--
m4/strings_h.m4 | 3 +-
modules/strcasecmp | 3 +-
modules/strings-h | 1 +
7 files changed, 89 insertions(+), 8 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 9669e2210d..bf5e91746b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2025-02-16 Bruno Haible <[email protected]>
+
+ strcasecmp: Work around Solaris, Cygwin bug.
+ * lib/strings.in.h (strcasecmp): Consider REPLACE_STRCASECMP. Use the
+ usual idioms.
+ * m4/strings_h.m4 (gl_STRINGS_H_DEFAULTS): Initialize
+ REPLACE_STRCASECMP.
+ * m4/strcasecmp.m4 (gl_STRCASECMP_WORKS): New macro.
+ (gl_FUNC_STRCASECMP): Invoke it. Set REPLACE_STRCASECMP.
+ * modules/strcasecmp (configure.ac): Consider REPLACE_STRCASECMP.
+ * modules/strings-h (Makefile.am): Substitute REPLACE_STRCASECMP.
+ * doc/posix-functions/strcasecmp.texi: Mention the Solaris, Cygwin bug.
+
2025-02-16 Bruno Haible <[email protected]>
strcasecmp, strncasecmp: Fix header reference.
diff --git a/doc/posix-functions/strcasecmp.texi b/doc/posix-functions/strcasecmp.texi
index af640589d9..8c07af89e9 100644
--- a/doc/posix-functions/strcasecmp.texi
+++ b/doc/posix-functions/strcasecmp.texi
@@ -12,6 +12,11 @@
@item
This function is missing on some platforms:
MSVC 14.
+@item
+This function uses the case mappings of a wrong locale on some platforms:
+Solaris 11.4,
+@c https://sourceware.org/pipermail/cygwin/2025-February/257347.html
+Cygwin 3.5.6.
@end itemize
Portability problems not fixed by Gnulib:
diff --git a/lib/strings.in.h b/lib/strings.in.h
index cad01dc34b..b356115181 100644
--- a/lib/strings.in.h
+++ b/lib/strings.in.h
@@ -77,9 +77,23 @@ _GL_WARN_ON_USE (ffs, "ffs is not portable - use the ffs module");
greater than zero if S1 is lexicographically less than, equal to or greater
than S2.
Note: This function does not work in multibyte locales. */
-# if ! @HAVE_STRCASECMP@
-extern int strcasecmp (char const *s1, char const *s2)
- _GL_ARG_NONNULL ((1, 2));
+# if @REPLACE_STRCASECMP@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef strcasecmp
+# define strcasecmp rpl_strcasecmp
+# endif
+_GL_FUNCDECL_RPL (strcasecmp, int, (const char *, const char *),
+ _GL_ARG_NONNULL ((1, 2)));
+_GL_CXXALIAS_RPL (strcasecmp, int, (const char *, const char *));
+# else
+# if !@HAVE_STRCASECMP@
+_GL_FUNCDECL_SYS (strcasecmp, int, (const char *, const char *),
+ _GL_ARG_NONNULL ((1, 2)));
+# endif
+_GL_CXXALIAS_SYS (strcasecmp, int, (const char *, const char *));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (strcasecmp);
# endif
#elif defined GNULIB_POSIXCHECK
/* strcasecmp() does not work with multibyte strings:
@@ -88,7 +102,7 @@ extern int strcasecmp (char const *s1, char const *s2)
# undef strcasecmp
# if HAVE_RAW_DECL_STRCASECMP
_GL_WARN_ON_USE (strcasecmp, "strcasecmp cannot work correctly on character "
- "strings in multibyte locales - "
+ "strings in multibyte locales and is unportable - "
"use mbscasecmp if you care about "
"internationalization, or use c_strcasecmp "
"(gnulib module c-strcasecmp) if you want a locale "
diff --git a/m4/strcasecmp.m4 b/m4/strcasecmp.m4
index 0bbcc5ebe2..e40ee5d14a 100644
--- a/m4/strcasecmp.m4
+++ b/m4/strcasecmp.m4
@@ -1,5 +1,5 @@
# strcasecmp.m4
-# serial 1
+# serial 2
dnl Copyright (C) 2002-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,
@@ -10,11 +10,57 @@ AC_DEFUN([gl_FUNC_STRCASECMP]
[
AC_REQUIRE([gl_STRINGS_H_DEFAULTS])
AC_CHECK_FUNCS([strcasecmp])
- if test $ac_cv_func_strcasecmp = no; then
+ if test $ac_cv_func_strcasecmp = yes; then
+ gl_STRCASECMP_WORKS
+ case "$gl_cv_func_strcasecmp_works" in
+ *yes) ;;
+ *) REPLACE_STRCASECMP=1 ;;
+ esac
+ else
HAVE_STRCASECMP=0
fi
])
+AC_DEFUN([gl_STRCASECMP_WORKS],
+[
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ AC_CACHE_CHECK([whether strcasecmp works],
+ [gl_cv_func_strcasecmp_works],
+ [dnl Prepare a guess, used when cross-compiling or when specific locales
+ dnl are not available.
+ case "$host_os" in
+ solaris* | cygwin*)
+ gl_cv_func_strcasecmp_works="guessing no" ;;
+ *)
+ gl_cv_func_strcasecmp_works="guessing yes" ;;
+ esac
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <ctype.h>
+#include <locale.h>
+#include <strings.h>
+int main ()
+{
+ if (setlocale (LC_ALL, "fr_FR.ISO-8859-1") != NULL
+ || setlocale (LC_ALL, "fr_FR.ISO8859-1") != NULL)
+ {
+ int c1 = (unsigned char) '\311';
+ int c2 = (unsigned char) '\351';
+ if (tolower (c1) == c2 && toupper (c2) == c1)
+ return strcasecmp ("Fej\311r", "Fej\351r") != 0;
+ }
+ return 2;
+}]])],
+ [gl_cv_func_strcasecmp_works=yes],
+ [if test $? = 1; then
+ gl_cv_func_strcasecmp_works=no
+ fi
+ ],
+ [])
+ ])
+])
+
# Prerequisites of lib/strcasecmp.c.
AC_DEFUN([gl_PREREQ_STRCASECMP], [
:
diff --git a/m4/strings_h.m4 b/m4/strings_h.m4
index 25596aa445..b88e1105fb 100644
--- a/m4/strings_h.m4
+++ b/m4/strings_h.m4
@@ -1,5 +1,5 @@
# strings_h.m4
-# serial 10
+# serial 11
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,
@@ -63,4 +63,5 @@ AC_DEFUN([gl_STRINGS_H_DEFAULTS]
HAVE_FFS=1; AC_SUBST([HAVE_FFS])
HAVE_STRCASECMP=1; AC_SUBST([HAVE_STRCASECMP])
HAVE_DECL_STRNCASECMP=1; AC_SUBST([HAVE_DECL_STRNCASECMP])
+ REPLACE_STRCASECMP=1; AC_SUBST([REPLACE_STRCASECMP])
])
diff --git a/modules/strcasecmp b/modules/strcasecmp
index b226990b2a..34d3050374 100644
--- a/modules/strcasecmp
+++ b/modules/strcasecmp
@@ -10,7 +10,8 @@ strings-h
configure.ac:
gl_FUNC_STRCASECMP
-gl_CONDITIONAL([GL_COND_OBJ_STRCASECMP], [test $HAVE_STRCASECMP = 0])
+gl_CONDITIONAL([GL_COND_OBJ_STRCASECMP],
+ [test $HAVE_STRCASECMP = 0 || test $REPLACE_STRCASECMP = 1])
AM_COND_IF([GL_COND_OBJ_STRCASECMP], [
gl_PREREQ_STRCASECMP
])
diff --git a/modules/strings-h b/modules/strings-h
index 7b3cc6ad44..5c6c6736c2 100644
--- a/modules/strings-h
+++ b/modules/strings-h
@@ -38,6 +38,7 @@ strings.h: strings.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(WARN_ON_USE
-e 's|@''HAVE_FFS''@|$(HAVE_FFS)|g' \
-e 's|@''HAVE_STRCASECMP''@|$(HAVE_STRCASECMP)|g' \
-e 's|@''HAVE_DECL_STRNCASECMP''@|$(HAVE_DECL_STRNCASECMP)|g' \
+ -e 's|@''REPLACE_STRCASECMP''@|$(REPLACE_STRCASECMP)|g' \
-e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
-e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
-e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
--
2.43.0
>From fc6bec11ffd3761d87e1e4b0c21697a3c6771694 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Sun, 16 Feb 2025 18:02:00 +0100
Subject: [PATCH 3/5] strcasecmp: Add tests.
* tests/test-strcasecmp-1.sh: New file.
* tests/test-strcasecmp-2.sh: New file.
* tests/test-strcasecmp.c: New file.
* modules/strcasecmp-tests: New file.
---
ChangeLog | 6 ++++
modules/strcasecmp-tests | 21 +++++++++++
tests/test-strcasecmp-1.sh | 9 +++++
tests/test-strcasecmp-2.sh | 15 ++++++++
tests/test-strcasecmp.c | 73 ++++++++++++++++++++++++++++++++++++++
5 files changed, 124 insertions(+)
create mode 100644 modules/strcasecmp-tests
create mode 100755 tests/test-strcasecmp-1.sh
create mode 100755 tests/test-strcasecmp-2.sh
create mode 100644 tests/test-strcasecmp.c
diff --git a/ChangeLog b/ChangeLog
index bf5e91746b..052ec74b9d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
2025-02-16 Bruno Haible <[email protected]>
+ strcasecmp: Add tests.
+ * tests/test-strcasecmp-1.sh: New file.
+ * tests/test-strcasecmp-2.sh: New file.
+ * tests/test-strcasecmp.c: New file.
+ * modules/strcasecmp-tests: New file.
+
strcasecmp: Work around Solaris, Cygwin bug.
* lib/strings.in.h (strcasecmp): Consider REPLACE_STRCASECMP. Use the
usual idioms.
diff --git a/modules/strcasecmp-tests b/modules/strcasecmp-tests
new file mode 100644
index 0000000000..aeff0381a0
--- /dev/null
+++ b/modules/strcasecmp-tests
@@ -0,0 +1,21 @@
+Files:
+tests/test-strcasecmp-1.sh
+tests/test-strcasecmp-2.sh
+tests/test-strcasecmp.c
+tests/signature.h
+tests/macros.h
+m4/locale-fr.m4
+m4/codeset.m4
+
+Depends-on:
+setlocale
+
+configure.ac:
+gt_LOCALE_FR
+
+Makefile.am:
+TESTS += test-strcasecmp-1.sh test-strcasecmp-2.sh
+TESTS_ENVIRONMENT += \
+ LOCALE_FR='@LOCALE_FR@'
+check_PROGRAMS += test-strcasecmp
+test_strcasecmp_LDADD = $(LDADD) $(SETLOCALE_LIB)
diff --git a/tests/test-strcasecmp-1.sh b/tests/test-strcasecmp-1.sh
new file mode 100755
index 0000000000..1a8ad80ac7
--- /dev/null
+++ b/tests/test-strcasecmp-1.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# Test in the POSIX locale.
+LC_ALL=C \
+${CHECKER} ./test-strcasecmp${EXEEXT} 1 || exit 1
+LC_ALL=POSIX \
+${CHECKER} ./test-strcasecmp${EXEEXT} 1 || exit 1
+
+exit 0
diff --git a/tests/test-strcasecmp-2.sh b/tests/test-strcasecmp-2.sh
new file mode 100755
index 0000000000..b590a7a069
--- /dev/null
+++ b/tests/test-strcasecmp-2.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# Test in an ISO-8859-1 or ISO-8859-15 locale.
+: "${LOCALE_FR=fr_FR}"
+if test $LOCALE_FR = none; then
+ if test -f /usr/bin/localedef; then
+ echo "Skipping test: no traditional french locale is installed"
+ else
+ echo "Skipping test: no traditional french locale is supported"
+ fi
+ exit 77
+fi
+
+LC_ALL=$LOCALE_FR \
+${CHECKER} ./test-strcasecmp${EXEEXT} 2
diff --git a/tests/test-strcasecmp.c b/tests/test-strcasecmp.c
new file mode 100644
index 0000000000..0be6ffaecb
--- /dev/null
+++ b/tests/test-strcasecmp.c
@@ -0,0 +1,73 @@
+/* Test of strcasecmp() function.
+ Copyright (C) 2008-2025 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/>. */
+
+#include <config.h>
+
+#include <strings.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (strcasecmp, int, (const char *, const char *));
+
+#include <locale.h>
+#include <stdlib.h>
+
+#include "macros.h"
+
+int
+main (int argc, char *argv[])
+{
+ /* configure should already have checked that the locale is supported. */
+ if (setlocale (LC_ALL, "") == NULL)
+ return 1;
+
+ ASSERT (strcasecmp ("paragraph", "Paragraph") == 0);
+
+ ASSERT (strcasecmp ("paragrapH", "parAgRaph") == 0);
+
+ ASSERT (strcasecmp ("paragraph", "paraLyzed") < 0);
+ ASSERT (strcasecmp ("paraLyzed", "paragraph") > 0);
+
+ ASSERT (strcasecmp ("para", "paragraph") < 0);
+ ASSERT (strcasecmp ("paragraph", "para") > 0);
+
+ if (argc > 1)
+ switch (argv[1][0])
+ {
+ case '1':
+ /* C or POSIX locale. */
+ return test_exit_status;
+
+ case '2':
+ /* Locale encoding is ISO-8859-1 or ISO-8859-15. */
+
+ /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */
+ /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */
+ ASSERT (strcasecmp ("Fej\311r", "Fej\351r") == 0);
+ ASSERT (strcasecmp ("Fej\351r", "Fej\311r") == 0);
+ ASSERT (strcasecmp ("Fejer", "Fej\311r") < 0);
+ ASSERT (strcasecmp ("Fej\311r", "Fejer") > 0);
+
+ /* Compare with U+00D7 MULTIPLICATION SIGN */
+ ASSERT (strcasecmp ("Fej\311r", "Fej\327") > 0);
+ ASSERT (strcasecmp ("Fej\327", "Fej\311r") < 0);
+ ASSERT (strcasecmp ("Fej\351r", "Fej\327") > 0);
+ ASSERT (strcasecmp ("Fej\327", "Fej\351r") < 0);
+
+ return test_exit_status;
+ }
+
+ return 1;
+}
--
2.43.0
>From 557f0e4958ca54ce1e660d611d8f4a872e2a981f Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Sun, 16 Feb 2025 18:22:31 +0100
Subject: [PATCH 4/5] strncasecmp: Work around Solaris, Cygwin bug.
* lib/strings.in.h (strncasecmp): Consider REPLACE_STRNCASECMP. Use the
usual idioms.
* m4/strings_h.m4 (gl_STRINGS_H_DEFAULTS): Initialize HAVE_STRNCASECMP,
REPLACE_STRNCASECMP.
* m4/strncasecmp.m4 (gl_FUNC_STRNCASECMP): Invoke gl_STRNCASECMP_WORKS.
Set REPLACE_STRNCASECMP. Assume that HAVE_STRNCASECMP is initialized.
* modules/strncasecmp (Files): Add m4/strcasecmp.m4.
(configure.ac): Consider REPLACE_STRNCASECMP.
* modules/strings-h (Makefile.am): Substitute HAVE_STRNCASECMP,
REPLACE_STRNCASECMP.
* doc/posix-functions/strncasecmp.texi: Mention the Solaris, Cygwin bug.
---
ChangeLog | 15 +++++++++++++++
doc/posix-functions/strncasecmp.texi | 5 +++++
lib/strings.in.h | 24 ++++++++++++------------
m4/strings_h.m4 | 4 +++-
m4/strncasecmp.m4 | 9 +++++++--
modules/strings-h | 2 ++
modules/strncasecmp | 4 +++-
7 files changed, 47 insertions(+), 16 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 052ec74b9d..e73bdd7804 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2025-02-16 Bruno Haible <[email protected]>
+
+ strncasecmp: Work around Solaris, Cygwin bug.
+ * lib/strings.in.h (strncasecmp): Consider REPLACE_STRNCASECMP. Use the
+ usual idioms.
+ * m4/strings_h.m4 (gl_STRINGS_H_DEFAULTS): Initialize HAVE_STRNCASECMP,
+ REPLACE_STRNCASECMP.
+ * m4/strncasecmp.m4 (gl_FUNC_STRNCASECMP): Invoke gl_STRNCASECMP_WORKS.
+ Set REPLACE_STRNCASECMP. Assume that HAVE_STRNCASECMP is initialized.
+ * modules/strncasecmp (Files): Add m4/strcasecmp.m4.
+ (configure.ac): Consider REPLACE_STRNCASECMP.
+ * modules/strings-h (Makefile.am): Substitute HAVE_STRNCASECMP,
+ REPLACE_STRNCASECMP.
+ * doc/posix-functions/strncasecmp.texi: Mention the Solaris, Cygwin bug.
+
2025-02-16 Bruno Haible <[email protected]>
strcasecmp: Add tests.
diff --git a/doc/posix-functions/strncasecmp.texi b/doc/posix-functions/strncasecmp.texi
index 1fc9e0307a..b52cedb4f3 100644
--- a/doc/posix-functions/strncasecmp.texi
+++ b/doc/posix-functions/strncasecmp.texi
@@ -12,6 +12,11 @@
@item
This function is missing on some platforms:
MSVC 14.
+@item
+This function uses the case mappings of a wrong locale on some platforms:
+Solaris 11.4,
+@c https://sourceware.org/pipermail/cygwin/2025-February/257347.html
+Cygwin 3.5.6.
@end itemize
Portability problems not fixed by Gnulib:
diff --git a/lib/strings.in.h b/lib/strings.in.h
index b356115181..12a9c40a7d 100644
--- a/lib/strings.in.h
+++ b/lib/strings.in.h
@@ -77,23 +77,23 @@ _GL_WARN_ON_USE (ffs, "ffs is not portable - use the ffs module");
greater than zero if S1 is lexicographically less than, equal to or greater
than S2.
Note: This function does not work in multibyte locales. */
-# if @REPLACE_STRCASECMP@
+# if @REPLACE_STRNCASECMP@
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
-# undef strcasecmp
-# define strcasecmp rpl_strcasecmp
+# undef strncasecmp
+# define strncasecmp rpl_strncasecmp
# endif
-_GL_FUNCDECL_RPL (strcasecmp, int, (const char *, const char *),
- _GL_ARG_NONNULL ((1, 2)));
-_GL_CXXALIAS_RPL (strcasecmp, int, (const char *, const char *));
+_GL_FUNCDECL_RPL (strncasecmp, int, (const char *, const char *, size_t),
+ _GL_ARG_NONNULL ((1, 2)));
+_GL_CXXALIAS_RPL (strncasecmp, int, (const char *, const char *, size_t));
# else
-# if !@HAVE_STRCASECMP@
-_GL_FUNCDECL_SYS (strcasecmp, int, (const char *, const char *),
- _GL_ARG_NONNULL ((1, 2)));
+# if !@HAVE_STRNCASECMP@
+_GL_FUNCDECL_SYS (strncasecmp, int, (const char *, const char *, size_t),
+ _GL_ARG_NONNULL ((1, 2)));
# endif
-_GL_CXXALIAS_SYS (strcasecmp, int, (const char *, const char *));
+_GL_CXXALIAS_SYS (strncasecmp, int, (const char *, const char *, size_t));
# endif
# if __GLIBC__ >= 2
-_GL_CXXALIASWARN (strcasecmp);
+_GL_CXXALIASWARN (strncasecmp);
# endif
#elif defined GNULIB_POSIXCHECK
/* strcasecmp() does not work with multibyte strings:
@@ -126,7 +126,7 @@ extern int strncasecmp (char const *s1, char const *s2, size_t n)
# undef strncasecmp
# if HAVE_RAW_DECL_STRNCASECMP
_GL_WARN_ON_USE (strncasecmp, "strncasecmp cannot work correctly on character "
- "strings in multibyte locales - "
+ "strings in multibyte locales and is unportable - "
"use mbsncasecmp or mbspcasecmp if you care about "
"internationalization, or use c_strncasecmp "
"(gnulib module c-strncasecmp) if you want a locale "
diff --git a/m4/strings_h.m4 b/m4/strings_h.m4
index b88e1105fb..f635575012 100644
--- a/m4/strings_h.m4
+++ b/m4/strings_h.m4
@@ -1,5 +1,5 @@
# strings_h.m4
-# serial 11
+# serial 12
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,
@@ -62,6 +62,8 @@ AC_DEFUN([gl_STRINGS_H_DEFAULTS]
dnl Assume proper GNU behavior unless another module says otherwise.
HAVE_FFS=1; AC_SUBST([HAVE_FFS])
HAVE_STRCASECMP=1; AC_SUBST([HAVE_STRCASECMP])
+ HAVE_STRNCASECMP=1; AC_SUBST([HAVE_STRNCASECMP])
HAVE_DECL_STRNCASECMP=1; AC_SUBST([HAVE_DECL_STRNCASECMP])
REPLACE_STRCASECMP=1; AC_SUBST([REPLACE_STRCASECMP])
+ REPLACE_STRNCASECMP=1; AC_SUBST([REPLACE_STRNCASECMP])
])
diff --git a/m4/strncasecmp.m4 b/m4/strncasecmp.m4
index d27a4a0c5e..c7c8b24036 100644
--- a/m4/strncasecmp.m4
+++ b/m4/strncasecmp.m4
@@ -1,5 +1,5 @@
# strncasecmp.m4
-# serial 1
+# serial 2
dnl Copyright (C) 2002-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,
@@ -11,7 +11,12 @@ AC_DEFUN([gl_FUNC_STRNCASECMP]
AC_REQUIRE([gl_STRINGS_H_DEFAULTS])
AC_CHECK_FUNCS([strncasecmp])
if test $ac_cv_func_strncasecmp = yes; then
- HAVE_STRNCASECMP=1
+ dnl Assume that strncasecmp and strcasecmp share the same bugs.
+ gl_STRCASECMP_WORKS
+ case "$gl_cv_func_strcasecmp_works" in
+ *yes) ;;
+ *) REPLACE_STRNCASECMP=1 ;;
+ esac
else
HAVE_STRNCASECMP=0
fi
diff --git a/modules/strings-h b/modules/strings-h
index 5c6c6736c2..2e0f0c1012 100644
--- a/modules/strings-h
+++ b/modules/strings-h
@@ -37,8 +37,10 @@ strings.h: strings.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(WARN_ON_USE
-e 's/@''GNULIB_STRNCASECMP''@/$(GNULIB_STRNCASECMP)/g' \
-e 's|@''HAVE_FFS''@|$(HAVE_FFS)|g' \
-e 's|@''HAVE_STRCASECMP''@|$(HAVE_STRCASECMP)|g' \
+ -e 's|@''HAVE_STRNCASECMP''@|$(HAVE_STRNCASECMP)|g' \
-e 's|@''HAVE_DECL_STRNCASECMP''@|$(HAVE_DECL_STRNCASECMP)|g' \
-e 's|@''REPLACE_STRCASECMP''@|$(REPLACE_STRCASECMP)|g' \
+ -e 's|@''REPLACE_STRNCASECMP''@|$(REPLACE_STRNCASECMP)|g' \
-e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
-e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
-e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
diff --git a/modules/strncasecmp b/modules/strncasecmp
index 5fca265bad..816cd0fae6 100644
--- a/modules/strncasecmp
+++ b/modules/strncasecmp
@@ -4,13 +4,15 @@ Case-insensitive string comparison function for unibyte locales.
Files:
lib/strncasecmp.c
m4/strncasecmp.m4
+m4/strcasecmp.m4
Depends-on:
strings-h
configure.ac:
gl_FUNC_STRNCASECMP
-gl_CONDITIONAL([GL_COND_OBJ_STRNCASECMP], [test $HAVE_STRNCASECMP = 0])
+gl_CONDITIONAL([GL_COND_OBJ_STRNCASECMP],
+ [test $HAVE_STRNCASECMP = 0 || test $REPLACE_STRNCASECMP = 1])
AM_COND_IF([GL_COND_OBJ_STRNCASECMP], [
gl_PREREQ_STRNCASECMP
])
--
2.43.0
>From 0a9ad49feb5dcdaf2a252caf95eb9eb19ae1a70d Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Sun, 16 Feb 2025 18:24:16 +0100
Subject: [PATCH 5/5] strncasecmp: Add tests.
* tests/test-strncasecmp-1.sh: New file.
* tests/test-strncasecmp-2.sh: New file.
* tests/test-strncasecmp.c: New file.
* modules/strncasecmp-tests: New file.
---
ChangeLog | 6 +++
modules/strncasecmp-tests | 21 +++++++++++
tests/test-strncasecmp-1.sh | 9 +++++
tests/test-strncasecmp-2.sh | 15 ++++++++
tests/test-strncasecmp.c | 73 +++++++++++++++++++++++++++++++++++++
5 files changed, 124 insertions(+)
create mode 100644 modules/strncasecmp-tests
create mode 100755 tests/test-strncasecmp-1.sh
create mode 100755 tests/test-strncasecmp-2.sh
create mode 100644 tests/test-strncasecmp.c
diff --git a/ChangeLog b/ChangeLog
index e73bdd7804..1fa41775dc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
2025-02-16 Bruno Haible <[email protected]>
+ strncasecmp: Add tests.
+ * tests/test-strncasecmp-1.sh: New file.
+ * tests/test-strncasecmp-2.sh: New file.
+ * tests/test-strncasecmp.c: New file.
+ * modules/strncasecmp-tests: New file.
+
strncasecmp: Work around Solaris, Cygwin bug.
* lib/strings.in.h (strncasecmp): Consider REPLACE_STRNCASECMP. Use the
usual idioms.
diff --git a/modules/strncasecmp-tests b/modules/strncasecmp-tests
new file mode 100644
index 0000000000..54f2a347b3
--- /dev/null
+++ b/modules/strncasecmp-tests
@@ -0,0 +1,21 @@
+Files:
+tests/test-strncasecmp-1.sh
+tests/test-strncasecmp-2.sh
+tests/test-strncasecmp.c
+tests/signature.h
+tests/macros.h
+m4/locale-fr.m4
+m4/codeset.m4
+
+Depends-on:
+setlocale
+
+configure.ac:
+gt_LOCALE_FR
+
+Makefile.am:
+TESTS += test-strncasecmp-1.sh test-strncasecmp-2.sh
+TESTS_ENVIRONMENT += \
+ LOCALE_FR='@LOCALE_FR@'
+check_PROGRAMS += test-strncasecmp
+test_strncasecmp_LDADD = $(LDADD) $(SETLOCALE_LIB)
diff --git a/tests/test-strncasecmp-1.sh b/tests/test-strncasecmp-1.sh
new file mode 100755
index 0000000000..b64a876a44
--- /dev/null
+++ b/tests/test-strncasecmp-1.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# Test in the POSIX locale.
+LC_ALL=C \
+${CHECKER} ./test-strncasecmp${EXEEXT} 1 || exit 1
+LC_ALL=POSIX \
+${CHECKER} ./test-strncasecmp${EXEEXT} 1 || exit 1
+
+exit 0
diff --git a/tests/test-strncasecmp-2.sh b/tests/test-strncasecmp-2.sh
new file mode 100755
index 0000000000..83a422d4bf
--- /dev/null
+++ b/tests/test-strncasecmp-2.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# Test in an ISO-8859-1 or ISO-8859-15 locale.
+: "${LOCALE_FR=fr_FR}"
+if test $LOCALE_FR = none; then
+ if test -f /usr/bin/localedef; then
+ echo "Skipping test: no traditional french locale is installed"
+ else
+ echo "Skipping test: no traditional french locale is supported"
+ fi
+ exit 77
+fi
+
+LC_ALL=$LOCALE_FR \
+${CHECKER} ./test-strncasecmp${EXEEXT} 2
diff --git a/tests/test-strncasecmp.c b/tests/test-strncasecmp.c
new file mode 100644
index 0000000000..fe479e408a
--- /dev/null
+++ b/tests/test-strncasecmp.c
@@ -0,0 +1,73 @@
+/* Test of strncasecmp() function.
+ Copyright (C) 2008-2025 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/>. */
+
+#include <config.h>
+
+#include <strings.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (strncasecmp, int, (const char *, const char *, size_t));
+
+#include <locale.h>
+#include <stdlib.h>
+
+#include "macros.h"
+
+int
+main (int argc, char *argv[])
+{
+ /* configure should already have checked that the locale is supported. */
+ if (setlocale (LC_ALL, "") == NULL)
+ return 1;
+
+ ASSERT (strncasecmp ("paragraph", "Paragraph", 9) == 0);
+
+ ASSERT (strncasecmp ("paragrapH", "parAgRaph", 9) == 0);
+
+ ASSERT (strncasecmp ("paragraph", "paraLyzed", 9) < 0);
+ ASSERT (strncasecmp ("paraLyzed", "paragraph", 9) > 0);
+
+ ASSERT (strncasecmp ("para", "paragraph", 9) < 0);
+ ASSERT (strncasecmp ("paragraph", "para", 9) > 0);
+
+ if (argc > 1)
+ switch (argv[1][0])
+ {
+ case '1':
+ /* C or POSIX locale. */
+ return test_exit_status;
+
+ case '2':
+ /* Locale encoding is ISO-8859-1 or ISO-8859-15. */
+
+ /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */
+ /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */
+ ASSERT (strncasecmp ("Fej\311r", "Fej\351r", 5) == 0);
+ ASSERT (strncasecmp ("Fej\351r", "Fej\311r", 5) == 0);
+ ASSERT (strncasecmp ("Fejer", "Fej\311r", 5) < 0);
+ ASSERT (strncasecmp ("Fej\311r", "Fejer", 5) > 0);
+
+ /* Compare with U+00D7 MULTIPLICATION SIGN */
+ ASSERT (strncasecmp ("Fej\311r", "Fej\327", 5) > 0);
+ ASSERT (strncasecmp ("Fej\327", "Fej\311r", 5) < 0);
+ ASSERT (strncasecmp ("Fej\351r", "Fej\327", 5) > 0);
+ ASSERT (strncasecmp ("Fej\327", "Fej\351r", 5) < 0);
+
+ return test_exit_status;
+ }
+
+ return 1;
+}
--
2.43.0