]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/fnmatch.c
First cut of bat rerun a Job from Jobs Run
[bacula/bacula] / bacula / src / lib / fnmatch.c
index c6cdb88772c87e556eb42b183dcfc763463bb1a1..c1ec13ded3784b926ba2860c97599775e6bfda89 100644 (file)
-/* Copyright (C) 1991,1992,1993,1996,1997,1998,1999,2000,2001,2002,2003,2007
-       Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library 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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
-
-#if HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-/* Enable GNU extensions in fnmatch.h.  */
-#ifndef _GNU_SOURCE
-# define _GNU_SOURCE   1
-#endif
-
-#include <assert.h>
-#include <errno.h>
+/*
+ * Copyright (c) 1989, 1993, 1994
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert */
+
+/*
+ * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ * Compares a filename or pathname to a pattern.
+ */
+
+/* Version: $Id$ */
+
+/* Define SYS to use the system fnmatch() rather than ours */
+/* #define SYS 1 */
+
+#include "bacula.h"
+#ifdef SYS
 #include <fnmatch.h>
-#include <ctype.h>
-
-#if HAVE_STRING_H || defined _LIBC
-# include <string.h>
 #else
-# include <strings.h>
-#endif
-
-#if defined STDC_HEADERS || defined _LIBC
-# include <stdlib.h>
+#include "fnmatch.h"
 #endif
 
-/* For platform which support the ISO C amendement 1 functionality we
-   support user defined character classes.  */
-#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
-/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
-# include <wchar.h>
-# include <wctype.h>
-#endif
-
-/* We need some of the locale data (the collation sequence information)
-   but there is no interface to get this information in general.  Therefore
-   we support a correct implementation only in glibc.  */
-#ifdef _LIBC
-# include "../locale/localeinfo.h"
-# include "../locale/elem-hash.h"
-# include "../locale/coll-lookup.h"
-# include <shlib-compat.h>
-
-# define CONCAT(a,b) __CONCAT(a,b)
-# define mbsrtowcs __mbsrtowcs
-# define fnmatch __fnmatch
-extern int fnmatch (const char *pattern, const char *string, int flags);
-#endif
-
-/* We often have to test for FNM_FILE_NAME and FNM_PERIOD being both set.  */
-#define NO_LEADING_PERIOD(flags) \
-  ((flags & (FNM_FILE_NAME | FNM_PERIOD)) == (FNM_FILE_NAME | FNM_PERIOD))
-
-/* Comment out all this code if we are using the GNU C Library, and are not
-   actually compiling the library itself.  This code is part of the GNU C
-   Library, but also included in many other GNU distributions.  Compiling
-   and linking in this code is a waste when using the GNU C library
-   (especially if it is a shared library).  Rather than having every GNU
-   program understand `configure --with-gnu-libc' and omit the object files,
-   it is simpler to just do this in the source for each such file.  */
-
-#if defined _LIBC || !defined __GNU_LIBRARY__
-
-
-# if defined STDC_HEADERS || !defined isascii
-#  define ISASCII(c) 1
-# else
-#  define ISASCII(c) isascii(c)
-# endif
-
-# ifdef isblank
-#  define ISBLANK(c) (ISASCII (c) && isblank (c))
-# else
-#  define ISBLANK(c) ((c) == ' ' || (c) == '\t')
-# endif
-# ifdef isgraph
-#  define ISGRAPH(c) (ISASCII (c) && isgraph (c))
-# else
-#  define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
-# endif
-
-# define ISPRINT(c) (ISASCII (c) && isprint (c))
-# define ISDIGIT(c) (ISASCII (c) && isdigit (c))
-# define ISALNUM(c) (ISASCII (c) && isalnum (c))
-# define ISALPHA(c) (ISASCII (c) && isalpha (c))
-# define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
-# define ISLOWER(c) (ISASCII (c) && islower (c))
-# define ISPUNCT(c) (ISASCII (c) && ispunct (c))
-# define ISSPACE(c) (ISASCII (c) && isspace (c))
-# define ISUPPER(c) (ISASCII (c) && isupper (c))
-# define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
-
-# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
-
-# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
-/* The GNU C library provides support for user-defined character classes
-   and the functions from ISO C amendement 1.  */
-#  ifdef CHARCLASS_NAME_MAX
-#   define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
-#  else
-/* This shouldn't happen but some implementation might still have this
-   problem.  Use a reasonable default value.  */
-#   define CHAR_CLASS_MAX_LENGTH 256
-#  endif
-
-#  ifdef _LIBC
-#   define IS_CHAR_CLASS(string) __wctype (string)
-#  else
-#   define IS_CHAR_CLASS(string) wctype (string)
-#  endif
-
-#  ifdef _LIBC
-#   define ISWCTYPE(WC, WT)    __iswctype (WC, WT)
-#  else
-#   define ISWCTYPE(WC, WT)    iswctype (WC, WT)
-#  endif
-
-#  if (HAVE_MBSTATE_T && HAVE_MBSRTOWCS) || _LIBC
-/* In this case we are implementing the multibyte character handling.  */
-#   define HANDLE_MULTIBYTE    1
-#  endif
+#undef  EOS
+#define EOS     '\0'
 
