On Thu, 2008-07-03 at 00:06 -0400, Matthias Clasen wrote:

> Its been a month since I sent out this proposal, and the only comment I
> have heard so far is that the name "Content types" may be slightly
> ambiguous. My proposal for addressing this is to change the section 
> title to "Content types for volumes", and stick with x-content for the
> type, since it is already in use. 
> 
> If there is no further feedback, I'd like to move ahead with adding this
> to the spec, since we want to implement content type sniffing in gvfs
> soon.

Here is an updated version of the patch for the spec, using a less
ambiguous title and adding some more details. 

Bastien, if there is no more feedback, can you commit this when you come
back ? 


Thanks, Matthias



Index: shared-mime-info-spec.xml
===================================================================
RCS file: /cvs/mime/shared-mime-info/shared-mime-info-spec.xml,v
retrieving revision 1.60
diff -u -r1.60 shared-mime-info-spec.xml
--- shared-mime-info-spec.xml	9 Jun 2008 09:27:30 -0000	1.60
+++ shared-mime-info-spec.xml	11 Jul 2008 14:22:01 -0000
@@ -1,8 +1,8 @@
 <?xml version="1.0" standalone="no"?>
 <!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
 "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"; [
-  <!ENTITY updated "25 January 2008">
-  <!ENTITY version "0.17">
+  <!ENTITY updated "11 July 2008">
+  <!ENTITY version "0.18">
 ]>
 <article id="index">
 
@@ -343,6 +343,34 @@
 If <userinput>localName</userinput> is present but empty then the document element may have
 any name, but the namespace must still match.
 				</para></listitem>
+				<listitem><para>
+<userinput>treemagic</userinput> elements contain a list of <userinput>treematch</userinput> elements,
+any of which may match, and an optional <userinput>priority</userinput> attribute for all of the 
+contained rules. The default priority value is 50, and the maximum is 100.
+				</para><para>
+Each <userinput>treematch</userinput> element has a number of attributes:
+
+<informaltable>
+	<tgroup cols="3">
+	<thead><row><entry>Attribute</entry><entry>Required?</entry><entry>Value</entry></row></thead>
+	<tbody>
+
+	<row><entry>path</entry><entry>Yes</entry><entry>A path that must be present on the mounted volume/filesystem.</entry></row>
+	
+	<row><entry>type</entry><entry>No</entry><entry>The type of path. Possible values: <userinput>file</userinput>, <userinput>directory</userinput>, <userinput>link</userinput></entry></row>
+
+	<row><entry>ignore-case</entry><entry>No</entry><entry>Whether path should be matched case-insensitively. Possible values: <userinput>true</userinput>, <userinput>false</userinput></entry></row>
+
+	<row><entry>executable</entry><entry>No</entry><entry>Whether the file must be executable. Possible values: <userinput>true</userinput>, <userinput>false</userinput></entry></row>
+
+	<row><entry>non-empty</entry><entry>No</entry><entry>Whether the directory must be non-empty. Possible values: <userinput>true</userinput>, <userinput>false</userinput></entry></row>
+
+	</tbody></tgroup>
+</informaltable>
+
+<userinput>treematch</userinput> elements can be nested, meaning that both the outer and the inner <userinput>treematch</userinput>
+must be satisfied for a "match".
+				</para></listitem>
 			</itemizedlist>
 Applications may also define their own elements, provided they are namespaced to prevent collisions.
 Unknown elements are copied directly to the output XML files like <userinput>comment</userinput>
@@ -574,6 +602,39 @@
 		</para>
 	</sect2>
 	<sect2>
