GDBusInterfaceVTable: clarify memory handling for the method() virtual function
[platform/upstream/glib.git] / gio / xdgmime / xdgmimeglob.c
index 4d786d0..73ef7a5 100644 (file)
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser 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.
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#include "config.h"
 #endif
 
 #include "xdgmimeglob.h"
 #include <string.h>
 #include <fnmatch.h>
 
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
 #ifndef        FALSE
 #define        FALSE   (0)
 #endif
@@ -53,6 +55,7 @@ struct XdgGlobHashNode
   xdg_unichar_t character;
   const char *mime_type;
   int weight;
+  int case_sensitive;
   XdgGlobHashNode *next;
   XdgGlobHashNode *child;
 };
@@ -61,6 +64,7 @@ struct XdgGlobList
   const char *data;
   const char *mime_type;
   int weight;
+  int case_sensitive;
   XdgGlobList *next;
 };
 
@@ -110,15 +114,27 @@ static XdgGlobList *
 _xdg_glob_list_append (XdgGlobList *glob_list,
                       void        *data,
                       const char  *mime_type,
-                      int          weight)
+                      int          weight,
+                      int          case_sensitive)
 {
   XdgGlobList *new_element;
   XdgGlobList *tmp_element;
 
+  tmp_element = glob_list;
+  while (tmp_element != NULL)
+    {
+      if (strcmp (tmp_element->data, data) == 0 &&
+         strcmp (tmp_element->mime_type, mime_type) == 0)
+       return glob_list;
+
+      tmp_element = tmp_element->next;
+    }
+
   new_element = _xdg_glob_list_new ();
   new_element->data = data;
   new_element->mime_type = mime_type;
   new_element->weight = weight;
+  new_element->case_sensitive = case_sensitive;
   if (glob_list == NULL)
     return new_element;
 
@@ -144,6 +160,8 @@ _xdg_glob_hash_node_new (void)
   return glob_hash_node;
 }
 
+#ifdef NOT_USED_IN_GIO
+
 static void
 _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
                          int depth)
@@ -163,11 +181,14 @@ _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
     _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
 }
 
+#endif
+
 static XdgGlobHashNode *
 _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node,
                            xdg_unichar_t   *text,
                            const char      *mime_type,
