[gtk/ci-file-filters] GtkFileFilter: Allow case-insensitive patterns



commit 2bdbebef81d88be9b670cc028088324d584dd7da
Author: Matthias Clasen <mclasen redhat com>
Date:   Wed Sep 26 15:55:47 2018 -0400

    GtkFileFilter: Allow case-insensitive patterns
    
    In the past, we've hardcoded case-insensitive matching
    on Windows, and case-sensitive matching everywhere else.
    
    With this change, we now have a way to explicitly add
    case-insensitive patterns to a filter. We still interpret
    all patterns case-insensitively on Windows.

 gtk/fnmatch.c       | 51 ++++++++++++++++++++------------------
 gtk/gtkfilefilter.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++----
 gtk/gtkfilefilter.h |  3 +++
 gtk/gtkprivate.h    |  3 ++-
 4 files changed, 97 insertions(+), 30 deletions(-)
---
diff --git a/gtk/fnmatch.c b/gtk/fnmatch.c
index 87582cab69..c2a0c60586 100644
--- a/gtk/fnmatch.c
+++ b/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 = 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,9 +248,10 @@ 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
diff --git a/gtk/gtkfilefilter.c b/gtk/gtkfilefilter.c
index a63d2ecce3..5583632ca2 100644
--- a/gtk/gtkfilefilter.c
+++ b/gtk/gtkfilefilter.c
@@ -82,6 +82,7 @@ typedef struct _FilterRule FilterRule;
 
 typedef enum {
   FILTER_RULE_PATTERN,
+  FILTER_RULE_PATTERN_CI,
   FILTER_RULE_MIME_TYPE,
   FILTER_RULE_PIXBUF_FORMATS,
   FILTER_RULE_CUSTOM
@@ -166,6 +167,7 @@ filter_rule_free (FilterRule *rule)
       g_free (rule->u.mime_type);
       break;
     case FILTER_RULE_PATTERN:
+    case FILTER_RULE_PATTERN_CI:
       g_free (rule->u.pattern);
       break;
     case FILTER_RULE_CUSTOM:
@@ -230,6 +232,7 @@ typedef struct {
   ParserType     type;
   GString       *string;
   gboolean       parsing;
+  gboolean       ignore_case;
 } SubParserData;
 
 static void
@@ -242,9 +245,23 @@ parser_start_element (GMarkupParseContext  *context,
 {
   SubParserData *data = (SubParserData*)user_data;
 
-  if (!g_markup_collect_attributes (element_name, names, values, error,
-                                    G_MARKUP_COLLECT_INVALID, NULL, NULL,
-                                    G_MARKUP_COLLECT_INVALID))
+  if (strcmp (element_name, "patterns") == 0)
+    {
+      gboolean ignore_case = FALSE;
+
+      if (!g_markup_collect_attributes (element_name, names, values, error,
+                                        G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "ignore-case", 
&ignore_case,
+                                        G_MARKUP_COLLECT_INVALID))
+        {
+          _gtk_builder_prefix_error (data->builder, context, error);
+          return;
+        }
+
+      data->ignore_case = ignore_case;
+    }
+  else if (!g_markup_collect_attributes (element_name, names, values, error,
+                                         G_MARKUP_COLLECT_INVALID, NULL, NULL,
+                                         G_MARKUP_COLLECT_INVALID))
     {
       _gtk_builder_prefix_error (data->builder, context, error);
       return;
@@ -307,7 +324,10 @@ parser_end_element (GMarkupParseContext *context,
           gtk_file_filter_add_mime_type (data->filter, data->string->str);
           break;
         case PARSE_PATTERNS:
-          gtk_file_filter_add_pattern (data->filter, data->string->str);
+          if (data->ignore_case)
+            gtk_file_filter_add_case_insensitive_pattern (data->filter, data->string->str);
+          else
+            gtk_file_filter_add_pattern (data->filter, data->string->str);
           break;
         default:
           break;
@@ -496,6 +516,31 @@ gtk_file_filter_add_pattern (GtkFileFilter *filter,
   file_filter_add_rule (filter, rule);
 }
 
+/**
+ * gtk_file_filter_add_case_insensitive_pattern:
+ * @filter: a #GtkFileFilter
+ * @pattern: a shell style glob
+ * 
+ * Adds a rule allowing a shell style glob to a filter,
+ * ignoring case.
+ **/
+void
+gtk_file_filter_add_case_insensitive_pattern (GtkFileFilter *filter,
+                                             const gchar   *pattern)
+{
+  FilterRule *rule;
+  
+  g_return_if_fail (GTK_IS_FILE_FILTER (filter));
+  g_return_if_fail (pattern != NULL);
+
+  rule = g_slice_new (FilterRule);
+  rule->type = FILTER_RULE_PATTERN_CI;
+  rule->needed = GTK_FILE_FILTER_DISPLAY_NAME;
+  rule->u.pattern = g_strdup (pattern);
+
+  file_filter_add_rule (filter, rule);
+}
+
 /**
  * gtk_file_filter_add_pixbuf_formats:
  * @filter: a #GtkFileFilter
@@ -609,6 +654,7 @@ NSArray * _gtk_file_filter_get_as_pattern_nsstrings (GtkFileFilter *filter)
          }
          break;
        case FILTER_RULE_PATTERN:
+       case FILTER_RULE_PATTERN_CI:
          {
            // patterns will need to be stripped of their leading *.
            GString *pattern = g_string_new (rule->u.pattern);
@@ -674,6 +720,7 @@ _gtk_file_filter_get_as_patterns (GtkFileFilter      *filter)
           return NULL;
          break;
        case FILTER_RULE_PATTERN:
+       case FILTER_RULE_PATTERN_CI:
           g_ptr_array_add (array, g_strdup (rule->u.pattern));
          break;
        case FILTER_RULE_PIXBUF_FORMATS:
@@ -728,6 +775,7 @@ gtk_file_filter_filter (GtkFileFilter           *filter,
   for (tmp_list = filter->rules; tmp_list; tmp_list = tmp_list->next)
     {
       FilterRule *rule = tmp_list->data;
+      gboolean casefold = FALSE;
 
       if ((filter_info->contains & rule->needed) != rule->needed)
        continue;
@@ -752,9 +800,15 @@ gtk_file_filter_filter (GtkFileFilter           *filter,
                 return TRUE;
         }
          break;
+       case FILTER_RULE_PATTERN_CI:
+          casefold = TRUE;
+          /* Fall thru */
        case FILTER_RULE_PATTERN:
+#ifdef G_PLATFORM_WIN32
+          casefold = TRUE;
+#endif
          if (filter_info->display_name != NULL &&
-             _gtk_fnmatch (rule->u.pattern, filter_info->display_name, FALSE))
+              _gtk_fnmatch (rule->u.pattern, filter_info->display_name, FALSE, casefold))
            return TRUE;
          break;
        case FILTER_RULE_PIXBUF_FORMATS:
@@ -821,6 +875,9 @@ gtk_file_filter_to_gvariant (GtkFileFilter *filter)
         case FILTER_RULE_PATTERN:
           g_variant_builder_add (&builder, "(us)", 0, rule->u.pattern);
           break;
+        case FILTER_RULE_PATTERN_CI:
+          g_variant_builder_add (&builder, "(us)", 2, rule->u.pattern);
+          break;
         case FILTER_RULE_MIME_TYPE:
           g_variant_builder_add (&builder, "(us)", 1, rule->u.mime_type);
           break;
@@ -881,6 +938,9 @@ gtk_file_filter_new_from_gvariant (GVariant *variant)
         case 0:
           gtk_file_filter_add_pattern (filter, tmp);
           break;
+        case 2:
+          gtk_file_filter_add_case_insensitive_pattern (filter, tmp);
+          break;
         case 1:
           gtk_file_filter_add_mime_type (filter, tmp);
           break;
diff --git a/gtk/gtkfilefilter.h b/gtk/gtkfilefilter.h
index 715556546a..7e3c470527 100644
--- a/gtk/gtkfilefilter.h
+++ b/gtk/gtkfilefilter.h
@@ -108,6 +108,9 @@ GDK_AVAILABLE_IN_ALL
 void gtk_file_filter_add_pattern        (GtkFileFilter      *filter,
                                         const gchar        *pattern);
 GDK_AVAILABLE_IN_ALL
+void gtk_file_filter_add_case_insensitive_pattern (GtkFileFilter *filter,
+                                                  const gchar   *pattern);
+GDK_AVAILABLE_IN_ALL
 void gtk_file_filter_add_pixbuf_formats (GtkFileFilter      *filter);
 GDK_AVAILABLE_IN_ALL
 void gtk_file_filter_add_custom         (GtkFileFilter      *filter,
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
index 58b2453930..6d1bf2c71d 100644
--- a/gtk/gtkprivate.h
+++ b/gtk/gtkprivate.h
@@ -57,7 +57,8 @@ const gchar * _gtk_get_data_prefix        (void);
 
 gboolean      _gtk_fnmatch                (const char *pattern,
                                            const char *string,
-                                           gboolean    no_leading_period);
+                                           gboolean    no_leading_period,
+                                           gboolean    casefold);
 
 gchar       * _gtk_get_lc_ctype           (void);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]