aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/xdgmimeglob.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/xdgmimeglob.cpp')
-rw-r--r--src/xdgmimeglob.cpp473
1 files changed, 473 insertions, 0 deletions
diff --git a/src/xdgmimeglob.cpp b/src/xdgmimeglob.cpp
new file mode 100644
index 00000000..4182f3c9
--- /dev/null
+++ b/src/xdgmimeglob.cpp
@@ -0,0 +1,473 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeglob.c: Private file. Datastructure for storing the globs.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * 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
+ * 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmimeglob.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+typedef struct XdgGlobHashNode XdgGlobHashNode;
+typedef struct XdgGlobList XdgGlobList;
+
+struct XdgGlobHashNode
+{
+ xdg_unichar_t character;
+ const char *mime_type;
+ XdgGlobHashNode *next;
+ XdgGlobHashNode *child;
+};
+struct XdgGlobList
+{
+ const char *data;
+ const char *mime_type;
+ XdgGlobList *next;
+};
+
+struct XdgGlobHash
+{
+ XdgGlobList *literal_list;
+ XdgGlobHashNode *simple_node;
+ XdgGlobList *full_list;
+};
+
+
+/* XdgGlobList
+ */
+static XdgGlobList *
+_xdg_glob_list_new(void)
+{
+ XdgGlobList *new_element;
+
+ new_element = (XdgGlobList *)calloc(1, sizeof(XdgGlobList));
+
+ return new_element;
+}
+
+/* Frees glob_list and all of it's children */
+static void
+_xdg_glob_list_free(XdgGlobList *glob_list)
+{
+ XdgGlobList *ptr, *next;
+
+ ptr = glob_list;
+
+ while (ptr != NULL)
+ {
+ next = ptr->next;
+
+ if (ptr->data)
+ free((void *) ptr->data);
+ if (ptr->mime_type)
+ free((void *) ptr->mime_type);
+ free(ptr);
+
+ ptr = next;
+ }
+}
+
+static XdgGlobList *
+_xdg_glob_list_append(XdgGlobList *glob_list,
+ void *data,
+ const char *mime_type)
+{
+ XdgGlobList *new_element;
+ XdgGlobList *tmp_element;
+
+ new_element = _xdg_glob_list_new();
+ new_element->data = (const char *)data;
+ new_element->mime_type = mime_type;
+ if (glob_list == NULL)
+ return new_element;
+
+ tmp_element = glob_list;
+ while (tmp_element->next != NULL)
+ tmp_element = tmp_element->next;
+
+ tmp_element->next = new_element;
+
+ return glob_list;
+}
+
+#if 0
+static XdgGlobList *
+_xdg_glob_list_prepend(XdgGlobList *glob_list,
+ void *data,
+ const char *mime_type)
+{
+ XdgGlobList *new_element;
+
+ new_element = _xdg_glob_list_new();
+ new_element->data = data;
+ new_element->next = glob_list;
+ new_element->mime_type = mime_type;
+
+ return new_element;
+}
+#endif
+
+/* XdgGlobHashNode
+ */
+
+static XdgGlobHashNode *
+_xdg_glob_hash_node_new(void)
+{
+ XdgGlobHashNode *glob_hash_node;
+
+ glob_hash_node = (XdgGlobHashNode *)calloc(1, sizeof(XdgGlobHashNode));
+
+ return glob_hash_node;
+}
+
+static void
+_xdg_glob_hash_node_dump(XdgGlobHashNode *glob_hash_node,
+ int depth)
+{
+ int i;
+ for (i = 0; i < depth; i++)
+ printf(" ");
+
+ printf("%c", (char)glob_hash_node->character);
+ if (glob_hash_node->mime_type)
+ printf(" - %s\n", glob_hash_node->mime_type);
+ else
+ printf("\n");
+ if (glob_hash_node->child)
+ _xdg_glob_hash_node_dump(glob_hash_node->child, depth + 1);
+ if (glob_hash_node->next)
+ _xdg_glob_hash_node_dump(glob_hash_node->next, depth);
+}
+
+static XdgGlobHashNode *
+_xdg_glob_hash_insert_text(XdgGlobHashNode *glob_hash_node,
+ const char *text,
+ const char *mime_type)
+{
+ XdgGlobHashNode *node;
+ xdg_unichar_t character;
+
+ character = _xdg_utf8_to_ucs4(text);
+
+ if ((glob_hash_node == NULL) ||
+ (character < glob_hash_node->character))
+ {
+ node = _xdg_glob_hash_node_new();
+ node->character = character;
+ node->next = glob_hash_node;
+ glob_hash_node = node;
+ }
+ else if (character == glob_hash_node->character)
+ {
+ node = glob_hash_node;
+ }
+ else
+ {
+ XdgGlobHashNode *prev_node;
+ int found_node = FALSE;
+
+ /* Look for the first character of text in glob_hash_node, and insert it if we
+ * have to.*/
+ prev_node = glob_hash_node;
+ node = prev_node->next;
+
+ while (node != NULL)
+ {
+ if (character < node->character)
+ {
+ node = _xdg_glob_hash_node_new();
+ node->character = character;
+ node->next = prev_node->next;
+ prev_node->next = node;
+
+ found_node = TRUE;
+ break;
+ }
+ else if (character == node->character)
+ {
+ found_node = TRUE;
+ break;
+ }
+ prev_node = node;
+ node = node->next;
+ }
+
+ if (! found_node)
+ {
+ node = _xdg_glob_hash_node_new();
+ node->character = character;
+ node->next = prev_node->next;
+ prev_node->next = node;
+ }
+ }
+
+ text = _xdg_utf8_next_char(text);
+ if (*text == '\000')
+ {
+ node->mime_type = mime_type;
+ }
+ else
+ {
+ node->child = _xdg_glob_hash_insert_text(node->child, text, mime_type);
+ }
+ return glob_hash_node;
+}
+
+static const char *
+_xdg_glob_hash_node_lookup_file_name(XdgGlobHashNode *glob_hash_node,
+ const char *file_name,
+ int ignore_case)
+{
+ XdgGlobHashNode *node;
+ xdg_unichar_t character;
+
+ if (glob_hash_node == NULL)
+ return NULL;
+
+ character = _xdg_utf8_to_ucs4(file_name);
+ if (ignore_case)
+ character = _xdg_ucs4_to_lower(character);
+
+ for (node = glob_hash_node; node && character >= node->character; node = node->next)
+ {
+ if (character == node->character)
+ {
+ file_name = _xdg_utf8_next_char(file_name);
+ if (*file_name == '\000')
+ return node->mime_type;
+ else
+ return _xdg_glob_hash_node_lookup_file_name(node->child,
+ file_name,
+ ignore_case);
+ }
+ }
+ return NULL;
+}
+
+const char *
+_xdg_glob_hash_lookup_file_name(XdgGlobHash *glob_hash,
+ const char *file_name)
+{
+ XdgGlobList *list;
+ const char *mime_type;
+ const char *ptr;
+ /* First, check the literals */
+
+ assert(file_name != NULL);
+
+ for (list = glob_hash->literal_list; list; list = list->next)
+ if (strcmp((const char *)list->data, file_name) == 0)
+ return list->mime_type;
+
+ ptr = strchr(file_name, '.');
+ while (ptr != NULL)
+ {
+ mime_type = (_xdg_glob_hash_node_lookup_file_name(glob_hash->simple_node, ptr, FALSE));
+ if (mime_type != NULL)
+ return mime_type;
+
+ mime_type = (_xdg_glob_hash_node_lookup_file_name(glob_hash->simple_node, ptr, TRUE));
+ if (mime_type != NULL)
+ return mime_type;
+
+ ptr = strchr(ptr+1, '.');
+ }
+
+ /* FIXME: Not UTF-8 safe */
+ for (list = glob_hash->full_list; list; list = list->next)
+ if (fnmatch((const char *)list->data, file_name, 0) == 0)
+ return list->mime_type;
+
+ return NULL;
+}
+
+
+
+/* XdgGlobHash
+ */
+
+XdgGlobHash *
+_xdg_glob_hash_new(void)
+{
+ XdgGlobHash *glob_hash;
+
+ glob_hash = (XdgGlobHash *)calloc(1, sizeof(XdgGlobHash));
+
+ return glob_hash;
+}
+
+
+static void
+_xdg_glob_hash_free_nodes(XdgGlobHashNode *node)
+{
+ if (node)
+ {
+ if (node->child)
+ _xdg_glob_hash_free_nodes(node->child);
+ if (node->next)
+ _xdg_glob_hash_free_nodes(node->next);
+ if (node->mime_type)
+ free((void *) node->mime_type);
+ free(node);
+ }
+}
+
+void
+_xdg_glob_hash_free(XdgGlobHash *glob_hash)
+{
+ _xdg_glob_list_free(glob_hash->literal_list);
+ _xdg_glob_list_free(glob_hash->full_list);
+ _xdg_glob_hash_free_nodes(glob_hash->simple_node);
+ free(glob_hash);
+}
+
+XdgGlobType
+_xdg_glob_determine_type(const char *glob)
+{
+ const char *ptr;
+ int maybe_in_simple_glob = FALSE;
+ int first_char = TRUE;
+
+ ptr = glob;
+
+ while (*ptr != '\000')
+ {
+ if (*ptr == '*' && first_char)
+ maybe_in_simple_glob = TRUE;
+ else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
+ return XDG_GLOB_FULL;
+
+ first_char = FALSE;
+ ptr = _xdg_utf8_next_char(ptr);
+ }
+ if (maybe_in_simple_glob)
+ return XDG_GLOB_SIMPLE;
+ else
+ return XDG_GLOB_LITERAL;
+}
+
+/* glob must be valid UTF-8 */
+void
+_xdg_glob_hash_append_glob(XdgGlobHash *glob_hash,
+ const char *glob,
+ const char *mime_type)
+{
+ XdgGlobType type;
+
+ assert(glob_hash != NULL);
+ assert(glob != NULL);
+
+ type = _xdg_glob_determine_type(glob);
+
+ switch (type)
+ {
+ case XDG_GLOB_LITERAL:
+ glob_hash->literal_list = _xdg_glob_list_append(glob_hash->literal_list, strdup(glob), strdup(mime_type));
+ break;
+ case XDG_GLOB_SIMPLE:
+ glob_hash->simple_node = _xdg_glob_hash_insert_text(glob_hash->simple_node, glob + 1, strdup(mime_type));
+ break;
+ case XDG_GLOB_FULL:
+ glob_hash->full_list = _xdg_glob_list_append(glob_hash->full_list, strdup(glob), strdup(mime_type));
+ break;
+ }
+}
+
+void
+_xdg_glob_hash_dump(XdgGlobHash *glob_hash)
+{
+ XdgGlobList *list;
+ printf("LITERAL STRINGS\n");
+ if (glob_hash->literal_list == NULL)
+ {
+ printf(" None\n");
+ }
+ else
+ {
+ for (list = glob_hash->literal_list; list; list = list->next)
+ printf(" %s - %s\n", (char *)list->data, list->mime_type);
+ }
+ printf("\nSIMPLE GLOBS\n");
+ _xdg_glob_hash_node_dump(glob_hash->simple_node, 4);
+
+ printf("\nFULL GLOBS\n");
+ if (glob_hash->full_list == NULL)
+ {
+ printf(" None\n");
+ }
+ else
+ {
+ for (list = glob_hash->full_list; list; list = list->next)
+ printf(" %s - %s\n", (char *)list->data, list->mime_type);
+ }
+}
+
+
+void
+_xdg_mime_glob_read_from_file(XdgGlobHash *glob_hash,
+ const char *file_name)
+{
+ FILE *glob_file;
+ char line[255];
+
+ /* OK to not use CLO_EXEC here because mimedb is single threaded */
+ glob_file = fopen(file_name, "r");
+
+ if (glob_file == NULL)
+ return;
+
+ /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
+ * Blah */
+ while (fgets(line, 255, glob_file) != NULL)
+ {
+ char *colon;
+ if (line[0] == '#')
+ continue;
+
+ colon = strchr(line, ':');
+ if (colon == NULL)
+ continue;
+ *(colon++) = '\000';
+ colon[strlen(colon) -1] = '\000';
+ _xdg_glob_hash_append_glob(glob_hash, colon, line);
+ }
+
+ fclose(glob_file);
+}