summaryrefslogtreecommitdiff
path: root/update-mime-database.c
diff options
context:
space:
mode:
authorBastien Nocera <hadess@hadess.net>2008-07-18 11:50:55 +0000
committerBastien Nocera <hadess@hadess.net>2008-07-18 11:50:55 +0000
commit1bac8224c7439452a85693dc1a25f64d3a467e7d (patch)
tree658a5b1ada9dfa833119e63423cce7797c2ce988 /update-mime-database.c
parentd5b5d265bbe54fda3c438816d2703fdb88423370 (diff)
downloadshared-mime-info-1bac8224c7439452a85693dc1a25f64d3a467e7d.tar.gz
2008-07-18 Bastien Nocera <hadess@hadess.net>
* .cvsignore: upd * shared-mime-info-spec.xml: Update the spec to contain "content-types" or "tree magic", to detect an "x-content/" mime-type from a directory structure, patch from Matthias Clasen <mclasen@redhat.com> * update-mime-database.c (process_freedesktop_node), (cmp_tree_magic), (tree_match_new), (tree_match_free), (build_tree_matches), (tree_magic_free), (tree_magic_new), (write_tree_magic_children), (write_tree_magic), (add_type), (write_types), (write_header), (write_types_cache), (write_cache), (main): Add support for writing the tree-magic file, as per the spec update above, patch from Matthias Clasen <mclasen@redhat.com> * freedesktop.org.xml.in: Add one single tree-magic item, for testing * configure.in: check for GIO, build the tree magic test if it's available * Makefile.am: use test-tree-magic if it's there * test-tree-magic.c: automated testing for tree magic, based on GIO code by Matthias Clasen <mclasen@redhat.com> * tests/tree-list: A few tests
Diffstat (limited to 'update-mime-database.c')
-rw-r--r--update-mime-database.c461
1 files changed, 457 insertions, 4 deletions
diff --git a/update-mime-database.c b/update-mime-database.c
index fc99be41..f534ec1e 100644
--- a/update-mime-database.c
+++ b/update-mime-database.c
@@ -66,6 +66,12 @@ typedef struct _Magic Magic;
/* A parsed <match> element */
typedef struct _Match Match;
+/* A parsed <treemagic> element */
+typedef struct _TreeMagic TreeMagic;
+
+/* A parsed <treematch> element */
+typedef struct _TreeMatch TreeMatch;
+
/* A parsed <glob> element */
typedef struct _Glob Glob;
@@ -102,6 +108,23 @@ struct _Match {
GList *matches;
};
+struct _TreeMagic {
+ int priority;
+ Type *type;
+ GList *matches;
+};
+
+struct _TreeMatch {
+ char *path;
+ gboolean match_case;
+ gboolean executable;
+ gboolean non_empty;
+ gint type;
+ char *mimetype;
+
+ GList *matches;
+};
+
/* Maps MIME type names to Types */
static GHashTable *types = NULL;
@@ -114,6 +137,9 @@ static GHashTable *globs_hash = NULL;
/* 'magic' nodes */
static GPtrArray *magic_array = NULL;
+/* 'treemagic' nodes */
+static GPtrArray *tree_magic_array = NULL;
+
/* Maps MIME type names to superclass names */
static GHashTable *subclass_hash = NULL;
@@ -132,6 +158,8 @@ static GLogLevelFlags enabled_log_levels = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITI
/* Static prototypes */
static Magic *magic_new(xmlNode *node, Type *type, GError **error);
+static TreeMagic *tree_magic_new(xmlNode *node, Type *type, GError **error);
+
static void g_log_handler (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *message,
@@ -372,6 +400,20 @@ static gboolean process_freedesktop_node(Type *type, xmlNode *field,
else
g_return_val_if_fail(magic == NULL, FALSE);
}
+ else if (strcmp((char *)field->name, "treemagic") == 0)
+ {
+ TreeMagic *magic;
+
+ magic = tree_magic_new(field, type, error);
+
+ if (!*error)
+ {
+ g_return_val_if_fail(magic != NULL, FALSE);
+ g_ptr_array_add(tree_magic_array, magic);
+ }
+ else
+ g_return_val_if_fail(magic == NULL, FALSE);
+ }
else if (strcmp((char *)field->name, "comment") == 0 ||
strcmp((char *)field->name, "acronym") == 0 ||
strcmp((char *)field->name, "expanded-acronym") == 0)
@@ -857,6 +899,25 @@ static gint cmp_magic(gconstpointer a, gconstpointer b)
return retval;
}
+/* Comparison function to get the tree magic rules in priority order */
+static gint cmp_tree_magic(gconstpointer a, gconstpointer b)
+{
+ TreeMagic *aa = *(TreeMagic **) a;
+ TreeMagic *bb = *(TreeMagic **) b;
+ int retval;
+
+ if (aa->priority > bb->priority)
+ return -1;
+ else if (aa->priority < bb->priority)
+ return 1;
+
+ retval = strcmp(aa->type->media, bb->type->media);
+ if (!retval)
+ retval = strcmp(aa->type->subtype, bb->type->subtype);
+
+ return retval;
+}
+
/* Write out 'n' as a two-byte big-endian number to 'stream' */
static void write16(FILE *stream, guint32 n)
{
@@ -1398,6 +1459,222 @@ static Magic *magic_new(xmlNode *node, Type *type, GError **error)
return magic;
}
+static TreeMatch *tree_match_new(void)
+{
+ TreeMatch *match;
+
+ match = g_new(TreeMatch, 1);
+ match->path = NULL;
+ match->match_case = 0;
+ match->executable = 0;
+ match->non_empty = 0;
+ match->type = 0;
+ match->mimetype = NULL;
+ match->matches = NULL;
+
+ return match;
+}
+
+static void tree_match_free(TreeMatch *match)
+{
+ GList *next;
+
+ g_return_if_fail(match != NULL);
+
+ for (next = match->matches; next; next = next->next)
+ tree_match_free((TreeMatch *) next->data);
+
+ g_list_free(match->matches);
+
+ g_free(match->path);
+ g_free(match->mimetype);
+
+ g_free(match);
+}
+
+/* Turn the list of child nodes of 'parent' into a list of TreeMatches */
+static GList *build_tree_matches(xmlNode *parent, GError **error)
+{
+ xmlNode *node;
+ GList *out = NULL;
+ char *attr;
+
+ g_return_val_if_fail(error != NULL, NULL);
+
+ for (node = parent->xmlChildrenNode; node; node = node->next)
+ {
+ TreeMatch *match;
+
+ if (node->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (node->ns == NULL || xmlStrcmp(node->ns->href, FREE_NS) != 0)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Element found with non-freedesktop.org "
+ "namespace"));
+ break;
+ }
+
+ if (strcmp((char *)node->name, "treematch") != 0)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Expected <treematch> element, but found "
+ "<%s> instead"), node->name);
+ break;
+ }
+
+ match = tree_match_new();
+
+ attr = my_xmlGetNsProp(node, "path", NULL);
+ if (attr)
+ {
+ match->path = g_strdup (attr);
+ xmlFree (attr);
+ }
+ else
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Missing 'path' attribute in <treematch>"));
+ }
+ if (!*error)
+ {
+ attr = my_xmlGetNsProp(node, "type", NULL);
+ if (attr)
+ {
+ if (strcmp (attr, "file") == 0)
+ {
+ match->type = 1;
+ }
+ else if (strcmp (attr, "directory") == 0)
+ {
+ match->type = 2;
+ }
+ else if (strcmp (attr, "link") == 0)
+ {
+ match->type = 3;
+ }
+ else
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Invalid 'type' attribute in <treematch>"));
+ }
+ xmlFree(attr);
+ }
+ }
+ if (!*error)
+ {
+ attr = my_xmlGetNsProp(node, "executable", NULL);
+ if (attr)
+ {
+ if (strcmp (attr, "true") == 0)
+ {
+ match->executable = 1;
+ }
+ xmlFree(attr);
+ }
+ }
+ if (!*error)
+ {
+ attr = my_xmlGetNsProp(node, "match-case", NULL);
+ if (attr)
+ {
+ if (strcmp (attr, "true") == 0)
+ {
+ match->match_case = 1;
+ }
+ xmlFree(attr);
+ }
+ }
+ if (!*error)
+ {
+ attr = my_xmlGetNsProp(node, "non-empty", NULL);
+ if (attr)
+ {
+ if (strcmp (attr, "true") == 0)
+ {
+ match->non_empty = 1;
+ }
+ xmlFree(attr);
+ }
+ }
+ if (!*error)
+ {
+ attr = my_xmlGetNsProp(node, "mimetype", NULL);
+ if (attr)
+ {
+ match->mimetype = g_strdup (attr);
+ xmlFree(attr);
+ }
+ }
+
+ if (*error)
+ {
+ tree_match_free(match);
+ break;
+ }
+
+ out = g_list_append(out, match);
+
+ match->matches = build_tree_matches(node, error);
+ if (*error)
+ break;
+ }
+
+ return out;
+}
+
+static void tree_magic_free(TreeMagic *magic)
+{
+ GList *next;
+
+ g_return_if_fail(magic != NULL);
+
+ for (next = magic->matches; next; next = next->next)
+ tree_match_free((TreeMatch *) next->data);
+ g_list_free(magic->matches);
+
+ g_free(magic);
+}
+
+/* Create a new TreeMagic object by parsing 'node' (a <treemagic> element) */
+static TreeMagic *tree_magic_new(xmlNode *node, Type *type, GError **error)
+{
+ TreeMagic *magic = NULL;
+ int prio;
+
+ g_return_val_if_fail(node != NULL, NULL);
+ g_return_val_if_fail(type != NULL, NULL);
+ g_return_val_if_fail(error != NULL, NULL);
+
+ prio = get_priority(node);
+
+ if (prio == -1)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Bad priority (%d) in <treemagic> element"), prio);
+ }
+ else
+ {
+ magic = g_new(TreeMagic, 1);
+ magic->priority = prio;
+ magic->type = type;
+ magic->matches = build_tree_matches(node, error);
+
+ if (*error)
+ {
+ gchar *old = (*error)->message;
+ tree_magic_free(magic);
+ magic = NULL;
+ (*error)->message = g_strconcat(
+ _("Error in <treematch> element: "), old, NULL);
+ g_free(old);
+ }
+ }
+
+ return magic;
+}
+
/* Write a list of Match elements (and their children) to the 'magic' file */
static void write_magic_children(FILE *stream, GList *matches, int indent)
{
@@ -1442,6 +1719,62 @@ static void write_magic(FILE *stream, Magic *magic)
write_magic_children(stream, magic->matches, 0);
}
+/* Write a list of TreeMatch elements (and their children) to the 'treemagic' file */
+static void write_tree_magic_children(FILE *stream, GList *matches, int indent)
+{
+ GList *next;
+
+ for (next = matches; next; next = next->next)
+ {
+ TreeMatch *match = (TreeMatch *) next->data;
+
+ if (indent)
+ g_fprintf(stream,
+ "%d>\"%s\"=",
+ indent,
+ match->path);
+ else
+ g_fprintf(stream, ">\"%s\"=", match->path);
+
+ switch (match->type)
+ {
+ default:
+ case 0:
+ fputs("any", stream);
+ break;
+ case 1:
+ fputs("file", stream);
+ break;
+ case 2:
+ fputs("directory", stream);
+ break;
+ case 3:
+ fputs("link", stream);
+ break;
+ }
+ if (match->match_case)
+ fputs (",match-case", stream);
+ if (match->executable)
+ fputs (",executable", stream);
+ if (match->non_empty)
+ fputs (",non-empty", stream);
+ if (match->mimetype)
+ g_fprintf (stream, ",%s", match->mimetype);
+
+ fputc('\n', stream);
+
+ write_tree_magic_children(stream, match->matches, indent + 1);
+ }
+}
+/* Write a whole TreeMagic element to the 'treemagic' file */
+static void write_tree_magic(FILE *stream, TreeMagic *magic)
+{
+ g_fprintf(stream, "[%d:%s/%s]\n", magic->priority,
+ magic->type->media, magic->type->subtype);
+
+ write_tree_magic_children(stream, magic->matches, 0);
+}
+
/* Check each of the directories with generated XML files, looking for types
* which we didn't get on this scan, and delete them.
*/
@@ -1587,6 +1920,38 @@ static void write_aliases(FILE *stream)
g_ptr_array_free(lines, TRUE);
}
+static void add_type(gpointer key, gpointer value, gpointer data)
+{
+ GPtrArray *lines = (GPtrArray *) data;
+
+ g_ptr_array_add(lines, g_strconcat((char *)key, "\n", NULL));
+}
+
+/* Write all the collected types */
+static void write_types(FILE *stream)
+{
+ GPtrArray *lines;
+ int i;
+
+ lines = g_ptr_array_new();
+
+ g_hash_table_foreach(types, add_type, lines);
+
+ g_ptr_array_sort(lines, strcmp2);
+
+ for (i = 0; i < lines->len; i++)
+ {
+ char *line = (char *) lines->pdata[i];
+
+ fwrite(line, 1, strlen(line), stream);
+
+ g_free(line);
+ }
+
+ g_ptr_array_free(lines, TRUE);
+}
+
+
static void write_one_icon(gpointer key, gpointer value, gpointer data)
{
char *mimetype = (char *)key;
@@ -1738,9 +2103,10 @@ write_header (FILE *cache,
gint namespace_offset,
gint icons_list_offset,
gint generic_icons_list_offset,
+ gint type_offset,
guint *offset)
{
- *offset = 40;
+ *offset = 44;
return (write_card16 (cache, MAJOR_VERSION) &&
write_card16 (cache, MINOR_VERSION) &&
@@ -1752,7 +2118,8 @@ write_header (FILE *cache,
write_card32 (cache, magic_offset) &&
write_card32 (cache, namespace_offset) &&
write_card32 (cache, icons_list_offset) &&
- write_card32 (cache, generic_icons_list_offset));
+ write_card32 (cache, generic_icons_list_offset) &&
+ write_card32 (cache, type_offset));
}
@@ -2608,6 +2975,44 @@ write_icons_cache (FILE *cache,
get_icon_value, FALSE, offset);
}
+/* Write all the collected types */
+static gboolean
+write_types_cache (FILE *cache,
+ GHashTable *strings,
+ GHashTable *types,
+ guint *offset)
+{
+ GPtrArray *lines;
+ int i;
+ char *mimetype;
+ guint mime_offset;
+
+ lines = g_ptr_array_new();
+
+ g_hash_table_foreach(types, add_type, lines);
+
+ g_ptr_array_sort(lines, strcmp2);
+
+ if (!write_card32 (cache, lines->len))
+ return FALSE;
+
+ for (i = 0; i < lines->len; i++)
+ {
+ mimetype = (char *) lines->pdata[i];
+ mime_offset = GPOINTER_TO_UINT (g_hash_table_lookup (strings, mimetype));
+ if (!write_card32 (cache, mime_offset))
+ return FALSE;
+
+ g_free(mimetype);
+ }
+
+ *offset += 4 + 4 * lines->len;
+
+ g_ptr_array_free(lines, TRUE);
+
+ return TRUE;
+}
+
static void
collect_alias (gpointer key,
gpointer value,
@@ -2789,11 +3194,12 @@ write_cache (FILE *cache)
guint namespace_offset;
guint icons_list_offset;
guint generic_icons_list_offset;
+ guint type_offset;
guint offset;
GHashTable *strings;
offset = 0;
- if (!write_header (cache, 0, 0, 0, 0, 0, 0, 0, 0, 0, &offset))
+ if (!write_header (cache, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &offset))
{
g_warning ("Failed to write header\n");
return FALSE;
@@ -2883,6 +3289,14 @@ write_cache (FILE *cache)
}
g_message ("Wrote generic icons list at %x - %x\n", generic_icons_list_offset, offset);
+ type_offset = offset;
+ if (!write_types_cache (cache, strings, types, &offset))
+ {
+ g_warning ("Failed to write types list\n");
+ return FALSE;
+ }
+ g_message ("Wrote types list at %x - %x\n", type_offset, offset);
+
rewind (cache);
offset = 0;
@@ -2890,7 +3304,8 @@ write_cache (FILE *cache)
alias_offset, parent_offset, literal_offset,
suffix_offset, glob_offset, magic_offset,
namespace_offset, icons_list_offset,
- generic_icons_list_offset, &offset))
+ generic_icons_list_offset, type_offset,
+ &offset))
{
g_warning ("Failed to rewrite header\n");
return FALSE;
@@ -2994,6 +3409,7 @@ int main(int argc, char **argv)
namespace_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
magic_array = g_ptr_array_new();
+ tree_magic_array = g_ptr_array_new();
subclass_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, free_string_list);
alias_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
@@ -3103,6 +3519,19 @@ int main(int argc, char **argv)
{
FILE *stream;
+ char *path;
+
+ path = g_strconcat(mime_dir, "/types.new", NULL);
+ stream = open_or_die(path);
+ write_types(stream);
+ fclose(stream);
+
+ atomic_update(path);
+ g_free(path);
+ }
+
+ {
+ FILE *stream;
char *icon_path;
icon_path = g_strconcat(mime_dir, "/generic-icons.new", NULL);
@@ -3130,6 +3559,28 @@ int main(int argc, char **argv)
{
FILE *stream;
char *path;
+ int i;
+ path = g_strconcat(mime_dir, "/treemagic.new", NULL);
+ stream = open_or_die(path);
+ fwrite("MIME-TreeMagic\0\n", 1, 16, stream);
+
+ if (tree_magic_array->len)
+ g_ptr_array_sort(tree_magic_array, cmp_tree_magic);
+ for (i = 0; i < tree_magic_array->len; i++)
+ {
+ TreeMagic *magic = (TreeMagic *) tree_magic_array->pdata[i];
+
+ write_tree_magic(stream, magic);
+ }
+ fclose(stream);
+
+ atomic_update(path);
+ g_free(path);
+ }
+
+ {
+ FILE *stream;
+ char *path;
path = g_strconcat(mime_dir, "/mime.cache.new", NULL);
stream = open_or_die(path);
@@ -3142,6 +3593,8 @@ int main(int argc, char **argv)
g_ptr_array_foreach(magic_array, (GFunc)magic_free, NULL);
g_ptr_array_free(magic_array, TRUE);
+ g_ptr_array_foreach(tree_magic_array, (GFunc)tree_magic_free, NULL);
+ g_ptr_array_free(tree_magic_array, TRUE);
g_hash_table_destroy(types);
g_hash_table_destroy(globs_hash);