+2023-07-24 Bruno Haible <bruno@clisp.org>
+
+ fnmatch: Overcome wchar_t limitations.
+ * m4/fnmatch.m4 (gl_FUNC_FNMATCH_POSIX): Set REPLACE_FNMATCH to 1 on
+ AIX in 32-bit mode.
+ * lib/fnmatch.c: Include <uchar.h>. Conditionally include unistr.h.
+ (UCHAR_TO_WCHAR): Renamed from BTOWC.
+ (WCHAR_T, WINT_T, BTOWC, MBSRTOWCS, WCSLEN, WCSCAT, WMEMPCPY, WMEMCHR,
+ TOWLOWER, WCTYPE_T, WCTYPE, ISWCTYPE): New macros.
+ (IS_CHAR_CLASS): Use WCTYPE instead of wctype.
+ (UCHAR_TO_WCHAR): Use BTOWC instead of btowc.
+ (FOLD): Use TOWLOWER instead of towlower.
+ (CHAR): Use WCHAR_T instead of wchar_t.
+ (UCHAR, INT): Use WINT_T instead of wint_t.
+ (STRLEN): Use WCSLEN instead of wcslen.
+ (STRCAT): Use WCSCAT instead of wcscat.
+ (MEMPCPY): Use WMEMPCPY instead of wmempcpy.
+ (MEMCHR): Use WMEMCHR instead of wmemchr.
+ (is_char_class): Use WCTYPE_T instead of wctype_t, WCHAR_T instead of
+ wchar_t, WCTYPE instead of wctype.
+ (fnmatch): Use WCHAR_T instead of wchar_t, MBSRTOWCS instead of
+ mbsrtowcs.
+ * lib/fnmatch_loop.c (FCT): Use WCTYPE_T instead of wctype_t, ISWCTYPE
+ instead of iswctype. Update for renamed BTOWC.
+ * modules/fnmatch (Depends-on): Add btoc32, c32tolower,
+ c32_get_type_test, c32_apply_type_test, mbsrtoc32s, unistr/u32-chr,
+ unistr/u32-pcpy, unistr/u32-strcat, unistr/u32-strlen.
+ (Link): Add $(LIBUNISTRING).
+ * modules/fnmatch-tests (Makefile.am): Link test-fnmatch with
+ $(LIBUNISTRING).
+ * doc/posix-functions/fnmatch.texi: Move the Cygwin, mingw, MSVC, AIX
+ bug descriptions to the "fixed by Gnulib" section.
+
2023-07-24 Bruno Haible <bruno@clisp.org>
fnmatch: Update doc regarding Solaris 10.
Unicode BMP on some platforms:
@c Failing test cases:
@c fnmatch ("x?y", "x\360\237\230\213y", 0) == 0
-Solaris 10.
+Solaris 10, Cygwin 3.4.6,
+@c Failing test cases:
+@c fnmatch ("x?y", "x\360\237\230\213y", 0) == 0
+@c fnmatch ("x[[:alnum:]]y", "x\360\220\214\260y", 0) == 0
+@c fnmatch ("x[[:alpha:]]y", "x\360\220\214\260y", 0) == 0
+@c fnmatch ("x[[:graph:]]y", "x\360\240\200\200y", 0) == 0
+@c fnmatch ("x[[:lower:]]y", "x\360\220\221\201y", 0) == 0
+@c fnmatch ("x[[:print:]]y", "x\360\240\200\200y", 0) == 0
+@c fnmatch ("x[[:punct:]]y", "x\360\235\204\200y", 0) == 0
+@c fnmatch ("x[[:upper:]]y", "x\360\220\220\231y", 0) == 0
+MSVC,
+@c Failing test cases:
+@c fnmatch ("x?y", "x\360\237\230\213y", 0) == 0
+@c fnmatch ("x[[:alnum:]]y", "x\360\220\214\260y", 0) == 0
+@c fnmatch ("x[[:alpha:]]y", "x\360\220\214\260y", 0) == 0
+@c fnmatch ("x[[:graph:]]y", "x\360\240\200\200y", 0) == 0
+@c fnmatch ("x[[:lower:]]y", "x\360\220\221\201y", 0) == 0
+@c fnmatch ("x[[:print:]]y", "x\360\240\200\200y", 0) == 0
+@c fnmatch ("x[[:upper:]]y", "x\360\220\220\231y", 0) == 0
+32-bit AIX.
@item
In the pattern, negated character ranges (such as @code{[!a-z]}) are not
supported on some platforms:
Portability problems not fixed by Gnulib:
@itemize
-@item
-The @code{"?"} pattern character fails to match characters outside the
-Unicode BMP on some platforms:
-@c Failing test cases:
-@c fnmatch ("x?y", "x\360\237\230\213y", 0) == 0
-Cygwin 3.4.6,
-@c Failing test cases:
-@c fnmatch ("x?y", "x\360\237\230\213y", 0) == 0
-@c fnmatch ("x[[:alnum:]]y", "x\360\220\214\260y", 0) == 0
-@c fnmatch ("x[[:alpha:]]y", "x\360\220\214\260y", 0) == 0
-@c fnmatch ("x[[:graph:]]y", "x\360\240\200\200y", 0) == 0
-@c fnmatch ("x[[:lower:]]y", "x\360\220\221\201y", 0) == 0
-@c fnmatch ("x[[:print:]]y", "x\360\240\200\200y", 0) == 0
-@c fnmatch ("x[[:punct:]]y", "x\360\235\204\200y", 0) == 0
-@c fnmatch ("x[[:upper:]]y", "x\360\220\220\231y", 0) == 0
-MSVC,
-@c Failing test cases:
-@c fnmatch ("x?y", "x\360\237\230\213y", 0) == 0
-@c fnmatch ("x[[:alnum:]]y", "x\360\220\214\260y", 0) == 0
-@c fnmatch ("x[[:alpha:]]y", "x\360\220\214\260y", 0) == 0
-@c fnmatch ("x[[:graph:]]y", "x\360\240\200\200y", 0) == 0
-@c fnmatch ("x[[:lower:]]y", "x\360\220\221\201y", 0) == 0
-@c fnmatch ("x[[:print:]]y", "x\360\240\200\200y", 0) == 0
-@c fnmatch ("x[[:upper:]]y", "x\360\220\220\231y", 0) == 0
-32-bit AIX.
@end itemize
Note: Gnulib's replacement function has some limitations:
It does not implement patterns with
collating elements (such as @code{"[[.ch.]]"}) or
equivalence classes (such as @code{"[[=a=]]"}).
-@item
-In the pattern, the character class @code{[:blank:]} inside bracket
-expressions is not supported on some platforms:
-@c Failing test cases:
-@c fnmatch ("[[:blank:]]", " ", 0) == 0
-mingw.
-@c Additionally, on mingw 10, there are test failures:
-@c fnmatch ("x?y", "x\223\372y", 0) == 0
-@c fnmatch ("x?y", "x\244\351y", 0) == 0
-@c fnmatch ("x?y", "x\310\325y", 0) == 0
@end itemize
#if defined _LIBC || HAVE_ALLOCA
# include <alloca.h>
#endif
-#include <wchar.h>
-#include <wctype.h>
#include <stddef.h>
+#include <uchar.h>
+#if defined _LIBC || !_GL_SMALL_WCHAR_T
+/* It's OK to use wchar_t, since it's wide enough. */
+# include <wchar.h>
+# include <wctype.h>
+# define WCHAR_T wchar_t
+# define WINT_T wint_t
+# define BTOWC btowc
+# define MBSRTOWCS mbsrtowcs
+# define WCSLEN wcslen
+# define WCSCAT wcscat
+# define WMEMPCPY wmempcpy
+# define WMEMCHR wmemchr
+# define TOWLOWER towlower
+# define WCTYPE_T wctype_t
+# define WCTYPE wctype
+# define ISWCTYPE iswctype
+#else
+/* wchar_t is too small, use char32_t instead. */
+# include "unistr.h"
+# define WCHAR_T char32_t
+# define WINT_T char32_t
+# define BTOWC btoc32
+# define MBSRTOWCS mbsrtoc32s
+# define WCSLEN u32_strlen
+# define WCSCAT u32_strcat
+# define WMEMPCPY u32_pcpy
+# define WMEMCHR(S, C, N) u32_chr (S, N, C)
+# define TOWLOWER c32tolower
+# define WCTYPE_T c32_type_test_t
+# define WCTYPE c32_get_type_test
+# define ISWCTYPE c32_apply_type_test
+#endif
/* We need some of the locale data (the collation sequence information)
but there is no interface to get this information in general. Therefore
# define CHAR_CLASS_MAX_LENGTH 256
#endif
-#define IS_CHAR_CLASS(string) wctype (string)
+#define IS_CHAR_CLASS(string) WCTYPE (string)
/* Avoid depending on library functions or files
whose names are inconsistent. */
#define END end_pattern
#define STRUCT fnmatch_struct
#define L_(CS) CS
-#define BTOWC(C) btowc (C)
+#define UCHAR_TO_WCHAR(C) BTOWC (C)
#define STRLEN(S) strlen (S)
#define STRCAT(D, S) strcat (D, S)
#define MEMPCPY(D, S, N) mempcpy (D, S, N)
#include "fnmatch_loop.c"
-#define FOLD(c) ((flags & FNM_CASEFOLD) ? towlower (c) : (c))
-#define CHAR wchar_t
-#define UCHAR wint_t
-#define INT wint_t
+#define FOLD(c) ((flags & FNM_CASEFOLD) ? TOWLOWER (c) : (c))
+#define CHAR WCHAR_T
+#define UCHAR WINT_T
+#define INT WINT_T
#define FCT internal_fnwmatch
#define EXT ext_wmatch
#define END end_wpattern
#define L_(CS) L##CS
-#define BTOWC(C) (C)
-#define STRLEN(S) wcslen (S)
-#define STRCAT(D, S) wcscat (D, S)
-#define MEMPCPY(D, S, N) wmempcpy (D, S, N)
-#define MEMCHR(S, C, N) wmemchr (S, C, N)
+#define UCHAR_TO_WCHAR(C) (C)
+#define STRLEN(S) WCSLEN (S)
+#define STRCAT(D, S) WCSCAT (D, S)
+#define MEMPCPY(D, S, N) WMEMPCPY (D, S, N)
+#define MEMCHR(S, C, N) WMEMCHR (S, C, N)
#define WIDE_CHAR_VERSION 1
#ifdef _LIBC
/* Change the name the header defines so it doesn't conflict with
for a member of the portable character set is the same code point as
its single-byte encoding, we can use a simplified method to convert the
string to a multibyte character string. */
-static wctype_t
-is_char_class (const wchar_t *wcs)
+static WCTYPE_T
+is_char_class (const WCHAR_T *wcs)
{
char s[CHAR_CLASS_MAX_LENGTH + 1];
char *cp = s;
#ifdef _LIBC
if (*wcs < 0x20 || *wcs > 0x7e
|| *wcs == 0x24 || *wcs == 0x40 || *wcs == 0x60)
- return (wctype_t) 0;
+ return (WCTYPE_T) 0;
#else
switch (*wcs)
{
case L'z': case L'{': case L'|': case L'}': case L'~':
break;
default:
- return (wctype_t) 0;
+ return (WCTYPE_T) 0;
}
#endif
/* Avoid overrunning the buffer. */
if (cp == s + CHAR_CLASS_MAX_LENGTH)
- return (wctype_t) 0;
+ return (WCTYPE_T) 0;
*cp++ = (char) *wcs++;
}
*cp = '\0';
- return wctype (s);
+ return WCTYPE (s);
}
#define IS_CHAR_CLASS(string) is_char_class (string)
mbstate_t ps;
size_t n;
const char *p;
- wchar_t *wpattern_malloc = NULL;
- wchar_t *wpattern;
- wchar_t *wstring_malloc = NULL;
- wchar_t *wstring;
+ WCHAR_T *wpattern_malloc = NULL;
+ WCHAR_T *wpattern;
+ WCHAR_T *wstring_malloc = NULL;
+ WCHAR_T *wstring;
size_t alloca_used = 0;
/* Convert the strings into wide characters. */
n = strnlen (pattern, 1024);
if (__glibc_likely (n < 1024))
{
- wpattern = (wchar_t *) alloca_account ((n + 1) * sizeof (wchar_t),
+ wpattern = (WCHAR_T *) alloca_account ((n + 1) * sizeof (WCHAR_T),
alloca_used);
- n = mbsrtowcs (wpattern, &p, n + 1, &ps);
+ n = MBSRTOWCS (wpattern, &p, n + 1, &ps);
if (__glibc_unlikely (n == (size_t) -1))
/* Something wrong.
XXX Do we have to set 'errno' to something which mbsrtows hasn't
else
{
prepare_wpattern:
- n = mbsrtowcs (NULL, &pattern, 0, &ps);
+ n = MBSRTOWCS (NULL, &pattern, 0, &ps);
if (__glibc_unlikely (n == (size_t) -1))
/* Something wrong.
XXX Do we have to set 'errno' to something which mbsrtows hasn't
already done? */
return -1;
- if (__glibc_unlikely (n >= (size_t) -1 / sizeof (wchar_t)))
+ if (__glibc_unlikely (n >= (size_t) -1 / sizeof (WCHAR_T)))
{
__set_errno (ENOMEM);
return -2;
}
wpattern_malloc = wpattern
- = (wchar_t *) malloc ((n + 1) * sizeof (wchar_t));
+ = (WCHAR_T *) malloc ((n + 1) * sizeof (WCHAR_T));
assert (mbsinit (&ps));
if (wpattern == NULL)
return -2;
- (void) mbsrtowcs (wpattern, &pattern, n + 1, &ps);
+ (void) MBSRTOWCS (wpattern, &pattern, n + 1, &ps);
}
assert (mbsinit (&ps));
p = string;
if (__glibc_likely (n < 1024))
{
- wstring = (wchar_t *) alloca_account ((n + 1) * sizeof (wchar_t),
+ wstring = (WCHAR_T *) alloca_account ((n + 1) * sizeof (WCHAR_T),
alloca_used);
- n = mbsrtowcs (wstring, &p, n + 1, &ps);
+ n = MBSRTOWCS (wstring, &p, n + 1, &ps);
if (__glibc_unlikely (n == (size_t) -1))
{
/* Something wrong.
else
{
prepare_wstring:
- n = mbsrtowcs (NULL, &string, 0, &ps);
+ n = MBSRTOWCS (NULL, &string, 0, &ps);
if (__glibc_unlikely (n == (size_t) -1))
/* Something wrong.
XXX Do we have to set 'errno' to something which mbsrtows hasn't
already done? */
goto free_return;
- if (__glibc_unlikely (n >= (size_t) -1 / sizeof (wchar_t)))
+ if (__glibc_unlikely (n >= (size_t) -1 / sizeof (WCHAR_T)))
{
free (wpattern_malloc);
__set_errno (ENOMEM);
}
wstring_malloc = wstring
- = (wchar_t *) malloc ((n + 1) * sizeof (wchar_t));
+ = (WCHAR_T *) malloc ((n + 1) * sizeof (WCHAR_T));
if (wstring == NULL)
{
free (wpattern_malloc);
return -2;
}
assert (mbsinit (&ps));
- (void) mbsrtowcs (wstring, &string, n + 1, &ps);
+ (void) MBSRTOWCS (wstring, &string, n + 1, &ps);
}
int res = internal_fnwmatch (wpattern, wstring, wstring + n,
/* Leave room for the null. */
CHAR str[CHAR_CLASS_MAX_LENGTH + 1];
size_t c1 = 0;
- wctype_t wt;
+ WCTYPE_T wt;
const CHAR *startp = p;
for (;;)
if (_ISCTYPE ((UCHAR) *n, wt))
goto matched;
#else
- if (iswctype (BTOWC ((UCHAR) *n), wt))
+ if (ISWCTYPE (UCHAR_TO_WCHAR ((UCHAR) *n), wt))
goto matched;
#endif
c = *p++;
#undef STRLEN
#undef STRCAT
#undef L_
-#undef BTOWC
+#undef UCHAR_TO_WCHAR
#undef WIDE_CHAR_VERSION
#undef FINDIDX
-# Check for fnmatch - serial 17 -*- coding: utf-8 -*-
+# Check for fnmatch - serial 18 -*- coding: utf-8 -*-
# Copyright (C) 2000-2007, 2009-2023 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
*yes) ;;
*) REPLACE_FNMATCH=1 ;;
esac
+ dnl On AIX 7.2 in 32-bit mode, fnmatch()'s only POSIX compliance problem is
+ dnl that is does not support characters outside the Unicode BMP correctly.
+ dnl Test case: fnmatch ("x?y", "x\360\237\230\213y", 0) == 0
+ dnl This is due to wchar_t being only 16 bits wide.
+ AC_REQUIRE([gl_UCHAR_H])
+ if test $SMALL_WCHAR_T = 1; then
+ REPLACE_FNMATCH=1
+ fi
fi
if test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; then
gl_REPLACE_FNMATCH_H
Depends-on:
fnmatch-h
-alloca-opt [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-attribute [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-btowc [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-builtin-expect [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-flexmember [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-idx [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-isblank [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-iswctype [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-libc-config [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-stdbool [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-stdckdint [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-strnlen [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-wchar [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-wctype-h [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-wmemchr [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-wmempcpy [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-memchr [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-memcmp [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-mempcpy [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-mbsrtowcs [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
-mbsinit [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+alloca-opt [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+attribute [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+builtin-expect [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+flexmember [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+idx [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+isblank [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+libc-config [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+stdbool [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+stdckdint [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+strnlen [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+uchar [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+memchr [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+memcmp [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+mempcpy [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+mbsinit [test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1]
+btowc [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 0]
+iswctype [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 0]
+mbsrtowcs [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 0]
+wchar [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 0]
+wctype-h [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 0]
+wmemchr [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 0]
+wmempcpy [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 0]
+btoc32 [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 1]
+c32tolower [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 1]
+c32_get_type_test [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 1]
+c32_apply_type_test [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 1]
+mbsrtoc32s [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 1]
+unistr/u32-chr [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 1]
+unistr/u32-pcpy [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 1]
+unistr/u32-strcat [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 1]
+unistr/u32-strlen [{ test $HAVE_FNMATCH = 0 || test $REPLACE_FNMATCH = 1; } && test $SMALL_WCHAR_T = 1]
configure.ac:
gl_FUNC_FNMATCH_POSIX
<fnmatch.h>
Link:
+$(LTLIBUNISTRING) when linking with libtool, $(LIBUNISTRING) otherwise
$(MBRTOWC_LIB)
License:
LOCALE_JA='@LOCALE_JA@' \
LOCALE_ZH_CN='@LOCALE_ZH_CN@'
check_PROGRAMS += test-fnmatch test-fnmatch-w32
-test_fnmatch_LDADD = $(LDADD) $(SETLOCALE_LIB) $(MBRTOWC_LIB)
+test_fnmatch_LDADD = $(LDADD) $(SETLOCALE_LIB) $(LIBUNISTRING) $(MBRTOWC_LIB)