-# else
-#  define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
+#define RANGE_MATCH     1
+#define RANGE_NOMATCH   0
+#define RANGE_ERROR     (-1)
 
-#  define IS_CHAR_CLASS(string)                                                      \
-   (STREQ (string, "alpha") || STREQ (string, "upper")                       \
-    || STREQ (string, "lower") || STREQ (string, "digit")                    \
-    || STREQ (string, "alnum") || STREQ (string, "xdigit")                   \
-    || STREQ (string, "space") || STREQ (string, "print")                    \
-    || STREQ (string, "punct") || STREQ (string, "graph")                    \
-    || STREQ (string, "cntrl") || STREQ (string, "blank"))
-# endif
+#define ISSET(x, y) ((x) & (y))
+#define FOLD(c) ((flags & FNM_CASEFOLD) && B_ISUPPER(c) ? tolower(c) : (c))
 
-/* Avoid depending on library functions or files
-   whose names are inconsistent.  */
+static int rangematch(const char *, char, int, char **);
 
-# if !defined _LIBC && !defined getenv
-extern char *getenv ();
-# endif
-
-# ifndef errno
-extern int errno;
-# endif
-
-/* Global variable.  */
-static int posixly_correct;
-
-/* This function doesn't exist on most systems.  */
-
-# if !defined HAVE___STRCHRNUL && !defined _LIBC
-static char *
-__strchrnul (s, c)
-     const char *s;
-     int c;
+#ifdef SYS 
+int xfnmatch(const char *pattern, const char *string, int flags)
+#else
+int fnmatch(const char *pattern, const char *string, int flags)
+#endif
 {
-  char *result = strchr (s, c);
-  if (result == NULL)
-    result = strchr (s, '\0');
-  return result;
+   const char *stringstart;
+   char *newp;
+   char c, test;
+
+   stringstart = string;
+   for ( ;; ) {
+      switch (c = *pattern++) {
+      case EOS:
+         if (ISSET(flags, FNM_LEADING_DIR) && IsPathSeparator(*string))
+            return (0);
+         return (*string == EOS ? 0 : FNM_NOMATCH);
+      case '?':
+         if (*string == EOS)
+            return (FNM_NOMATCH);
+         if (IsPathSeparator(*string) && ISSET(flags, FNM_PATHNAME))
+            return (FNM_NOMATCH);
+         if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
+             (string == stringstart ||
+              (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1)))))
+            return (FNM_NOMATCH);
+         ++string;
+         break;
+      case '*':
+         c = *pattern;
+         /* Collapse multiple stars. */
+         while (c == '*') {
+            c = *++pattern;
+         }
+
+         if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
+             (string == stringstart ||
+              (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1))))) {
+            return (FNM_NOMATCH);
+         }
+
+         /* Optimize for pattern with * at end or before /. */
+         if (c == EOS) {
+            if (ISSET(flags, FNM_PATHNAME)) {
+               return (ISSET(flags, FNM_LEADING_DIR) ||
+                       strchr(string, '/') == NULL ? 0 : FNM_NOMATCH);
+            } else {
+               return (0);
+            }
+         } else if (IsPathSeparator(c) && ISSET(flags, FNM_PATHNAME)) {
+            if ((string = strchr(string, '/')) == NULL)
+               return (FNM_NOMATCH);
+            break;
+         }
+
+         /* General case, use recursion. */
+         while ((test = *string) != EOS) {
+            if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) {
+               return (0);
+            }
+            if (test == '/' && ISSET(flags, FNM_PATHNAME)) {
+               break;
+            }
+            ++string;
+         }
+         return (FNM_NOMATCH);
+      case '[':
+         if (*string == EOS)
+            return (FNM_NOMATCH);
+         if (IsPathSeparator(*string) && ISSET(flags, FNM_PATHNAME))
+            return (FNM_NOMATCH);
+         if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
+             (string == stringstart ||
+              (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1)))))
+            return (FNM_NOMATCH);
+
+         switch (rangematch(pattern, *string, flags, &newp)) {
+         case RANGE_ERROR:
+            /* not a good range, treat as normal text */
+            goto normal;
+         case RANGE_MATCH:
+            pattern = newp;
+            break;
+         case RANGE_NOMATCH:
+            return (FNM_NOMATCH);
+         }
+         ++string;
+         break;
+
+      case '\\':
+         if (!ISSET(flags, FNM_NOESCAPE)) {
+            if ((c = *pattern++) == EOS) {
+               c = '\\';
+               --pattern;
+            }
+         }
+         /* FALLTHROUGH */
+      default:
+normal:
+         if (FOLD(c) != FOLD(*string)) {
+            return (FNM_NOMATCH);
+         }
+         ++string;
+         break;
+      }
+   }
+   /* NOTREACHED */
 }