+		<title>The treemagic files</title>
+		<para>
+The tree magic data is stored in a file with a format that is very similar to the magic file format.
+		</para>
+		<para>
+The file starts with the magic string "MIME-TreeMagic\0\n". There is no version number in the file.
+Incompatible changes will be handled by creating both the current `treemagic' and a newer `treemagic2' 
+in the new format. Where possible, changes will be made in a compatible fashion. 
+		</para>
+		<para>
+The rest of the file is made up of a sequence of small sections. Each section is introduced by giving
+the priority and type in brackeds, followed by a newline character. Higher priority entries come
+first. Example:
+<screen>[50:x-content/image-dcf]\n</screen>
+Each line in the section takes the form:
+<screen>[ indent ] ">" "\"" path "\"" "=" type [ "," option ]* "\n"</screen>
+<informaltable>
+	<tgroup cols="2">
+	<thead><row><entry>Part</entry><entry>Meaning</entry></row></thead>
+	<tbody>
+
+	<row><entry>indent</entry><entry>The nesting depth of the rule.</entry></row>
+	<row><entry>path</entry><entry>The path to match.</entry></row>
+	<row><entry>type</entry><entry>The required file type, one of "file", "directory", "link" or "any"</entry></row>
+	<row><entry>option</entry><entry>Optional for the optional attributes of <userinput>treematch</userinput> elements. 
+Possible values are "executable", "ignore-case", "non-empty", or a MIME type</entry></row>
+	</tbody>
+	</tgroup>
+</informaltable>
+		</para><para>
+		</para>
+	</sect2>
+	<sect2>
 		<title>The mime.cache files</title>
 		<para>
 The <filename>mime.cache</filename> files contain the same information as the 
@@ -847,6 +908,22 @@
 		</para>
 	</sect2>
 	<sect2>
+		<title>Content types for volumes</title>
+		<para>
+Traditional MIME types apply to individual files or bytestreams. It is often useful 
+to apply the same methodologies when classifying the content of mountable volumes or 
+filesystems. The x-content type has been introduced for this purpose. Typical examples 
+are x-content/audio-dvd, x-content/blank-cd or x-content/image-dcf. 
+		</para>
+		<para>
+Matching of content types works with <userinput>treemagic</userinput> elements, which 
+are analogous to the <userinput>magic</userinput> elements used for MIME type matching.
+Instead of looking for byte sequences in files, <userinput>treemagic</userinput> element
+allow to look for files with certain names, permissions or mime types in a directory
+hierarchy.
+		</para>
+	</sect2>
+	<sect2>
 		<title>Security implications</title>
 		<para>
 The system described in this document is intended to allow different programs
--- update-mime-database.c.beforetree	2008-06-09 00:08:13.000000000 -0400
+++ update-mime-database.c	2008-06-09 23:02:22.000000000 -0400
@@ -66,6 +66,12 @@
 /* A parsed <match> element */
 typedef struct _Match Match;
 
+/* A parsed <treemagic> element */
+typedef struct _TreeMagic TreeMagic;
+
+/* A parsed <treematch> element */
+typedef struct _TreeMatch TreeMatch;
+
 /* A parsed <glob> element */
 typedef struct _Glob Glob;
 
@@ -102,6 +108,23 @@
 	GList *matches;
 };
 
+struct _TreeMagic {
+	int priority;
+	Type *type;
+	GList *matches;
+};
+
+struct _TreeMatch {
+	char *path;
+	gboolean ignore_case;
+	gboolean executable;
+	gboolean non_empty;
+	gint type;
+	char *mimetype;
+
+	GList *matches;
+};
+
 /* Maps MIME type names to Types */
 static GHashTable *types = NULL;
 
@@ -114,6 +137,9 @@
 /* 'magic' nodes */
 static GPtrArray *magic_array = NULL;
 
+/* 'treemagic' nodes */
+static GPtrArray *tree_magic_array = NULL;
+
 /* Maps MIME type names to superclass names */
 static GHashTable *subclass_hash = NULL;
 
@@ -132,6 +158,8 @@
 /* Static prototypes */
 static Magic *magic_new(xmlNode *node, Type *type, GError **error);
 
+static TreeMagic *tree_magic_new(xmlNode *node, Type *type, GError **error);
+
 static void g_log_handler (const gchar   *log_domain,
 			   GLogLevelFlags log_level,
 			   const gchar   *message,
@@ -372,6 +400,20 @@
 		else
 			g_return_val_if_fail(magic == NULL, FALSE);
 	}
+	else if (strcmp((char *)field->name, "treemagic") == 0)
+	{
+		TreeMagic *magic;
+
+		magic = tree_magic_new(field, type, error);
+
+		if (!*error)
+		{
+			g_return_val_if_fail(magic != NULL, FALSE);
+			g_ptr_array_add(tree_magic_array, magic);
+		}
+		else
+			g_return_val_if_fail(magic == NULL, FALSE);
+	}
 	else if (strcmp((char *)field->name, "comment") == 0 ||
 		 strcmp((char *)field->name, "acronym") == 0 ||
 		 strcmp((char *)field->name, "expanded-acronym") == 0)
