12 #include <glib/gprintf.h>
15 #include <libxml/parser.h>
16 #include <libxml/tree.h>
18 #include <sys/types.h>
20 #define XML_NS XML_XML_NAMESPACE
21 #define XMLNS_NS "http://www.w3.org/2000/xmlns/"
22 #define FREE_NS (xmlChar *)"http://www.freedesktop.org/standards/shared-mime-info"
25 N_("Copyright (C) 2003 Thomas Leonard.\n" \
26 "update-mime-database comes with ABSOLUTELY NO WARRANTY,\n" \
27 "to the extent permitted by law.\n" \
28 "You may redistribute copies of update-mime-database\n" \
29 "under the terms of the GNU General Public License.\n" \
30 "For more information about these matters, " \
31 "see the file named COPYING.\n")
33 #define MIME_ERROR g_quark_from_static_string("mime-error-quark")
35 #ifndef PATH_SEPARATOR
37 # define PATH_SEPARATOR ";"
39 # define PATH_SEPARATOR ":"
43 /* This is the list of directories to scan when finding old type files to
44 * delete. It is also used to warn about invalid MIME types.
46 const char *media_types[] = {
60 /* Represents a MIME type */
61 typedef struct _Type Type;
63 /* A parsed <magic> element */
64 typedef struct _Magic Magic;
66 /* A parsed <match> element */
67 typedef struct _Match Match;
69 /* A parsed <treemagic> element */
70 typedef struct _TreeMagic TreeMagic;
72 /* A parsed <treematch> element */
73 typedef struct _TreeMatch TreeMatch;
75 /* A parsed <glob> element */
76 typedef struct _Glob Glob;
82 /* Contains xmlNodes for elements that are being copied to the output.
83 * That is, <comment>, <sub-class-of> and <alias> nodes, and anything
84 * with an unknown namespace.
128 /* Maps MIME type names to Types */
129 static GHashTable *types = NULL;
131 /* Maps "namespaceURI localName" strings to Types */
132 static GHashTable *namespace_hash = NULL;
134 /* Maps glob patterns to Types */
135 static GHashTable *globs_hash = NULL;
138 static GPtrArray *magic_array = NULL;
140 /* 'treemagic' nodes */
141 static GPtrArray *tree_magic_array = NULL;
143 /* Maps MIME type names to superclass names */
144 static GHashTable *subclass_hash = NULL;
146 /* Maps aliases to Types */
147 static GHashTable *alias_hash = NULL;
149 /* Maps MIME type names to icon names */
150 static GHashTable *icon_hash = NULL;
152 /* Maps MIME type names to icon names */
153 static GHashTable *generic_icon_hash = NULL;
155 /* Lists enabled log levels */
156 static GLogLevelFlags enabled_log_levels = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING;
158 /* Static prototypes */
159 static Magic *magic_new(xmlNode *node, Type *type, GError **error);
161 static TreeMagic *tree_magic_new(xmlNode *node, Type *type, GError **error);
163 static void g_log_handler (const gchar *log_domain,
164 GLogLevelFlags log_level,
165 const gchar *message,
166 gpointer unused_data)
168 if (log_level & enabled_log_levels) {
169 g_printf("%s\n", message);
174 static void usage(const char *name)
176 g_fprintf(stderr, _("Usage: %s [-hvV] MIME-DIR\n"), name);
179 static void free_type(gpointer data)
181 Type *type = (Type *) data;
184 g_free(type->subtype);
186 xmlFreeDoc(type->output);
191 /* Ugly workaround to shut up gcc4 warnings about signedness issues
192 * (xmlChar is typedef'ed to unsigned char)
194 static char *my_xmlGetNsProp (xmlNodePtr node,
196 const xmlChar *namespace)
198 return (char *)xmlGetNsProp (node, (xmlChar *)name, namespace);
201 /* If we've seen this type before, return the existing object.
202 * Otherwise, create a new one. Checks that the name looks sensible;
203 * if not, sets error and returns NULL.
204 * Also warns about unknown media types, but does not set error.
206 static Type *get_type(const char *name, GError **error)
214 slash = strchr(name, '/');
215 if (!slash || strchr(slash + 1, '/'))
217 g_set_error(error, MIME_ERROR, 0,
218 _("Invalid MIME-type '%s'"), name);
222 type = g_hash_table_lookup(types, name);
226 type = g_new(Type, 1);
227 type->media = g_strndup(name, slash - name);
228 type->subtype = g_strdup(slash + 1);
229 g_hash_table_insert(types, g_strdup(name), type);
231 type->output = xmlNewDoc((xmlChar *)"1.0");
232 root = xmlNewDocNode(type->output, NULL, (xmlChar *)"mime-type", NULL);
233 ns = xmlNewNs(root, FREE_NS, NULL);
235 xmlDocSetRootElement(type->output, root);
236 xmlSetNsProp(root, NULL, (xmlChar *)"type", (xmlChar *)name);
237 xmlAddChild(root, xmlNewDocComment(type->output,
238 (xmlChar *)"Created automatically by update-mime-database. DO NOT EDIT!"));
240 for (i = 0; i < G_N_ELEMENTS(media_types); i++)
242 if (strcmp(media_types[i], type->media) == 0)
246 g_warning("Unknown media type in type '%s'\n", name);
251 /* Test that this node has the expected name and namespace */
252 static gboolean match_node(xmlNode *node,
253 const char *namespaceURI,
254 const char *localName)
258 strcmp((char *)node->ns->href, namespaceURI) == 0 &&
259 strcmp((char *)node->name, localName) == 0;
261 return strcmp((char *)node->name, localName) == 0 && !node->ns;
264 static int get_int_attribute(xmlNode *node, const char *name)
269 prio_string = my_xmlGetNsProp(node, name, NULL);
274 p = strtol(prio_string, &end, 10);
275 if (*prio_string == '\0' || *end != '\0')
277 xmlFree(prio_string);
278 if (p < 0 || p > 100)
286 /* Return the priority of a <magic> node.
287 * Returns 50 if no priority is given, or -1 if a priority is given but
290 static int get_priority(xmlNode *node)
292 return get_int_attribute (node, "priority");
295 /* Return the weight a <glob> node.
296 * Returns 50 if no weight is given, or -1 if a weight is given but
299 static int get_weight(xmlNode *node)
301 return get_int_attribute (node, "weight");
304 /* Process a <root-XML> element by adding a rule to namespace_hash */
305 static void add_namespace(Type *type, const char *namespaceURI,
306 const char *localName, GError **error)
308 g_return_if_fail(type != NULL);
312 g_set_error(error, MIME_ERROR, 0,
313 _("Missing 'namespaceURI' attribute'"));
319 g_set_error(error, MIME_ERROR, 0,
320 _("Missing 'localName' attribute'"));
324 if (!*namespaceURI && !*localName)
326 g_set_error(error, MIME_ERROR, 0,
327 _("namespaceURI and localName attributes can't "
332 if (strpbrk(namespaceURI, " \n") || strpbrk(localName, " \n"))
334 g_set_error(error, MIME_ERROR, 0,
335 _("namespaceURI and localName cannot contain "
336 "spaces or newlines"));
340 g_hash_table_insert(namespace_hash,
341 g_strconcat(namespaceURI, " ", localName, NULL),
345 /* 'field' was found in the definition of 'type' and has the freedesktop.org
346 * namespace. If it's a known field, process it and return TRUE, else
347 * return FALSE to add it to the output XML document.
348 * On error, returns FALSE and sets 'error'.
350 static gboolean process_freedesktop_node(Type *type, xmlNode *field,
353 if (strcmp((char *)field->name, "glob") == 0)
358 weight = get_weight(field);
362 g_set_error(error, MIME_ERROR, 0,
363 _("Bad weight (%d) in <glob> element"), weight);
365 pattern = my_xmlGetNsProp(field, "pattern", NULL);
367 if (pattern && *pattern)
370 GList *list = g_hash_table_lookup (globs_hash, pattern);
372 glob = g_new0 (Glob, 1);
373 glob->pattern = g_strdup (pattern);
375 glob->weight = weight;
376 list = g_list_append (list, glob);
377 g_hash_table_insert(globs_hash, g_strdup (glob->pattern), list);
384 g_set_error(error, MIME_ERROR, 0,
385 _("Missing 'pattern' attribute in <glob> "
389 else if (strcmp((char *)field->name, "magic") == 0)
393 magic = magic_new(field, type, error);
397 g_return_val_if_fail(magic != NULL, FALSE);
398 g_ptr_array_add(magic_array, magic);
401 g_return_val_if_fail(magic == NULL, FALSE);
403 else if (strcmp((char *)field->name, "treemagic") == 0)
407 magic = tree_magic_new(field, type, error);
411 g_return_val_if_fail(magic != NULL, FALSE);
412 g_ptr_array_add(tree_magic_array, magic);
415 g_return_val_if_fail(magic == NULL, FALSE);
417 else if (strcmp((char *)field->name, "comment") == 0 ||
418 strcmp((char *)field->name, "acronym") == 0 ||
419 strcmp((char *)field->name, "expanded-acronym") == 0)
421 else if (strcmp((char *)field->name, "alias") == 0 ||
422 strcmp((char *)field->name, "sub-class-of") == 0)
426 GSList *list, *nlist;
428 other_type = my_xmlGetNsProp(field, "type", NULL);
429 valid = other_type && strchr(other_type, '/');
434 typename = g_strdup_printf("%s/%s",
438 if (strcmp((char *)field->name, "alias") == 0)
439 g_hash_table_insert(alias_hash,
440 g_strdup(other_type), type);
444 list = g_hash_table_lookup(subclass_hash, typename);
445 nlist = g_slist_append (list, g_strdup(other_type));
447 g_hash_table_insert(subclass_hash,
448 g_strdup(typename), nlist);
453 return FALSE; /* Copy through */
457 g_set_error(error, MIME_ERROR, 0,
458 _("Incorrect or missing 'type' attribute "
459 "in <%s>"), field->name);
461 else if (strcmp((char *)field->name, "root-XML") == 0)
463 char *namespaceURI, *localName;
465 namespaceURI = my_xmlGetNsProp(field, "namespaceURI", NULL);
466 localName = my_xmlGetNsProp(field, "localName", NULL);
468 add_namespace(type, namespaceURI, localName, error);
471 xmlFree(namespaceURI);
475 else if (strcmp((char *)field->name, "generic-icon") == 0 ||
476 strcmp((char *)field->name, "icon") == 0)
481 icon = my_xmlGetNsProp(field, "name", NULL);
485 typename = g_strdup_printf("%s/%s",
489 if (strcmp((char *)field->name, "icon") == 0)
490 g_hash_table_insert(icon_hash,
491 typename, g_strdup (icon));
493 g_hash_table_insert(generic_icon_hash,
494 typename, g_strdup (icon));
498 return FALSE; /* Copy through */
505 /* Checks to see if 'node' has the given value for xml:lang.
506 * If 'lang' is NULL, checks that 'node' doesn't have an xml:lang.
508 static gboolean has_lang(xmlNode *node, const char *lang)
512 lang2 = my_xmlGetNsProp(node, "lang", XML_NS);
516 if (lang && strcmp(lang, lang2) == 0)
525 /* We're about to add 'new' to the list of fields to be output for the
526 * type. Remove any existing nodes which it replaces.
528 static void remove_old(Type *type, xmlNode *new)
530 xmlNode *field, *fields;
533 if (new->ns == NULL || xmlStrcmp(new->ns->href, FREE_NS) != 0)
534 return; /* No idea what we're doing -- leave it in! */
536 if (strcmp((char *)new->name, "comment") != 0)
539 lang = my_xmlGetNsProp(new, "lang", XML_NS);
541 fields = xmlDocGetRootElement(type->output);
542 for (field = fields->xmlChildrenNode; field; field = field->next)
544 if (match_node(field, (char *)FREE_NS, "comment") &&
545 has_lang(field, lang))
547 xmlUnlinkNode(field);
556 /* 'node' is a <mime-type> node from a source file, whose type is 'type'.
557 * Process all the child elements, setting 'error' if anything goes wrong.
559 static void load_type(Type *type, xmlNode *node, GError **error)
563 g_return_if_fail(type != NULL);
564 g_return_if_fail(node != NULL);
565 g_return_if_fail(error != NULL);
567 for (field = node->xmlChildrenNode; field; field = field->next)
571 if (field->type != XML_ELEMENT_NODE)
574 if (field->ns && xmlStrcmp(field->ns->href, FREE_NS) == 0)
576 if (process_freedesktop_node(type, field, error))
578 g_return_if_fail(*error == NULL);
586 copy = xmlDocCopyNode(field, type->output, 1);
588 /* Ugly hack to stop the xmlns= attributes appearing on
591 if (copy->ns && copy->ns->prefix == NULL &&
592 xmlStrcmp(copy->ns->href, FREE_NS) == 0)
596 /* Still used somewhere... */
597 /* xmlFreeNsList(copy->nsDef); */
603 remove_old(type, field);
605 xmlAddChild(xmlDocGetRootElement(type->output), copy);
609 /* Parse 'filename' as an XML file and add all the information to the
610 * database. If called more than once, information read in later calls
611 * overrides information read previously.
613 static void load_source_file(const char *filename)
616 xmlNode *root, *node;
618 doc = xmlParseFile(filename);
621 g_warning(_("Failed to parse '%s'\n"), filename);
625 root = xmlDocGetRootElement(doc);
627 if (root->ns == NULL || xmlStrcmp(root->ns->href, FREE_NS) != 0)
629 g_warning("* Wrong namespace on document element\n"
631 "* (should be %s)\n", filename, FREE_NS);
635 if (strcmp((char *)root->name, "mime-info") != 0)
637 g_warning("* Root element <%s> is not <mime-info>\n"
638 "* (in '%s')\n", root->name, filename);
642 for (node = root->xmlChildrenNode; node; node = node->next)
645 char *type_name = NULL;
646 GError *error = NULL;
648 if (node->type != XML_ELEMENT_NODE)
651 if (!match_node(node, (char *)FREE_NS, "mime-type"))
652 g_set_error(&error, MIME_ERROR, 0,
653 _("Excepted <mime-type>, but got wrong name "
658 type_name = my_xmlGetNsProp(node, "type", NULL);
661 g_set_error(&error, MIME_ERROR, 0,
662 _("<mime-type> element has no 'type' "
668 type = get_type(type_name, &error);
674 g_return_if_fail(type != NULL);
675 load_type(type, node, &error);
678 g_return_if_fail(type == NULL);
682 g_warning("* Error in type '%s/%s'\n"
685 type ? type->media : _("unknown"),
686 type ? type->subtype : _("unknown"),
687 filename, error->message);
695 /* Used as the sort function for sorting GPtrArrays */
696 static gint strcmp2(gconstpointer a, gconstpointer b)
698 const char *aa = *(char **) a;
699 const char *bb = *(char **) b;
701 return strcmp(aa, bb);
704 /* 'path' should be a 'packages' directory. Loads the information from
705 * every file in the directory.
707 static void scan_source_dir(const char *path)
714 gboolean have_override = FALSE;
719 perror("scan_source_dir");
723 files = g_ptr_array_new();
724 while ((ent = readdir(dir)))
727 l = strlen(ent->d_name);
728 if (l < 4 || strcmp(ent->d_name + l - 4, ".xml") != 0)
730 if (strcmp(ent->d_name, "Override.xml") == 0)
732 have_override = TRUE;
735 g_ptr_array_add(files, g_strdup(ent->d_name));
739 g_ptr_array_sort(files, strcmp2);
742 g_ptr_array_add(files, g_strdup("Override.xml"));
744 for (i = 0; i < files->len; i++)
746 gchar *leaf = (gchar *) files->pdata[i];
748 filename = g_strconcat(path, "/", leaf, NULL);
749 load_source_file(filename);
753 for (i = 0; i < files->len; i++)
754 g_free(files->pdata[i]);
755 g_ptr_array_free(files, TRUE);
758 /* Save doc as XML as filename, 0 on success or -1 on failure */
759 static int save_xml_file(xmlDocPtr doc, const gchar *filename)
761 #if LIBXML_VERSION > 20400
762 if (xmlSaveFormatFileEnc(filename, doc, "utf-8", 1) < 0)
767 out = fopen(filename, "w");
771 xmlDocDump(out, doc); /* Some versions return void */
780 /* Write out globs for one pattern to the 'globs' file */
781 static void write_out_glob(GList *globs, FILE *stream)
787 for (list = globs; list; list = list->next) {
788 glob = (Glob *)list->data;
790 if (strchr(glob->pattern, '\n'))
791 g_warning("* Glob patterns can't contain literal newlines "
792 "(%s in type %s/%s)\n", glob->pattern,
793 type->media, type->subtype);
795 g_fprintf(stream, "%s/%s:%s\n",
796 type->media, type->subtype, glob->pattern);
800 /* Write out globs and weights for one pattern to the 'globs2' file */
801 static void write_out_glob2(GList *globs, FILE *stream)
807 for (list = globs ; list; list = list->next) {
808 glob = (Glob *)list->data;
810 if (strchr(glob->pattern, '\n'))
811 g_warning("* Glob patterns can't contain literal newlines "
812 "(%s in type %s/%s)\n", glob->pattern,
813 type->media, type->subtype);
815 g_fprintf(stream, "%d:%s/%s:%s\n",
816 glob->weight, type->media, type->subtype, glob->pattern);
820 static void collect_glob2(gpointer key, gpointer value, gpointer data)
822 GList **listp = data;
824 *listp = g_list_concat (*listp, g_list_copy ((GList *)value));
827 static int compare_by_weight (gpointer a, gpointer b)
829 Glob *ag = (Glob *)a;
830 Glob *bg = (Glob *)b;
832 return bg->weight - ag->weight;
835 /* Renames pathname by removing the .new extension */
836 static void atomic_update(const gchar *pathname)
841 len = strlen(pathname);
843 g_return_if_fail(strcmp(pathname + len - 4, ".new") == 0);
845 new_name = g_strndup(pathname, len - 4);
848 /* we need to remove the old file first! */
851 if (rename(pathname, new_name))
852 g_warning("Failed to rename %s as %s , errno: %d\n", pathname, new_name, errno);
857 /* Write out an XML file for one type */
858 static void write_out_type(gpointer key, gpointer value, gpointer data)
860 Type *type = (Type *) value;
861 const char *mime_dir = (char *) data;
862 char *media, *filename;
864 media = g_strconcat(mime_dir, "/", type->media, NULL);
871 filename = g_strconcat(media, "/", type->subtype, ".xml.new", NULL);
875 if (save_xml_file(type->output, filename) != 0)
876 g_warning("Failed to write out '%s'\n", filename);
878 atomic_update(filename);
883 /* Comparison function to get the magic rules in priority order */
884 static gint cmp_magic(gconstpointer a, gconstpointer b)
886 Magic *aa = *(Magic **) a;
887 Magic *bb = *(Magic **) b;
890 if (aa->priority > bb->priority)
892 else if (aa->priority < bb->priority)
895 retval = strcmp(aa->type->media, bb->type->media);
897 retval = strcmp(aa->type->subtype, bb->type->subtype);
902 /* Comparison function to get the tree magic rules in priority order */
903 static gint cmp_tree_magic(gconstpointer a, gconstpointer b)
905 TreeMagic *aa = *(TreeMagic **) a;
906 TreeMagic *bb = *(TreeMagic **) b;
909 if (aa->priority > bb->priority)
911 else if (aa->priority < bb->priority)
914 retval = strcmp(aa->type->media, bb->type->media);
916 retval = strcmp(aa->type->subtype, bb->type->subtype);
921 /* Write out 'n' as a two-byte big-endian number to 'stream' */
922 static void write16(FILE *stream, guint32 n)
924 guint16 big = GUINT16_TO_BE(n);
926 g_return_if_fail(n <= 0xffff);
928 fwrite(&big, sizeof(big), 1, stream);
931 /* Single hex char to int; -1 if not a hex char.
934 static int hextoint(int c)
936 if (!isascii((unsigned char) c))
938 if (isdigit((unsigned char) c))
940 if ((c >= 'a')&&(c <= 'f'))
942 if (( c>= 'A')&&(c <= 'F'))
948 * Convert a string containing C character escapes. Stop at an unescaped
950 * Copy the converted version to "p", returning its length in *slen.
951 * Return updated scan pointer as function result.
952 * Stolen from file(1) and heavily modified.
954 static void getstr(const char *s, GString *out)
959 while ((c = *s++) != '\0') {
967 g_string_append_c(out, (char) c);
971 g_string_append_c(out, '\n');
975 g_string_append_c(out, '\r');
979 g_string_append_c(out, '\b');
983 g_string_append_c(out, '\t');
987 g_string_append_c(out, '\f');
991 g_string_append_c(out, '\v');
994 /* \ and up to 3 octal digits */
1004 c = *s++; /* try for 2 */
1005 if(c >= '0' && c <= '7') {
1006 val = (val<<3) | (c - '0');
1007 c = *s++; /* try for 3 */
1008 if(c >= '0' && c <= '7')
1009 val = (val<<3) | (c-'0');
1015 g_string_append_c(out, (char)val);
1018 /* \x and up to 2 hex digits */
1020 val = 'x'; /* Default if no digits */
1021 c = hextoint(*s++); /* Get next char */
1026 val = (val << 4) + c;
1031 g_string_append_c(out, (char)val);
1035 g_string_append_c(out, (char)c);
1039 /* Parse the value and mask attributes of a <match> element with a
1040 * numerical type (anything except "string").
1042 static void parse_int_value(int bytes, const char *in, const char *in_mask,
1043 GString *parsed_value, char **parsed_mask,
1044 gboolean big_endian, GError **error)
1047 char *out_mask = NULL;
1048 unsigned long value;
1051 value = strtoul(in, &end, 0);
1052 if (errno == ERANGE) {
1053 g_set_error(error, MIME_ERROR, 0,
1054 "Number out-of-range (%s should fit in %d bytes)",
1061 g_set_error(error, MIME_ERROR, 0, "Value is not a number");
1065 for (b = 0; b < bytes; b++)
1067 int shift = (big_endian ? (bytes - b - 1) : b) * 8;
1068 g_string_append_c(parsed_value, (value >> shift) & 0xff);
1071 if ((bytes == 1 && (value & ~0xff)) ||
1072 (bytes == 2 && (value & ~0xffff)))
1074 g_set_error(error, MIME_ERROR, 0,
1075 "Number out-of-range (%lx should fit in %d bytes)",
1085 mask = strtoul(in_mask, &end, 0);
1086 if (errno == ERANGE) {
1087 g_set_error(error, MIME_ERROR, 0,
1088 "Mask out-of-range (%s should fit in %d bytes)",
1096 g_set_error(error, MIME_ERROR, 0,
1097 "Mask is not a number");
1101 out_mask = g_new(char, bytes);
1102 for (b = 0; b < bytes; b++)
1104 int shift = (big_endian ? (bytes - b - 1) : b) * 8;
1105 out_mask[b] = (mask >> shift) & 0xff;
1109 *parsed_mask = out_mask;
1112 /* 'len' is the length of the value. The mask created will be the same
1115 static char *parse_string_mask(const char *mask, int len, GError **error)
1118 char *parsed_mask = NULL;
1120 g_return_val_if_fail(mask != NULL, NULL);
1121 g_return_val_if_fail(len > 0, NULL);
1123 if (mask[0] != '0' || mask[1] != 'x')
1125 g_set_error(error, MIME_ERROR, 0,
1126 "String masks must be in base 16 (starting with 0x)");
1131 parsed_mask = g_new0(char, len);
1133 i = 0; /* Nybble to write to next */
1138 c = hextoint(mask[i]);
1141 g_set_error(error, MIME_ERROR, 0,
1142 "'%c' is not a valid hex digit", mask[i]);
1148 g_set_error(error, MIME_ERROR, 0,
1149 "Mask is longer than value");
1154 parsed_mask[i >> 1] |= c;
1156 parsed_mask[i >> 1] |= c << 4;
1163 g_return_val_if_fail(error == NULL || *error != NULL, NULL);
1164 g_free(parsed_mask);
1168 /* Parse the value and mask attributes for a <match> element */
1169 static void parse_value(const char *type, const char *in, const char *in_mask,
1170 GString *parsed_value, char **parsed_mask,
1173 *parsed_mask = NULL;
1175 if (in == NULL || !in[0])
1177 g_set_error(error, MIME_ERROR, 0, "No value specified");
1181 if (strstr(type, "16"))
1182 parse_int_value(2, in, in_mask, parsed_value, parsed_mask,
1183 type[0] != 'l', error);
1184 else if (strstr(type, "32"))
1185 parse_int_value(4, in, in_mask, parsed_value, parsed_mask,
1186 type[0] != 'l', error);
1187 else if (strcmp(type, "byte") == 0)
1188 parse_int_value(1, in, in_mask, parsed_value, parsed_mask,
1190 else if (strcmp(type, "string") == 0)
1192 getstr(in, parsed_value);
1194 *parsed_mask = parse_string_mask(in_mask,
1195 parsed_value->len, error);
1198 g_assert_not_reached();
1201 static Match *match_new(void)
1205 match = g_new(Match, 1);
1206 match->range_start = 0;
1207 match->range_length = 1;
1208 match->word_size = 1;
1209 match->data_length = 0;
1212 match->matches = NULL;
1217 static void match_free(Match *match)
1221 g_return_if_fail(match != NULL);
1223 for (next = match->matches; next; next = next->next)
1224 match_free((Match *) next->data);
1226 g_list_free(match->matches);
1228 g_free(match->data);
1229 g_free(match->mask);
1234 /* Sets match->range_start and match->range_length */
1235 static void match_offset(Match *match, xmlNode *node, GError **error)
1237 char *offset = NULL;
1240 offset = my_xmlGetNsProp(node, "offset", NULL);
1241 if (offset == NULL || !*offset)
1243 g_set_error(error, MIME_ERROR, 0, "Missing 'offset' attribute");
1247 match->range_start = strtol(offset, &end, 10);
1248 if (errno == ERANGE) {
1250 number = g_strndup(offset, end-offset);
1251 g_set_error(error, MIME_ERROR, 0,
1252 "Number out-of-range (%s should fit in 4 bytes)",
1258 if (*end == ':' && end[1] && match->range_start >= 0)
1264 last = strtol(begin, &end, 10);
1265 if (errno == ERANGE) {
1267 number = g_strndup(begin, end-begin);
1268 g_set_error(error, MIME_ERROR, 0,
1269 "Number out-of-range (%s should fit in 8 bytes)",
1275 if (*end == '\0' && last >= match->range_start)
1276 match->range_length = last - match->range_start + 1;
1278 g_set_error(error, MIME_ERROR, 0, "Invalid offset");
1280 else if (*end != '\0')
1281 g_set_error(error, MIME_ERROR, 0, "Invalid offset");
1286 /* Sets match->data, match->data_length and match->mask */
1287 static void match_value_and_mask(Match *match, xmlNode *node, GError **error)
1292 char *parsed_mask = NULL;
1293 GString *parsed_value;
1295 type = my_xmlGetNsProp(node, "type", NULL);
1296 g_return_if_fail(type != NULL);
1298 mask = my_xmlGetNsProp(node, "mask", NULL);
1299 value = my_xmlGetNsProp(node, "value", NULL);
1301 parsed_value = g_string_new(NULL);
1303 parse_value(type, value, mask, parsed_value,
1304 &parsed_mask, error);
1308 g_string_free(parsed_value, TRUE);
1309 g_return_if_fail(parsed_mask == NULL);
1313 match->data = parsed_value->str;
1314 match->data_length = parsed_value->len;
1315 match->mask = parsed_mask;
1317 g_string_free(parsed_value, FALSE);
1327 /* Sets match->word_size */
1328 static void match_word_size(Match *match, xmlNode *node, GError **error)
1332 type = my_xmlGetNsProp(node, "type", NULL);
1336 g_set_error(error, MIME_ERROR, 0,
1337 _("Missing 'type' attribute in <match>"));
1341 if (strcmp(type, "host16") == 0)
1342 match->word_size = 2;
1343 else if (strcmp(type, "host32") == 0)
1344 match->word_size = 4;
1345 else if (!*error && strcmp(type, "big16") &&
1346 strcmp(type, "big32") &&
1347 strcmp(type, "little16") && strcmp(type, "little32") &&
1348 strcmp(type, "string") && strcmp(type, "byte"))
1350 g_set_error(error, MIME_ERROR, 0,
1351 "Unknown magic type '%s'", type);
1357 /* Turn the list of child nodes of 'parent' into a list of Matches */
1358 static GList *build_matches(xmlNode *parent, GError **error)
1363 g_return_val_if_fail(error != NULL, NULL);
1365 for (node = parent->xmlChildrenNode; node; node = node->next)
1369 if (node->type != XML_ELEMENT_NODE)
1372 if (node->ns == NULL || xmlStrcmp(node->ns->href, FREE_NS) != 0)
1374 g_set_error(error, MIME_ERROR, 0,
1375 _("Element found with non-freedesktop.org "
1380 if (strcmp((char *)node->name, "match") != 0)
1382 g_set_error(error, MIME_ERROR, 0,
1383 _("Expected <match> element, but found "
1384 "<%s> instead"), node->name);
1388 match = match_new();
1389 match_offset(match, node, error);
1391 match_word_size(match, node, error);
1393 match_value_and_mask(match, node, error);
1401 out = g_list_append(out, match);
1403 match->matches = build_matches(node, error);
1411 static void magic_free(Magic *magic)
1415 g_return_if_fail(magic != NULL);
1417 for (next = magic->matches; next; next = next->next)
1418 match_free((Match *) next->data);
1419 g_list_free(magic->matches);
1424 /* Create a new Magic object by parsing 'node' (a <magic> element) */
1425 static Magic *magic_new(xmlNode *node, Type *type, GError **error)
1427 Magic *magic = NULL;
1430 g_return_val_if_fail(node != NULL, NULL);
1431 g_return_val_if_fail(type != NULL, NULL);
1432 g_return_val_if_fail(error != NULL, NULL);
1434 prio = get_priority(node);
1438 g_set_error(error, MIME_ERROR, 0,
1439 _("Bad priority (%d) in <magic> element"), prio);
1443 magic = g_new(Magic, 1);
1444 magic->priority = prio;
1446 magic->matches = build_matches(node, error);
1450 gchar *old = (*error)->message;
1453 (*error)->message = g_strconcat(
1454 _("Error in <match> element: "), old, NULL);
1462 static TreeMatch *tree_match_new(void)
1466 match = g_new(TreeMatch, 1);
1468 match->match_case = 0;
1469 match->executable = 0;
1470 match->non_empty = 0;
1472 match->mimetype = NULL;
1473 match->matches = NULL;
1478 static void tree_match_free(TreeMatch *match)
1482 g_return_if_fail(match != NULL);
1484 for (next = match->matches; next; next = next->next)
1485 tree_match_free((TreeMatch *) next->data);
1487 g_list_free(match->matches);
1489 g_free(match->path);
1490 g_free(match->mimetype);
1495 /* Turn the list of child nodes of 'parent' into a list of TreeMatches */
1496 static GList *build_tree_matches(xmlNode *parent, GError **error)
1502 g_return_val_if_fail(error != NULL, NULL);
1504 for (node = parent->xmlChildrenNode; node; node = node->next)
1508 if (node->type != XML_ELEMENT_NODE)
1511 if (node->ns == NULL || xmlStrcmp(node->ns->href, FREE_NS) != 0)
1513 g_set_error(error, MIME_ERROR, 0,
1514 _("Element found with non-freedesktop.org "
1519 if (strcmp((char *)node->name, "treematch") != 0)
1521 g_set_error(error, MIME_ERROR, 0,
1522 _("Expected <treematch> element, but found "
1523 "<%s> instead"), node->name);
1527 match = tree_match_new();
1529 attr = my_xmlGetNsProp(node, "path", NULL);
1532 match->path = g_strdup (attr);
1537 g_set_error(error, MIME_ERROR, 0,
1538 _("Missing 'path' attribute in <treematch>"));
1542 attr = my_xmlGetNsProp(node, "type", NULL);
1545 if (strcmp (attr, "file") == 0)
1549 else if (strcmp (attr, "directory") == 0)
1553 else if (strcmp (attr, "link") == 0)
1559 g_set_error(error, MIME_ERROR, 0,
1560 _("Invalid 'type' attribute in <treematch>"));
1567 attr = my_xmlGetNsProp(node, "executable", NULL);
1570 if (strcmp (attr, "true") == 0)
1572 match->executable = 1;
1579 attr = my_xmlGetNsProp(node, "match-case", NULL);
1582 if (strcmp (attr, "true") == 0)
1584 match->match_case = 1;
1591 attr = my_xmlGetNsProp(node, "non-empty", NULL);
1594 if (strcmp (attr, "true") == 0)
1596 match->non_empty = 1;
1603 attr = my_xmlGetNsProp(node, "mimetype", NULL);
1606 match->mimetype = g_strdup (attr);
1613 tree_match_free(match);
1617 out = g_list_append(out, match);
1619 match->matches = build_tree_matches(node, error);
1627 static void tree_magic_free(TreeMagic *magic)
1631 g_return_if_fail(magic != NULL);
1633 for (next = magic->matches; next; next = next->next)
1634 tree_match_free((TreeMatch *) next->data);
1635 g_list_free(magic->matches);
1640 /* Create a new TreeMagic object by parsing 'node' (a <treemagic> element) */
1641 static TreeMagic *tree_magic_new(xmlNode *node, Type *type, GError **error)
1643 TreeMagic *magic = NULL;
1646 g_return_val_if_fail(node != NULL, NULL);
1647 g_return_val_if_fail(type != NULL, NULL);
1648 g_return_val_if_fail(error != NULL, NULL);
1650 prio = get_priority(node);
1654 g_set_error(error, MIME_ERROR, 0,
1655 _("Bad priority (%d) in <treemagic> element"), prio);
1659 magic = g_new(TreeMagic, 1);
1660 magic->priority = prio;
1662 magic->matches = build_tree_matches(node, error);
1666 gchar *old = (*error)->message;
1667 tree_magic_free(magic);
1669 (*error)->message = g_strconcat(
1670 _("Error in <treematch> element: "), old, NULL);
1678 /* Write a list of Match elements (and their children) to the 'magic' file */
1679 static void write_magic_children(FILE *stream, GList *matches, int indent)
1683 for (next = matches; next; next = next->next)
1685 Match *match = (Match *) next->data;
1691 match->range_start);
1693 g_fprintf(stream, ">%ld=", match->range_start);
1695 write16(stream, match->data_length);
1696 fwrite(match->data, match->data_length, 1, stream);
1700 fwrite(match->mask, match->data_length, 1, stream);
1702 if (match->word_size != 1)
1703 g_fprintf(stream, "~%d", match->word_size);
1704 if (match->range_length != 1)
1705 g_fprintf(stream, "+%d", match->range_length);
1707 fputc('\n', stream);
1709 write_magic_children(stream, match->matches, indent + 1);
1713 /* Write a whole Magic element to the 'magic' file */
1714 static void write_magic(FILE *stream, Magic *magic)
1716 g_fprintf(stream, "[%d:%s/%s]\n", magic->priority,
1717 magic->type->media, magic->type->subtype);
1719 write_magic_children(stream, magic->matches, 0);
1722 /* Write a list of TreeMatch elements (and their children) to the 'treemagic' file */
1723 static void write_tree_magic_children(FILE *stream, GList *matches, int indent)
1727 for (next = matches; next; next = next->next)
1729 TreeMatch *match = (TreeMatch *) next->data;
1737 g_fprintf(stream, ">\"%s\"=", match->path);
1739 switch (match->type)
1743 fputs("any", stream);
1746 fputs("file", stream);
1749 fputs("directory", stream);
1752 fputs("link", stream);
1755 if (match->match_case)
1756 fputs (",match-case", stream);
1757 if (match->executable)
1758 fputs (",executable", stream);
1759 if (match->non_empty)
1760 fputs (",non-empty", stream);
1761 if (match->mimetype)
1762 g_fprintf (stream, ",%s", match->mimetype);
1764 fputc('\n', stream);
1766 write_tree_magic_children(stream, match->matches, indent + 1);
1769 /* Write a whole TreeMagic element to the 'treemagic' file */
1770 static void write_tree_magic(FILE *stream, TreeMagic *magic)
1772 g_fprintf(stream, "[%d:%s/%s]\n", magic->priority,
1773 magic->type->media, magic->type->subtype);
1775 write_tree_magic_children(stream, magic->matches, 0);
1778 /* Check each of the directories with generated XML files, looking for types
1779 * which we didn't get on this scan, and delete them.
1781 static void delete_old_types(const gchar *mime_dir)
1785 for (i = 0; i < G_N_ELEMENTS(media_types); i++)
1791 media_dir = g_strconcat(mime_dir, "/", media_types[i], NULL);
1792 dir = opendir(media_dir);
1797 while ((ent = readdir(dir)))
1801 l = strlen(ent->d_name);
1802 if (l < 4 || strcmp(ent->d_name + l - 4, ".xml") != 0)
1805 type_name = g_strconcat(media_types[i], "/",
1807 type_name[strlen(type_name) - 4] = '\0';
1808 if (!g_hash_table_lookup(types, type_name))
1811 path = g_strconcat(mime_dir, "/",
1812 type_name, ".xml", NULL);
1814 g_warning("* Removing old info for type %s\n",
1827 /* Extract one entry from namespace_hash and put it in the GPtrArray so
1830 static void add_ns(gpointer key, gpointer value, gpointer data)
1832 GPtrArray *lines = (GPtrArray *) data;
1833 const gchar *ns = (gchar *) key;
1834 Type *type = (Type *) value;
1836 g_ptr_array_add(lines, g_strconcat(ns, " ", type->media,
1837 "/", type->subtype, "\n", NULL));
1840 /* Write all the collected namespace rules to 'XMLnamespaces' */
1841 static void write_namespaces(FILE *stream)
1846 lines = g_ptr_array_new();
1848 g_hash_table_foreach(namespace_hash, add_ns, lines);
1850 g_ptr_array_sort(lines, strcmp2);
1852 for (i = 0; i < lines->len; i++)
1854 char *line = (char *) lines->pdata[i];
1856 fwrite(line, 1, strlen(line), stream);
1861 g_ptr_array_free(lines, TRUE);
1864 static void write_subclass(gpointer key, gpointer value, gpointer data)
1866 GSList *list = value;
1867 FILE *stream = data;
1871 for (l = list; l; l = l->next)
1873 line = g_strconcat (key, " ", l->data, "\n", NULL);
1874 fwrite(line, 1, strlen(line), stream);
1879 /* Write all the collected subclass information to 'subclasses' */
1880 static void write_subclasses(FILE *stream)
1882 g_hash_table_foreach(subclass_hash, write_subclass, stream);
1885 /* Extract one entry from alias_hash and put it in the GPtrArray so
1888 static void add_alias(gpointer key, gpointer value, gpointer data)
1890 GPtrArray *lines = (GPtrArray *) data;
1891 const gchar *alias = (gchar *) key;
1892 Type *type = (Type *) value;
1894 g_ptr_array_add(lines, g_strconcat(alias, " ", type->media,
1895 "/", type->subtype, "\n",
1899 /* Write all the collected aliases */
1900 static void write_aliases(FILE *stream)
1905 lines = g_ptr_array_new();
1907 g_hash_table_foreach(alias_hash, add_alias, lines);
1909 g_ptr_array_sort(lines, strcmp2);
1911 for (i = 0; i < lines->len; i++)
1913 char *line = (char *) lines->pdata[i];
1915 fwrite(line, 1, strlen(line), stream);
1920 g_ptr_array_free(lines, TRUE);
1923 static void add_type(gpointer key, gpointer value, gpointer data)
1925 GPtrArray *lines = (GPtrArray *) data;
1927 g_ptr_array_add(lines, g_strconcat((char *)key, "\n", NULL));
1930 /* Write all the collected types */
1931 static void write_types(FILE *stream)
1936 lines = g_ptr_array_new();
1938 g_hash_table_foreach(types, add_type, lines);
1940 g_ptr_array_sort(lines, strcmp2);
1942 for (i = 0; i < lines->len; i++)
1944 char *line = (char *) lines->pdata[i];
1946 fwrite(line, 1, strlen(line), stream);
1951 g_ptr_array_free(lines, TRUE);
1955 static void write_one_icon(gpointer key, gpointer value, gpointer data)
1957 char *mimetype = (char *)key;
1958 char *iconname = (char *)value;
1959 FILE *stream = (FILE *)data;
1962 line = g_strconcat (mimetype, ":", iconname, "\n", NULL);
1963 fwrite(line, 1, strlen(line), stream);
1967 static void write_icons(GHashTable *icons, FILE *stream)
1969 g_hash_table_foreach(icons, write_one_icon, stream);
1972 /* Issue a warning if 'path' won't be found by applications */
1973 static void check_in_path_xdg_data(const char *mime_path)
1975 struct stat path_info, dir_info;
1981 path = g_path_get_dirname(mime_path);
1983 if (stat(path, &path_info))
1985 g_warning("Can't stat '%s' directory: %s",
1986 path, g_strerror(errno));
1990 env = getenv("XDG_DATA_DIRS");
1992 env = "/usr/local/share/"PATH_SEPARATOR"/usr/share/";
1993 dirs = g_strsplit(env, PATH_SEPARATOR, 0);
1994 g_return_if_fail(dirs != NULL);
1995 for (n = 0; dirs[n]; n++)
1997 env = getenv("XDG_DATA_HOME");
1999 dirs[n] = g_strdup(env);
2001 dirs[n] = g_build_filename(g_get_home_dir(), ".local",
2005 for (i = 0; i < n; i++)
2007 if (stat(dirs[i], &dir_info) == 0 &&
2008 dir_info.st_ino == path_info.st_ino &&
2009 dir_info.st_dev == path_info.st_dev)
2015 g_warning(_("\nNote that '%s' is not in the search path\n"
2016 "set by the XDG_DATA_HOME and XDG_DATA_DIRS\n"
2017 "environment variables, so applications may not\n"
2018 "be able to find it until you set them. The\n"
2019 "directories currently searched are:\n\n"), path);
2020 g_printerr("- %s\n", dirs[n - 1]);
2021 for (i = 0; i < n - 1; i++)
2022 g_printerr("- %s\n", dirs[i]);
2026 for (i = 0; i < n; i++)
2033 static void free_string_list(gpointer data)
2035 GSList *list = data;
2037 g_slist_foreach(list, (GFunc)g_free, NULL);
2041 #define ALIGN_VALUE(this, boundary) \
2042 (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
2046 write_data (FILE *cache, const gchar *n, gint len)
2051 l = ALIGN_VALUE (len, 4);
2056 i = fwrite (s, l, 1, cache);
2063 write_string (FILE *cache, const gchar *n)
2065 return write_data (cache, n, strlen (n) + 1);
2069 write_card16 (FILE *cache, guint16 n)
2073 n = GUINT16_TO_BE (n);
2075 i = fwrite ((char *)&n, 2, 1, cache);
2081 write_card32 (FILE *cache, guint32 n)
2085 n = GUINT32_TO_BE (n);
2087 i = fwrite ((char *)&n, 4, 1, cache);
2092 #define MAJOR_VERSION 1
2093 #define MINOR_VERSION 1
2096 write_header (FILE *cache,
2099 gint literal_offset,
2103 gint namespace_offset,
2104 gint icons_list_offset,
2105 gint generic_icons_list_offset,
2111 return (write_card16 (cache, MAJOR_VERSION) &&
2112 write_card16 (cache, MINOR_VERSION) &&
2113 write_card32 (cache, alias_offset) &&
2114 write_card32 (cache, parent_offset) &&
2115 write_card32 (cache, literal_offset) &&
2116 write_card32 (cache, suffix_offset) &&
2117 write_card32 (cache, glob_offset) &&
2118 write_card32 (cache, magic_offset) &&
2119 write_card32 (cache, namespace_offset) &&
2120 write_card32 (cache, icons_list_offset) &&
2121 write_card32 (cache, generic_icons_list_offset) &&
2122 write_card32 (cache, type_offset));
2126 typedef gboolean (FilterFunc) (gpointer key);
2127 typedef gchar ** (GetValueFunc) (gpointer data, gchar *key);
2134 GetValueFunc *get_value;
2141 write_map_entry (gpointer key,
2144 MapData *map_data = (MapData *)data;
2149 values = (* map_data->get_value) (map_data->data, key);
2150 for (i = 0; values[i]; i++)
2152 if (map_data->weighted && (i % 3 == 2))
2154 weight = atoi (values[i]);
2156 if (!write_card32 (map_data->cache, weight))
2157 map_data->error = TRUE;
2159 map_data->offset += 4;
2163 offset = GPOINTER_TO_UINT (g_hash_table_lookup (map_data->pool, values[i]));
2166 g_warning ("Missing string: '%s'\n", values[i]);
2167 map_data->error = TRUE;
2169 if (!write_card32 (map_data->cache, offset))
2170 map_data->error = TRUE;
2171 map_data->offset += 4;
2175 g_strfreev (values);
2185 add_key (gpointer key,
2189 FilterData *filter_data = (FilterData *)data;
2191 if (!filter_data->filter || (* filter_data->filter) (key))
2192 g_ptr_array_add (filter_data->keys, key);
2196 write_map (FILE *cache,
2197 GHashTable *strings,
2200 GetValueFunc *get_value,
2206 FilterData filter_data;
2208 keys = g_ptr_array_new ();
2210 filter_data.keys = keys;
2211 filter_data.filter = filter;
2212 g_hash_table_foreach (map, add_key, &filter_data);
2214 g_ptr_array_sort (keys, strcmp2);
2216 if (!write_card32 (cache, keys->len))
2219 map_data.cache = cache;
2220 map_data.pool = strings;
2221 map_data.get_value = get_value;
2222 map_data.data = map;
2223 map_data.weighted = weighted;
2224 map_data.offset = *offset + 4;
2225 map_data.error = FALSE;
2227 g_ptr_array_foreach (keys, write_map_entry, &map_data);
2229 *offset = map_data.offset;
2231 return !map_data.error;
2235 get_type_value (gpointer data,
2241 type = (Type *)g_hash_table_lookup ((GHashTable *)data, key);
2243 result = g_new0 (gchar *, 3);
2244 result[0] = g_strdup (key);
2245 result[1] = g_strdup_printf ("%s/%s", type->media, type->subtype);
2251 get_glob_list_value (gpointer data,
2260 list = (GList *)g_hash_table_lookup ((GHashTable *)data, key);
2262 result = g_new0 (gchar *, 1 + 3 * g_list_length (list));
2265 for (; list; list = list->next)
2267 glob = (Glob *)list->data;
2270 result[i++] = g_strdup (glob->pattern);
2271 result[i++] = g_strdup_printf ("%s/%s", type->media, type->subtype);
2272 result[i++] = g_strdup_printf ("%d", glob->weight);
2278 write_alias_cache (FILE *cache,
2279 GHashTable *strings,
2282 return write_map (cache, strings, alias_hash, NULL, get_type_value, FALSE, offset);
2286 write_parent_entry (gpointer key,
2289 gchar *mimetype = (gchar *)key;
2290 MapData *map_data = (MapData *)data;
2291 guint parents_offset, offset;
2294 parents = (GList *)g_hash_table_lookup (subclass_hash, mimetype);
2295 offset = GPOINTER_TO_UINT (g_hash_table_lookup (map_data->pool, mimetype));
2298 g_warning ("Missing string: '%s'\n", (gchar *)key);
2299 map_data->error = TRUE;
2302 parents_offset = map_data->offset;
2303 map_data->offset += 4 + 4 * g_list_length (parents);
2305 if (!write_card32 (map_data->cache, offset) ||
2306 !write_card32 (map_data->cache, parents_offset))
2307 map_data->error = TRUE;
2311 write_parent_list (gpointer key,
2314 gchar *mimetype = (gchar *)key;
2315 MapData *map_data = (MapData *)data;
2319 parents = (GList *)g_hash_table_lookup (subclass_hash, mimetype);
2321 if (!write_card32 (map_data->cache, g_list_length (parents)))
2322 map_data->error = TRUE;
2324 for (p = parents; p; p = p->next)
2326 gchar *parent = (gchar *)p->data;
2328 offset = GPOINTER_TO_UINT (g_hash_table_lookup (map_data->pool, parent));
2331 g_warning ("Missing string: '%s'\n", parent);
2332 map_data->error = TRUE;
2335 if (!write_card32 (map_data->cache, offset))
2336 map_data->error = TRUE;
2339 map_data->offset += 4 + 4 * g_list_length (parents);
2343 write_parent_cache (FILE *cache,
2344 GHashTable *strings,
2349 FilterData filter_data;
2351 keys = g_ptr_array_new ();
2353 filter_data.keys = keys;
2354 filter_data.filter = NULL;
2355 g_hash_table_foreach (subclass_hash, add_key, &filter_data);
2357 g_ptr_array_sort (keys, strcmp2);
2359 if (!write_card32 (cache, keys->len))
2362 map_data.cache = cache;
2363 map_data.pool = strings;
2364 map_data.offset = *offset + 4 + keys->len * 8;
2365 map_data.error = FALSE;
2367 g_ptr_array_foreach (keys, write_parent_entry, &map_data);
2369 map_data.offset = *offset + 4 + keys->len * 8;
2370 g_ptr_array_foreach (keys, write_parent_list, &map_data);
2372 *offset = map_data.offset;
2374 return !map_data.error;
2385 glob_type (gchar *glob)
2388 gboolean maybe_in_simple_glob = FALSE;
2389 gboolean first_char = TRUE;
2393 while (*ptr != '\0')
2395 if (*ptr == '*' && first_char)
2396 maybe_in_simple_glob = TRUE;
2397 else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
2401 ptr = g_utf8_next_char (ptr);
2404 if (maybe_in_simple_glob)
2407 return GLOB_LITERAL;
2411 is_literal_glob (gpointer key)
2413 return glob_type ((gchar *)key) == GLOB_LITERAL;
2417 is_simple_glob (gpointer key)
2419 return glob_type ((gchar *)key) == GLOB_SIMPLE;
2423 is_full_glob (gpointer key)
2425 return glob_type ((gchar *)key) == GLOB_FULL;
2429 write_literal_cache (FILE *cache,
2430 GHashTable *strings,
2433 return write_map (cache, strings, globs_hash, is_literal_glob,
2434 get_glob_list_value, TRUE, offset);
2438 write_glob_cache (FILE *cache,
2439 GHashTable *strings,
2442 return write_map (cache, strings, globs_hash, is_full_glob,
2443 get_glob_list_value, TRUE, offset);
2446 typedef struct _SuffixEntry SuffixEntry;
2459 insert_suffix (gunichar *suffix,
2465 SuffixEntry *s = NULL;
2467 for (l = suffixes; l; l = l->next)
2469 s = (SuffixEntry *)l->data;
2471 if (s->character > suffix[0])
2473 s = g_new0 (SuffixEntry, 1);
2474 s->character = suffix[0];
2478 suffixes = g_list_insert_before (suffixes, l, s);
2481 if (s->character == suffix[0])
2485 if (!s || s->character != suffix[0])
2487 s = g_new0 (SuffixEntry, 1);
2488 s->character = suffix[0];
2492 suffixes = g_list_append (suffixes, s);
2499 gboolean found = FALSE;
2501 for (l2 = s->children; l2; l2 = l2->next)
2503 s2 = (SuffixEntry *)l2->data;
2504 if (s2->character != 0)
2506 if (strcmp (s2->mimetype, mimetype) == 0)
2508 if (s2->weight < weight)
2509 s2->weight = weight;
2516 s2 = g_new0 (SuffixEntry, 1);
2518 s2->mimetype = mimetype;
2519 s2->weight = weight;
2520 s2->children = NULL;
2521 s->children = g_list_insert_before (s->children, l2, s2);
2525 s->children = insert_suffix (suffix + 1, mimetype, weight, s->children);
2531 ucs4_reverse (gunichar *in, glong len)
2536 for (i = 0; i < len - i - 1; i++)
2539 in[i] = in[len - i - 1];
2540 in[len - i - 1] = c;
2545 build_suffixes (gpointer key,
2549 gchar *pattern = (gchar *)key;
2550 GList *list = (GList *)value;
2551 GList **suffixes = (GList **)data;
2558 if (is_simple_glob (pattern))
2560 suffix = g_utf8_to_ucs4 (pattern + 1, -1, NULL, &len, NULL);
2564 g_warning ("Glob '%s' is not valid UTF-8\n", pattern);
2568 ucs4_reverse (suffix, len);
2569 for ( ; list; list = list->next)
2571 glob = (Glob *)list->data;
2573 mimetype = g_strdup_printf ("%s/%s", type->media, type->subtype);
2575 *suffixes = insert_suffix (suffix, mimetype, glob->weight, *suffixes);
2583 calculate_size (SuffixEntry *entry)
2589 for (s = entry->children; s; s= s->next)
2591 SuffixEntry *child = (SuffixEntry *)s->data;
2593 calculate_size (child);
2594 entry->size += 1 + child->size;
2595 entry->depth = MAX (entry->depth, child->depth + 1);
2600 write_suffix_entries (FILE *cache,
2603 GHashTable *strings,
2604 guint *child_offset)
2611 gboolean error = FALSE;
2613 for (c = entry->children; c; c = c->next)
2615 SuffixEntry *child = (SuffixEntry *)c->data;
2616 if (!write_suffix_entries (cache, depth - 1, child, strings, child_offset))
2623 if (entry->mimetype)
2625 offset = GPOINTER_TO_UINT(g_hash_table_lookup (strings, entry->mimetype));
2628 g_warning ("Missing string: '%s'\n", entry->mimetype);
2635 if (entry->character == 0)
2637 if (!write_card32 (cache, entry->character))
2640 if (!write_card32 (cache, offset))
2643 if (!write_card32 (cache, entry->weight))
2648 if (!write_card32 (cache, entry->character))
2651 if (!write_card32 (cache, g_list_length (entry->children)))
2654 if (!write_card32 (cache, *child_offset))
2658 *child_offset += 12 * g_list_length (entry->children);
2664 write_suffix_cache (FILE *cache,
2665 GHashTable *strings,
2668 GList *suffixes, *s;
2675 g_hash_table_foreach (globs_hash, build_suffixes, &suffixes);
2677 n_entries = g_list_length (suffixes);
2680 child_offset = *offset + 12 * n_entries;
2682 for (s = suffixes; s; s= s->next)
2684 SuffixEntry *entry = (SuffixEntry *)s->data;
2685 calculate_size (entry);
2686 depth = MAX (depth, entry->depth + 1);
2689 if (!write_card32 (cache, n_entries) || !write_card32 (cache, *offset))
2692 for (d = 0; d < depth; d++)
2694 for (s = suffixes; s; s = s->next)
2696 SuffixEntry *entry = (SuffixEntry *)s->data;
2698 if (!write_suffix_entries (cache, d, entry, strings, &child_offset))
2703 *offset = child_offset;
2710 GHashTable *strings;
2718 write_match (gpointer key,
2721 Magic *magic = (Magic *)key;
2722 WriteMatchData *mdata = (WriteMatchData *)data;
2726 if (!write_card32 (mdata->cache, magic->priority))
2728 mdata->error = TRUE;
2732 mimetype = g_strdup_printf ("%s/%s", magic->type->media, magic->type->subtype);
2733 offset = GPOINTER_TO_UINT (g_hash_table_lookup (mdata->strings, mimetype));
2736 g_warning ("Missing string: '%s'\n", mimetype);
2738 mdata->error = TRUE;
2743 if (!write_card32 (mdata->cache, offset))
2745 mdata->error = TRUE;
2749 if (!write_card32 (mdata->cache, g_list_length (magic->matches)))
2751 mdata->error = TRUE;
2755 offset = mdata->offset + 32 * g_list_index (mdata->matches, magic->matches->data);
2757 if (!write_card32 (mdata->cache, offset))
2759 mdata->error = TRUE;
2765 write_matchlet (FILE *cache,
2771 if (!write_card32 (cache, match->range_start) ||
2772 !write_card32 (cache, match->range_length) ||
2773 !write_card32 (cache, match->word_size) ||
2774 !write_card32 (cache, match->data_length) ||
2775 !write_card32 (cache, *offset2))
2778 *offset2 = ALIGN_VALUE (*offset2 + match->data_length, 4);
2782 if (!write_card32 (cache, *offset2))
2785 *offset2 = ALIGN_VALUE (*offset2 + match->data_length, 4);
2789 if (!write_card32 (cache, 0))
2795 if (!write_card32 (cache, g_list_length (match->matches)) ||
2796 !write_card32 (cache, offset + 32 * g_list_index (matches, match->matches->data)))
2801 if (!write_card32 (cache, 0) ||
2802 !write_card32 (cache, 0))
2810 write_matchlet_data (FILE *cache,
2814 if (!write_data (cache, match->data, match->data_length))
2817 *offset2 = ALIGN_VALUE (*offset2 + match->data_length, 4);
2821 if (!write_data (cache, match->mask, match->data_length))
2824 *offset2 = ALIGN_VALUE (*offset2 + match->data_length, 4);
2831 collect_matches_list (GList *list, GList **matches)
2835 for (l = list; l; l = l->next)
2836 *matches = g_list_prepend (*matches, l->data);
2838 for (l = list; l; l = l->next)
2840 Match *match = (Match *)l->data;
2841 collect_matches_list (match->matches, matches);
2846 collect_matches (gpointer key, gpointer data)
2848 Magic *magic = (Magic *)key;
2849 GList **matches = (GList **)data;
2851 collect_matches_list (magic->matches, matches);
2855 write_magic_cache (FILE *cache,
2856 GHashTable *strings,
2859 guint n_entries, max_extent;
2862 WriteMatchData data;
2864 data.matches = NULL;
2865 g_ptr_array_foreach (magic_array, collect_matches, &data.matches);
2866 data.matches = g_list_reverse (data.matches);
2869 for (m = data.matches; m; m = m->next)
2871 Match *match = (Match *)m->data;
2872 max_extent = MAX (max_extent, match->data_length + match->range_start + match->range_length);
2875 n_entries = magic_array->len;
2879 if (!write_card32 (cache, n_entries) ||
2880 !write_card32 (cache, max_extent) ||
2881 !write_card32 (cache, *offset))
2884 *offset += 16 * n_entries;
2887 data.strings = strings;
2888 data.offset = *offset;
2891 offset2 = *offset + 32 * g_list_length (data.matches);
2893 g_ptr_array_foreach (magic_array, write_match, &data);
2894 for (m = data.matches; m; m = m->next)
2896 Match *match = (Match *)m->data;
2897 write_matchlet (cache, match, data.matches, *offset, &offset2);
2900 offset2 = *offset + 32 * g_list_length (data.matches);
2902 for (m = data.matches; m; m = m->next)
2904 Match *match = (Match *)m->data;
2905 write_matchlet_data (cache, match, &offset2);
2910 g_list_free (data.matches);
2916 get_namespace_value (gpointer data,
2923 type = (Type *)g_hash_table_lookup ((GHashTable *)data, key);
2925 result = g_new0 (gchar *, 4);
2926 space = strchr (key, ' ');
2930 result[0] = g_strdup (key);
2931 result[1] = g_strdup (space + 1);
2935 result[0] = g_strdup (key);
2937 result[2] = g_strdup_printf ("%s/%s", type->media, type->subtype);
2943 write_namespace_cache (FILE *cache,
2944 GHashTable *strings,
2947 return write_map (cache, strings, namespace_hash, NULL,
2948 get_namespace_value, FALSE, offset);
2952 get_icon_value (gpointer data,
2958 iconname = (gchar *)g_hash_table_lookup ((GHashTable *)data, key);
2960 result = g_new0 (gchar *, 3);
2961 result[0] = g_strdup (key);
2962 result[1] = g_strdup (iconname);
2969 write_icons_cache (FILE *cache,
2970 GHashTable *strings,
2971 GHashTable *icon_hash,
2974 return write_map (cache, strings, icon_hash, NULL,
2975 get_icon_value, FALSE, offset);
2978 /* Write all the collected types */
2980 write_types_cache (FILE *cache,
2981 GHashTable *strings,
2990 lines = g_ptr_array_new();
2992 g_hash_table_foreach(types, add_type, lines);
2994 g_ptr_array_sort(lines, strcmp2);
2996 if (!write_card32 (cache, lines->len))
2999 for (i = 0; i < lines->len; i++)
3001 mimetype = (char *) lines->pdata[i];
3002 mime_offset = GPOINTER_TO_UINT (g_hash_table_lookup (strings, mimetype));
3003 if (!write_card32 (cache, mime_offset))
3009 *offset += 4 + 4 * lines->len;
3011 g_ptr_array_free(lines, TRUE);
3017 collect_alias (gpointer key,
3021 GHashTable *strings = (GHashTable *)data;
3022 Type *type = (Type *)value;
3025 mimetype = g_strdup_printf ("%s/%s", type->media, type->subtype);
3026 g_hash_table_insert (strings, key, NULL);
3027 g_hash_table_insert (strings, mimetype, NULL);
3032 collect_parents (gpointer key,
3036 GList *parents = (GList *)value;
3037 GHashTable *strings = (GHashTable *)data;
3040 g_hash_table_insert (strings, key, NULL);
3041 for (p = parents; p; p = p->next)
3042 g_hash_table_insert (strings, p->data, NULL);
3046 collect_glob (gpointer key,
3050 GList *list = (GList *)value;
3051 GHashTable *strings = (GHashTable *)data;
3056 switch (glob_type ((char *)key))
3060 g_hash_table_insert (strings, key, NULL);
3066 for (; list; list = list->next)
3068 glob = (Glob *)list->data;
3070 mimetype = g_strdup_printf ("%s/%s", type->media, type->subtype);
3072 g_hash_table_insert (strings, mimetype, NULL);
3077 collect_magic (gpointer key,
3080 Magic *magic = (Magic *)key;
3081 GHashTable *strings = (GHashTable *)data;
3084 mimetype = g_strdup_printf ("%s/%s", magic->type->media, magic->type->subtype);
3085 g_hash_table_insert (strings, mimetype, NULL);
3089 collect_namespace (gpointer key,
3093 gchar *ns = (gchar *)key;
3094 Type *type = (Type *)value;
3095 GHashTable *strings = (GHashTable *)data;
3099 mimetype = g_strdup_printf ("%s/%s", type->media, type->subtype);
3100 g_hash_table_insert (strings, mimetype, NULL);
3102 space = strchr (ns, ' ');
3107 g_hash_table_insert (strings, g_strdup (ns), NULL);
3108 g_hash_table_insert (strings, space + 1, NULL);
3112 g_hash_table_insert (strings, ns, NULL);
3116 collect_icons(gpointer key,
3120 gchar *mimetype = (gchar *)key;
3121 gchar *iconname = (gchar *)value;
3122 GHashTable *strings = (GHashTable *)data;
3124 g_hash_table_insert (strings, mimetype, NULL);
3125 g_hash_table_insert (strings, iconname, NULL);
3130 collect_strings (GHashTable *strings)
3132 g_hash_table_foreach (alias_hash, collect_alias, strings);
3133 g_hash_table_foreach (subclass_hash, collect_parents, strings);
3134 g_hash_table_foreach (globs_hash, collect_glob, strings);
3135 g_ptr_array_foreach (magic_array, collect_magic, strings);
3136 g_hash_table_foreach (namespace_hash, collect_namespace, strings);
3137 g_hash_table_foreach (generic_icon_hash, collect_icons, strings);
3138 g_hash_table_foreach (icon_hash, collect_icons, strings);
3144 GHashTable *strings;
3150 write_one_string (gpointer key,
3154 gchar *str = (gchar *)key;
3155 StringData *sdata = (StringData *)data;
3157 if (!write_string (sdata->cache, str))
3158 sdata->error = TRUE;
3160 g_hash_table_insert (sdata->strings, str, GUINT_TO_POINTER (sdata->offset));
3162 sdata->offset = ALIGN_VALUE (sdata->offset + strlen (str) + 1, 4);
3166 write_strings (FILE *cache,
3167 GHashTable *strings,
3173 data.strings = strings;
3174 data.offset = *offset;
3177 g_hash_table_foreach (strings, write_one_string, &data);
3179 *offset = data.offset;
3185 write_cache (FILE *cache)
3187 guint strings_offset;
3189 guint parent_offset;
3190 guint literal_offset;
3191 guint suffix_offset;
3194 guint namespace_offset;
3195 guint icons_list_offset;
3196 guint generic_icons_list_offset;
3199 GHashTable *strings;
3202 if (!write_header (cache, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &offset))
3204 g_warning ("Failed to write header\n");
3208 strings = g_hash_table_new (g_str_hash, g_str_equal);
3209 collect_strings (strings);
3210 strings_offset = offset;
3212 if (!write_strings (cache, strings, &offset))
3214 g_warning ("Failed to write strings\n");
3217 g_message ("Wrote %d strings at %x - %x\n",
3218 g_hash_table_size (strings), strings_offset, offset);
3220 alias_offset = offset;
3221 if (!write_alias_cache (cache, strings, &offset))
3223 g_warning ("Failed to write alias list\n");
3226 g_message ("Wrote aliases at %x - %x\n", alias_offset, offset);
3228 parent_offset = offset;
3229 if (!write_parent_cache (cache, strings, &offset))
3231 g_warning ("Failed to write parent list\n");
3234 g_message ("Wrote parents at %x - %x\n", parent_offset, offset);
3236 literal_offset = offset;
3237 if (!write_literal_cache (cache, strings, &offset))
3239 g_warning ("Failed to write literal list\n");
3242 g_message ("Wrote literal globs at %x - %x\n", literal_offset, offset);
3244 suffix_offset = offset;
3245 if (!write_suffix_cache (cache, strings, &offset))
3247 g_warning ("Failed to write suffix list\n");
3250 g_message ("Wrote suffix globs at %x - %x\n", suffix_offset, offset);
3252 glob_offset = offset;
3253 if (!write_glob_cache (cache, strings, &offset))
3255 g_warning ("Failed to write glob list\n");
3258 g_message ("Wrote full globs at %x - %x\n", glob_offset, offset);
3260 magic_offset = offset;
3261 if (!write_magic_cache (cache, strings, &offset))
3263 g_warning ("Failed to write magic list\n");
3266 g_message ("Wrote magic at %x - %x\n", magic_offset, offset);
3268 namespace_offset = offset;
3269 if (!write_namespace_cache (cache, strings, &offset))
3271 g_warning ("Failed to write namespace list\n");
3274 g_message ("Wrote namespace list at %x - %x\n", namespace_offset, offset);
3276 icons_list_offset = offset;
3277 if (!write_icons_cache (cache, strings, icon_hash, &offset))
3279 g_warning ("Failed to write icons list\n");
3282 g_message ("Wrote icons list at %x - %x\n", icons_list_offset, offset);
3284 generic_icons_list_offset = offset;
3285 if (!write_icons_cache (cache, strings, generic_icon_hash, &offset))
3287 g_warning ("Failed to write generic icons list\n");
3290 g_message ("Wrote generic icons list at %x - %x\n", generic_icons_list_offset, offset);
3292 type_offset = offset;
3293 if (!write_types_cache (cache, strings, types, &offset))
3295 g_warning ("Failed to write types list\n");
3298 g_message ("Wrote types list at %x - %x\n", type_offset, offset);
3303 if (!write_header (cache,
3304 alias_offset, parent_offset, literal_offset,
3305 suffix_offset, glob_offset, magic_offset,
3306 namespace_offset, icons_list_offset,
3307 generic_icons_list_offset, type_offset,
3310 g_warning ("Failed to rewrite header\n");
3314 g_hash_table_destroy (strings);
3321 open_or_die(const char *filename)
3323 FILE *stream = fopen(filename, "wb");
3327 g_printerr("Failed to open '%s' for writing\n", filename);
3334 int main(int argc, char **argv)
3336 char *mime_dir = NULL;
3337 char *package_dir = NULL;
3340 /* Install the filtering log handler */
3341 g_log_set_default_handler(g_log_handler, NULL);
3343 while ((opt = getopt(argc, argv, "hvV")) != -1)
3349 return EXIT_FAILURE;
3352 return EXIT_SUCCESS;
3355 "update-mime-database (" PACKAGE ") "
3356 VERSION "\n" COPYING);
3357 return EXIT_SUCCESS;
3359 enabled_log_levels |= G_LOG_LEVEL_MESSAGE
3363 return EXIT_FAILURE;
3367 if (optind != argc - 1)
3370 return EXIT_FAILURE;
3373 LIBXML_TEST_VERSION;
3375 mime_dir = argv[optind];
3377 /* Strip trailing / characters */
3379 int l = strlen(mime_dir);
3380 while (l > 1 && mime_dir[l - 1] == '/')
3387 package_dir = g_strconcat(mime_dir, "/packages", NULL);
3389 if (access(mime_dir, W_OK))
3391 g_warning(_("%s: I don't have write permission on %s.\n"
3392 "Try rerunning me as root.\n"), argv[0], mime_dir);
3393 return EXIT_FAILURE;
3396 g_message("Updating MIME database in %s...\n", mime_dir);
3398 if (access(package_dir, F_OK))
3401 _("Directory '%s' does not exist!\n"), package_dir);
3402 return EXIT_FAILURE;
3405 types = g_hash_table_new_full(g_str_hash, g_str_equal,
3407 globs_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
3409 namespace_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
3411 magic_array = g_ptr_array_new();
3412 tree_magic_array = g_ptr_array_new();
3413 subclass_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
3414 g_free, free_string_list);
3415 alias_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
3417 icon_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
3419 generic_icon_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
3422 scan_source_dir(package_dir);
3423 g_free(package_dir);
3425 delete_old_types(mime_dir);
3427 g_hash_table_foreach(types, write_out_type, (gpointer) mime_dir);
3432 GList *glob_list = NULL;
3434 g_hash_table_foreach(globs_hash, collect_glob2, &glob_list);
3435 glob_list = g_list_sort(glob_list, (GCompareFunc)compare_by_weight);
3436 globs_path = g_strconcat(mime_dir, "/globs.new", NULL);
3437 globs = open_or_die(globs_path);
3439 "# This file was automatically generated by the\n"
3440 "# update-mime-database command. DO NOT EDIT!\n");
3441 write_out_glob(glob_list, globs);
3443 atomic_update(globs_path);
3446 globs_path = g_strconcat(mime_dir, "/globs2.new", NULL);
3447 globs = open_or_die(globs_path);
3449 "# This file was automatically generated by the\n"
3450 "# update-mime-database command. DO NOT EDIT!\n");
3451 write_out_glob2 (glob_list, globs);
3453 atomic_update(globs_path);
3456 g_list_free (glob_list);
3463 magic_path = g_strconcat(mime_dir, "/magic.new", NULL);
3464 stream = open_or_die(magic_path);
3465 fwrite("MIME-Magic\0\n", 1, 12, stream);
3467 if (magic_array->len)
3468 g_ptr_array_sort(magic_array, cmp_magic);
3469 for (i = 0; i < magic_array->len; i++)
3471 Magic *magic = (Magic *) magic_array->pdata[i];
3473 write_magic(stream, magic);
3477 atomic_update(magic_path);
3485 ns_path = g_strconcat(mime_dir, "/XMLnamespaces.new", NULL);
3486 stream = open_or_die(ns_path);
3487 write_namespaces(stream);
3490 atomic_update(ns_path);
3498 path = g_strconcat(mime_dir, "/subclasses.new", NULL);
3499 stream = open_or_die(path);
3500 write_subclasses(stream);
3503 atomic_update(path);
3511 path = g_strconcat(mime_dir, "/aliases.new", NULL);
3512 stream = open_or_die(path);
3513 write_aliases(stream);
3516 atomic_update(path);
3524 path = g_strconcat(mime_dir, "/types.new", NULL);
3525 stream = open_or_die(path);
3526 write_types(stream);
3529 atomic_update(path);
3537 icon_path = g_strconcat(mime_dir, "/generic-icons.new", NULL);
3538 stream = open_or_die(icon_path);
3539 write_icons(generic_icon_hash, stream);
3542 atomic_update(icon_path);
3550 icon_path = g_strconcat(mime_dir, "/icons.new", NULL);
3551 stream = open_or_die(icon_path);
3552 write_icons(icon_hash, stream);
3555 atomic_update(icon_path);
3563 path = g_strconcat(mime_dir, "/treemagic.new", NULL);
3564 stream = open_or_die(path);
3565 fwrite("MIME-TreeMagic\0\n", 1, 16, stream);
3567 if (tree_magic_array->len)
3568 g_ptr_array_sort(tree_magic_array, cmp_tree_magic);
3569 for (i = 0; i < tree_magic_array->len; i++)
3571 TreeMagic *magic = (TreeMagic *) tree_magic_array->pdata[i];
3573 write_tree_magic(stream, magic);
3577 atomic_update(path);
3585 path = g_strconcat(mime_dir, "/mime.cache.new", NULL);
3586 stream = open_or_die(path);
3587 write_cache(stream);
3590 atomic_update(path);
3594 g_ptr_array_foreach(magic_array, (GFunc)magic_free, NULL);
3595 g_ptr_array_free(magic_array, TRUE);
3596 g_ptr_array_foreach(tree_magic_array, (GFunc)tree_magic_free, NULL);
3597 g_ptr_array_free(tree_magic_array, TRUE);
3599 g_hash_table_destroy(types);
3600 g_hash_table_destroy(globs_hash);
3601 g_hash_table_destroy(namespace_hash);
3602 g_hash_table_destroy(subclass_hash);
3603 g_hash_table_destroy(alias_hash);
3604 g_hash_table_destroy(icon_hash);
3605 g_hash_table_destroy(generic_icon_hash);
3607 check_in_path_xdg_data(mime_dir);
3609 return EXIT_SUCCESS;