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 <br...@clisp.org> 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 <br...@clisp.org> 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 <br...@clisp.org> 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 <br...@clisp.org> 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 <br...@clisp.org> + + strcasecmp, strncasecmp: Fix header reference. + * lib/strcasecmp.c: Include <strings.h>, not <string.h>. + * lib/strncasecmp.c: Likewise. + 2025-02-16 Bruno Haible <br...@clisp.org> 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 <br...@clisp.org> 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 <br...@clisp.org> + + 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 <br...@clisp.org> 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 <br...@clisp.org> 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 <br...@clisp.org> + 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 <br...@clisp.org> 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 <br...@clisp.org> + + 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 <br...@clisp.org> 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 <br...@clisp.org> 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 <br...@clisp.org> + 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