-                           int              weight)
+                           int              weight,
+                           int              case_sensitive)
 {
   XdgGlobHashNode *node;
   xdg_unichar_t character;
@@ -231,11 +252,11 @@ _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node,
     {
       if (node->mime_type)
        {
-         if (strcmp (node->mime_type, mime_type))
+         if (strcmp (node->mime_type, mime_type) != 0)
            {
              XdgGlobHashNode *child;
              int found_node = FALSE;
-             
+
              child = node->child;
              while (child && child->character == 0)
                {
@@ -253,6 +274,7 @@ _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node,
                  child->character = 0;
                  child->mime_type = strdup (mime_type);
                  child->weight = weight;
+                 child->case_sensitive = case_sensitive;
                  child->child = NULL;
                  child->next = node->child;
                  node->child = child;
@@ -263,11 +285,12 @@ _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node,
        {
          node->mime_type = strdup (mime_type);
          node->weight = weight;
+         node->case_sensitive = case_sensitive;
        }
     }
   else
     {
-      node->child = _xdg_glob_hash_insert_ucs4 (node->child, text, mime_type, weight);
+      node->child = _xdg_glob_hash_insert_ucs4 (node->child, text, mime_type, weight, case_sensitive);
     }
   return glob_hash_node;
 }
@@ -277,7 +300,8 @@ static XdgGlobHashNode *
 _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
                            const char      *text,
                            const char      *mime_type,
-                           int              weight)
+                           int              weight,
+                           int              case_sensitive)
 {
   XdgGlobHashNode *node;
   xdg_unichar_t *unitext;
@@ -285,7 +309,7 @@ _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
 
   unitext = _xdg_convert_to_ucs4 (text, &len);
   _xdg_reverse_ucs4 (unitext, len);
-  node = _xdg_glob_hash_insert_ucs4 (glob_hash_node, unitext, mime_type, weight);
+  node = _xdg_glob_hash_insert_ucs4 (glob_hash_node, unitext, mime_type, weight, case_sensitive);
   free (unitext);
   return node;
 }
@@ -297,9 +321,9 @@ typedef struct {
 
 static int
 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
-                                     xdg_unichar_t   *file_name,
+                                     const char      *file_name,
                                      int              len,
-                                     int              ignore_case,
+                                     int              case_sensitive_check,
                                      MimeWeight       mime_types[],
                                      int              n_mime_types)
 {
@@ -311,8 +335,6 @@ _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
     return 0;
 
   character = file_name[len - 1];
-  if (ignore_case)
-    character = _xdg_ucs4_to_lower(character);
 
   for (node = glob_hash_node; node && character >= node->character; node = node->next)
     {
@@ -325,13 +347,15 @@ _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
              n = _xdg_glob_hash_node_lookup_file_name (node->child,
                                                        file_name,
                                                        len,
-                                                       ignore_case,
+                                                       case_sensitive_check,
                                                        mime_types,
                                                        n_mime_types);
            }
          if (n == 0)
            {
-              if (node->mime_type)
+              if (node->mime_type &&
+                 (case_sensitive_check ||
+                  !node->case_sensitive))
                 {
                  mime_types[n].mime = node->mime_type;
                  mime_types[n].weight = node->weight;
@@ -340,7 +364,9 @@ _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
              node = node->child;
              while (n < n_mime_types && node && node->character == 0)
                {
-                  if (node->mime_type)
+                  if (node->mime_type &&
+                     (case_sensitive_check ||
+                      !node->case_sensitive))
                    {
                      mime_types[n].mime = node->mime_type;
                      mime_types[n].weight = node->weight;
@@ -361,7 +387,51 @@ static int compare_mime_weight (const void *a, const void *b)
   const MimeWeight *aa = (const MimeWeight *)a;
   const MimeWeight *bb = (const MimeWeight *)b;
 
-  return aa->weight - bb->weight;
+  return bb->weight - aa->weight;
+}
+
+#define ISUPPER(c)             ((c) >= 'A' && (c) <= 'Z')
+static char *
+ascii_tolower (const char *str)
+{
+  char *p, *lower;
+
+  lower = strdup (str);
+  p = lower;
+  while (*p != 0)
+    {
+      char c = *p;
+      *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
+    }
+  return lower;
+}
+
+static int
+filter_out_dupes (MimeWeight mimes[], int n_mimes)
+{
+  int last;
+  int i, j;
+
+  last = n_mimes;
+
+  for (i = 0; i < last; i++)
+    {
+      j = i + 1;
+      while (j < last)
+        {
+          if (strcmp (mimes[i].mime, mimes[j].mime) == 0)
+            {
+              mimes[i].weight = MAX (mimes[i].weight, mimes[j].weight);
+              last--;
+              mimes[j].mime = mimes[last].mime;
+              mimes[j].weight = mimes[last].weight;
+            }
+          else
+            j++;
+        }
+    }
+
+  return last;
 }
 
 int
@@ -374,8 +444,8 @@ _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
   int i, n;
   MimeWeight mimes[10];
   int n_mimes = 10;
-  xdg_unichar_t *ucs4;
   int len;
+  char *lower_case;
 
   /* First, check the literals */
 
@@ -383,25 +453,38 @@ _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
 
   n = 0;
 
+  lower_case = ascii_tolower (file_name);
+
   for (list = glob_hash->literal_list; list; list = list->next)
     {
       if (strcmp ((const char *)list->data, file_name) == 0)
        {
          mime_types[0] = list->mime_type;
+         free (lower_case);
+         return 1;
+       }
+    }
+
+  for (list = glob_hash->literal_list; list; list = list->next)
+    {
+      if (!list->case_sensitive &&
+         strcmp ((const char *)list->data, lower_case) == 0)
+       {
+         mime_types[0] = list->mime_type;
+         free (lower_case);
          return 1;
        }
     }
 
-  ucs4 = _xdg_convert_to_ucs4 (file_name, &len);
-  n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ucs4, len, FALSE,
+
+  len = strlen (file_name);
+  n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, lower_case, len, FALSE,
                                            mimes, n_mimes);
-  if (n == 0)
-    n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ucs4, len, TRUE,
-                                             mimes, n_mimes);
-  free(ucs4);
+  if (n < 2)
+    n += _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, file_name, len, TRUE,
+                                              mimes + n, n_mimes - n);
 
-  /* FIXME: Not UTF-8 safe */
-  if (n == 0)
+  if (n < 2)
     {
       for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next)
         {
@@ -413,6 +496,9 @@ _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
            }
         }
     }