-# endif
 
-# if HANDLE_MULTIBYTE && !defined HAVE___STRCHRNUL && !defined _LIBC
-static wchar_t *
-__wcschrnul (s, c)
-     const wchar_t *s;
-     wint_t c;
+static int rangematch(const char *pattern, char test, int flags,
+                      char **newp)
 {
-  wchar_t *result = wcschr (s, c);
-  if (result == NULL)
-    result = wcschr (s, '\0');
-  return result;
+   int negate, ok;
+   char c, c2;
+
+   /*
+    * A bracket expression starting with an unquoted circumflex
+    * character produces unspecified results (IEEE 1003.2-1992,
+    * 3.13.2).  This implementation treats it like '!', for
+    * consistency with the regular expression syntax.
+    * J.T. Conklin (conklin@ngai.kaleida.com)
+    */
+   if ((negate = (*pattern == '!' || *pattern == '^')))
+      ++pattern;
+
+   test = FOLD(test);
+
+   /*
+    * A right bracket shall lose its special meaning and represent
+    * itself in a bracket expression if it occurs first in the list.
+    * -- POSIX.2 2.8.3.2
+    */
+   ok = 0;
+   c = *pattern++;
+   do {
+      if (c == '\\' && !ISSET(flags, FNM_NOESCAPE))
+         c = *pattern++;
+      if (c == EOS)
+         return (RANGE_ERROR);
+      if (c == '/' && ISSET(flags, FNM_PATHNAME))
+         return (RANGE_NOMATCH);
+      c = FOLD(c);
+      if (*pattern == '-' && (c2 = *(pattern + 1)) != EOS && c2 != ']') {
+         pattern += 2;
+         if (c2 == '\\' && !ISSET(flags, FNM_NOESCAPE))
+            c2 = *pattern++;
+         if (c2 == EOS)
+            return (RANGE_ERROR);
+         c2 = FOLD(c2);
+         if (c <= test && test <= c2)
+            ok = 1;
+      } else if (c == test)
+         ok = 1;
+   } while ((c = *pattern++) != ']');
+
+   *newp = (char *) pattern;
+   return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
 }
