From 47400e4bd849092b8b6a4404844a4fb042ae86a9 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 3 Jun 2021 23:50:09 -0400 Subject: [PATCH] Add a helper to make case-insensitive globs This will be used in GtkFileFilter in the future. Tests included. --- gtk/fnmatch.c | 64 +++++++++++++++++++++++++++++++++++++++++ gtk/gtkprivate.h | 3 ++ testsuite/gtk/fnmatch.c | 37 ++++++++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/gtk/fnmatch.c b/gtk/fnmatch.c index 0ad1cd8992..9b51df208d 100644 --- a/gtk/fnmatch.c +++ b/gtk/fnmatch.c @@ -253,3 +253,67 @@ _gtk_fnmatch (const char *pattern, { return gtk_fnmatch_intern (pattern, string, TRUE, no_leading_period, casefold); } + +/* Turn a glob pattern into a case-insensitive one, by replacing + * alphabetic characters by [xX] ranges. + */ +char * +_gtk_make_ci_glob_pattern (const char *pattern) +{ + GString *s; + gboolean in_range = FALSE; + + s = g_string_new (""); + for (const char *p = pattern; *p; p = g_utf8_next_char (p)) + { + gunichar c = g_utf8_get_char (p); + if (in_range) + { + g_string_append_unichar (s, c); + if (c == ']') + in_range = FALSE; + continue; + } + +#if DO_ESCAPE + if (c == '\\') + { + g_string_append (s, "\\"); + p = g_utf8_next_char (p); + if (*p == '\0') + break; + + c = g_utf8_get_char (p); + g_string_append_unichar (s, c); + continue; + } +#endif + + if (c == '[') + { + g_string_append (s, "["); + p = g_utf8_next_char (p); + if (*p == '\0') + break; + + c = g_utf8_get_char (p); + g_string_append_unichar (s, c); + + in_range = TRUE; + continue; + } + else if (g_unichar_isalpha (c)) + { + g_string_append (s, "["); + g_string_append_unichar (s, g_unichar_tolower (c)); + g_string_append_unichar (s, g_unichar_toupper (c)); + g_string_append (s, "]"); + } + else + { + g_string_append_unichar (s, c); + } + } + + return g_string_free (s, FALSE); +} diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h index d24c272065..8cb11c761c 100644 --- a/gtk/gtkprivate.h +++ b/gtk/gtkprivate.h @@ -64,6 +64,9 @@ gboolean _gtk_fnmatch (const char *pattern, gboolean no_leading_period, gboolean casefold); +char * _gtk_make_ci_glob_pattern (const char *pattern); + + char * _gtk_get_lc_ctype (void); void _gtk_ensure_resources (void); diff --git a/testsuite/gtk/fnmatch.c b/testsuite/gtk/fnmatch.c index a614c1f99b..e16bdae016 100644 --- a/testsuite/gtk/fnmatch.c +++ b/testsuite/gtk/fnmatch.c @@ -125,6 +125,37 @@ test_fnmatch (gconstpointer data) g_assert_true (_gtk_fnmatch (test->pat, test->str, test->no_leading_period, test->ci) == test->result); } +typedef struct { + const char *glob; + const char *ci; +} CITest; + +static CITest citests[] = { + { "*.txt", "*.[tT][xX][tT]" }, + { "*.TXT", "*.[tT][xX][tT]" }, + { "*?[]-abc]t", "*?[]-abc][tT]" }, +#ifdef DO_ESCAPE + /* Tests of escaping */ + { "\\\\", "\\\\" }, + { "\\??", "\\??" }, + { "\\**", "\\**" }, + { "\\[", "\\[" }, + { "\\[a-", "\\[[aA]-" }, + { "\\[]", "\\[]" }, +#endif +}; + +static void +test_ci_glob (gconstpointer data) +{ + const CITest *test = data; + char *ci; + + ci = _gtk_make_ci_glob_pattern (test->glob); + g_assert_cmpstr (ci, ==, test->ci); + g_free (ci); +} + int main (int argc, char *argv[]) { @@ -136,5 +167,11 @@ main (int argc, char *argv[]) g_test_add_data_func (path, &tests[i], test_fnmatch); } + for (int i = 0; i < G_N_ELEMENTS (citests); i++) + { + char *path = g_strdup_printf ("/ci-glob/test%d", i); + g_test_add_data_func (path, &citests[i], test_ci_glob); + } + return g_test_run (); }