@@ -857,6 +899,25 @@
 	return retval;
 }
 
+/* Comparison function to get the tree magic rules in priority order */
+static gint cmp_tree_magic(gconstpointer a, gconstpointer b)
+{
+	TreeMagic *aa = *(TreeMagic **) a;
+	TreeMagic *bb = *(TreeMagic **) b;
+	int retval;
+
+	if (aa->priority > bb->priority)
+		return -1;
+	else if (aa->priority < bb->priority)
+		return 1;
+
+	retval = strcmp(aa->type->media, bb->type->media);
+	if (!retval)
+		retval = strcmp(aa->type->subtype, bb->type->subtype);
+
+	return retval;
+}
+
 /* Write out 'n' as a two-byte big-endian number to 'stream' */
 static void write16(FILE *stream, guint32 n)
 {
@@ -1398,6 +1459,222 @@
 	return magic;
 }
 
+static TreeMatch *tree_match_new(void)
+{
+	TreeMatch *match;
+
+	match = g_new(TreeMatch, 1);
+	match->path = NULL;
+	match->ignore_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, "ignore-case", NULL);
+			if (attr)
+			{
+				if (strcmp (attr, "true") == 0) 
+				{
+					match->ignore_case = 1;
+				}
+				xmlFree(attr);
+			}
+		}
+		if (!*error)
+		{
+			attr = my_xmlGetNsProp(node, "non-empty", NULL);
+			if (attr)
+			{
+				if (strcmp (attr, "true") == 0) 
+				{
+					match->non_empty = 1;
+				}
+				xmlFree(attr);
+			}
+		}
+		if (!*error)
+		{
+			attr = my_xmlGetNsProp(node, "mimetype", NULL);
+			if (attr)
+			{
+				match->mimetype = g_strdup (attr);
+				xmlFree(attr);
+			}
+		}
+
+		if (*error)
+		{
+			tree_match_free(match);
+			break;
+		}
+
+		out = g_list_append(out, match);
+
+		match->matches = build_tree_matches(node, error);
+		if (*error)
+			break;
+	}
+
+	return out;
+}
+
+static void tree_magic_free(TreeMagic *magic)
+{
+	GList *next;
+
+	g_return_if_fail(magic != NULL);
+
+	for (next = magic->matches; next; next = next->next)
+		tree_match_free((TreeMatch *) next->data);
+	g_list_free(magic->matches);
+
+	g_free(magic);
+}
+
+/* Create a new TreeMagic object by parsing 'node' (a <treemagic> element) */
+static TreeMagic *tree_magic_new(xmlNode *node, Type *type, GError **error)
+{
+	TreeMagic *magic = NULL;
+	int prio;
+
+	g_return_val_if_fail(node != NULL, NULL);
+	g_return_val_if_fail(type != NULL, NULL);
+	g_return_val_if_fail(error != NULL, NULL);
+
+	prio = get_priority(node);
+
+	if (prio == -1)
+	{
+		g_set_error(error, MIME_ERROR, 0,
+			_("Bad priority (%d) in <treemagic> element"), prio);
+	}
+	else
+	{
+		magic = g_new(TreeMagic, 1);
+		magic->priority = prio;
+		magic->type = type;
+		magic->matches = build_tree_matches(node, error);
+
+		if (*error)
+		{
+			gchar *old = (*error)->message;
+			tree_magic_free(magic);
+			magic = NULL;
+			(*error)->message = g_strconcat(
+				_("Error in <treematch> element: "), old, NULL);
+			g_free(old);
+		}
+	}
+
+	return magic;
+}
+
 /* Write a list of Match elements (and their children) to the 'magic' file */
 static void write_magic_children(FILE *stream, GList *matches, int indent)
 {
@@ -1442,6 +1719,62 @@
 	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->ignore_case)
+			fputs (",ignore-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.
  */
@@ -3076,6 +3409,7 @@
 	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,
@@ -3225,6 +3559,28 @@
 	{
 		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);
@@ -3237,6 +3593,8 @@
 
 	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);
_______________________________________________
xdg mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/xdg

Reply via email to