From ccb59e4c36bea4915303a24ea1c7dec109db97eb Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Wed, 19 Apr 2023 17:59:03 +0200 Subject: wcsncmp: Work around two ISO C compliance bugs on several platforms. * lib/wchar.in.h (wcsncmp): Consider REPLACE_WCSNCMP. * lib/wcsncmp-impl.h (wcsncmp): Don't assume that the two wide characters are in the range 0..INT_MAX. * m4/wcsncmp.m4 (gl_FUNC_WCSNCMP): Test whether wcsncmp works for all wide characters. Set REPLACE_WCSNCMP. * m4/wchar_h.m4 (gl_WCHAR_H_DEFAULTS): Initialize REPLACE_WCSNCMP. * modules/wchar (Makefile.am): Substitute REPLACE_WCSNCMP. * modules/wcsncmp (Status, Notice): Un-obsolete this module. (configure.ac): Consider REPLACE_WCSNCMP. * doc/posix-functions/wcsncmp.texi: Mention the two bugs. --- ChangeLog | 14 ++++++++++ doc/posix-functions/wcsncmp.texi | 8 ++++++ lib/wchar.in.h | 16 +++++++++-- lib/wcsncmp-impl.h | 5 ++-- m4/wchar_h.m4 | 3 ++- m4/wcsncmp.m4 | 58 +++++++++++++++++++++++++++++++++++++++- modules/wchar | 1 + modules/wcsncmp | 9 ++----- 8 files changed, 101 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index a88dfc1d81..46cfb56ea2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2023-04-19 Bruno Haible + + wcsncmp: Work around two ISO C compliance bugs on several platforms. + * lib/wchar.in.h (wcsncmp): Consider REPLACE_WCSNCMP. + * lib/wcsncmp-impl.h (wcsncmp): Don't assume that the two wide + characters are in the range 0..INT_MAX. + * m4/wcsncmp.m4 (gl_FUNC_WCSNCMP): Test whether wcsncmp works for all + wide characters. Set REPLACE_WCSNCMP. + * m4/wchar_h.m4 (gl_WCHAR_H_DEFAULTS): Initialize REPLACE_WCSNCMP. + * modules/wchar (Makefile.am): Substitute REPLACE_WCSNCMP. + * modules/wcsncmp (Status, Notice): Un-obsolete this module. + (configure.ac): Consider REPLACE_WCSNCMP. + * doc/posix-functions/wcsncmp.texi: Mention the two bugs. + 2023-04-18 Bruno Haible wcscmp: Add tests. diff --git a/doc/posix-functions/wcsncmp.texi b/doc/posix-functions/wcsncmp.texi index b6282606ed..46874af46f 100644 --- a/doc/posix-functions/wcsncmp.texi +++ b/doc/posix-functions/wcsncmp.texi @@ -8,6 +8,14 @@ Gnulib module: wcsncmp Portability problems fixed by Gnulib: @itemize +@item +This function compares the wide characters as if they were unsigned, although +@code{wchar_t} is signed, on some platforms: +glibc 2.14.1 on x86 or x86_64, musl libc 1.2.3, macOS 12.5, FreeBSD 13.2, NetBSD 9.0, OpenBSD 7.2, Solaris 11.4. +@item +This function may return a wrong result if the two arguments are of different +length, on some platforms: +AIX 7.2 in 64-bit mode. @end itemize Portability problems not fixed by Gnulib: diff --git a/lib/wchar.in.h b/lib/wchar.in.h index c347256368..69fa2f8f59 100644 --- a/lib/wchar.in.h +++ b/lib/wchar.in.h @@ -967,13 +967,25 @@ _GL_WARN_ON_USE (wcscmp, "wcscmp is unportable - " /* Compare no more than N wide characters of S1 and S2. */ #if @GNULIB_WCSNCMP@ -# if !@HAVE_WCSNCMP@ +# if @REPLACE_WCSNCMP@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef wcsncmp +# define wcsncmp rpl_wcsncmp +# endif +_GL_FUNCDECL_RPL (wcsncmp, int, + (const wchar_t *s1, const wchar_t *s2, size_t n) + _GL_ATTRIBUTE_PURE); +_GL_CXXALIAS_RPL (wcsncmp, int, + (const wchar_t *s1, const wchar_t *s2, size_t n)); +# else +# if !@HAVE_WCSNCMP@ _GL_FUNCDECL_SYS (wcsncmp, int, (const wchar_t *s1, const wchar_t *s2, size_t n) _GL_ATTRIBUTE_PURE); -# endif +# endif _GL_CXXALIAS_SYS (wcsncmp, int, (const wchar_t *s1, const wchar_t *s2, size_t n)); +# endif # if __GLIBC__ >= 2 _GL_CXXALIASWARN (wcsncmp); # endif diff --git a/lib/wcsncmp-impl.h b/lib/wcsncmp-impl.h index 4ab49ebb3a..72cfce7ac4 100644 --- a/lib/wcsncmp-impl.h +++ b/lib/wcsncmp-impl.h @@ -27,8 +27,9 @@ wcsncmp (const wchar_t *s1, const wchar_t *s2, size_t n) n--; continue; } - /* Note that wc1 and wc2 each have at most 31 bits. */ - return (int)wc1 - (int)wc2; + /* ISO C requires wcsncmp to work with all wchar_t values. + We cannot assume that wc1 and wc2 are in the range 0..INT_MAX. */ + return _GL_CMP (wc1, wc2); /* > 0 if wc1 > wc2, < 0 if wc1 < wc2, = 0 if wc1 and wc2 are both '\0'. */ } diff --git a/m4/wchar_h.m4 b/m4/wchar_h.m4 index b9fa7cec84..442932be44 100644 --- a/m4/wchar_h.m4 +++ b/m4/wchar_h.m4 @@ -7,7 +7,7 @@ dnl with or without modifications, as long as this notice is preserved. dnl Written by Eric Blake. -# wchar_h.m4 serial 59 +# wchar_h.m4 serial 60 AC_DEFUN_ONCE([gl_WCHAR_H], [ @@ -254,6 +254,7 @@ AC_DEFUN([gl_WCHAR_H_DEFAULTS], REPLACE_WCSWIDTH=0; AC_SUBST([REPLACE_WCSWIDTH]) REPLACE_WCSFTIME=0; AC_SUBST([REPLACE_WCSFTIME]) REPLACE_WCSCMP=0; AC_SUBST([REPLACE_WCSCMP]) + REPLACE_WCSNCMP=0; AC_SUBST([REPLACE_WCSNCMP]) REPLACE_WCSSTR=0; AC_SUBST([REPLACE_WCSSTR]) REPLACE_WCSTOK=0; AC_SUBST([REPLACE_WCSTOK]) REPLACE_WMEMCMP=0; AC_SUBST([REPLACE_WMEMCMP]) diff --git a/m4/wcsncmp.m4 b/m4/wcsncmp.m4 index 6253af3c22..4ef469f1ba 100644 --- a/m4/wcsncmp.m4 +++ b/m4/wcsncmp.m4 @@ -1,4 +1,4 @@ -# wcsncmp.m4 serial 2 +# wcsncmp.m4 serial 3 dnl Copyright (C) 2011-2023 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,5 +10,61 @@ AC_DEFUN([gl_FUNC_WCSNCMP], AC_CHECK_FUNCS_ONCE([wcsncmp]) if test $ac_cv_func_wcsncmp = no; then HAVE_WCSNCMP=0 + else + AC_CACHE_CHECK([whether wcsncmp works for all wide characters], + [gl_cv_func_wcsncmp_works], + [AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ + #include + int main () + { + int result = 0; + { /* This test fails on glibc < 2.15, musl libc 1.2.3, macOS 12.5, + FreeBSD 13.2, NetBSD 9.0, OpenBSD 7.2, Solaris 11.4. */ + wchar_t a[2] = { (wchar_t) 0x76547654, 0 }; + wchar_t b[2] = { (wchar_t) 0x9abc9abc, 0 }; + int cmp = wcsncmp (a, b, 1); + if (!((wchar_t)-1 < 0 ? cmp > 0 : cmp < 0)) + result |= 1; + } + { /* This test fails on AIX in 64-bit mode. */ + wchar_t c[2] = { (wchar_t) 'x', 0 }; + wchar_t d[3] = { (wchar_t) 'x', (wchar_t) 0x9abc9abc, 0 }; + int cmp = wcsncmp (c, d, 2); + if (!((wchar_t)-1 < 0 ? cmp > 0 : cmp < 0)) + result |= 2; + } + return result; + } + ]]) + ], + [gl_cv_func_wcsncmp_works=yes], + [gl_cv_func_wcsncmp_works=no], + [case "$host_on" in + # Guess no on glibc versions < 2.15. + *-gnu* | gnu*) + AC_EGREP_CPP([Unlucky], + [ +#include +#ifdef __GNU_LIBRARY__ + #if (__GLIBC__ == 2 && __GLIBC_MINOR__ < 15) + Unlucky GNU user + #endif +#endif + ], + [gl_cv_func_wcsncmp_works="guessing no"], + [gl_cv_func_wcsncmp_works="guessing yes"]) + ;; + # Guess no on musl systems. + *-musl* | midipix*) gl_cv_func_wcsncmp_works="guessing no" ;; + # If we don't know, obey --enable-cross-guesses. + *) gl_cv_func_wcsncmp_works="$gl_cross_guess_normal" ;; + esac + ]) + ]) + case "$gl_cv_func_wcsncmp_works" in + *yes) ;; + *) REPLACE_WCSNCMP=1 ;; + esac fi ]) diff --git a/modules/wchar b/modules/wchar index 88b442b525..e0dd69268d 100644 --- a/modules/wchar +++ b/modules/wchar @@ -143,6 +143,7 @@ wchar.h: wchar.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) -e 's|@''REPLACE_WCSWIDTH''@|$(REPLACE_WCSWIDTH)|g' \ -e 's|@''REPLACE_WCSFTIME''@|$(REPLACE_WCSFTIME)|g' \ -e 's|@''REPLACE_WCSCMP''@|$(REPLACE_WCSCMP)|g' \ + -e 's|@''REPLACE_WCSNCMP''@|$(REPLACE_WCSNCMP)|g' \ -e 's|@''REPLACE_WCSSTR''@|$(REPLACE_WCSSTR)|g' \ -e 's|@''REPLACE_WCSTOK''@|$(REPLACE_WCSTOK)|g' \ -e 's|@''REPLACE_WMEMCMP''@|$(REPLACE_WMEMCMP)|g' \ diff --git a/modules/wcsncmp b/modules/wcsncmp index a09ab92bfd..e67861d359 100644 --- a/modules/wcsncmp +++ b/modules/wcsncmp @@ -1,12 +1,6 @@ Description: wcsncmp() function: compare two wide strings. -Status: -obsolete - -Notice: -This module is obsolete. - Files: lib/wcsncmp.c lib/wcsncmp-impl.h @@ -17,7 +11,8 @@ wchar configure.ac: gl_FUNC_WCSNCMP -gl_CONDITIONAL([GL_COND_OBJ_WCSNCMP], [test $HAVE_WCSNCMP = 0]) +gl_CONDITIONAL([GL_COND_OBJ_WCSNCMP], + [test $HAVE_WCSNCMP = 0 || test $REPLACE_WCSNCMP = 1]) gl_WCHAR_MODULE_INDICATOR([wcsncmp]) Makefile.am: -- cgit v1.2.1