-# endif
-
-# ifndef internal_function
-/* Inside GNU libc we mark some function in a special way.  In other
-   environments simply ignore the marking.  */
-#  define internal_function
-# endif
-
-/* Note that this evaluates C many times.  */
-# ifdef _LIBC
-#  define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
-# else
-#  define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
-# endif
-# define CHAR  char
-# define UCHAR unsigned char
-# define INT   int
-# define FCT   internal_fnmatch
-# define EXT   ext_match
-# define END   end_pattern
-# define STRUCT        fnmatch_struct
-# define L(CS) CS
-# ifdef _LIBC
-#  define BTOWC(C)     __btowc (C)
-# else
-#  define BTOWC(C)     btowc (C)
-# endif
-# define STRLEN(S) strlen (S)
-# define STRCAT(D, S) strcat (D, S)
-# define MEMPCPY(D, S, N) __mempcpy (D, S, N)
-# define MEMCHR(S, C, N) memchr (S, C, N)
-# define STRCOLL(S1, S2) strcoll (S1, S2)
-# include "fnmatch_loop.c"
-
-
-# if HANDLE_MULTIBYTE
-/* Note that this evaluates C many times.  */
-#  ifdef _LIBC
-#   define FOLD(c) ((flags & FNM_CASEFOLD) ? towlower (c) : (c))
-#  else
-#   define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? towlower (c) : (c))
-#  endif
-#  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 STRUCT fnwmatch_struct
-#  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 STRCOLL(S1, S2) wcscoll (S1, S2)
-#  define WIDE_CHAR_VERSION 1
-
-#  undef IS_CHAR_CLASS
-/* We have to convert the wide character string in a multibyte string.  But
-   we know that the character class names consist of alphanumeric characters
-   from the portable character set, and since the wide character encoding
-   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)
-{
-  char s[CHAR_CLASS_MAX_LENGTH + 1];
-  char *cp = s;
-
-  do
-    {
-      /* Test for a printable character from the portable character set.  */
-#  ifdef _LIBC
-      if (*wcs < 0x20 || *wcs > 0x7e
-         || *wcs == 0x24 || *wcs == 0x40 || *wcs == 0x60)
-       return (wctype_t) 0;
-#  else
-      switch (*wcs)
-       {
-       case L' ': case L'!': case L'"': case L'#': case L'%':
-       case L'&': case L'\'': case L'(': case L')': case L'*':
-       case L'+': case L',': case L'-': case L'.': case L'/':
-       case L'0': case L'1': case L'2': case L'3': case L'4':
-       case L'5': case L'6': case L'7': case L'8': case L'9':
-       case L':': case L';': case L'<': case L'=': case L'>':
-       case L'?':
-       case L'A': case L'B': case L'C': case L'D': case L'E':
-       case L'F': case L'G': case L'H': case L'I': case L'J':
-       case L'K': case L'L': case L'M': case L'N': case L'O':
-       case L'P': case L'Q': case L'R': case L'S': case L'T':
-       case L'U': case L'V': case L'W': case L'X': case L'Y':
-       case L'Z':
-       case L'[': case L'\\': case L']': case L'^': case L'_':
-       case L'a': case L'b': case L'c': case L'd': case L'e':
-       case L'f': case L'g': case L'h': case L'i': case L'j':
-       case L'k': case L'l': case L'm': case L'n': case L'o':
-       case L'p': case L'q': case L'r': case L's': case L't':
-       case L'u': case L'v': case L'w': case L'x': case L'y':
-       case L'z': case L'{': case L'|': case L'}': case L'~':
-         break;
-       default:
-         return (wctype_t) 0;
-       }
-#  endif
-
-      /* Avoid overrunning the buffer.  */
-      if (cp == s + CHAR_CLASS_MAX_LENGTH)
-       return (wctype_t) 0;
 
