forked from AuroraMiddleware/gtk
Merge branch 'matthiasc/for-master' into 'master'
fnmatch: Support case-folding See merge request GNOME/gtk!3628
This commit is contained in:
commit
036dd60a4e
157
gtk/fnmatch.c
157
gtk/fnmatch.c
@ -36,14 +36,14 @@
|
||||
#include <glib.h>
|
||||
|
||||
static gunichar
|
||||
get_char (const char **str)
|
||||
get_char (const char **str,
|
||||
gboolean casefold)
|
||||
{
|
||||
gunichar c = g_utf8_get_char (*str);
|
||||
*str = g_utf8_next_char (*str);
|
||||
|
||||
#ifdef G_PLATFORM_WIN32
|
||||
c = g_unichar_tolower (c);
|
||||
#endif
|
||||
if (casefold)
|
||||
c = g_unichar_tolower (c);
|
||||
|
||||
return c;
|
||||
}
|
||||
@ -56,13 +56,14 @@ get_char (const char **str)
|
||||
|
||||
static gunichar
|
||||
get_unescaped_char (const char **str,
|
||||
gboolean *was_escaped)
|
||||
gboolean *was_escaped,
|
||||
gboolean casefold)
|
||||
{
|
||||
gunichar c = get_char (str);
|
||||
gunichar c = get_char (str, casefold);
|
||||
|
||||
*was_escaped = DO_ESCAPE && c == '\\';
|
||||
if (*was_escaped)
|
||||
c = get_char (str);
|
||||
c = get_char (str, casefold);
|
||||
|
||||
return c;
|
||||
}
|
||||
@ -74,7 +75,8 @@ static gboolean
|
||||
gtk_fnmatch_intern (const char *pattern,
|
||||
const char *string,
|
||||
gboolean component_start,
|
||||
gboolean no_leading_period)
|
||||
gboolean no_leading_period,
|
||||
gboolean casefold)
|
||||
{
|
||||
const char *p = pattern, *n = string;
|
||||
|
||||
@ -82,8 +84,8 @@ gtk_fnmatch_intern (const char *pattern,
|
||||
{
|
||||
const char *last_n = n;
|
||||
|
||||
gunichar c = get_char (&p);
|
||||
gunichar nc = get_char (&n);
|
||||
gunichar c = get_char (&p, casefold);
|
||||
gunichar nc = get_char (&n, casefold);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
@ -97,7 +99,7 @@ gtk_fnmatch_intern (const char *pattern,
|
||||
break;
|
||||
case '\\':
|
||||
if (DO_ESCAPE)
|
||||
c = get_char (&p);
|
||||
c = get_char (&p, casefold);
|
||||
if (nc != c)
|
||||
return FALSE;
|
||||
break;
|
||||
@ -108,9 +110,9 @@ gtk_fnmatch_intern (const char *pattern,
|
||||
{
|
||||
const char *last_p;
|
||||
|
||||
for (last_p = p, c = get_char (&p);
|
||||
for (last_p = p, c = get_char (&p, casefold);
|
||||
c == '?' || c == '*';
|
||||
last_p = p, c = get_char (&p))
|
||||
last_p = p, c = get_char (&p, casefold))
|
||||
{
|
||||
if (c == '?')
|
||||
{
|
||||
@ -120,7 +122,7 @@ gtk_fnmatch_intern (const char *pattern,
|
||||
return FALSE;
|
||||
else
|
||||
{
|
||||
last_n = n; nc = get_char (&n);
|
||||
last_n = n; nc = get_char (&n, casefold);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,17 +140,17 @@ gtk_fnmatch_intern (const char *pattern,
|
||||
}
|
||||
|
||||
if (DO_ESCAPE && c == '\\')
|
||||
c = get_char (&p);
|
||||
c = get_char (&p, casefold);
|
||||
|
||||
for (p = last_p; nc != '\0';)
|
||||
{
|
||||
if ((c == '[' || nc == c) &&
|
||||
gtk_fnmatch_intern (p, last_n, component_start, no_leading_period))
|
||||
gtk_fnmatch_intern (p, last_n, component_start, no_leading_period, casefold))
|
||||
return TRUE;
|
||||
|
||||
component_start = (nc == G_DIR_SEPARATOR);
|
||||
last_n = n;
|
||||
nc = get_char (&n);
|
||||
nc = get_char (&n, casefold);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
@ -170,7 +172,7 @@ gtk_fnmatch_intern (const char *pattern,
|
||||
if (not)
|
||||
++p;
|
||||
|
||||
c = get_unescaped_char (&p, &was_escaped);
|
||||
c = get_unescaped_char (&p, &was_escaped, casefold);
|
||||
for (;;)
|
||||
{
|
||||
register gunichar cstart = c, cend = c;
|
||||
@ -178,15 +180,15 @@ gtk_fnmatch_intern (const char *pattern,
|
||||
/* [ (unterminated) loses. */
|
||||
return FALSE;
|
||||
|
||||
c = get_unescaped_char (&p, &was_escaped);
|
||||
c = get_unescaped_char (&p, &was_escaped, casefold);
|
||||
|
||||
if (!was_escaped && c == '-' && *p != ']')
|
||||
{
|
||||
cend = get_unescaped_char (&p, &was_escaped);
|
||||
cend = get_unescaped_char (&p, &was_escaped, casefold);
|
||||
if (cend == '\0')
|
||||
return FALSE;
|
||||
|
||||
c = get_char (&p);
|
||||
c = get_char (&p, casefold);
|
||||
}
|
||||
|
||||
if (nc >= cstart && nc <= cend)
|
||||
@ -208,7 +210,7 @@ gtk_fnmatch_intern (const char *pattern,
|
||||
/* [... (unterminated) loses. */
|
||||
return FALSE;
|
||||
|
||||
c = get_unescaped_char (&p, &was_escaped);
|
||||
c = get_unescaped_char (&p, &was_escaped, casefold);
|
||||
}
|
||||
if (not)
|
||||
return FALSE;
|
||||
@ -246,113 +248,8 @@ gtk_fnmatch_intern (const char *pattern,
|
||||
gboolean
|
||||
_gtk_fnmatch (const char *pattern,
|
||||
const char *string,
|
||||
gboolean no_leading_period)
|
||||
gboolean no_leading_period,
|
||||
gboolean casefold)
|
||||
{
|
||||
return gtk_fnmatch_intern (pattern, string, TRUE, no_leading_period);
|
||||
return gtk_fnmatch_intern (pattern, string, TRUE, no_leading_period, casefold);
|
||||
}
|
||||
|
||||
#undef FNMATCH_TEST_CASES
|
||||
#ifdef FNMATCH_TEST_CASES
|
||||
|
||||
#define TEST(pat, str, no_leading_period, result) \
|
||||
g_assert (_gtk_fnmatch ((pat), (str), (no_leading_period)) == result)
|
||||
|
||||
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 /* FNMATCH_TEST_CASES */
|
||||
|
@ -804,7 +804,7 @@ gtk_file_filter_match (GtkFilter *filter,
|
||||
display_name = g_file_info_get_display_name (info);
|
||||
if (display_name)
|
||||
{
|
||||
if (_gtk_fnmatch (rule->u.pattern, display_name, FALSE))
|
||||
if (_gtk_fnmatch (rule->u.pattern, display_name, FALSE, FALSE))
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,8 @@ const char * _gtk_get_data_prefix (void);
|
||||
|
||||
gboolean _gtk_fnmatch (const char *pattern,
|
||||
const char *string,
|
||||
gboolean no_leading_period);
|
||||
gboolean no_leading_period,
|
||||
gboolean casefold);
|
||||
|
||||
char * _gtk_get_lc_ctype (void);
|
||||
|
||||
|
140
testsuite/gtk/fnmatch.c
Normal file
140
testsuite/gtk/fnmatch.c
Normal file
@ -0,0 +1,140 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include "gtk/gtkprivate.h"
|
||||
|
||||
#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
|
||||
#define DO_ESCAPE 0
|
||||
#else
|
||||
#define DO_ESCAPE 1
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
const char *pat;
|
||||
const char *str;
|
||||
gboolean no_leading_period;
|
||||
gboolean ci;
|
||||
gboolean result;
|
||||
} TestCase;
|
||||
|
||||
static TestCase tests[] = {
|
||||
{ "[a-]", "-", TRUE, FALSE, TRUE },
|
||||
|
||||
{ "a", "a", TRUE, FALSE, TRUE },
|
||||
{ "a", "b", TRUE, FALSE, FALSE },
|
||||
|
||||
/* Test what ? matches */
|
||||
{ "?", "a", TRUE, FALSE, TRUE },
|
||||
{ "?", ".", TRUE, FALSE, FALSE },
|
||||
{ "a?", "a.", TRUE, FALSE, TRUE },
|
||||
{ "a/?", "a/b", TRUE, FALSE, TRUE },
|
||||
{ "a/?", "a/.", TRUE, FALSE, FALSE },
|
||||
{ "?", "/", TRUE, FALSE, FALSE },
|
||||
|
||||
/* Test what * matches */
|
||||
{ "*", "a", TRUE, FALSE, TRUE },
|
||||
{ "*", ".", TRUE, FALSE, FALSE },
|
||||
{ "a*", "a.", TRUE, FALSE, TRUE },
|
||||
{ "a/*", "a/b", TRUE, FALSE, TRUE },
|
||||
{ "a/*", "a/.", TRUE, FALSE, FALSE },
|
||||
{ "*", "/", TRUE, FALSE, FALSE },
|
||||
|
||||
/* Range tests */
|
||||
{ "[ab]", "a", TRUE, FALSE, TRUE },
|
||||
{ "[ab]", "c", TRUE, FALSE, FALSE },
|
||||
{ "[^ab]", "a", TRUE, FALSE, FALSE },
|
||||
{ "[!ab]", "a", TRUE, FALSE, FALSE },
|
||||
{ "[^ab]", "c", TRUE, FALSE, TRUE },
|
||||
{ "[!ab]", "c", TRUE, FALSE, TRUE },
|
||||
{ "[a-c]", "b", TRUE, FALSE, TRUE },
|
||||
{ "[a-c]", "d", TRUE, FALSE, FALSE },
|
||||
{ "[a-]", "-", TRUE, FALSE, TRUE },
|
||||
{ "[]]", "]", TRUE, FALSE, TRUE },
|
||||
{ "[^]]", "a", TRUE, FALSE, TRUE },
|
||||
{ "[!]]", "a", TRUE, FALSE, TRUE },
|
||||
|
||||
/* Various unclosed ranges */
|
||||
{ "[ab", "a", TRUE, FALSE, FALSE },
|
||||
{ "[a-", "a", TRUE, FALSE, FALSE },
|
||||
{ "[ab", "c", TRUE, FALSE, FALSE },
|
||||
{ "[a-", "c", TRUE, FALSE, FALSE },
|
||||
{ "[^]", "a", TRUE, FALSE, FALSE },
|
||||
|
||||
/* Ranges and special no-wildcard matches */
|
||||
{ "[.]", ".", TRUE, FALSE, FALSE },
|
||||
{ "a[.]", "a.", TRUE, FALSE, TRUE },
|
||||
{ "a/[.]", "a/.", TRUE, FALSE, FALSE },
|
||||
{ "[/]", "/", TRUE, FALSE, FALSE },
|
||||
{ "[^/]", "a", TRUE, FALSE, TRUE },
|
||||
|
||||
/* Basic tests of * (and combinations of * and ?) */
|
||||
{ "a*b", "ab", TRUE, FALSE, TRUE },
|
||||
{ "a*b", "axb", TRUE, FALSE, TRUE },
|
||||
{ "a*b", "axxb", TRUE, FALSE, TRUE },
|
||||
{ "a**b", "ab", TRUE, FALSE, TRUE },
|
||||
{ "a**b", "axb", TRUE, FALSE, TRUE },
|
||||
{ "a**b", "axxb", TRUE, FALSE, TRUE },
|
||||
{ "a*?*b", "ab", TRUE, FALSE, FALSE },
|
||||
{ "a*?*b", "axb", TRUE, FALSE, TRUE },
|
||||
{ "a*?*b", "axxb", TRUE, FALSE, TRUE },
|
||||
|
||||
/* Test of *[range] */
|
||||
{ "a*[cd]", "ac", TRUE, FALSE, TRUE },
|
||||
{ "a*[cd]", "axc", TRUE, FALSE, TRUE },
|
||||
{ "a*[cd]", "axx", TRUE, FALSE, FALSE },
|
||||
|
||||
{ "a/[.]", "a/.", TRUE, FALSE, FALSE },
|
||||
{ "a*[.]", "a/.", TRUE, FALSE, FALSE },
|
||||
|
||||
|
||||
/* Test of UTF-8 */
|
||||
|
||||
{ "ä", "ä", TRUE, FALSE, TRUE },
|
||||
{ "?", "ä", TRUE, FALSE, TRUE },
|
||||
{ "*ö", "äö", TRUE, FALSE, TRUE },
|
||||
{ "*ö", "ääö", TRUE, FALSE, TRUE },
|
||||
{ "[ä]", "ä", TRUE, FALSE, TRUE },
|
||||
{ "[ä-ö]", "é", TRUE, FALSE, TRUE },
|
||||
{ "[ä-ö]", "a", TRUE, FALSE, FALSE },
|
||||
|
||||
/* ci patterns */
|
||||
{ "*.txt", "a.TXT", TRUE, TRUE, TRUE },
|
||||
{ "*.txt", "a.TxT", TRUE, TRUE, TRUE },
|
||||
{ "*.txt", "a.txT", TRUE, TRUE, TRUE },
|
||||
{ "*ö", "äÖ", TRUE, TRUE, TRUE },
|
||||
|
||||
#ifdef DO_ESCAPE
|
||||
/* Tests of escaping */
|
||||
{ "\\\\", "\\", TRUE, FALSE, TRUE },
|
||||
{ "\\?", "?", TRUE, FALSE, TRUE },
|
||||
{ "\\?", "a", TRUE, FALSE, FALSE },
|
||||
{ "\\*", "*", TRUE, FALSE, TRUE },
|
||||
{ "\\*", "a", TRUE, FALSE, FALSE },
|
||||
{ "\\[a-b]", "[a-b]", TRUE, FALSE, TRUE },
|
||||
{ "[\\\\]", "\\", TRUE, FALSE, TRUE },
|
||||
{ "[\\^a]", "a", TRUE, FALSE, TRUE },
|
||||
{ "[a\\-c]", "b", TRUE, FALSE, FALSE },
|
||||
{ "[a\\-c]", "-", TRUE, FALSE, TRUE },
|
||||
{ "[a\\]", "a", TRUE, FALSE, FALSE },
|
||||
#endif /* DO_ESCAPE */
|
||||
};
|
||||
|
||||
static void
|
||||
test_fnmatch (gconstpointer data)
|
||||
{
|
||||
const TestCase *test = data;
|
||||
|
||||
g_assert_true (_gtk_fnmatch (test->pat, test->str, test->no_leading_period, test->ci) == test->result);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
(g_test_init) (&argc, &argv, NULL);
|
||||
|
||||
for (int i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
char *path = g_strdup_printf ("/fnmatch/test%d", i);
|
||||
g_test_add_data_func (path, &tests[i], test_fnmatch);
|
||||
}
|
||||
|
||||
return g_test_run ();
|
||||
}
|
@ -118,6 +118,7 @@ internal_tests = [
|
||||
{ 'name': 'rbtree' },
|
||||
{ 'name': 'timsort' },
|
||||
{ 'name': 'texthistory' },
|
||||
{ 'name': 'fnmatch' },
|
||||
]
|
||||
|
||||
# Tests that are expected to fail
|
||||
|
Loading…
Reference in New Issue
Block a user