+  free (lower_case);
+
+  n = filter_out_dupes (mimes, n);
 
   qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
 
@@ -495,7 +581,8 @@ void
 _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
                            const char  *glob,
                            const char  *mime_type,
-                           int          weight)
+                           int          weight,
+                           int          case_sensitive)
 {
   XdgGlobType type;
 
@@ -507,17 +594,19 @@ _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
   switch (type)
     {
     case XDG_GLOB_LITERAL:
-      glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type), weight);
+      glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type), weight, case_sensitive);
       break;
     case XDG_GLOB_SIMPLE:
-      glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type, weight);
+      glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type, weight, case_sensitive);
       break;
     case XDG_GLOB_FULL:
-      glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type), weight);
+      glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type), weight, case_sensitive);
       break;
     }
 }
 
+#ifdef NOT_USED_IN_GIO
+
 void
 _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
 {
@@ -554,13 +643,16 @@ _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
     }
 }
 
+#endif
 
 void
 _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
-                              const char  *file_name)
+                              const char  *file_name,
+                              int          version_two)
 {
   FILE *glob_file;
   char line[255];
+  char *p;
 
   glob_file = fopen (file_name, "r");
 
@@ -571,33 +663,67 @@ _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
    * Blah */
   while (fgets (line, 255, glob_file) != NULL)
     {
-      char *colon, *colon2;
-      char *mimetype, *glob;
+      char *colon;
+      char *mimetype, *glob, *end;
       int weight;
+      int case_sensitive;
 
-      if (line[0] == '#')
+      if (line[0] == '#' || line[0] == 0)
        continue;
 
-      colon = strchr (line, ':');
+      end = line + strlen(line) - 1;
+      if (*end == '\n')
+       *end = 0;
+
+      p = line;
+      if (version_two)
+       {
+         colon = strchr (p, ':');
+         if (colon == NULL)
+           continue;
+         *colon = 0;
+          weight = atoi (p);
+         p = colon + 1;
+       }
+      else
+       weight = 50;
+
+      colon = strchr (p, ':');
       if (colon == NULL)
        continue;
-      *(colon++) = '\0';
-      colon[strlen (colon) -1] = '\0';
-      colon2 = strchr (colon, ':');
-      if (colon2) 
-        {
-          *(colon2++) = '\000';
-          weight = atoi (line);
-          mimetype = colon;
-          glob = colon2;
-        }
-      else 
-        {
-          weight = 50;
-          mimetype = line;
-          glob = colon;
-        }
-      _xdg_glob_hash_append_glob (glob_hash, glob, mimetype, weight);
+      *colon = 0;
+
+      mimetype = p;
+      p = colon + 1;
+      glob = p;
+      case_sensitive = FALSE;
+
+      colon = strchr (p, ':');
+      if (version_two && colon != NULL)
+       {
+         char *flag;
+
+         /* We got flags */
+         *colon = 0;
+         p = colon + 1;
+
+         /* Flags end at next colon */
+         colon = strchr (p, ':');
+         if (colon != NULL)
+           *colon = 0;
+
+         flag = strstr (p, "cs");
+         if (flag != NULL &&
+             /* Start or after comma */
+             (flag == p ||
+              flag[-1] == ',') &&
+             /* ends with comma or end of string */
+             (flag[2] == 0 ||
+              flag[2] == ','))
+           case_sensitive = TRUE;
+       }
+
+      _xdg_glob_hash_append_glob (glob_hash, glob, mimetype, weight, case_sensitive);
     }
 
   fclose (glob_file);