-      *cp++ = (char) *wcs++;
-    }
-  while (*wcs != L'\0');
-
-  *cp = '\0';
+#ifdef TEST_PROGRAM
+struct test {
+   const char *pattern;
+   const char *string;
+   const int options;
+   const int result; 
+};
+
+/*
+ * Note, some of these tests were duplicated from a patch file I found
+ *  in an email, so I am unsure what the license is.  Since this code is
+ *  never turned on in any release, it probably doesn't matter at all.
+ * If by some chance someone finds this to be a problem please let
+ *  me know.
+ */
+static struct test tests[] = {
+/*1*/  {"x", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+       {"x", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+       {"x", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+       {"*", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+/*5*/  {"*", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+       {"*", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+       {"*x", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+       {"*x", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+       {"*x", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+/*10*/ {"x*", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+       {"x*", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+       {"x*", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
+       {"a*b/*", "abbb/.x", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH},
+       {"a*b/*", "abbb/xy", FNM_PATHNAME|FNM_PERIOD, 0},
+/*15*/ {"[A-[]", "A", 0, 0},
+       {"[A-[]", "a", 0, FNM_NOMATCH},
+       {"[a-{]", "A", 0, FNM_NOMATCH},
+       {"[a-{]", "a", 0, 0},
+       {"[A-[]", "A", FNM_CASEFOLD, FNM_NOMATCH},
+/*20*/ {"[A-[]", "a", FNM_CASEFOLD, FNM_NOMATCH},
+       {"[a-{]", "A", FNM_CASEFOLD, 0},
+       {"[a-{]", "a", FNM_CASEFOLD, 0},
+       { "*LIB*", "lib", FNM_PERIOD, FNM_NOMATCH },
+       { "*LIB*", "lib", FNM_CASEFOLD, 0},
+/*25*/ { "a[/]b", "a/b", 0, 0},
+       { "a[/]b", "a/b", FNM_PATHNAME, FNM_NOMATCH },
+       { "[a-z]/[a-z]", "a/b", 0, 0 },
+       { "a/b", "*", FNM_PATHNAME, FNM_NOMATCH },
+       { "*", "a/b", FNM_PATHNAME, FNM_NOMATCH },
+       { "*[/]b", "a/b", FNM_PATHNAME, FNM_NOMATCH },
+/*30*/ { "\\[/b", "[/b", 0, 0 },
+       { "?\?/b", "aa/b", 0, 0 },
+       { "???b", "aa/b", 0, 0 },
+       { "???b", "aa/b", FNM_PATHNAME, FNM_NOMATCH },
+       { "?a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+/*35*/ { "a/?b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+       { "*a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+       { "a/*b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+       { "[.]a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+       { "a/[.]b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+/*40*/ { "*/?", "a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
+       { "?/*", "a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
+       { ".*/?", ".a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
+       { "*/.?", "a/.b", FNM_PATHNAME|FNM_PERIOD, 0 },
+       { "*/*", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+/*45*/ { "*[.]/b", "a./b", FNM_PATHNAME|FNM_PERIOD, 0 },
+       { "a?b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
+       { "a*b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
+       { "a[.]b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
+/*49*/ { "*a*", "a/b", FNM_PATHNAME|FNM_LEADING_DIR, 0 },
+       { "[/b", "[/b", 0, 0},
+#ifdef FULL_TEST
+       /* This test takes a *long* time */
+       {"a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z*",
+          "aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmm"
+          "nnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyy", 0, FNM_NOMATCH},
+#endif
 
-#  ifdef _LIBC
-  return __wctype (s);
-#  else
-  return wctype (s);
-#  endif
-}
-#  define IS_CHAR_CLASS(string) is_char_class (string)
+       /* Keep dummy last to avoid compiler warnings */
+       {"dummy", "dummy", 0, 0}
 
-#  include "fnmatch_loop.c"
-# endif
+};
 
+#define ntests ((int)(sizeof(tests)/sizeof(struct test)))
 
-int
-fnmatch (pattern, string, flags)
-     const char *pattern;
-     const char *string;
-     int flags;
+int main()
 {
-# if HANDLE_MULTIBYTE
-  if (__builtin_expect (MB_CUR_MAX, 1) != 1)
-    {
-      mbstate_t ps;
-      size_t n;
-      const char *p;
-      wchar_t *wpattern;
-      wchar_t *wstring;
-
-      /* Convert the strings into wide characters.  */
-      memset (&ps, '\0', sizeof (ps));
-      p = pattern;
-#ifdef _LIBC
-      n = strnlen (pattern, 1024);
-#else
-      n = strlen (pattern);
-#endif
-      if (__builtin_expect (n < 1024, 1))
-       {
-         wpattern = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
-         n = mbsrtowcs (wpattern, &p, n + 1, &ps);
-         if (__builtin_expect (n == (size_t) -1, 0))
-           /* Something wrong.
-              XXX Do we have to set `errno' to something which mbsrtows hasn't
-              already done?  */
-           return -1;
-         if (p)
-           memset (&ps, '\0', sizeof (ps));
-       }
-      if (__builtin_expect (p != NULL, 0))
-       {
-         n = mbsrtowcs (NULL, &pattern, 0, &ps);
-         if (__builtin_expect (n == (size_t) -1, 0))
-           /* Something wrong.
-              XXX Do we have to set `errno' to something which mbsrtows hasn't
-              already done?  */
-           return -1;
-         wpattern = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
-         assert (mbsinit (&ps));
-         (void) mbsrtowcs (wpattern, &pattern, n + 1, &ps);
-       }
-
-      assert (mbsinit (&ps));
-#ifdef _LIBC
-      n = strnlen (string, 1024);
-#else
-      n = strlen (string);
-#endif
-      p = string;
-      if (__builtin_expect (n < 1024, 1))
-       {
-         wstring = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
-         n = mbsrtowcs (wstring, &p, n + 1, &ps);
-         if (__builtin_expect (n == (size_t) -1, 0))
-           /* Something wrong.
-              XXX Do we have to set `errno' to something which mbsrtows hasn't
-              already done?  */
-           return -1;
-         if (p)
-           memset (&ps, '\0', sizeof (ps));
-       }
-      if (__builtin_expect (p != NULL, 0))
-       {
-         n = mbsrtowcs (NULL, &string, 0, &ps);
-         if (__builtin_expect (n == (size_t) -1, 0))
-           /* Something wrong.
-              XXX Do we have to set `errno' to something which mbsrtows hasn't
-              already done?  */
-           return -1;
-         wstring = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
-         assert (mbsinit (&ps));
-         (void) mbsrtowcs (wstring, &string, n + 1, &ps);
-       }
-
-      return internal_fnwmatch (wpattern, wstring, wstring + n,
-                               flags & FNM_PERIOD, flags, NULL);
-    }
-# endif  /* mbstate_t and mbsrtowcs or _LIBC.  */
-
-  return internal_fnmatch (pattern, string, string + strlen (string),
-                          flags & FNM_PERIOD, flags, NULL);
+   bool fail = false;
+   for (int i=0; i<ntests; i++) {
+      if (fnmatch(tests[i].pattern, tests[i].string, tests[i].options) != tests[i].result) {
+         printf("Test %d failed: pat=%s str=%s expect=%s got=%s\n",
+            i+1, tests[i].pattern, tests[i].string, 
+            tests[i].result==0?"matches":"no match",
+            tests[i].result==0?"no match":"matches");
+         fail = true;
+      } else {
+         printf("Test %d succeeded\n", i+1);
+      }
+   }
+   return fail;
 }
-
-# ifdef _LIBC
-#  undef fnmatch
-versioned_symbol (libc, __fnmatch, fnmatch, GLIBC_2_2_3);
-#  if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_2_3)
-strong_alias (__fnmatch, __fnmatch_old)
-compat_symbol (libc, __fnmatch_old, fnmatch, GLIBC_2_0);
-#  endif
-libc_hidden_ver (__fnmatch, fnmatch)
-# endif
-
-#endif /* _LIBC or not __GNU_LIBRARY__.  */
+#endif /* TEST_PROGRAM */