X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Ffnmatch.c;h=0031331cb16a8a5d92114827a876d7db3efefff8;hb=fa4878979e0a72890ca577a210ccd7cf6291dbf0;hp=4aecb7f3d6b7f21ee85305bbcfd0ff32c2a0e88a;hpb=279e878bddb61086f813385dc94fd04a5465473a;p=~andy%2Fgtk
diff --git a/gtk/fnmatch.c b/gtk/fnmatch.c
index 4aecb7f3d..0031331cb 100644
--- a/gtk/fnmatch.c
+++ b/gtk/fnmatch.c
@@ -1,215 +1,365 @@
/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This 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
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
*
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
*/
/*
- * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
-#ifdef HAVE_CONFIG_H
+/*
+ * Stripped down, converted to UTF-8 and test cases added
+ *
+ * Owen Taylor, 13 December 2002;
+ */
+
#include "config.h"
-#endif
-#include
+#include "gtkprivate.h"
+
+#include
-/* Added for GTK. We need to make sure that all constants are defined
- * to properly compile this file */
+#include
+
+/* We need to make sure that all constants are defined
+ * to properly compile this file
+ */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
-#include "fnmatch.h"
-#include
+static gunichar
+get_char (const char **str)
+{
+ gunichar c = g_utf8_get_char (*str);
+ *str = g_utf8_next_char (*str);
+#ifdef G_PLATFORM_WIN32
+ c = g_unichar_tolower (c);
+#endif
-/* 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. */
+ return c;
+}
-#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
+#define DO_ESCAPE 0
+#else
+#define DO_ESCAPE 1
+#endif
+static gunichar
+get_unescaped_char (const char **str,
+ gboolean *was_escaped)
+{
+ gunichar c = get_char (str);
-#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
-extern int errno;
-#endif
+ *was_escaped = DO_ESCAPE && c == '\\';
+ if (*was_escaped)
+ c = get_char (str);
+
+ return c;
+}
/* Match STRING against the filename pattern PATTERN, returning zero if
it matches, nonzero if not. */
-int
-fnmatch (pattern, string, flags)
- const char *pattern;
- const char *string;
- int flags;
-{
- register const char *p = pattern, *n = string;
- register char c;
-/* Note that this evalutes C many times. */
-#define FOLD(c) ((flags & FNM_CASEFOLD) && isupper (c) ? tolower (c) : (c))
-
- while ((c = *p++) != '\0')
+static gboolean
+gtk_fnmatch_intern (const char *pattern,
+ const char *string,
+ gboolean component_start,
+ gboolean no_leading_period)
+{
+ const char *p = pattern, *n = string;
+
+ while (*p)
{
- c = FOLD (c);
-
+ const char *last_n = n;
+
+ gunichar c = get_char (&p);
+ gunichar nc = get_char (&n);
+
switch (c)
{
- case '?':
- if (*n == '\0')
- return FNM_NOMATCH;
- else if ((flags & FNM_FILE_NAME) && *n == '/')
- return FNM_NOMATCH;
- else if ((flags & FNM_PERIOD) && *n == '.' &&
- (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
- return FNM_NOMATCH;
+ case '?':
+ if (nc == '\0')
+ return FALSE;
+ else if (nc == G_DIR_SEPARATOR)
+ return FALSE;
+ else if (nc == '.' && component_start && no_leading_period)
+ return FALSE;
break;
-
case '\\':
- if (!(flags & FNM_NOESCAPE))
- {
- c = *p++;
- c = FOLD (c);
- }
- if (FOLD (*n) != c)
- return FNM_NOMATCH;
+ if (DO_ESCAPE)
+ c = get_char (&p);
+ if (nc != c)
+ return FALSE;
break;
-
case '*':
- if ((flags & FNM_PERIOD) && *n == '.' &&
- (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
- return FNM_NOMATCH;
+ if (nc == '.' && component_start && no_leading_period)
+ return FALSE;
+
+ {
+ const char *last_p = p;
+
+ for (last_p = p, c = get_char (&p);
+ c == '?' || c == '*';
+ last_p = p, c = get_char (&p))
+ {
+ if (c == '?')
+ {
+ if (nc == '\0')
+ return FALSE;
+ else if (nc == G_DIR_SEPARATOR)
+ return FALSE;
+ else
+ {
+ last_n = n; nc = get_char (&n);
+ }
+ }
+ }
- for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
- if (((flags & FNM_FILE_NAME) && *n == '/') ||
- (c == '?' && *n == '\0'))
- return FNM_NOMATCH;
+ /* If the pattern ends with wildcards, we have a
+ * guaranteed match unless there is a dir separator
+ * in the remainder of the string.
+ */
+ if (c == '\0')
+ {
+ if (strchr (last_n, G_DIR_SEPARATOR) != NULL)
+ return FALSE;
+ else
+ return TRUE;
+ }
- if (c == '\0')
- return 0;
+ if (DO_ESCAPE && c == '\\')
+ c = get_char (&p);
- {
- char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
- c1 = FOLD (c1);
- for (--p; *n != '\0'; ++n)
- if ((c == '[' || FOLD (*n) == c1) &&
- fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
- return 0;
- return FNM_NOMATCH;
+ for (p = last_p; nc != '\0';)
+ {
+ if ((c == '[' || nc == c) &&
+ gtk_fnmatch_intern (p, last_n, component_start, no_leading_period))
+ return TRUE;
+
+ component_start = (nc == G_DIR_SEPARATOR);
+ last_n = n;
+ nc = get_char (&n);
+ }
+
+ return FALSE;
}
case '[':
{
/* Nonzero if the sense of the character class is inverted. */
- register int not;
+ gboolean not;
+ gboolean was_escaped;
- if (*n == '\0')
- return FNM_NOMATCH;
+ if (nc == '\0' || nc == G_DIR_SEPARATOR)
+ return FALSE;
- if ((flags & FNM_PERIOD) && *n == '.' &&
- (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
- return FNM_NOMATCH;
+ if (nc == '.' && component_start && no_leading_period)
+ return FALSE;
not = (*p == '!' || *p == '^');
if (not)
++p;
- c = *p++;
+ c = get_unescaped_char (&p, &was_escaped);
for (;;)
{
- register char cstart = c, cend = c;
-
- if (!(flags & FNM_NOESCAPE) && c == '\\')
- cstart = cend = *p++;
-
- cstart = cend = FOLD (cstart);
-
+ register gunichar cstart = c, cend = c;
if (c == '\0')
/* [ (unterminated) loses. */
- return FNM_NOMATCH;
-
- c = *p++;
- c = FOLD (c);
+ return FALSE;
- if ((flags & FNM_FILE_NAME) && c == '/')
- /* [/] can never match. */
- return FNM_NOMATCH;
-
- if (c == '-' && *p != ']')
+ c = get_unescaped_char (&p, &was_escaped);
+
+ if (!was_escaped && c == '-' && *p != ']')
{
- cend = *p++;
- if (!(flags & FNM_NOESCAPE) && cend == '\\')
- cend = *p++;
+ cend = get_unescaped_char (&p, &was_escaped);
if (cend == '\0')
- return FNM_NOMATCH;
- cend = FOLD (cend);
+ return FALSE;
- c = *p++;
+ c = get_char (&p);
}
- if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
+ if (nc >= cstart && nc <= cend)
goto matched;
- if (c == ']')
+ if (!was_escaped && c == ']')
break;
}
if (!not)
- return FNM_NOMATCH;
+ return FALSE;
break;
matched:;
/* Skip the rest of the [...] that already matched. */
- while (c != ']')
+ /* XXX 1003.2d11 is unclear if was_escaped is right. */
+ while (was_escaped || c != ']')
{
if (c == '\0')
/* [... (unterminated) loses. */
- return FNM_NOMATCH;
+ return FALSE;
- c = *p++;
- if (!(flags & FNM_NOESCAPE) && c == '\\')
- /* XXX 1003.2d11 is unclear if this is right. */
- ++p;
+ c = get_unescaped_char (&p, &was_escaped);
}
if (not)
- return FNM_NOMATCH;
+ return FALSE;
}
break;
default:
- if (c != FOLD (*n))
- return FNM_NOMATCH;
+ if (c != nc)
+ return FALSE;
}
- ++n;
+ component_start = (nc == G_DIR_SEPARATOR);
}
if (*n == '\0')
- return 0;
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Match STRING against the filename pattern PATTERN, returning zero if
+ * it matches, nonzero if not.
+ *
+ * GTK+ used to use a old version of GNU fnmatch() that was buggy
+ * in various ways and didn't handle UTF-8. The following is
+ * converted to UTF-8. To simplify the process of making it
+ * correct, this is special-cased to the combinations of flags
+ * that gtkfilesel.c uses.
+ *
+ * FNM_FILE_NAME - always set
+ * FNM_LEADING_DIR - never set
+ * FNM_NOESCAPE - set only on windows
+ * FNM_CASEFOLD - set only on windows
+ */
+gboolean
+_gtk_fnmatch (const char *pattern,
+ const char *string,
+ gboolean no_leading_period)
+{
+ return gtk_fnmatch_intern (pattern, string, TRUE, no_leading_period);
+}
+
+#undef FNMATCH_TEST_CASES
+#ifdef FNMATCH_TEST_CASES
- if ((flags & FNM_LEADING_DIR) && *n == '/')
- /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
- return 0;
+#define TEST(pat, str, no_leading_period, result) \
+ g_assert (_gtk_fnmatch ((pat), (str), (no_leading_period)) == result)
- return FNM_NOMATCH;
+int main (int argc, char **argv)
+{
+ TEST ("[a-]", "-", TRUE, TRUE);
+
+ TEST ("a", "a", TRUE, TRUE);
+ TEST ("a", "b", TRUE, FALSE);
+
+ /* Test what ? matches */
+ TEST ("?", "a", TRUE, TRUE);
+ TEST ("?", ".", TRUE, FALSE);
+ TEST ("a?", "a.", TRUE, TRUE);
+ TEST ("a/?", "a/b", TRUE, TRUE);
+ TEST ("a/?", "a/.", TRUE, FALSE);
+ TEST ("?", "/", TRUE, FALSE);
+
+ /* Test what * matches */
+ TEST ("*", "a", TRUE, TRUE);
+ TEST ("*", ".", TRUE, FALSE);
+ TEST ("a*", "a.", TRUE, TRUE);
+ TEST ("a/*", "a/b", TRUE, TRUE);
+ TEST ("a/*", "a/.", TRUE, FALSE);
+ TEST ("*", "/", TRUE, FALSE);
+
+ /* Range tests */
+ TEST ("[ab]", "a", TRUE, TRUE);
+ TEST ("[ab]", "c", TRUE, FALSE);
+ TEST ("[^ab]", "a", TRUE, FALSE);
+ TEST ("[!ab]", "a", TRUE, FALSE);
+ TEST ("[^ab]", "c", TRUE, TRUE);
+ TEST ("[!ab]", "c", TRUE, TRUE);
+ TEST ("[a-c]", "b", TRUE, TRUE);
+ TEST ("[a-c]", "d", TRUE, FALSE);
+ TEST ("[a-]", "-", TRUE, TRUE);
+ TEST ("[]]", "]", TRUE, TRUE);
+ TEST ("[^]]", "a", TRUE, TRUE);
+ TEST ("[!]]", "a", TRUE, TRUE);
+
+ /* Various unclosed ranges */
+ TEST ("[ab", "a", TRUE, FALSE);
+ TEST ("[a-", "a", TRUE, FALSE);
+ TEST ("[ab", "c", TRUE, FALSE);
+ TEST ("[a-", "c", TRUE, FALSE);
+ TEST ("[^]", "a", TRUE, FALSE);
+
+ /* Ranges and special no-wildcard matches */
+ TEST ("[.]", ".", TRUE, FALSE);
+ TEST ("a[.]", "a.", TRUE, TRUE);
+ TEST ("a/[.]", "a/.", TRUE, FALSE);
+ TEST ("[/]", "/", TRUE, FALSE);
+ TEST ("[^/]", "a", TRUE, TRUE);
+
+ /* Basic tests of * (and combinations of * and ?) */
+ TEST ("a*b", "ab", TRUE, TRUE);
+ TEST ("a*b", "axb", TRUE, TRUE);
+ TEST ("a*b", "axxb", TRUE, TRUE);
+ TEST ("a**b", "ab", TRUE, TRUE);
+ TEST ("a**b", "axb", TRUE, TRUE);
+ TEST ("a**b", "axxb", TRUE, TRUE);
+ TEST ("a*?*b", "ab", TRUE, FALSE);
+ TEST ("a*?*b", "axb", TRUE, TRUE);
+ TEST ("a*?*b", "axxb", TRUE, TRUE);
+
+ /* Test of *[range] */
+ TEST ("a*[cd]", "ac", TRUE, TRUE);
+ TEST ("a*[cd]", "axc", TRUE, TRUE);
+ TEST ("a*[cd]", "axx", TRUE, FALSE);
+
+ TEST ("a/[.]", "a/.", TRUE, FALSE);
+ TEST ("a*[.]", "a/.", TRUE, FALSE);
+
+ /* Test of UTF-8 */
+
+ TEST ("ä", "ä", TRUE, TRUE); /* TEST ("ä", "ä", TRUE); */
+ TEST ("?", "ä", TRUE, TRUE); /* TEST ("?", "ä", TRUE); */
+ TEST ("*ö", "äö", TRUE, TRUE); /* TEST ("*ö", "äö", TRUE); */
+ TEST ("*ö", "ääö", TRUE, TRUE); /* TEST ("*ö", "ääö", TRUE); */
+ TEST ("[ä]", "ä", TRUE, TRUE); /* TEST ("[ä]", "ä", TRUE); */
+ TEST ("[ä-ö]", "é", TRUE, TRUE); /* TEST ("[ä-ö]", "é", TRUE); */
+ TEST ("[ä-ö]", "a", TRUE, FALSE); /* TEST ("[ä-ö]", "a", FALSE); */
+
+#ifdef DO_ESCAPE
+ /* Tests of escaping */
+ TEST ("\\\\", "\\", TRUE, TRUE);
+ TEST ("\\?", "?", TRUE, TRUE);
+ TEST ("\\?", "a", TRUE, FALSE);
+ TEST ("\\*", "*", TRUE, TRUE);
+ TEST ("\\*", "a", TRUE, FALSE);
+ TEST ("\\[a-b]", "[a-b]", TRUE, TRUE);
+ TEST ("[\\\\]", "\\", TRUE, TRUE);
+ TEST ("[\\^a]", "a", TRUE, TRUE);
+ TEST ("[a\\-c]", "b", TRUE, FALSE);
+ TEST ("[a\\-c]", "-", TRUE, TRUE);
+ TEST ("[a\\]", "a", TRUE, FALSE);
+#endif /* DO_ESCAPE */
+
+ return 0;
}
-#endif /* _LIBC or not __GNU_LIBRARY__. */
+#endif /* FNMATCH_TEST_CASES */