summaryrefslogtreecommitdiff
path: root/update-mime-database.c
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2012-11-06 14:01:48 -0800
committerAnas Nashif <anas.nashif@intel.com>2012-11-06 14:01:48 -0800
commitd4ea616868e3621b7d46898012295d86f734d19e (patch)
treee2f79378160b77e960dd732ec4dbdad378fbeb09 /update-mime-database.c
downloadshared-mime-info-d4ea616868e3621b7d46898012295d86f734d19e.tar.gz
shared-mime-info-d4ea616868e3621b7d46898012295d86f734d19e.tar.bz2
shared-mime-info-d4ea616868e3621b7d46898012295d86f734d19e.zip
Imported Upstream version 1.0upstream/1.0
Diffstat (limited to 'update-mime-database.c')
-rw-r--r--update-mime-database.c3739
1 files changed, 3739 insertions, 0 deletions
diff --git a/update-mime-database.c b/update-mime-database.c
new file mode 100644
index 0000000..bd28ce0
--- /dev/null
+++ b/update-mime-database.c
@@ -0,0 +1,3739 @@
+#include <config.h>
+
+#define N_(x) x
+#define _(x) (x)
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define XML_NS XML_XML_NAMESPACE
+#define XMLNS_NS "http://www.w3.org/2000/xmlns/"
+#define FREE_NS (xmlChar *)"http://www.freedesktop.org/standards/shared-mime-info"
+
+#define COPYING \
+ N_("Copyright (C) 2003 Thomas Leonard.\n" \
+ "update-mime-database comes with ABSOLUTELY NO WARRANTY,\n" \
+ "to the extent permitted by law.\n" \
+ "You may redistribute copies of update-mime-database\n" \
+ "under the terms of the GNU General Public License.\n" \
+ "For more information about these matters, " \
+ "see the file named COPYING.\n")
+
+#define MIME_ERROR g_quark_from_static_string("mime-error-quark")
+
+#define NOGLOBS "__NOGLOBS__"
+#define NOMAGIC "__NOMAGIC__"
+
+#ifndef PATH_SEPARATOR
+# ifdef _WIN32
+# define PATH_SEPARATOR ";"
+# else
+# define PATH_SEPARATOR ":"
+# endif
+#endif
+
+/* This is the list of directories to scan when finding old type files to
+ * delete. It is also used to warn about invalid MIME types.
+ */
+const char *media_types[] = {
+ "text",
+ "application",
+ "image",
+ "audio",
+ "inode",
+ "video",
+ "message",
+ "model",
+ "multipart",
+ "x-content",
+ "x-epoc",
+ "x-scheme-handler",
+};
+
+/* Represents a MIME type */
+typedef struct _Type Type;
+
+/* A parsed <magic> element */
+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;
+
+struct _Type {
+ char *media;
+ char *subtype;
+
+ /* Contains xmlNodes for elements that are being copied to the output.
+ * That is, <comment>, <sub-class-of> and <alias> nodes, and anything
+ * with an unknown namespace.
+ */
+ xmlDoc *output;
+};
+
+struct _Glob {
+ int weight;
+ char *pattern;
+ Type *type;
+ gboolean noglob;
+ gboolean case_sensitive;
+};
+
+struct _Magic {
+ int priority;
+ Type *type;
+ GList *matches;
+ gboolean nomagic;
+};
+
+struct _Match {
+ long range_start;
+ int range_length;
+ char word_size;
+ int data_length;
+ char *data;
+ char *mask;
+ 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;
+
+/* Maps "namespaceURI localName" strings to Types */
+static GHashTable *namespace_hash = NULL;
+
+/* Maps glob patterns to Types */
+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;
+
+/* Maps aliases to Types */
+static GHashTable *alias_hash = NULL;
+
+/* Maps MIME type names to icon names */
+static GHashTable *icon_hash = NULL;
+
+/* Maps MIME type names to icon names */
+static GHashTable *generic_icon_hash = NULL;
+
+/* Lists enabled log levels */
+static GLogLevelFlags enabled_log_levels = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING;
+
+/* Static prototypes */
+static Magic *magic_new(xmlNode *node, Type *type, GError **error);
+static Match *match_new(void);
+
+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,
+ gpointer unused_data)
+{
+ if (log_level & enabled_log_levels) {
+ g_printf("%s\n", message);
+ }
+}
+
+
+static void usage(const char *name)
+{
+ g_fprintf(stderr, _("Usage: %s [-hvV] MIME-DIR\n"), name);
+}
+
+static void free_type(gpointer data)
+{
+ Type *type = (Type *) data;
+
+ g_free(type->media);
+ g_free(type->subtype);
+
+ xmlFreeDoc(type->output);
+
+ g_free(type);
+}
+
+/* Ugly workaround to shut up gcc4 warnings about signedness issues
+ * (xmlChar is typedef'ed to unsigned char)
+ */
+static char *my_xmlGetNsProp (xmlNodePtr node,
+ const char *name,
+ const xmlChar *namespace)
+{
+ return (char *)xmlGetNsProp (node, (xmlChar *)name, namespace);
+}
+
+/* If we've seen this type before, return the existing object.
+ * Otherwise, create a new one. Checks that the name looks sensible;
+ * if not, sets error and returns NULL.
+ * Also warns about unknown media types, but does not set error.
+ */
+static Type *get_type(const char *name, GError **error)
+{
+ xmlNode *root;
+ xmlNs *ns;
+ const char *slash;
+ Type *type;
+ int i;
+
+ slash = strchr(name, '/');
+ if (!slash || strchr(slash + 1, '/'))
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Invalid MIME-type '%s'"), name);
+ return NULL;
+ }
+
+ type = g_hash_table_lookup(types, name);
+ if (type)
+ return type;
+
+ type = g_new(Type, 1);
+ type->media = g_strndup(name, slash - name);
+ type->subtype = g_strdup(slash + 1);
+ g_hash_table_insert(types, g_strdup(name), type);
+
+ type->output = xmlNewDoc((xmlChar *)"1.0");
+ root = xmlNewDocNode(type->output, NULL, (xmlChar *)"mime-type", NULL);
+ ns = xmlNewNs(root, FREE_NS, NULL);
+ xmlSetNs(root, ns);
+ xmlDocSetRootElement(type->output, root);
+ xmlSetNsProp(root, NULL, (xmlChar *)"type", (xmlChar *)name);
+ xmlAddChild(root, xmlNewDocComment(type->output,
+ (xmlChar *)"Created automatically by update-mime-database. DO NOT EDIT!"));
+
+ for (i = 0; i < G_N_ELEMENTS(media_types); i++)
+ {
+ if (strcmp(media_types[i], type->media) == 0)
+ return type;
+ }
+
+ g_warning("Unknown media type in type '%s'", name);
+
+ return type;
+}
+
+/* Test that this node has the expected name and namespace */
+static gboolean match_node(xmlNode *node,
+ const char *namespaceURI,
+ const char *localName)
+{
+ if (namespaceURI)
+ return node->ns &&
+ strcmp((char *)node->ns->href, namespaceURI) == 0 &&
+ strcmp((char *)node->name, localName) == 0;
+ else
+ return strcmp((char *)node->name, localName) == 0 && !node->ns;
+}
+
+static int get_int_attribute(xmlNode *node, const char *name)
+{
+ char *prio_string;
+ int p;
+
+ prio_string = my_xmlGetNsProp(node, name, NULL);
+ if (prio_string)
+ {
+ char *end;
+
+ p = strtol(prio_string, &end, 10);
+ if (*prio_string == '\0' || *end != '\0')
+ p = -1;
+ xmlFree(prio_string);
+ if (p < 0 || p > 100)
+ return -1;
+ return p;
+ }
+ else
+ return 50;
+}
+
+/* Return the priority of a <magic> node.
+ * Returns 50 if no priority is given, or -1 if a priority is given but
+ * is invalid.
+ */
+static int get_priority(xmlNode *node)
+{
+ return get_int_attribute (node, "priority");
+}
+
+/* Return the weight a <glob> node.
+ * Returns 50 if no weight is given, or -1 if a weight is given but
+ * is invalid.
+ */
+static int get_weight(xmlNode *node)
+{
+ return get_int_attribute (node, "weight");
+}
+
+/* Return the value of a false/true attribute, which defaults to false.
+ * Returns 0 or 1.
+ */
+static gboolean get_boolean_attribute(xmlNode *node, const char* name)
+{
+ char *attr;
+ attr = my_xmlGetNsProp(node, name, NULL);
+ if (attr)
+ {
+ if (strcmp (attr, "true") == 0)
+ {
+ return TRUE;
+ }
+ xmlFree(attr);
+ }
+ return FALSE;
+}
+
+/* Process a <root-XML> element by adding a rule to namespace_hash */
+static void add_namespace(Type *type, const char *namespaceURI,
+ const char *localName, GError **error)
+{
+ g_return_if_fail(type != NULL);
+
+ if (!namespaceURI)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Missing 'namespaceURI' attribute'"));
+ return;
+ }
+
+ if (!localName)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Missing 'localName' attribute'"));
+ return;
+ }
+
+ if (!*namespaceURI && !*localName)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("namespaceURI and localName attributes can't "
+ "both be empty"));
+ return;
+ }
+
+ if (strpbrk(namespaceURI, " \n") || strpbrk(localName, " \n"))
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("namespaceURI and localName cannot contain "
+ "spaces or newlines"));
+ return;
+ }
+
+ g_hash_table_insert(namespace_hash,
+ g_strconcat(namespaceURI, " ", localName, NULL),
+ type);
+}
+
+/* 'field' was found in the definition of 'type' and has the freedesktop.org
+ * namespace. If it's a known field, process it and return TRUE, else
+ * return FALSE to add it to the output XML document.
+ * On error, returns FALSE and sets 'error'.
+ */
+static gboolean process_freedesktop_node(Type *type, xmlNode *field,
+ GError **error)
+{
+ gboolean copy_to_xml;
+
+ copy_to_xml = FALSE;
+ if (strcmp((char *)field->name, "glob") == 0)
+ {
+ gchar *pattern;
+ gint weight;
+ gboolean case_sensitive;
+
+ weight = get_weight(field);
+ case_sensitive = get_boolean_attribute(field, "case-sensitive");
+
+ if (weight == -1)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Bad weight (%d) in <glob> element"), weight);
+ }
+ pattern = my_xmlGetNsProp(field, "pattern", NULL);
+
+ if (pattern && *pattern)
+ {
+ Glob *glob;
+ char *pat = case_sensitive ? g_strdup (pattern) : g_ascii_strdown (pattern, -1);
+ GList *list = g_hash_table_lookup (globs_hash, pat);
+
+ glob = g_new0 (Glob, 1);
+ glob->pattern = pat;
+ glob->type = type;
+ glob->weight = weight;
+ glob->case_sensitive = case_sensitive;
+ list = g_list_append (list, glob);
+ g_hash_table_insert(globs_hash, g_strdup (glob->pattern), list);
+ xmlFree(pattern);
+ copy_to_xml = TRUE;
+ }
+ else
+ {
+ if (pattern)
+ xmlFree(pattern);
+ g_set_error(error, MIME_ERROR, 0,
+ _("Missing 'pattern' attribute in <glob> "
+ "element"));
+ }
+ }
+ else if (strcmp((char *)field->name, "glob-deleteall") == 0)
+ {
+ Glob *glob;
+ GList *list = g_hash_table_lookup (globs_hash, NOGLOBS);
+
+ glob = g_new0 (Glob, 1);
+ glob->pattern = g_strdup (NOGLOBS);
+ glob->type = type;
+ glob->weight = 0;
+ glob->noglob = TRUE;
+ glob->case_sensitive = FALSE;
+ list = g_list_append (list, glob);
+ g_hash_table_insert(globs_hash, g_strdup (glob->pattern), list);
+ copy_to_xml = TRUE;
+ }
+ else if (strcmp((char *)field->name, "magic") == 0)
+ {
+ Magic *magic;
+
+ magic = magic_new(field, type, error);
+
+ if (!*error)
+ {
+ g_return_val_if_fail(magic != NULL, FALSE);
+ g_ptr_array_add(magic_array, magic);
+ }
+ else
+ g_return_val_if_fail(magic == NULL, FALSE);
+ }
+ else if (strcmp((char *)field->name, "magic-deleteall") == 0)
+ {
+ Magic *magic;
+ Match *match;
+
+ magic = g_new0(Magic, 1);
+ magic->priority = 0;
+ magic->type = type;
+ magic->nomagic = TRUE;
+ match = match_new ();
+ match->data = g_strdup (NOMAGIC);
+ match->data_length = strlen (NOMAGIC);
+ magic->matches = g_list_prepend (NULL, match);
+
+ g_ptr_array_add(magic_array, magic);
+ }
+ 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)
+ copy_to_xml = TRUE;
+ else if (strcmp((char *)field->name, "alias") == 0 ||
+ strcmp((char *)field->name, "sub-class-of") == 0)
+ {
+ char *other_type;
+ gboolean valid;
+ GSList *list, *nlist;
+
+ other_type = my_xmlGetNsProp(field, "type", NULL);
+ valid = other_type && strchr(other_type, '/');
+ if (valid)
+ {
+ char *typename;
+
+ typename = g_strdup_printf("%s/%s",
+ type->media,
+ type->subtype);
+
+ if (strcmp((char *)field->name, "alias") == 0)
+ g_hash_table_insert(alias_hash,
+ g_strdup(other_type), type);
+
+ else
+ {
+ list = g_hash_table_lookup(subclass_hash, typename);
+ nlist = g_slist_append (list, g_strdup(other_type));
+ if (list == NULL)
+ g_hash_table_insert(subclass_hash,
+ g_strdup(typename), nlist);
+ }
+ g_free(typename);
+ xmlFree(other_type);
+
+ copy_to_xml = TRUE; /* Copy through */
+ }
+ else
+ {
+ xmlFree(other_type);
+ g_set_error(error, MIME_ERROR, 0,
+ _("Incorrect or missing 'type' attribute "
+ "in <%s>"), field->name);
+ }
+ }
+ else if (strcmp((char *)field->name, "root-XML") == 0)
+ {
+ char *namespaceURI, *localName;
+
+ namespaceURI = my_xmlGetNsProp(field, "namespaceURI", NULL);
+ localName = my_xmlGetNsProp(field, "localName", NULL);
+
+ add_namespace(type, namespaceURI, localName, error);
+
+ if (namespaceURI)
+ xmlFree(namespaceURI);
+ if (localName)
+ xmlFree(localName);
+ }
+ else if (strcmp((char *)field->name, "generic-icon") == 0 ||
+ strcmp((char *)field->name, "icon") == 0)
+ {
+ char *icon;
+ char *typename;
+
+ icon = my_xmlGetNsProp(field, "name", NULL);
+
+ if (icon)
+ {
+ typename = g_strdup_printf("%s/%s",
+ type->media,
+ type->subtype);
+
+ if (strcmp((char *)field->name, "icon") == 0)
+ g_hash_table_insert(icon_hash,
+ typename, g_strdup (icon));
+ else
+ g_hash_table_insert(generic_icon_hash,
+ typename, g_strdup (icon));
+
+ xmlFree (icon);
+
+ copy_to_xml = TRUE; /* Copy through */
+ }
+ }
+
+ if (*error)
+ return FALSE;
+ return !copy_to_xml;
+}
+
+/* Checks to see if 'node' has the given value for xml:lang.
+ * If 'lang' is NULL, checks that 'node' doesn't have an xml:lang.
+ */
+static gboolean has_lang(xmlNode *node, const char *lang)
+{
+ char *lang2;
+
+ lang2 = my_xmlGetNsProp(node, "lang", XML_NS);
+ if (!lang2)
+ return !lang;
+
+ if (lang && strcmp(lang, lang2) == 0)
+ {
+ xmlFree(lang2);
+ return TRUE;
+ }
+ xmlFree(lang2);
+ return FALSE;
+}
+
+/* We're about to add 'new' to the list of fields to be output for the
+ * type. Remove any existing nodes which it replaces.
+ */
+static void remove_old(Type *type, xmlNode *new)
+{
+ xmlNode *field, *fields;
+ char *lang;
+
+ if (new->ns == NULL || xmlStrcmp(new->ns->href, FREE_NS) != 0)
+ return; /* No idea what we're doing -- leave it in! */
+
+ if (strcmp((char *)new->name, "comment") != 0)
+ return;
+
+ lang = my_xmlGetNsProp(new, "lang", XML_NS);
+
+ fields = xmlDocGetRootElement(type->output);
+ for (field = fields->xmlChildrenNode; field; field = field->next)
+ {
+ if (match_node(field, (char *)FREE_NS, "comment") &&
+ has_lang(field, lang))
+ {
+ xmlUnlinkNode(field);
+ xmlFreeNode(field);
+ break;
+ }
+ }
+
+ xmlFree(lang);
+}
+
+/* 'node' is a <mime-type> node from a source file, whose type is 'type'.
+ * Process all the child elements, setting 'error' if anything goes wrong.
+ */
+static void load_type(Type *type, xmlNode *node, GError **error)
+{
+ xmlNode *field;
+
+ g_return_if_fail(type != NULL);
+ g_return_if_fail(node != NULL);
+ g_return_if_fail(error != NULL);
+
+ for (field = node->xmlChildrenNode; field; field = field->next)
+ {
+ xmlNode *copy;
+
+ if (field->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (field->ns && xmlStrcmp(field->ns->href, FREE_NS) == 0)
+ {
+ if (process_freedesktop_node(type, field, error))
+ {
+ g_return_if_fail(*error == NULL);
+ continue;
+ }
+ }
+
+ if (*error)
+ return;
+
+ copy = xmlDocCopyNode(field, type->output, 1);
+
+ /* Ugly hack to stop the xmlns= attributes appearing on
+ * every node...
+ */
+ if (copy->ns && copy->ns->prefix == NULL &&
+ xmlStrcmp(copy->ns->href, FREE_NS) == 0)
+ {
+ if (copy->nsDef)
+ {
+ /* Still used somewhere... */
+ /* xmlFreeNsList(copy->nsDef); */
+ /* (this leaks) */
+ copy->nsDef = NULL;
+ }
+ }
+
+ remove_old(type, field);
+
+ xmlAddChild(xmlDocGetRootElement(type->output), copy);
+ }
+}
+
+/* Parse 'filename' as an XML file and add all the information to the
+ * database. If called more than once, information read in later calls
+ * overrides information read previously.
+ */
+static void load_source_file(const char *filename)
+{
+ xmlDoc *doc;
+ xmlNode *root, *node;
+
+ doc = xmlParseFile(filename);
+ if (!doc)
+ {
+ g_warning(_("Failed to parse '%s'"), filename);
+ return;
+ }
+
+ root = xmlDocGetRootElement(doc);
+
+ if (root->ns == NULL || xmlStrcmp(root->ns->href, FREE_NS) != 0)
+ {
+ g_warning("Wrong namespace on document element in '%s' (should be %s)", filename, FREE_NS);
+ goto out;
+ }
+
+ if (strcmp((char *)root->name, "mime-info") != 0)
+ {
+ g_warning("Root element <%s> is not <mime-info> (in '%s')", root->name, filename);
+ goto out;
+ }
+
+ for (node = root->xmlChildrenNode; node; node = node->next)
+ {
+ Type *type = NULL;
+ char *type_name = NULL;
+ GError *error = NULL;
+
+ if (node->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (!match_node(node, (char *)FREE_NS, "mime-type"))
+ g_set_error(&error, MIME_ERROR, 0,
+ _("Excepted <mime-type>, but got wrong name "
+ "or namespace"));
+
+ if (!error)
+ {
+ type_name = my_xmlGetNsProp(node, "type", NULL);
+
+ if (!type_name)
+ g_set_error(&error, MIME_ERROR, 0,
+ _("<mime-type> element has no 'type' "
+ "attribute"));
+ }
+
+ if (type_name)
+ {
+ type = get_type(type_name, &error);
+ xmlFree(type_name);
+ }
+
+ if (!error)
+ {
+ g_return_if_fail(type != NULL);
+ load_type(type, node, &error);
+ }
+ else
+ g_return_if_fail(type == NULL);
+
+ if (error)
+ {
+ g_warning("Error in type '%s/%s' (in %s): %s.",
+ type ? type->media : _("unknown"),
+ type ? type->subtype : _("unknown"),
+ filename, error->message);
+ g_error_free(error);
+ }
+ }
+out:
+ xmlFreeDoc(doc);
+}
+
+/* Used as the sort function for sorting GPtrArrays */
+static gint strcmp2(gconstpointer a, gconstpointer b)
+{
+ const char *aa = *(char **) a;
+ const char *bb = *(char **) b;
+
+ return strcmp(aa, bb);
+}
+
+/* 'path' should be a 'packages' directory. Loads the information from
+ * every file in the directory.
+ */
+static void scan_source_dir(const char *path)
+{
+ DIR *dir;
+ struct dirent *ent;
+ char *filename;
+ GPtrArray *files;
+ int i;
+ gboolean have_override = FALSE;
+
+ dir = opendir(path);
+ if (!dir)
+ {
+ perror("scan_source_dir");
+ exit(EXIT_FAILURE);
+ }
+
+ files = g_ptr_array_new();
+ while ((ent = readdir(dir)))
+ {
+ int l;
+ l = strlen(ent->d_name);
+ if (l < 4 || strcmp(ent->d_name + l - 4, ".xml") != 0)
+ continue;
+ if (strcmp(ent->d_name, "Override.xml") == 0)
+ {
+ have_override = TRUE;
+ continue;
+ }
+ g_ptr_array_add(files, g_strdup(ent->d_name));
+ }
+ closedir(dir);
+
+ g_ptr_array_sort(files, strcmp2);
+
+ if (have_override)
+ g_ptr_array_add(files, g_strdup("Override.xml"));
+
+ for (i = 0; i < files->len; i++)
+ {
+ gchar *leaf = (gchar *) files->pdata[i];
+
+ filename = g_strconcat(path, "/", leaf, NULL);
+ load_source_file(filename);
+ g_free(filename);
+ }
+
+ for (i = 0; i < files->len; i++)
+ g_free(files->pdata[i]);
+ g_ptr_array_free(files, TRUE);
+}
+
+/* Save doc as XML as filename, 0 on success or -1 on failure */
+static int save_xml_file(xmlDocPtr doc, const gchar *filename)
+{
+#if LIBXML_VERSION > 20400
+ if (xmlSaveFormatFileEnc(filename, doc, "utf-8", 1) < 0)
+ return 1;
+#else
+ FILE *out;
+
+ out = fopen(filename, "w");
+ if (!out)
+ return 1;
+
+ xmlDocDump(out, doc); /* Some versions return void */
+
+ if (fclose(out))
+ return 1;
+#endif
+
+ return 0;
+}
+
+/* Write out globs for one pattern to the 'globs' file */
+static void write_out_glob(GList *globs, FILE *stream)
+{
+ GList *list;
+ Glob *glob;
+ Type *type;
+
+ for (list = globs; list; list = list->next) {
+ glob = (Glob *)list->data;
+ type = glob->type;
+ if (strchr(glob->pattern, '\n'))
+ g_warning("Glob patterns can't contain literal newlines "
+ "(%s in type %s/%s)", glob->pattern,
+ type->media, type->subtype);
+ else
+ g_fprintf(stream, "%s/%s:%s\n",
+ type->media, type->subtype, glob->pattern);
+ }
+}
+
+/* Write out globs and weights for one pattern to the 'globs2' file */
+static void write_out_glob2(GList *globs, FILE *stream)
+{
+ GList *list;
+ Glob *glob;
+ Type *type;
+ gboolean need_flags;
+
+ for (list = globs ; list; list = list->next) {
+ glob = (Glob *)list->data;
+ type = glob->type;
+ if (strchr(glob->pattern, '\n'))
+ g_warning("Glob patterns can't contain literal newlines "
+ "(%s in type %s/%s)", glob->pattern,
+ type->media, type->subtype);
+ else
+ {
+ need_flags = FALSE;
+ if (glob->case_sensitive)
+ need_flags = TRUE;
+
+ if (need_flags) {
+ g_fprintf(stream, "%d:%s/%s:%s%s\n",
+ glob->weight, type->media, type->subtype, glob->pattern,
+ glob->case_sensitive ? ":cs" : "");
+ }
+
+ /* Always write the line without the flags, for older parsers */
+ g_fprintf(stream, "%d:%s/%s:%s\n",
+ glob->weight, type->media, type->subtype, glob->pattern);
+ }
+ }
+}
+
+static void collect_glob2(gpointer key, gpointer value, gpointer data)
+{
+ GList **listp = data;
+
+ *listp = g_list_concat (*listp, g_list_copy ((GList *)value));
+}
+
+static int compare_glob_by_weight (gpointer a, gpointer b)
+{
+ Glob *ag = (Glob *)a;
+ Glob *bg = (Glob *)b;
+
+ if (ag->noglob || bg->noglob)
+ return bg->noglob - ag->noglob;
+
+ return bg->weight - ag->weight;
+}
+
+/* Renames pathname by removing the .new extension */
+static void atomic_update(const gchar *pathname)
+{
+ gchar *new_name;
+ int len;
+
+ len = strlen(pathname);
+
+ g_return_if_fail(strcmp(pathname + len - 4, ".new") == 0);
+
+ new_name = g_strndup(pathname, len - 4);
+
+#ifdef _WIN32
+ /* we need to remove the old file first! */
+ remove(new_name);
+#endif
+ if (rename(pathname, new_name))
+ g_warning("Failed to rename %s as %s , errno: %d", pathname, new_name, errno);
+
+ g_free(new_name);
+}
+
+/* Write out an XML file for one type */
+static void write_out_type(gpointer key, gpointer value, gpointer data)
+{
+ Type *type = (Type *) value;
+ const char *mime_dir = (char *) data;
+ char *media, *filename;
+
+ media = g_strconcat(mime_dir, "/", type->media, NULL);
+#ifdef _WIN32
+ mkdir(media);
+#else
+ mkdir(media, 0755);
+#endif
+
+ filename = g_strconcat(media, "/", type->subtype, ".xml.new", NULL);
+ g_free(media);
+ media = NULL;
+
+ if (save_xml_file(type->output, filename) != 0)
+ g_warning("Failed to write out '%s'", filename);
+
+ atomic_update(filename);
+
+ g_free(filename);
+}
+
+/* Comparison function to get the magic rules in priority order */
+static gint cmp_magic(gconstpointer a, gconstpointer b)
+{
+ Magic *aa = *(Magic **) a;
+ Magic *bb = *(Magic **) b;
+ int retval;
+
+ /* Sort nomagic items at start */
+ if (aa->nomagic || bb->nomagic)
+ return bb->nomagic - aa->nomagic;
+
+ 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;
+}
+
+/* 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)
+{
+ guint16 big = GUINT16_TO_BE(n);
+
+ g_return_if_fail(n <= 0xffff);
+
+ fwrite(&big, sizeof(big), 1, stream);
+}
+
+/* Single hex char to int; -1 if not a hex char.
+ * From file(1).
+ */
+static int hextoint(int c)
+{
+ if (!isascii((unsigned char) c))
+ return -1;
+ if (isdigit((unsigned char) c))
+ return c - '0';
+ if ((c >= 'a')&&(c <= 'f'))
+ return c + 10 - 'a';
+ if (( c>= 'A')&&(c <= 'F'))
+ return c + 10 - 'A';
+ return -1;
+}
+
+/*
+ * Convert a string containing C character escapes. Stop at an unescaped
+ * space or tab.
+ * Copy the converted version to "p", returning its length in *slen.
+ * Return updated scan pointer as function result.
+ * Stolen from file(1) and heavily modified.
+ */
+static void getstr(const char *s, GString *out)
+{
+ int c;
+ int val;
+
+ while ((c = *s++) != '\0') {
+ if(c == '\\') {
+ switch(c = *s++) {
+
+ case '\0':
+ return;
+
+ default:
+ g_string_append_c(out, (char) c);
+ break;
+
+ case 'n':
+ g_string_append_c(out, '\n');
+ break;
+
+ case 'r':
+ g_string_append_c(out, '\r');
+ break;
+
+ case 'b':
+ g_string_append_c(out, '\b');
+ break;
+
+ case 't':
+ g_string_append_c(out, '\t');
+ break;
+
+ case 'f':
+ g_string_append_c(out, '\f');
+ break;
+
+ case 'v':
+ g_string_append_c(out, '\v');
+ break;
+
+ /* \ and up to 3 octal digits */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ val = c - '0';
+ c = *s++; /* try for 2 */
+ if(c >= '0' && c <= '7') {
+ val = (val<<3) | (c - '0');
+ c = *s++; /* try for 3 */
+ if(c >= '0' && c <= '7')
+ val = (val<<3) | (c-'0');
+ else
+ --s;
+ }
+ else
+ --s;
+ g_string_append_c(out, (char)val);
+ break;
+
+ /* \x and up to 2 hex digits */
+ case 'x':
+ val = 'x'; /* Default if no digits */
+ c = hextoint(*s++); /* Get next char */
+ if (c >= 0) {
+ val = c;
+ c = hextoint(*s++);
+ if (c >= 0)
+ val = (val << 4) + c;
+ else
+ --s;
+ } else
+ --s;
+ g_string_append_c(out, (char)val);
+ break;
+ }
+ } else
+ g_string_append_c(out, (char)c);
+ }
+}
+
+/* Parse the value and mask attributes of a <match> element with a
+ * numerical type (anything except "string").
+ */
+static void parse_int_value(int bytes, const char *in, const char *in_mask,
+ GString *parsed_value, char **parsed_mask,
+ gboolean big_endian, GError **error)
+{
+ char *end;
+ char *out_mask = NULL;
+ unsigned long value;
+ int b;
+
+ value = strtoul(in, &end, 0);
+ if (errno == ERANGE) {
+ g_set_error(error, MIME_ERROR, 0,
+ "Number out-of-range (%s should fit in %d bytes)",
+ in, bytes);
+ return;
+ }
+
+ if (*end != '\0')
+ {
+ g_set_error(error, MIME_ERROR, 0, "Value is not a number");
+ return;
+ }
+
+ for (b = 0; b < bytes; b++)
+ {
+ int shift = (big_endian ? (bytes - b - 1) : b) * 8;
+ g_string_append_c(parsed_value, (value >> shift) & 0xff);
+ }
+
+ if ((bytes == 1 && (value & ~0xff)) ||
+ (bytes == 2 && (value & ~0xffff)))
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ "Number out-of-range (%lx should fit in %d bytes)",
+ value, bytes);
+ return;
+ }
+
+ if (in_mask)
+ {
+ int b;
+ unsigned long mask;
+
+ mask = strtoul(in_mask, &end, 0);
+ if (errno == ERANGE) {
+ g_set_error(error, MIME_ERROR, 0,
+ "Mask out-of-range (%s should fit in %d bytes)",
+ in_mask, bytes);
+ return;
+ }
+
+
+ if (*end != '\0')
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ "Mask is not a number");
+ return;
+ }
+
+ out_mask = g_new(char, bytes);
+ for (b = 0; b < bytes; b++)
+ {
+ int shift = (big_endian ? (bytes - b - 1) : b) * 8;
+ out_mask[b] = (mask >> shift) & 0xff;
+ }
+ }
+
+ *parsed_mask = out_mask;
+}
+
+/* 'len' is the length of the value. The mask created will be the same
+ * length.
+ */
+static char *parse_string_mask(const char *mask, int len, GError **error)
+{
+ int i;
+ char *parsed_mask = NULL;
+
+ g_return_val_if_fail(mask != NULL, NULL);
+ g_return_val_if_fail(len > 0, NULL);
+
+ if (mask[0] != '0' || mask[1] != 'x')
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ "String masks must be in base 16 (starting with 0x)");
+ goto err;
+ }
+ mask += 2;
+
+ parsed_mask = g_new0(char, len);
+
+ i = 0; /* Nybble to write to next */
+ while (mask[i])
+ {
+ int c;
+
+ c = hextoint(mask[i]);
+ if (c == -1)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ "'%c' is not a valid hex digit", mask[i]);
+ goto err;
+ }
+
+ if (i >= len * 2)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ "Mask is longer than value");
+ goto err;
+ }
+
+ if (i & 1)
+ parsed_mask[i >> 1] |= c;
+ else
+ parsed_mask[i >> 1] |= c << 4;
+
+ i++;
+ }
+
+ return parsed_mask;
+err:
+ g_return_val_if_fail(error == NULL || *error != NULL, NULL);
+ g_free(parsed_mask);
+ return NULL;
+}
+
+/* Parse the value and mask attributes for a <match> element */
+static void parse_value(const char *type, const char *in, const char *in_mask,
+ GString *parsed_value, char **parsed_mask,
+ GError **error)
+{
+ *parsed_mask = NULL;
+
+ if (in == NULL || !in[0])
+ {
+ g_set_error(error, MIME_ERROR, 0, "No value specified");
+ return;
+ }
+
+ if (strstr(type, "16"))
+ parse_int_value(2, in, in_mask, parsed_value, parsed_mask,
+ type[0] != 'l', error);
+ else if (strstr(type, "32"))
+ parse_int_value(4, in, in_mask, parsed_value, parsed_mask,
+ type[0] != 'l', error);
+ else if (strcmp(type, "byte") == 0)
+ parse_int_value(1, in, in_mask, parsed_value, parsed_mask,
+ FALSE, error);
+ else if (strcmp(type, "string") == 0)
+ {
+ getstr(in, parsed_value);
+ if (in_mask)
+ *parsed_mask = parse_string_mask(in_mask,
+ parsed_value->len, error);
+ }
+ else
+ g_assert_not_reached();
+}
+
+static Match *match_new(void)
+{
+ Match *match;
+
+ match = g_new(Match, 1);
+ match->range_start = 0;
+ match->range_length = 1;
+ match->word_size = 1;
+ match->data_length = 0;
+ match->data = NULL;
+ match->mask = NULL;
+ match->matches = NULL;
+
+ return match;
+}
+
+static void match_free(Match *match)
+{
+ GList *next;
+
+ g_return_if_fail(match != NULL);
+
+ for (next = match->matches; next; next = next->next)
+ match_free((Match *) next->data);
+
+ g_list_free(match->matches);
+
+ g_free(match->data);
+ g_free(match->mask);
+
+ g_free(match);
+}
+
+/* Sets match->range_start and match->range_length */
+static void match_offset(Match *match, xmlNode *node, GError **error)
+{
+ char *offset = NULL;
+ char *end;
+
+ offset = my_xmlGetNsProp(node, "offset", NULL);
+ if (offset == NULL || !*offset)
+ {
+ g_set_error(error, MIME_ERROR, 0, "Missing 'offset' attribute");
+ goto err;
+ }
+
+ match->range_start = strtol(offset, &end, 10);
+ if (errno == ERANGE) {
+ char *number;
+ number = g_strndup(offset, end-offset);
+ g_set_error(error, MIME_ERROR, 0,
+ "Number out-of-range (%s should fit in 4 bytes)",
+ number);
+ g_free(number);
+ return;
+ }
+
+ if (*end == ':' && end[1] && match->range_start >= 0)
+ {
+ int last;
+ char *begin;
+
+ begin = end + 1;
+ last = strtol(begin, &end, 10);
+ if (errno == ERANGE) {
+ char *number;
+ number = g_strndup(begin, end-begin);
+ g_set_error(error, MIME_ERROR, 0,
+ "Number out-of-range (%s should fit in 8 bytes)",
+ number);
+ g_free(number);
+ return;
+ }
+
+ if (*end == '\0' && last >= match->range_start)
+ match->range_length = last - match->range_start + 1;
+ else
+ g_set_error(error, MIME_ERROR, 0, "Invalid offset");
+ }
+ else if (*end != '\0')
+ g_set_error(error, MIME_ERROR, 0, "Invalid offset");
+err:
+ xmlFree(offset);
+}
+
+/* Sets match->data, match->data_length and match->mask */
+static void match_value_and_mask(Match *match, xmlNode *node, GError **error)
+{
+ char *mask = NULL;
+ char *value = NULL;
+ char *type = NULL;
+ char *parsed_mask = NULL;
+ GString *parsed_value;
+
+ type = my_xmlGetNsProp(node, "type", NULL);
+ g_return_if_fail(type != NULL);
+
+ mask = my_xmlGetNsProp(node, "mask", NULL);
+ value = my_xmlGetNsProp(node, "value", NULL);
+
+ parsed_value = g_string_new(NULL);
+
+ parse_value(type, value, mask, parsed_value,
+ &parsed_mask, error);
+
+ if (*error)
+ {
+ g_string_free(parsed_value, TRUE);
+ g_return_if_fail(parsed_mask == NULL);
+ }
+ else
+ {
+ match->data = parsed_value->str;
+ match->data_length = parsed_value->len;
+ match->mask = parsed_mask;
+
+ g_string_free(parsed_value, FALSE);
+ }
+
+ if (mask)
+ xmlFree(mask);
+ if (value)
+ xmlFree(value);
+ xmlFree(type);
+}
+
+/* Sets match->word_size */
+static void match_word_size(Match *match, xmlNode *node, GError **error)
+{
+ char *type;
+
+ type = my_xmlGetNsProp(node, "type", NULL);
+
+ if (!type)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Missing 'type' attribute in <match>"));
+ return;
+ }
+
+ if (strcmp(type, "host16") == 0)
+ match->word_size = 2;
+ else if (strcmp(type, "host32") == 0)
+ match->word_size = 4;
+ else if (!*error && strcmp(type, "big16") &&
+ strcmp(type, "big32") &&
+ strcmp(type, "little16") && strcmp(type, "little32") &&
+ strcmp(type, "string") && strcmp(type, "byte"))
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ "Unknown magic type '%s'", type);
+ }
+
+ xmlFree(type);
+}
+
+/* Turn the list of child nodes of 'parent' into a list of Matches */
+static GList *build_matches(xmlNode *parent, GError **error)
+{
+ xmlNode *node;
+ GList *out = NULL;
+
+ g_return_val_if_fail(error != NULL, NULL);
+
+ for (node = parent->xmlChildrenNode; node; node = node->next)
+ {
+ Match *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, "match") != 0)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Expected <match> element, but found "
+ "<%s> instead"), node->name);
+ break;
+ }
+
+ match = match_new();
+ match_offset(match, node, error);
+ if (!*error)
+ match_word_size(match, node, error);
+ if (!*error)
+ match_value_and_mask(match, node, error);
+
+ if (*error)
+ {
+ match_free(match);
+ break;
+ }
+
+ out = g_list_append(out, match);
+
+ match->matches = build_matches(node, error);
+ if (*error)
+ break;
+ }
+
+ return out;
+}
+
+static void magic_free(Magic *magic)
+{
+ GList *next;
+
+ g_return_if_fail(magic != NULL);
+
+ for (next = magic->matches; next; next = next->next)
+ match_free((Match *) next->data);
+ g_list_free(magic->matches);
+
+ g_free(magic);
+}
+
+/* Create a new Magic object by parsing 'node' (a <magic> element) */
+static Magic *magic_new(xmlNode *node, Type *type, GError **error)
+{
+ Magic *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 <magic> element"), prio);
+ }
+ else
+ {
+ magic = g_new0(Magic, 1);
+ magic->priority = prio;
+ magic->type = type;
+ magic->matches = build_matches(node, error);
+
+
+ if (*error)
+ {
+ gchar *old = (*error)->message;
+ magic_free(magic);
+ magic = NULL;
+ (*error)->message = g_strconcat(
+ _("Error in <match> element: "), old, NULL);
+ g_free(old);
+ } else if (magic->matches == NULL) {
+ magic_free(magic);
+ magic = NULL;
+ g_set_error(error, MIME_ERROR, 0,
+ _("Incomplete <magic> element"));
+ }
+ }
+
+ 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)
+{
+ GList *next;
+
+ for (next = matches; next; next = next->next)
+ {
+ Match *match = (Match *) next->data;
+
+ if (indent)
+ g_fprintf(stream,
+ "%d>%ld=",
+ indent,
+ match->range_start);
+ else
+ g_fprintf(stream, ">%ld=", match->range_start);
+
+ write16(stream, match->data_length);
+ fwrite(match->data, match->data_length, 1, stream);
+ if (match->mask)
+ {
+ fputc('&', stream);
+ fwrite(match->mask, match->data_length, 1, stream);
+ }
+ if (match->word_size != 1)
+ g_fprintf(stream, "~%d", match->word_size);
+ if (match->range_length != 1)
+ g_fprintf(stream, "+%d", match->range_length);
+
+ fputc('\n', stream);
+
+ write_magic_children(stream, match->matches, indent + 1);
+ }
+}
+
+/* Write a whole Magic element to the 'magic' file */
+static void write_magic(FILE *stream, Magic *magic)
+{
+ g_fprintf(stream, "[%d:%s/%s]\n", magic->priority,
+ magic->type->media, magic->type->subtype);
+
+ 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.
+ */
+static void delete_old_types(const gchar *mime_dir)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS(media_types); i++)
+ {
+ gchar *media_dir;
+ DIR *dir;
+ struct dirent *ent;
+
+ media_dir = g_strconcat(mime_dir, "/", media_types[i], NULL);
+ dir = opendir(media_dir);
+ g_free(media_dir);
+ if (!dir)
+ continue;
+
+ while ((ent = readdir(dir)))
+ {
+ char *type_name;
+ int l;
+ l = strlen(ent->d_name);
+ if (l < 4 || strcmp(ent->d_name + l - 4, ".xml") != 0)
+ continue;
+
+ type_name = g_strconcat(media_types[i], "/",
+ ent->d_name, NULL);
+ type_name[strlen(type_name) - 4] = '\0';
+ if (!g_hash_table_lookup(types, type_name))
+ {
+ char *path;
+ path = g_strconcat(mime_dir, "/",
+ type_name, ".xml", NULL);
+#if 0
+ g_warning("Removing old info for type %s",
+ path);
+#endif
+ unlink(path);
+ g_free(path);
+ }
+ g_free(type_name);
+ }
+
+ closedir(dir);
+ }
+}
+
+/* Extract one entry from namespace_hash and put it in the GPtrArray so
+ * we can sort it.
+ */
+static void add_ns(gpointer key, gpointer value, gpointer data)
+{
+ GPtrArray *lines = (GPtrArray *) data;
+ const gchar *ns = (gchar *) key;
+ Type *type = (Type *) value;
+
+ g_ptr_array_add(lines, g_strconcat(ns, " ", type->media,
+ "/", type->subtype, "\n", NULL));
+}
+
+/* Write all the collected namespace rules to 'XMLnamespaces' */
+static void write_namespaces(FILE *stream)
+{
+ GPtrArray *lines;
+ int i;
+
+ lines = g_ptr_array_new();
+
+ g_hash_table_foreach(namespace_hash, add_ns, 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_subclass(gpointer key, gpointer value, gpointer data)
+{
+ GSList *list = value;
+ FILE *stream = data;
+ GSList *l;
+ char *line;
+
+ for (l = list; l; l = l->next)
+ {
+ line = g_strconcat (key, " ", l->data, "\n", NULL);
+ fwrite(line, 1, strlen(line), stream);
+ g_free (line);
+ }
+}
+
+/* Write all the collected subclass information to 'subclasses' */
+static void write_subclasses(FILE *stream)
+{
+ g_hash_table_foreach(subclass_hash, write_subclass, stream);
+}
+
+/* Extract one entry from alias_hash and put it in the GPtrArray so
+ * we can sort it.
+ */
+static void add_alias(gpointer key, gpointer value, gpointer data)
+{
+ GPtrArray *lines = (GPtrArray *) data;
+ const gchar *alias = (gchar *) key;
+ Type *type = (Type *) value;
+
+ g_ptr_array_add(lines, g_strconcat(alias, " ", type->media,
+ "/", type->subtype, "\n",
+ NULL));
+}
+
+/* Write all the collected aliases */
+static void write_aliases(FILE *stream)
+{
+ GPtrArray *lines;
+ int i;
+
+ lines = g_ptr_array_new();
+
+ g_hash_table_foreach(alias_hash, add_alias, 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 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;
+ char *iconname = (char *)value;
+ FILE *stream = (FILE *)data;
+ char *line;
+
+ line = g_strconcat (mimetype, ":", iconname, "\n", NULL);
+ fwrite(line, 1, strlen(line), stream);
+ g_free (line);
+}
+
+static void write_icons(GHashTable *icons, FILE *stream)
+{
+ g_hash_table_foreach(icons, write_one_icon, stream);
+}
+
+/* Issue a warning if 'path' won't be found by applications */
+static void check_in_path_xdg_data(const char *mime_path)
+{
+ struct stat path_info, dir_info;
+ const char *env;
+ char **dirs;
+ char *path;
+ int i, n;
+
+ path = g_path_get_dirname(mime_path);
+
+ if (stat(path, &path_info))
+ {
+ g_warning("Can't stat '%s' directory: %s",
+ path, g_strerror(errno));
+ goto out;
+ }
+
+ env = getenv("XDG_DATA_DIRS");
+ if (!env)
+ env = "/usr/local/share/"PATH_SEPARATOR"/usr/share/";
+ dirs = g_strsplit(env, PATH_SEPARATOR, 0);
+ g_return_if_fail(dirs != NULL);
+ for (n = 0; dirs[n]; n++)
+ ;
+ env = getenv("XDG_DATA_HOME");
+ if (env)
+ dirs[n] = g_strdup(env);
+ else
+ dirs[n] = g_build_filename(g_get_home_dir(), ".local",
+ "share", NULL);
+ n++;
+
+ for (i = 0; i < n; i++)
+ {
+ if (stat(dirs[i], &dir_info) == 0 &&
+ dir_info.st_ino == path_info.st_ino &&
+ dir_info.st_dev == path_info.st_dev)
+ break;
+ }
+
+ if (i == n)
+ {
+ g_printerr(_("\nNote that '%s' is not in the search path\n"
+ "set by the XDG_DATA_HOME and XDG_DATA_DIRS\n"
+ "environment variables, so applications may not\n"
+ "be able to find it until you set them. The\n"
+ "directories currently searched are:\n\n"), path);
+ g_printerr("- %s\n", dirs[n - 1]);
+ for (i = 0; i < n - 1; i++)
+ g_printerr("- %s\n", dirs[i]);
+ g_printerr("\n");
+ }
+
+ for (i = 0; i < n; i++)
+ g_free(dirs[i]);
+ g_free(dirs);
+out:
+ g_free(path);
+}
+
+static void free_string_list(gpointer data)
+{
+ GSList *list = data;
+
+ g_slist_foreach(list, (GFunc)g_free, NULL);
+ g_slist_free(list);
+}
+
+#define ALIGN_VALUE(this, boundary) \
+ (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
+
+
+static gint
+write_data (FILE *cache, const gchar *n, gint len)
+{
+ gchar *s;
+ int i, l;
+
+ l = ALIGN_VALUE (len, 4);
+
+ s = g_malloc0 (l);
+ memcpy (s, n, len);
+
+ i = fwrite (s, l, 1, cache);
+
+ return i == 1;
+
+}
+
+static gint
+write_string (FILE *cache, const gchar *n)
+{
+ return write_data (cache, n, strlen (n) + 1);
+}
+
+static gboolean
+write_card16 (FILE *cache, guint16 n)
+{
+ int i;
+
+ n = GUINT16_TO_BE (n);
+
+ i = fwrite ((char *)&n, 2, 1, cache);
+
+ return i == 1;
+}
+
+static gboolean
+write_card32 (FILE *cache, guint32 n)
+{
+ int i;
+
+ n = GUINT32_TO_BE (n);
+
+ i = fwrite ((char *)&n, 4, 1, cache);
+
+ return i == 1;
+}
+
+#define MAJOR_VERSION 1
+#define MINOR_VERSION 2
+
+static gboolean
+write_header (FILE *cache,
+ gint alias_offset,
+ gint parent_offset,
+ gint literal_offset,
+ gint suffix_offset,
+ gint glob_offset,
+ gint magic_offset,
+ gint namespace_offset,
+ gint icons_list_offset,
+ gint generic_icons_list_offset,
+ gint type_offset,
+ guint *offset)
+{
+ *offset = 44;
+
+ return (write_card16 (cache, MAJOR_VERSION) &&
+ write_card16 (cache, MINOR_VERSION) &&
+ write_card32 (cache, alias_offset) &&
+ write_card32 (cache, parent_offset) &&
+ write_card32 (cache, literal_offset) &&
+ write_card32 (cache, suffix_offset) &&
+ write_card32 (cache, glob_offset) &&
+ 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, type_offset));
+}
+
+
+typedef gboolean (FilterFunc) (gpointer key);
+typedef gchar ** (GetValueFunc) (gpointer data, gchar *key);
+
+typedef struct
+{
+ FILE *cache;
+ GHashTable *pool;
+ guint offset;
+ GetValueFunc *get_value;
+ gpointer data;
+ gboolean weighted;
+ gboolean error;
+} MapData;
+
+static void
+write_map_entry (gpointer key,
+ gpointer data)
+{
+ MapData *map_data = (MapData *)data;
+ gchar **values;
+ guint offset, i;
+ guint weight;
+
+ values = (* map_data->get_value) (map_data->data, key);
+ for (i = 0; values[i]; i++)
+ {
+ if (map_data->weighted && (i % 3 == 2))
+ {
+ weight = atoi (values[i]);
+
+ if (!write_card32 (map_data->cache, weight))
+ map_data->error = TRUE;
+
+ map_data->offset += 4;
+ }
+ else
+ {
+ offset = GPOINTER_TO_UINT (g_hash_table_lookup (map_data->pool, values[i]));
+ if (offset == 0)
+ {
+ g_warning ("Missing string: '%s'", values[i]);
+ map_data->error = TRUE;
+ }
+ if (!write_card32 (map_data->cache, offset))
+ map_data->error = TRUE;
+ map_data->offset += 4;
+ }
+ }
+
+ g_strfreev (values);
+}
+
+typedef struct
+{
+ FilterFunc *filter;
+ GPtrArray *keys;
+} FilterData;
+
+static void
+add_key (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ FilterData *filter_data = (FilterData *)data;
+
+ if (!filter_data->filter || (* filter_data->filter) (key))
+ g_ptr_array_add (filter_data->keys, key);
+}
+
+static gboolean
+write_map (FILE *cache,
+ GHashTable *strings,
+ GHashTable *map,
+ FilterFunc *filter,
+ GetValueFunc *get_value,
+ gboolean weighted,
+ guint *offset)
+{
+ GPtrArray *keys;
+ MapData map_data;
+ FilterData filter_data;
+
+ keys = g_ptr_array_new ();
+
+ filter_data.keys = keys;
+ filter_data.filter = filter;
+ g_hash_table_foreach (map, add_key, &filter_data);
+
+ g_ptr_array_sort (keys, strcmp2);
+
+ if (!write_card32 (cache, keys->len))
+ return FALSE;
+
+ map_data.cache = cache;
+ map_data.pool = strings;
+ map_data.get_value = get_value;
+ map_data.data = map;
+ map_data.weighted = weighted;
+ map_data.offset = *offset + 4;
+ map_data.error = FALSE;
+
+ g_ptr_array_foreach (keys, write_map_entry, &map_data);
+
+ *offset = map_data.offset;
+
+ return !map_data.error;
+}
+
+static gchar **
+get_type_value (gpointer data,
+ gchar *key)
+{
+ Type *type;
+ gchar **result;
+
+ type = (Type *)g_hash_table_lookup ((GHashTable *)data, key);
+
+ result = g_new0 (gchar *, 3);
+ result[0] = g_strdup (key);
+ result[1] = g_strdup_printf ("%s/%s", type->media, type->subtype);
+
+ return result;
+}
+
+static guint32
+get_glob_weight_and_flags (Glob *glob)
+{
+ guint32 res;
+
+ res = glob->weight & 0xff;
+ if (glob->case_sensitive)
+ res |= 0x100;
+ return res;
+}
+
+static gchar **
+get_glob_list_value (gpointer data,
+ gchar *key)
+{
+ GList *list;
+ Glob *glob;
+ Type *type;
+ gchar **result;
+ gint i;
+
+ list = (GList *)g_hash_table_lookup ((GHashTable *)data, key);
+
+ result = g_new0 (gchar *, 1 + 3 * g_list_length (list));
+
+ i = 0;
+ for (; list; list = list->next)
+ {
+ glob = (Glob *)list->data;
+ type = glob->type;
+
+ result[i++] = g_strdup (glob->pattern);
+ result[i++] = g_strdup_printf ("%s/%s", type->media, type->subtype);
+ result[i++] = g_strdup_printf ("%ud", get_glob_weight_and_flags (glob));
+ }
+ return result;
+}
+
+static gboolean
+write_alias_cache (FILE *cache,
+ GHashTable *strings,
+ guint *offset)
+{
+ return write_map (cache, strings, alias_hash, NULL, get_type_value, FALSE, offset);
+}
+
+static void
+write_parent_entry (gpointer key,
+ gpointer data)
+{
+ gchar *mimetype = (gchar *)key;
+ MapData *map_data = (MapData *)data;
+ guint parents_offset, offset;
+ GList *parents;
+
+ parents = (GList *)g_hash_table_lookup (subclass_hash, mimetype);
+ offset = GPOINTER_TO_UINT (g_hash_table_lookup (map_data->pool, mimetype));
+ if (offset == 0)
+ {
+ g_warning ("Missing string: '%s'", (gchar *)key);
+ map_data->error = TRUE;
+ }
+
+ parents_offset = map_data->offset;
+ map_data->offset += 4 + 4 * g_list_length (parents);
+
+ if (!write_card32 (map_data->cache, offset) ||
+ !write_card32 (map_data->cache, parents_offset))
+ map_data->error = TRUE;
+}
+
+static void
+write_parent_list (gpointer key,
+ gpointer data)
+{
+ gchar *mimetype = (gchar *)key;
+ MapData *map_data = (MapData *)data;
+ guint offset;
+ GList *parents, *p;
+
+ parents = (GList *)g_hash_table_lookup (subclass_hash, mimetype);
+
+ if (!write_card32 (map_data->cache, g_list_length (parents)))
+ map_data->error = TRUE;
+
+ for (p = parents; p; p = p->next)
+ {
+ gchar *parent = (gchar *)p->data;
+
+ offset = GPOINTER_TO_UINT (g_hash_table_lookup (map_data->pool, parent));
+ if (offset == 0)
+ {
+ g_warning ("Missing string: '%s'", parent);
+ map_data->error = TRUE;
+ }
+
+ if (!write_card32 (map_data->cache, offset))
+ map_data->error = TRUE;
+ }
+
+ map_data->offset += 4 + 4 * g_list_length (parents);
+}
+
+static gboolean
+write_parent_cache (FILE *cache,
+ GHashTable *strings,
+ guint *offset)
+{
+ GPtrArray *keys;
+ MapData map_data;
+ FilterData filter_data;
+
+ keys = g_ptr_array_new ();
+
+ filter_data.keys = keys;
+ filter_data.filter = NULL;
+ g_hash_table_foreach (subclass_hash, add_key, &filter_data);
+
+ g_ptr_array_sort (keys, strcmp2);
+
+ if (!write_card32 (cache, keys->len))
+ return FALSE;
+
+ map_data.cache = cache;
+ map_data.pool = strings;
+ map_data.offset = *offset + 4 + keys->len * 8;
+ map_data.error = FALSE;
+
+ g_ptr_array_foreach (keys, write_parent_entry, &map_data);
+
+ map_data.offset = *offset + 4 + keys->len * 8;
+ g_ptr_array_foreach (keys, write_parent_list, &map_data);
+
+ *offset = map_data.offset;
+
+ return !map_data.error;
+}
+
+typedef enum
+{
+ GLOB_LITERAL,
+ GLOB_SIMPLE,
+ GLOB_FULL
+} GlobType;
+
+static GlobType
+glob_type (gchar *glob)
+{
+ gchar *ptr;
+ gboolean maybe_in_simple_glob = FALSE;
+ gboolean first_char = TRUE;
+
+ ptr = glob;
+
+ while (*ptr != '\0')
+ {
+ if (*ptr == '*' && first_char)
+ maybe_in_simple_glob = TRUE;
+ else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
+ return GLOB_FULL;
+
+ first_char = FALSE;
+ ptr = g_utf8_next_char (ptr);
+ }
+
+ if (maybe_in_simple_glob)
+ return GLOB_SIMPLE;
+
+ return GLOB_LITERAL;
+}
+
+static gboolean
+is_literal_glob (gpointer key)
+{
+ return glob_type ((gchar *)key) == GLOB_LITERAL;
+}
+
+static gboolean
+is_simple_glob (gpointer key)
+{
+ return glob_type ((gchar *)key) == GLOB_SIMPLE;
+}
+
+static gboolean
+is_full_glob (gpointer key)
+{
+ return glob_type ((gchar *)key) == GLOB_FULL;
+}
+
+static gboolean
+write_literal_cache (FILE *cache,
+ GHashTable *strings,
+ guint *offset)
+{
+ return write_map (cache, strings, globs_hash, is_literal_glob,
+ get_glob_list_value, TRUE, offset);
+}
+
+static gboolean
+write_glob_cache (FILE *cache,
+ GHashTable *strings,
+ guint *offset)
+{
+ return write_map (cache, strings, globs_hash, is_full_glob,
+ get_glob_list_value, TRUE, offset);
+}
+
+typedef struct _SuffixEntry SuffixEntry;
+
+struct _SuffixEntry
+{
+ gunichar character;
+ gchar *mimetype;
+ gint weight;
+ guint32 flags;
+ GList *children;
+ guint size;
+ guint depth;
+};
+
+static GList *
+insert_suffix (gunichar *suffix,
+ gchar *mimetype,
+ gint weight,
+ guint32 flags,
+ GList *suffixes)
+{
+ GList *l;
+ SuffixEntry *s = NULL;
+
+ for (l = suffixes; l; l = l->next)
+ {
+ s = (SuffixEntry *)l->data;
+
+ if (s->character > suffix[0])
+ {
+ s = g_new0 (SuffixEntry, 1);
+ s->character = suffix[0];
+ s->mimetype = NULL;
+ s->children = NULL;
+
+ suffixes = g_list_insert_before (suffixes, l, s);
+ }
+
+ if (s->character == suffix[0])
+ break;
+ }
+
+ if (!s || s->character != suffix[0])
+ {
+ s = g_new0 (SuffixEntry, 1);
+ s->character = suffix[0];
+ s->mimetype = NULL;
+ s->children = NULL;
+
+ suffixes = g_list_append (suffixes, s);
+ }
+
+ if (suffix[1] == 0)
+ {
+ GList *l2;
+ SuffixEntry *s2;
+ gboolean found = FALSE;
+
+ for (l2 = s->children; l2; l2 = l2->next)
+ {
+ s2 = (SuffixEntry *)l2->data;
+ if (s2->character != 0)
+ break;
+ if (strcmp (s2->mimetype, mimetype) == 0)
+ {
+ if (s2->weight < weight)
+ s2->weight = weight;
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found)
+ {
+ s2 = g_new0 (SuffixEntry, 1);
+ s2->character = 0;
+ s2->mimetype = mimetype;
+ s2->weight = weight;
+ s2->flags = flags;
+ s2->children = NULL;
+ s->children = g_list_insert_before (s->children, l2, s2);
+ }
+ }
+ else
+ s->children = insert_suffix (suffix + 1, mimetype, weight, flags, s->children);
+
+ return suffixes;
+}
+
+static void
+ucs4_reverse (gunichar *in, glong len)
+{
+ int i;
+ gunichar c;
+
+ for (i = 0; i < len - i - 1; i++)
+ {
+ c = in[i];
+ in[i] = in[len - i - 1];
+ in[len - i - 1] = c;
+ }
+}
+
+static void
+build_suffixes (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ gchar *pattern = (gchar *)key;
+ GList *list = (GList *)value;
+ GList **suffixes = (GList **)data;
+ gunichar *suffix;
+ gchar *mimetype;
+ Glob *glob;
+ Type *type;
+ glong len;
+ guint32 flags;
+
+ if (is_simple_glob (pattern))
+ {
+ suffix = g_utf8_to_ucs4 (pattern + 1, -1, NULL, &len, NULL);
+
+ if (suffix == NULL)
+ {
+ g_warning ("Glob '%s' is not valid UTF-8", pattern);
+ return;
+ }
+
+ ucs4_reverse (suffix, len);
+ for ( ; list; list = list->next)
+ {
+ glob = (Glob *)list->data;
+ type = glob->type;
+ mimetype = g_strdup_printf ("%s/%s", type->media, type->subtype);
+
+ flags = 0;
+ if (glob->case_sensitive)
+ flags |= 0x100;
+ *suffixes = insert_suffix (suffix, mimetype, glob->weight, flags, *suffixes);
+ }
+
+ g_free (suffix);
+ }
+}
+
+static void
+calculate_size (SuffixEntry *entry)
+{
+ GList *s;
+
+ entry->size = 0;
+ entry->depth = 0;
+ for (s = entry->children; s; s= s->next)
+ {
+ SuffixEntry *child = (SuffixEntry *)s->data;
+
+ calculate_size (child);
+ entry->size += 1 + child->size;
+ entry->depth = MAX (entry->depth, child->depth + 1);
+ }
+}
+
+static gboolean
+write_suffix_entries (FILE *cache,
+ guint depth,
+ SuffixEntry *entry,
+ GHashTable *strings,
+ guint *child_offset)
+{
+ GList *c;
+ guint offset;
+
+ if (depth > 0)
+ {
+ gboolean error = FALSE;
+
+ for (c = entry->children; c; c = c->next)
+ {
+ SuffixEntry *child = (SuffixEntry *)c->data;
+ if (!write_suffix_entries (cache, depth - 1, child, strings, child_offset))
+ error = TRUE;
+ }
+
+ return !error;
+ }
+
+ if (entry->mimetype)
+ {
+ offset = GPOINTER_TO_UINT(g_hash_table_lookup (strings, entry->mimetype));
+ if (offset == 0)
+ {
+ g_warning ("Missing string: '%s'", entry->mimetype);
+ return FALSE;
+ }
+ }
+ else
+ offset = 0;
+
+ if (entry->character == 0)
+ {
+ if (!write_card32 (cache, entry->character))
+ return FALSE;
+
+ if (!write_card32 (cache, offset))
+ return FALSE;
+
+ if (!write_card32 (cache, (entry->weight & 0xff) | entry->flags))
+ return FALSE;
+ }
+ else
+ {
+ if (!write_card32 (cache, entry->character))
+ return FALSE;
+
+ if (!write_card32 (cache, g_list_length (entry->children)))
+ return FALSE;
+
+ if (!write_card32 (cache, *child_offset))
+ return FALSE;
+ }
+
+ *child_offset += 12 * g_list_length (entry->children);
+
+ return TRUE;
+}
+
+static gboolean
+write_suffix_cache (FILE *cache,
+ GHashTable *strings,
+ guint *offset)
+{
+ GList *suffixes, *s;
+ guint n_entries;
+ guint child_offset;
+ guint depth, d;
+
+ suffixes = NULL;
+
+ g_hash_table_foreach (globs_hash, build_suffixes, &suffixes);
+
+ n_entries = g_list_length (suffixes);
+
+ *offset += 8;
+ child_offset = *offset + 12 * n_entries;
+ depth = 0;
+ for (s = suffixes; s; s= s->next)
+ {
+ SuffixEntry *entry = (SuffixEntry *)s->data;
+ calculate_size (entry);
+ depth = MAX (depth, entry->depth + 1);
+ }
+
+ if (!write_card32 (cache, n_entries) || !write_card32 (cache, *offset))
+ return FALSE;
+
+ for (d = 0; d < depth; d++)
+ {
+ for (s = suffixes; s; s = s->next)
+ {
+ SuffixEntry *entry = (SuffixEntry *)s->data;
+
+ if (!write_suffix_entries (cache, d, entry, strings, &child_offset))
+ return FALSE;
+ }
+ }
+
+ *offset = child_offset;
+
+ return TRUE;
+}
+
+typedef struct {
+ FILE *cache;
+ GHashTable *strings;
+ GList *matches;
+ guint offset;
+ gboolean error;
+} WriteMatchData;
+
+
+static void
+write_match (gpointer key,
+ gpointer data)
+{
+ Magic *magic = (Magic *)key;
+ WriteMatchData *mdata = (WriteMatchData *)data;
+ gchar *mimetype;
+ guint offset;
+
+ if (!write_card32 (mdata->cache, magic->priority))
+ {
+ mdata->error = TRUE;
+ return;
+ }
+
+ mimetype = g_strdup_printf ("%s/%s", magic->type->media, magic->type->subtype);
+ offset = GPOINTER_TO_UINT (g_hash_table_lookup (mdata->strings, mimetype));
+ if (offset == 0)
+ {
+ g_warning ("Missing string: '%s'", mimetype);
+ g_free (mimetype);
+ mdata->error = TRUE;
+ return;
+ }
+ g_free (mimetype);
+
+ if (!write_card32 (mdata->cache, offset))
+ {
+ mdata->error = TRUE;
+ return;
+ }
+
+ if (!write_card32 (mdata->cache, g_list_length (magic->matches)))
+ {
+ mdata->error = TRUE;
+ return;
+ }
+
+ offset = mdata->offset + 32 * g_list_index (mdata->matches, magic->matches->data);
+
+ if (!write_card32 (mdata->cache, offset))
+ {
+ mdata->error = TRUE;
+ return;
+ }
+}
+
+static gboolean
+write_matchlet (FILE *cache,
+ Match *match,
+ GList *matches,
+ gint offset,
+ gint *offset2)
+{
+ if (!write_card32 (cache, match->range_start) ||
+ !write_card32 (cache, match->range_length) ||
+ !write_card32 (cache, match->word_size) ||
+ !write_card32 (cache, match->data_length) ||
+ !write_card32 (cache, *offset2))
+ return FALSE;
+
+ *offset2 = ALIGN_VALUE (*offset2 + match->data_length, 4);
+
+ if (match->mask)
+ {
+ if (!write_card32 (cache, *offset2))
+ return FALSE;
+
+ *offset2 = ALIGN_VALUE (*offset2 + match->data_length, 4);
+ }
+ else
+ {
+ if (!write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ if (match->matches)
+ {
+ if (!write_card32 (cache, g_list_length (match->matches)) ||
+ !write_card32 (cache, offset + 32 * g_list_index (matches, match->matches->data)))
+ return FALSE;
+ }
+ else
+ {
+ if (!write_card32 (cache, 0) ||
+ !write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+write_matchlet_data (FILE *cache,
+ Match *match,
+ gint *offset2)
+{
+ if (!write_data (cache, match->data, match->data_length))
+ return FALSE;
+
+ *offset2 = ALIGN_VALUE (*offset2 + match->data_length, 4);
+
+ if (match->mask)
+ {
+ if (!write_data (cache, match->mask, match->data_length))
+ return FALSE;
+
+ *offset2 = ALIGN_VALUE (*offset2 + match->data_length, 4);
+ }
+
+ return TRUE;
+}
+
+static void
+collect_matches_list (GList *list, GList **matches)
+{
+ GList *l;
+
+ for (l = list; l; l = l->next)
+ *matches = g_list_prepend (*matches, l->data);
+
+ for (l = list; l; l = l->next)
+ {
+ Match *match = (Match *)l->data;
+ collect_matches_list (match->matches, matches);
+ }
+}
+
+static void
+collect_matches (gpointer key, gpointer data)
+{
+ Magic *magic = (Magic *)key;
+ GList **matches = (GList **)data;
+
+ collect_matches_list (magic->matches, matches);
+}
+
+static gboolean
+write_magic_cache (FILE *cache,
+ GHashTable *strings,
+ guint *offset)
+{
+ guint n_entries, max_extent;
+ gint offset2;
+ GList *m;
+ WriteMatchData data;
+
+ data.matches = NULL;
+ g_ptr_array_foreach (magic_array, collect_matches, &data.matches);
+ data.matches = g_list_reverse (data.matches);
+
+ max_extent = 0;
+ for (m = data.matches; m; m = m->next)
+ {
+ Match *match = (Match *)m->data;
+ max_extent = MAX (max_extent, match->data_length + match->range_start + match->range_length);
+ }
+
+ n_entries = magic_array->len;
+
+ *offset += 12;
+
+ if (!write_card32 (cache, n_entries) ||
+ !write_card32 (cache, max_extent) ||
+ !write_card32 (cache, *offset))
+ return FALSE;
+
+ *offset += 16 * n_entries;
+
+ data.cache = cache;
+ data.strings = strings;
+ data.offset = *offset;
+ data.error = FALSE;
+
+ offset2 = *offset + 32 * g_list_length (data.matches);
+
+ g_ptr_array_foreach (magic_array, write_match, &data);
+ for (m = data.matches; m; m = m->next)
+ {
+ Match *match = (Match *)m->data;
+ write_matchlet (cache, match, data.matches, *offset, &offset2);
+ }
+
+ offset2 = *offset + 32 * g_list_length (data.matches);
+
+ for (m = data.matches; m; m = m->next)
+ {
+ Match *match = (Match *)m->data;
+ write_matchlet_data (cache, match, &offset2);
+ }
+
+ *offset = offset2;
+
+ g_list_free (data.matches);
+
+ return !data.error;
+}
+
+static gchar **
+get_namespace_value (gpointer data,
+ gchar *key)
+{
+ Type *type;
+ gchar **result;
+ gchar *space;
+
+ type = (Type *)g_hash_table_lookup ((GHashTable *)data, key);
+
+ result = g_new0 (gchar *, 4);
+ space = strchr (key, ' ');
+ if (*space)
+ {
+ *space = '\0';
+ result[0] = g_strdup (key);
+ result[1] = g_strdup (space + 1);
+ *space = ' ';
+ }
+ else
+ result[0] = g_strdup (key);
+
+ result[2] = g_strdup_printf ("%s/%s", type->media, type->subtype);
+
+ return result;
+}
+
+static gboolean
+write_namespace_cache (FILE *cache,
+ GHashTable *strings,
+ guint *offset)
+{
+ return write_map (cache, strings, namespace_hash, NULL,
+ get_namespace_value, FALSE, offset);
+}
+
+static gchar **
+get_icon_value (gpointer data,
+ gchar *key)
+{
+ gchar *iconname;
+ gchar **result;
+
+ iconname = (gchar *)g_hash_table_lookup ((GHashTable *)data, key);
+
+ result = g_new0 (gchar *, 3);
+ result[0] = g_strdup (key);
+ result[1] = g_strdup (iconname);
+ result[2] = NULL;
+
+ return result;
+}
+
+static gboolean
+write_icons_cache (FILE *cache,
+ GHashTable *strings,
+ GHashTable *icon_hash,
+ guint *offset)
+{
+ return write_map (cache, strings, icon_hash, NULL,
+ 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,
+ gpointer data)
+{
+ GHashTable *strings = (GHashTable *)data;
+ Type *type = (Type *)value;
+ gchar *mimetype;
+
+ mimetype = g_strdup_printf ("%s/%s", type->media, type->subtype);
+ g_hash_table_insert (strings, key, NULL);
+ g_hash_table_insert (strings, mimetype, NULL);
+}
+
+
+static void
+collect_parents (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ GList *parents = (GList *)value;
+ GHashTable *strings = (GHashTable *)data;
+ GList *p;
+
+ g_hash_table_insert (strings, key, NULL);
+ for (p = parents; p; p = p->next)
+ g_hash_table_insert (strings, p->data, NULL);
+}
+
+static void
+collect_glob (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ GList *list = (GList *)value;
+ GHashTable *strings = (GHashTable *)data;
+ gchar *mimetype;
+ Glob *glob;
+ Type *type;
+
+ switch (glob_type ((char *)key))
+ {
+ case GLOB_LITERAL:
+ case GLOB_FULL:
+ g_hash_table_insert (strings, key, NULL);
+ break;
+ default:
+ break;
+ }
+
+ for (; list; list = list->next)
+ {
+ glob = (Glob *)list->data;
+ type = glob->type;
+ mimetype = g_strdup_printf ("%s/%s", type->media, type->subtype);
+
+ g_hash_table_insert (strings, mimetype, NULL);
+ }
+}
+
+static void
+collect_magic (gpointer key,
+ gpointer data)
+{
+ Magic *magic = (Magic *)key;
+ GHashTable *strings = (GHashTable *)data;
+ gchar *mimetype;
+
+ mimetype = g_strdup_printf ("%s/%s", magic->type->media, magic->type->subtype);
+ g_hash_table_insert (strings, mimetype, NULL);
+}
+
+static void
+collect_namespace (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ gchar *ns = (gchar *)key;
+ Type *type = (Type *)value;
+ GHashTable *strings = (GHashTable *)data;
+ gchar *mimetype;
+ gchar *space;
+
+ mimetype = g_strdup_printf ("%s/%s", type->media, type->subtype);
+ g_hash_table_insert (strings, mimetype, NULL);
+
+ space = strchr (ns, ' ');
+
+ if (space)
+ {
+ *space = '\0';
+ g_hash_table_insert (strings, g_strdup (ns), NULL);
+ g_hash_table_insert (strings, space + 1, NULL);
+ *space = ' ';
+ }
+ else
+ g_hash_table_insert (strings, ns, NULL);
+}
+
+static void
+collect_icons(gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ gchar *mimetype = (gchar *)key;
+ gchar *iconname = (gchar *)value;
+ GHashTable *strings = (GHashTable *)data;
+
+ g_hash_table_insert (strings, mimetype, NULL);
+ g_hash_table_insert (strings, iconname, NULL);
+}
+
+
+static void
+collect_strings (GHashTable *strings)
+{
+ g_hash_table_foreach (alias_hash, collect_alias, strings);
+ g_hash_table_foreach (subclass_hash, collect_parents, strings);
+ g_hash_table_foreach (globs_hash, collect_glob, strings);
+ g_ptr_array_foreach (magic_array, collect_magic, strings);
+ g_hash_table_foreach (namespace_hash, collect_namespace, strings);
+ g_hash_table_foreach (generic_icon_hash, collect_icons, strings);
+ g_hash_table_foreach (icon_hash, collect_icons, strings);
+}
+
+typedef struct
+{
+ FILE *cache;
+ GHashTable *strings;
+ guint offset;
+ gboolean error;
+} StringData;
+
+static void
+write_one_string (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ gchar *str = (gchar *)key;
+ StringData *sdata = (StringData *)data;
+
+ if (!write_string (sdata->cache, str))
+ sdata->error = TRUE;
+
+ g_hash_table_insert (sdata->strings, str, GUINT_TO_POINTER (sdata->offset));
+
+ sdata->offset = ALIGN_VALUE (sdata->offset + strlen (str) + 1, 4);
+}
+
+static gboolean
+write_strings (FILE *cache,
+ GHashTable *strings,
+ guint *offset)
+{
+ StringData data;
+
+ data.cache = cache;
+ data.strings = strings;
+ data.offset = *offset;
+ data.error = FALSE;
+
+ g_hash_table_foreach (strings, write_one_string, &data);
+
+ *offset = data.offset;
+
+ return !data.error;
+}
+
+static gboolean
+write_cache (FILE *cache)
+{
+ guint strings_offset;
+ guint alias_offset;
+ guint parent_offset;
+ guint literal_offset;
+ guint suffix_offset;
+ guint glob_offset;
+ guint magic_offset;
+ 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, 0, &offset))
+ {
+ g_warning ("Failed to write header");
+ return FALSE;
+ }
+
+ strings = g_hash_table_new (g_str_hash, g_str_equal);
+ collect_strings (strings);
+ strings_offset = offset;
+
+ if (!write_strings (cache, strings, &offset))
+ {
+ g_warning ("Failed to write strings");
+ return FALSE;
+ }
+ g_message ("Wrote %d strings at %x - %x\n",
+ g_hash_table_size (strings), strings_offset, offset);
+
+ alias_offset = offset;
+ if (!write_alias_cache (cache, strings, &offset))
+ {
+ g_warning ("Failed to write alias list");
+ return FALSE;
+ }
+ g_message ("Wrote aliases at %x - %x\n", alias_offset, offset);
+
+ parent_offset = offset;
+ if (!write_parent_cache (cache, strings, &offset))
+ {
+ g_warning ("Failed to write parent list");
+ return FALSE;
+ }
+ g_message ("Wrote parents at %x - %x\n", parent_offset, offset);
+
+ literal_offset = offset;
+ if (!write_literal_cache (cache, strings, &offset))
+ {
+ g_warning ("Failed to write literal list");
+ return FALSE;
+ }
+ g_message ("Wrote literal globs at %x - %x\n", literal_offset, offset);
+
+ suffix_offset = offset;
+ if (!write_suffix_cache (cache, strings, &offset))
+ {
+ g_warning ("Failed to write suffix list");
+ return FALSE;
+ }
+ g_message ("Wrote suffix globs at %x - %x\n", suffix_offset, offset);
+
+ glob_offset = offset;
+ if (!write_glob_cache (cache, strings, &offset))
+ {
+ g_warning ("Failed to write glob list");
+ return FALSE;
+ }
+ g_message ("Wrote full globs at %x - %x\n", glob_offset, offset);
+
+ magic_offset = offset;
+ if (!write_magic_cache (cache, strings, &offset))
+ {
+ g_warning ("Failed to write magic list");
+ return FALSE;
+ }
+ g_message ("Wrote magic at %x - %x\n", magic_offset, offset);
+
+ namespace_offset = offset;
+ if (!write_namespace_cache (cache, strings, &offset))
+ {
+ g_warning ("Failed to write namespace list");
+ return FALSE;
+ }
+ g_message ("Wrote namespace list at %x - %x\n", namespace_offset, offset);
+
+ icons_list_offset = offset;
+ if (!write_icons_cache (cache, strings, icon_hash, &offset))
+ {
+ g_warning ("Failed to write icons list");
+ return FALSE;
+ }
+ g_message ("Wrote icons list at %x - %x\n", icons_list_offset, offset);
+
+ generic_icons_list_offset = offset;
+ if (!write_icons_cache (cache, strings, generic_icon_hash, &offset))
+ {
+ g_warning ("Failed to write generic icons list");
+ return FALSE;
+ }
+ 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");
+ return FALSE;
+ }
+ g_message ("Wrote types list at %x - %x\n", type_offset, offset);
+
+ rewind (cache);
+ offset = 0;
+
+ if (!write_header (cache,
+ alias_offset, parent_offset, literal_offset,
+ suffix_offset, glob_offset, magic_offset,
+ namespace_offset, icons_list_offset,
+ generic_icons_list_offset, type_offset,
+ &offset))
+ {
+ g_warning ("Failed to rewrite header");
+ return FALSE;
+ }
+
+ g_hash_table_destroy (strings);
+
+ return TRUE;
+}
+
+
+static FILE *
+open_or_die(const char *filename)
+{
+ FILE *stream = fopen(filename, "wb");
+
+ if (!stream)
+ {
+ g_printerr("Failed to open '%s' for writing\n", filename);
+ exit(EXIT_FAILURE);
+ }
+
+ return stream;
+}
+
+int main(int argc, char **argv)
+{
+ char *mime_dir = NULL;
+ char *package_dir = NULL;
+ int opt;
+
+ /* Install the filtering log handler */
+ g_log_set_default_handler(g_log_handler, NULL);
+
+ while ((opt = getopt(argc, argv, "hvV")) != -1)
+ {
+ switch (opt)
+ {
+ case '?':
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ case 'h':
+ usage(argv[0]);
+ return EXIT_SUCCESS;
+ case 'v':
+ g_fprintf(stderr,
+ "update-mime-database (" PACKAGE ") "
+ VERSION "\n" COPYING);
+ return EXIT_SUCCESS;
+ case 'V':
+ enabled_log_levels |= G_LOG_LEVEL_MESSAGE
+ | G_LOG_LEVEL_INFO;
+ break;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (optind != argc - 1)
+ {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ LIBXML_TEST_VERSION;
+
+ mime_dir = argv[optind];
+
+ /* Strip trailing / characters */
+ {
+ int l = strlen(mime_dir);
+ while (l > 1 && mime_dir[l - 1] == '/')
+ {
+ l--;
+ mime_dir[l] = '\0';
+ }
+ }
+
+ package_dir = g_strconcat(mime_dir, "/packages", NULL);
+
+ if (access(mime_dir, F_OK))
+ {
+ g_warning(_("Directory '%s' does not exist!"), package_dir);
+ return EXIT_FAILURE;
+ }
+
+ if (access(mime_dir, W_OK))
+ {
+ g_warning(_("%s: I don't have write permission on %s. "
+ "Try rerunning me as root."), argv[0], mime_dir);
+ return EXIT_FAILURE;
+ }
+
+ g_message("Updating MIME database in %s...\n", mime_dir);
+
+ if (access(package_dir, F_OK))
+ {
+ g_fprintf(stderr,
+ _("Directory '%s' does not exist!\n"), package_dir);
+ return EXIT_FAILURE;
+ }
+
+ types = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, free_type);
+ globs_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
+ 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,
+ g_free, NULL);
+ icon_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
+ generic_icon_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
+
+ scan_source_dir(package_dir);
+ g_free(package_dir);
+
+ delete_old_types(mime_dir);
+
+ g_hash_table_foreach(types, write_out_type, (gpointer) mime_dir);
+
+ {
+ FILE *globs;
+ char *globs_path;
+ GList *glob_list = NULL;
+
+ g_hash_table_foreach(globs_hash, collect_glob2, &glob_list);
+ glob_list = g_list_sort(glob_list, (GCompareFunc)compare_glob_by_weight);
+ globs_path = g_strconcat(mime_dir, "/globs.new", NULL);
+ globs = open_or_die(globs_path);
+ g_fprintf(globs,
+ "# This file was automatically generated by the\n"
+ "# update-mime-database command. DO NOT EDIT!\n");
+ write_out_glob(glob_list, globs);
+ fclose(globs);
+ atomic_update(globs_path);
+ g_free(globs_path);
+
+ globs_path = g_strconcat(mime_dir, "/globs2.new", NULL);
+ globs = open_or_die(globs_path);
+ g_fprintf(globs,
+ "# This file was automatically generated by the\n"
+ "# update-mime-database command. DO NOT EDIT!\n");
+ write_out_glob2 (glob_list, globs);
+ fclose(globs);
+ atomic_update(globs_path);
+ g_free(globs_path);
+
+ g_list_free (glob_list);
+ }
+
+ {
+ FILE *stream;
+ char *magic_path;
+ int i;
+ magic_path = g_strconcat(mime_dir, "/magic.new", NULL);
+ stream = open_or_die(magic_path);
+ fwrite("MIME-Magic\0\n", 1, 12, stream);
+
+ if (magic_array->len)
+ g_ptr_array_sort(magic_array, cmp_magic);
+ for (i = 0; i < magic_array->len; i++)
+ {
+ Magic *magic = (Magic *) magic_array->pdata[i];
+
+ write_magic(stream, magic);
+ }
+ fclose(stream);
+
+ atomic_update(magic_path);
+ g_free(magic_path);
+ }
+
+ {
+ FILE *stream;
+ char *ns_path;
+
+ ns_path = g_strconcat(mime_dir, "/XMLnamespaces.new", NULL);
+ stream = open_or_die(ns_path);
+ write_namespaces(stream);
+ fclose(stream);
+
+ atomic_update(ns_path);
+ g_free(ns_path);
+ }
+
+ {
+ FILE *stream;
+ char *path;
+
+ path = g_strconcat(mime_dir, "/subclasses.new", NULL);
+ stream = open_or_die(path);
+ write_subclasses(stream);
+ fclose(stream);
+
+ atomic_update(path);
+ g_free(path);
+ }
+
+ {
+ FILE *stream;
+ char *path;
+
+ path = g_strconcat(mime_dir, "/aliases.new", NULL);
+ stream = open_or_die(path);
+ write_aliases(stream);
+ fclose(stream);
+
+ atomic_update(path);
+ g_free(path);
+ }
+
+ {
+ 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);
+ stream = open_or_die(icon_path);
+ write_icons(generic_icon_hash, stream);
+ fclose(stream);
+
+ atomic_update(icon_path);
+ g_free(icon_path);
+ }
+
+ {
+ FILE *stream;
+ char *icon_path;
+
+ icon_path = g_strconcat(mime_dir, "/icons.new", NULL);
+ stream = open_or_die(icon_path);
+ write_icons(icon_hash, stream);
+ fclose(stream);
+
+ atomic_update(icon_path);
+ g_free(icon_path);
+ }
+
+ {
+ 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);
+ write_cache(stream);
+ fclose(stream);
+
+ atomic_update(path);
+ g_free(path);
+ }
+
+ {
+ FILE *stream;
+ char *path;
+
+ path = g_strconcat(mime_dir, "/version.new", NULL);
+ stream = open_or_die(path);
+ g_fprintf(stream,
+ VERSION "\n");
+ fclose(stream);
+
+ atomic_update(path);
+ g_free(path);
+ }
+
+ 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);
+ g_hash_table_destroy(namespace_hash);
+ g_hash_table_destroy(subclass_hash);
+ g_hash_table_destroy(alias_hash);
+ g_hash_table_destroy(icon_hash);
+ g_hash_table_destroy(generic_icon_hash);
+
+ check_in_path_xdg_data(mime_dir);
+
+ return EXIT_SUCCESS;
+}