summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Taylor <otaylor@redhat.com>2003-07-16 21:07:38 +0000
committerOwen Taylor <otaylor@src.gnome.org>2003-07-16 21:07:38 +0000
commit7e54248bc3c4890aabdde5da3a929bca425a6649 (patch)
tree4bb378fd3d278cb15241f19c94a85fdad7a953ec
parentbc8567d488c8222f655f2ed746ecfa8041c81137 (diff)
downloadgtk+-7e54248bc3c4890aabdde5da3a929bca425a6649.tar.gz
auto-ize.
Wed Jul 16 16:50:31 2003 Owen Taylor <otaylor@redhat.com> * configure.ac Makefile.am: auto-ize. * xdgmime/: Add freedesktop.org MIME spec implementatin by Jonathan Blandford. * gtkfilesystem.[ch]: Add gtk_file_info_render_icon() gtk_file_info_set/get_icon_type to do icon handling based on MIME type. Add a simple icon caching system. * gtkfilesystemgnomevfs.c: Implement ensure_types() so that extending the set of types for a loaded directory works. Set the MIME type to get the default icon handling. * gtkfilesystemunix.c: Look up the MIME type using xdgmime. * gtkfilechooserimpldefault.c: Display icons in the list.
-rw-r--r--gtk/gtkfilechooserdefault.c46
-rw-r--r--gtk/gtkfilechooserdialog.c2
-rw-r--r--gtk/gtkfilechooserentry.c4
-rw-r--r--gtk/gtkfilechooserwidget.c4
-rw-r--r--gtk/gtkfilesystem.c174
-rw-r--r--gtk/gtkfilesystem.h24
-rw-r--r--gtk/gtkfilesystemmodel.h2
-rw-r--r--gtk/gtkfilesystemunix.c49
-rw-r--r--gtk/gtkfilesystemunix.h6
-rw-r--r--gtk/xdgmime/Makefile.am15
-rw-r--r--gtk/xdgmime/test-mime.c53
-rw-r--r--gtk/xdgmime/xdgmime.c235
-rw-r--r--gtk/xdgmime/xdgmime.h60
-rw-r--r--gtk/xdgmime/xdgmimeglob.c465
-rw-r--r--gtk/xdgmime/xdgmimeglob.h53
-rw-r--r--gtk/xdgmime/xdgmimeint.c124
-rw-r--r--gtk/xdgmime/xdgmimeint.h54
-rw-r--r--gtk/xdgmime/xdgmimemagic.c733
-rw-r--r--gtk/xdgmime/xdgmimemagic.h43
-rw-r--r--tests/testfilechooser.c4
20 files changed, 2100 insertions, 50 deletions
diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
index c62e446944..9c5222e79b 100644
--- a/gtk/gtkfilechooserdefault.c
+++ b/gtk/gtkfilechooserdefault.c
@@ -25,10 +25,12 @@
#include "gtkfilechooser.h"
#include "gtkfilesystemmodel.h"
+#include <gtk/gtkcellrendererpixbuf.h>
#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtkentry.h>
#include <gtk/gtkhbox.h>
#include <gtk/gtkhpaned.h>
+#include <gtk/gtkicontheme.h>
#include <gtk/gtklabel.h>
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtktreeview.h>
@@ -119,6 +121,11 @@ static void tree_name_data_func (GtkTreeViewColumn *tree_column,
GtkTreeModel *tree_model,
GtkTreeIter *iter,
gpointer data);
+static void list_icon_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data);
static void list_name_data_func (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell,
GtkTreeModel *tree_model,
@@ -130,7 +137,7 @@ static void list_size_data_func (GtkTreeViewColumn *tree_column,
GtkTreeIter *iter,
gpointer data);
-GObjectClass *parent_class;
+static GObjectClass *parent_class;
GType
_gtk_file_chooser_impl_default_get_type (void)
@@ -174,6 +181,8 @@ gtk_file_chooser_impl_default_class_init (GtkFileChooserImplDefaultClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ parent_class = g_type_class_peek_parent (class);
+
gobject_class->finalize = gtk_file_chooser_impl_default_finalize;
gobject_class->constructor = gtk_file_chooser_impl_default_constructor;
gobject_class->set_property = gtk_file_chooser_impl_default_set_property;
@@ -350,11 +359,19 @@ gtk_file_chooser_impl_default_constructor (GType type,
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, "File name");
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ list_icon_data_func, impl, NULL);
+ gtk_tree_view_column_set_sort_column_id (column, 0);
+
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func (column, renderer,
list_name_data_func, impl, NULL);
gtk_tree_view_column_set_sort_column_id (column, 0);
+
gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), column);
column = gtk_tree_view_column_new ();
@@ -822,6 +839,7 @@ tree_selection_changed (GtkTreeSelection *selection,
impl->list_model = _gtk_file_system_model_new (impl->file_system,
file_path, 0,
+ GTK_FILE_INFO_ICON |
GTK_FILE_INFO_DISPLAY_NAME |
GTK_FILE_INFO_SIZE);
_gtk_file_system_model_set_show_folders (impl->list_model, FALSE);
@@ -915,7 +933,7 @@ entry_activate (GtkEntry *entry,
}
}
-const GtkFileInfo *
+static const GtkFileInfo *
get_list_file_info (GtkFileChooserImplDefault *impl,
GtkTreeIter *iter)
{
@@ -947,6 +965,30 @@ tree_name_data_func (GtkTreeViewColumn *tree_column,
}
static void
+list_icon_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GtkFileChooserImplDefault *impl = data;
+ const GtkFileInfo *info = get_list_file_info (impl, iter);
+
+ if (info)
+ {
+ GtkWidget *widget = GTK_TREE_VIEW_COLUMN (tree_column)->tree_view;
+ GdkPixbuf *pixbuf = gtk_file_info_render_icon (info, widget, 36);
+
+ g_object_set (cell,
+ "pixbuf", pixbuf,
+ NULL);
+
+ if (pixbuf)
+ g_object_unref (pixbuf);
+ }
+}
+
+static void
list_name_data_func (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell,
GtkTreeModel *tree_model,
diff --git a/gtk/gtkfilechooserdialog.c b/gtk/gtkfilechooserdialog.c
index fd40dc1ca5..854783f19c 100644
--- a/gtk/gtkfilechooserdialog.c
+++ b/gtk/gtkfilechooserdialog.c
@@ -50,7 +50,7 @@ static void gtk_file_chooser_dialog_get_property (GObject *obj
GValue *value,
GParamSpec *pspec);
-GObjectClass *parent_class;
+static GObjectClass *parent_class;
GType
gtk_file_chooser_dialog_get_type (void)
diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c
index 2717187492..ca29f02e26 100644
--- a/gtk/gtkfilechooserentry.c
+++ b/gtk/gtkfilechooserentry.c
@@ -67,8 +67,8 @@ static void gtk_file_chooser_entry_do_insert_text (GtkEditable *editabl
static void clear_completion_callback (GtkFileChooserEntry *chooser_entry,
GParamSpec *pspec);
-GObjectClass *parent_class;
-GtkEditableClass *parent_editable_iface;
+static GObjectClass *parent_class;
+static GtkEditableClass *parent_editable_iface;
GType
_gtk_file_chooser_entry_get_type (void)
diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c
index 677d41d41c..cb7e8edc68 100644
--- a/gtk/gtkfilechooserwidget.c
+++ b/gtk/gtkfilechooserwidget.c
@@ -48,7 +48,7 @@ static void gtk_file_chooser_widget_get_property (GObject *obj
GValue *value,
GParamSpec *pspec);
-GObjectClass *parent_class;
+static GObjectClass *parent_class;
GType
gtk_file_chooser_widget_get_type (void)
@@ -130,7 +130,7 @@ gtk_file_chooser_widget_constructor (GType type,
gtk_widget_push_composite_child ();
if (!priv->file_system)
- priv->file_system = _gtk_file_system_unix_new ();
+ priv->file_system = gtk_file_system_unix_new ();
priv->impl = _gtk_file_chooser_impl_default_new (priv->file_system);
gtk_box_pack_start (GTK_BOX (object), priv->impl, TRUE, TRUE, 0);
diff --git a/gtk/gtkfilesystem.c b/gtk/gtkfilesystem.c
index 80ee95d7fd..abcf20548c 100644
--- a/gtk/gtkfilesystem.c
+++ b/gtk/gtkfilesystem.c
@@ -18,6 +18,8 @@
* Boston, MA 02111-1307, USA.
*/
+#include <gtk/gtkicontheme.h>
+
#include "gtkfilesystem.h"
#include <string.h>
@@ -29,7 +31,7 @@ struct _GtkFileInfo
gchar *display_name;
gchar *display_key;
gchar *mime_type;
- GdkPixbuf *icon;
+ GtkFileIconType icon_type : 4;
guint is_folder : 1;
guint is_hidden : 1;
};
@@ -84,8 +86,6 @@ gtk_file_info_copy (GtkFileInfo *info)
new_info->display_name = g_strdup (new_info->display_name);
if (new_info->mime_type)
new_info->mime_type = g_strdup (new_info->mime_type);
- if (new_info->icon)
- g_object_ref (new_info->icon);
return new_info;
}
@@ -99,8 +99,6 @@ gtk_file_info_free (GtkFileInfo *info)
g_free (info->display_name);
if (info->mime_type)
g_free (info->mime_type);
- if (info->icon)
- g_object_unref (info->icon);
}
G_CONST_RETURN gchar *
@@ -134,7 +132,6 @@ gtk_file_info_get_display_key (const GtkFileInfo *info)
((GtkFileInfo *)info)->display_key = g_utf8_collate_key (info->display_name, -1);
}
-
return info->display_key;
}
@@ -244,31 +241,166 @@ gtk_file_info_set_size (GtkFileInfo *info,
info->size = size;
}
-GdkPixbuf *
-gtk_file_info_get_icon (const GtkFileInfo *info)
+void
+gtk_file_info_set_icon_type (GtkFileInfo *info,
+ GtkFileIconType icon_type)
{
- g_return_val_if_fail (info != NULL, NULL);
+ g_return_if_fail (info != NULL);
- return info->icon;
+ info->icon_type = icon_type;
}
-void
-gtk_file_info_set_icon (GtkFileInfo *info,
- GdkPixbuf *icon)
+GtkFileIconType
+gtk_file_info_get_icon_type (const GtkFileInfo *info)
{
- g_return_if_fail (info != NULL);
- g_return_if_fail (icon == NULL || GDK_IS_PIXBUF (icon));
+ g_return_val_if_fail (info != NULL, GTK_FILE_ICON_REGULAR);
+
+ return info->icon_type;
+}
- if (icon != info->icon)
+typedef struct _IconCacheElement IconCacheElement;
+
+struct _IconCacheElement
+{
+ gint size;
+ GdkPixbuf *pixbuf;
+};
+
+static void
+icon_cache_element_free (IconCacheElement *element)
+{
+ if (element->pixbuf)
+ g_object_unref (element->pixbuf);
+ g_free (element);
+}
+
+static void
+icon_theme_changed (GtkIconTheme *icon_theme)
+{
+ GHashTable *cache;
+
+ /* Difference from the initial creation is that we don't
+ * reconnect the signal
+ */
+ cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)icon_cache_element_free);
+ g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
+ cache, (GDestroyNotify)g_hash_table_destroy);
+}
+
+static GdkPixbuf *
+get_cached_icon (GtkWidget *widget,
+ const gchar *name,
+ gint pixel_size)
+{
+ GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
+ GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
+ IconCacheElement *element;
+
+ if (!cache)
+ {
+ cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)icon_cache_element_free);
+
+ g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
+ cache, (GDestroyNotify)g_hash_table_destroy);
+ g_signal_connect (icon_theme, "changed",
+ G_CALLBACK (icon_theme_changed), NULL);
+ }
+
+ element = g_hash_table_lookup (cache, name);
+ if (!element)
{
- if (info->icon)
- g_object_unref (info->icon);
+ element = g_new0 (IconCacheElement, 1);
+ g_hash_table_insert (cache, g_strdup (name), element);
+ }
+
+ if (element->size != pixel_size)
+ {
+ if (element->pixbuf)
+ g_object_unref (element->pixbuf);
+ element->size = pixel_size;
+ element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
+ pixel_size, 0, NULL);
+ }
- info->icon = icon;
+ return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
+}
+
+
+GdkPixbuf *
+gtk_file_info_render_icon (const GtkFileInfo *info,
+ GtkWidget *widget,
+ gint pixel_size)
+{
+ const gchar *separator;
+ GdkPixbuf *pixbuf;
+ GString *icon_name;
+
+ g_return_val_if_fail (info != NULL, NULL);
+ g_return_val_if_fail (widget != NULL, NULL);
+ g_return_val_if_fail (pixel_size > 0, NULL);
+
+ if (info->icon_type != GTK_FILE_ICON_REGULAR)
+ {
+ const char *name = NULL; /* Quiet gcc */
- if (info->icon)
- g_object_ref (info->icon);
+ switch (info->icon_type)
+ {
+ case GTK_FILE_ICON_BLOCK_DEVICE:
+ name ="gnome-fs-blockdev";
+ break;
+ case GTK_FILE_ICON_BROKEN_SYMBOLIC_LINK:
+ name = "gnome-fs-symlink";
+ break;
+ case GTK_FILE_ICON_CHARACTER_DEVICE:
+ name = "gnome-fs-chardev";
+ break;
+ case GTK_FILE_ICON_DIRECTORY:
+ name = "gnome-fs-directory";
+ break;
+ case GTK_FILE_ICON_EXECUTABLE:
+ name ="gnome-fs-executable";
+ break;
+ case GTK_FILE_ICON_FIFO:
+ name = "gnome-fs-fifo";
+ break;
+ case GTK_FILE_ICON_SOCKET:
+ name = "gnome-fs-socket";
+ break;
+ case GTK_FILE_ICON_REGULAR:
+ g_assert_not_reached ();
+ }
+
+ return get_cached_icon (widget, name, pixel_size);
}
+
+ if (!info->mime_type)
+ return NULL;
+
+ separator = strchr (info->mime_type, '/');
+ if (!separator)
+ return NULL;
+
+ icon_name = g_string_new ("gnome-mime-");
+ g_string_append_len (icon_name, info->mime_type, separator - info->mime_type);
+ g_string_append_c (icon_name, '-');
+ g_string_append (icon_name, separator + 1);
+ pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
+ g_string_free (icon_name, TRUE);
+ if (pixbuf)
+ return pixbuf;
+
+ icon_name = g_string_new ("gnome-mime-");
+ g_string_append_len (icon_name, info->mime_type, separator - info->mime_type);
+ pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
+ g_string_free (icon_name, TRUE);
+ if (pixbuf)
+ return pixbuf;
+
+ return get_cached_icon (widget, "gnome-fs-regular", pixel_size);
}
/*****************************************
diff --git a/gtk/gtkfilesystem.h b/gtk/gtkfilesystem.h
index 055b9d9bf5..275efb2bf2 100644
--- a/gtk/gtkfilesystem.h
+++ b/gtk/gtkfilesystem.h
@@ -22,7 +22,7 @@
#define __GTK_FILE_SYSTEM_H__
#include <glib-object.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtkwidget.h> /* For icon handling */
G_BEGIN_DECLS
@@ -50,6 +50,19 @@ typedef enum {
GTK_FILE_INFO_ALL = (1 << 7) - 1
} GtkFileInfoType;
+/* Icon type, supplemented by MIME type
+ */
+typedef enum {
+ GTK_FILE_ICON_REGULAR, /* Use mime type for icon */
+ GTK_FILE_ICON_BLOCK_DEVICE,
+ GTK_FILE_ICON_BROKEN_SYMBOLIC_LINK,
+ GTK_FILE_ICON_CHARACTER_DEVICE,
+ GTK_FILE_ICON_DIRECTORY,
+ GTK_FILE_ICON_EXECUTABLE,
+ GTK_FILE_ICON_FIFO,
+ GTK_FILE_ICON_SOCKET
+} GtkFileIconType;
+
/* GError enumeration for GtkFileSystem
*/
@@ -96,9 +109,12 @@ void gtk_file_info_set_modification_time (GtkFileInfo *in
gint64 gtk_file_info_get_size (const GtkFileInfo *info);
void gtk_file_info_set_size (GtkFileInfo *info,
gint64 size);
-GdkPixbuf * gtk_file_info_get_icon (const GtkFileInfo *info);
-void gtk_file_info_set_icon (GtkFileInfo *info,
- GdkPixbuf *icon);
+void gtk_file_info_set_icon_type (GtkFileInfo *info,
+ GtkFileIconType icon_type);
+GtkFileIconType gtk_file_info_get_icon_type (const GtkFileInfo *info);
+GdkPixbuf * gtk_file_info_render_icon (const GtkFileInfo *info,
+ GtkWidget *widget,
+ gint pixel_size);
/* The base GtkFileSystem interface
*/
diff --git a/gtk/gtkfilesystemmodel.h b/gtk/gtkfilesystemmodel.h
index b3647dc6c2..e84d004def 100644
--- a/gtk/gtkfilesystemmodel.h
+++ b/gtk/gtkfilesystemmodel.h
@@ -35,7 +35,7 @@ typedef struct _GtkFileSystemModel GtkFileSystemModel;
GType _gtk_file_system_model_get_type (void);
-enum {
+typedef enum {
GTK_FILE_SYSTEM_MODEL_INFO,
GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME,
GTK_FILE_SYSTEM_MODEL_N_COLUMNS
diff --git a/gtk/gtkfilesystemunix.c b/gtk/gtkfilesystemunix.c
index 41ef0ddae0..8bdf6dc032 100644
--- a/gtk/gtkfilesystemunix.c
+++ b/gtk/gtkfilesystemunix.c
@@ -21,6 +21,8 @@
#include "gtkfilesystem.h"
#include "gtkfilesystemunix.h"
+#include "xdgmime/xdgmime.h"
+
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
@@ -66,8 +68,8 @@ struct _GtkFileFolderUnix
gchar *filename;
};
-GObjectClass *system_parent_class;
-GObjectClass *folder_parent_class;
+static GObjectClass *system_parent_class;
+static GObjectClass *folder_parent_class;
static void gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class);
static void gtk_file_system_unix_iface_init (GtkFileSystemIface *iface);
@@ -135,7 +137,7 @@ static GtkFileInfo *filename_get_info (const gchar *filename,
* GtkFileSystemUnix
*/
GType
-_gtk_file_system_unix_get_type (void)
+gtk_file_system_unix_get_type (void)
{
static GType file_system_unix_type = 0;
@@ -173,7 +175,7 @@ _gtk_file_system_unix_get_type (void)
}
/**
- * _gtk_file_system_unix_new:
+ * gtk_file_system_unix_new:
*
* Creates a new #GtkFileSystemUnix object. #GtkFileSystemUnix
* implements the #GtkFileSystem interface with direct access to
@@ -182,7 +184,7 @@ _gtk_file_system_unix_get_type (void)
* Return value: the new #GtkFileSystemUnix object
**/
GtkFileSystem *
-_gtk_file_system_unix_new (void)
+gtk_file_system_unix_new (void)
{
return g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL);
}
@@ -678,6 +680,7 @@ filename_get_info (const gchar *filename,
GError **error)
{
GtkFileInfo *info;
+ GtkFileIconType icon_type = GTK_FILE_ICON_REGULAR;
struct stat statbuf;
/* If stat fails, try to fall back to lstat to catch broken links
@@ -735,9 +738,36 @@ filename_get_info (const gchar *filename,
gtk_file_info_set_is_folder (info, S_ISDIR (statbuf.st_mode));
}
- if (types & GTK_FILE_INFO_MIME_TYPE)
+ if (types & GTK_FILE_INFO_ICON)
+ {
+ if (S_ISBLK (statbuf.st_mode))
+ icon_type = GTK_FILE_ICON_BLOCK_DEVICE;
+ else if (S_ISLNK (statbuf.st_mode))
+ icon_type = GTK_FILE_ICON_BROKEN_SYMBOLIC_LINK;
+ else if (S_ISCHR (statbuf.st_mode))
+ icon_type = GTK_FILE_ICON_CHARACTER_DEVICE;
+ else if (S_ISDIR (statbuf.st_mode))
+ icon_type = GTK_FILE_ICON_DIRECTORY;
+ else if (S_ISFIFO (statbuf.st_mode))
+ icon_type = GTK_FILE_ICON_FIFO;
+ else if (S_ISSOCK (statbuf.st_mode))
+ icon_type = GTK_FILE_ICON_SOCKET;
+
+ gtk_file_info_set_icon_type (info, icon_type);
+ }
+
+ if ((types & GTK_FILE_INFO_MIME_TYPE) ||
+ ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR))
{
- gtk_file_info_set_mime_type (info, "application/octet-stream");
+ const char *mime_type = xdg_mime_get_mime_type_for_file (filename);
+ gtk_file_info_set_mime_type (info, mime_type);
+
+ if ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR &&
+ (statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) &&
+ (strcmp (mime_type, XDG_MIME_TYPE_UNKNOWN) == 0 ||
+ strcmp (mime_type, "application/x-executable") == 0 ||
+ strcmp (mime_type, "application/x-shellscript") == 0))
+ gtk_file_info_set_icon_type (info, GTK_FILE_ICON_EXECUTABLE);
}
if (types & GTK_FILE_INFO_MODIFICATION_TIME)
@@ -750,11 +780,6 @@ filename_get_info (const gchar *filename,
gtk_file_info_set_size (info, (gint64)statbuf.st_size);
}
- if (types & GTK_FILE_INFO_ICON)
- {
- /* NOT YET IMPLEMENTED */
- }
-
return info;
}
diff --git a/gtk/gtkfilesystemunix.h b/gtk/gtkfilesystemunix.h
index a6b820d015..2c2e6d5400 100644
--- a/gtk/gtkfilesystemunix.h
+++ b/gtk/gtkfilesystemunix.h
@@ -26,14 +26,14 @@
G_BEGIN_DECLS
-#define GTK_TYPE_FILE_SYSTEM_UNIX (_gtk_file_system_unix_get_type ())
+#define GTK_TYPE_FILE_SYSTEM_UNIX (gtk_file_system_unix_get_type ())
#define GTK_FILE_SYSTEM_UNIX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnix))
#define GTK_IS_FILE_SYSTEM_UNIX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_SYSTEM_UNIX))
typedef struct _GtkFileSystemUnix GtkFileSystemUnix;
-GtkFileSystem *_gtk_file_system_unix_new (void);
-GType _gtk_file_system_unix_get_type (void);
+GtkFileSystem *gtk_file_system_unix_new (void);
+GType gtk_file_system_unix_get_type (void);
G_END_DECLS
diff --git a/gtk/xdgmime/Makefile.am b/gtk/xdgmime/Makefile.am
new file mode 100644
index 0000000000..5edd02d7d0
--- /dev/null
+++ b/gtk/xdgmime/Makefile.am
@@ -0,0 +1,15 @@
+INCLUDES = -DXDG_PREFIX=_xdg
+
+noinst_LTLIBRARIES = libxdgmime.la
+
+libxdgmime_la_SOURCES = \
+ xdgmime.c \
+ xdgmimeglob.c \
+ xdgmimeint.c \
+ xdgmimemagic.c
+
+noinst_PROGRAMS = test-mime
+
+test_mime_LDADD = libxdgmime.la
+test_mime_SOURCES = test-mime.c
+
diff --git a/gtk/xdgmime/test-mime.c b/gtk/xdgmime/test-mime.c
new file mode 100644
index 0000000000..170d224931
--- /dev/null
+++ b/gtk/xdgmime/test-mime.c
@@ -0,0 +1,53 @@
+#include "xdgmime.h"
+#include "xdgmimeglob.h"
+#include <string.h>
+#include <stdio.h>
+
+
+static void
+test_individual_glob (const char *glob,
+ XdgGlobType expected_type)
+{
+ XdgGlobType test_type;
+
+ test_type = _xdg_glob_determine_type (glob);
+ if (test_type != expected_type)
+ {
+ printf ("Test Failed: %s is of type %s, but %s is expected\n",
+ glob,
+ ((test_type == XDG_GLOB_LITERAL)?"XDG_GLOB_LITERAL":
+ ((test_type == XDG_GLOB_SIMPLE)?"XDG_GLOB_SIMPLE":"XDG_GLOB_FULL")),
+ ((expected_type == XDG_GLOB_LITERAL)?"XDG_GLOB_LITERAL":
+ ((expected_type == XDG_GLOB_SIMPLE)?"XDG_GLOB_SIMPLE":"XDG_GLOB_COMPLEX")));
+ }
+}
+
+static void
+test_glob_type (void)
+{
+ test_individual_glob ("*.gif", XDG_GLOB_SIMPLE);
+ test_individual_glob ("Foo*.gif", XDG_GLOB_FULL);
+ test_individual_glob ("*[4].gif", XDG_GLOB_FULL);
+ test_individual_glob ("Makefile", XDG_GLOB_LITERAL);
+ test_individual_glob ("sldkfjvlsdf\\\\slkdjf", XDG_GLOB_FULL);
+ test_individual_glob ("tree.[ch]", XDG_GLOB_FULL);
+}
+
+int
+main (int argc, char *argv[])
+{
+ const char *result;
+ const char *file_name;
+ int i;
+
+ test_glob_type ();
+ for (i = 1; i < argc; i++)
+ {
+ file_name = argv[i];
+ result = xdg_mime_get_mime_type_for_file (file_name);
+ printf ("File \"%s\" has a mime-type of %s\n", file_name, result);
+ }
+
+ return 0;
+}
+
diff --git a/gtk/xdgmime/xdgmime.c b/gtk/xdgmime/xdgmime.c
new file mode 100644
index 0000000000..a8ea4d383d
--- /dev/null
+++ b/gtk/xdgmime/xdgmime.c
@@ -0,0 +1,235 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmime.c: XDG Mime Spec mime resolver. Based on version 0.11 of the spec.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "xdgmime.h"
+#include "xdgmimeint.h"
+#include "xdgmimeglob.h"
+#include "xdgmimemagic.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+XdgGlobHash *global_hash = NULL;
+XdgMimeMagic *global_magic = NULL;
+
+
+static void
+_xdg_mime_init_from_directory (const char *directory)
+{
+ char *file_name;
+
+ file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
+ strcpy (file_name, directory);
+ strcat (file_name, "/mime/globs");
+ _xdg_mime_glob_read_from_file (global_hash, file_name);
+ free (file_name);
+
+ file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
+ strcpy (file_name, directory);
+ strcat (file_name, "/mime/magic");
+ _xdg_mime_magic_read_from_file (global_magic, file_name);
+ free (file_name);
+}
+
+static void
+xdg_mime_init (void)
+{
+ static int initted = 0;
+
+ if (initted == 0)
+ {
+ const char *xdg_config_home;
+ const char *xdg_data_dirs;
+ const char *ptr;
+
+ global_hash = _xdg_glob_hash_new ();
+ global_magic = _xdg_mime_magic_new ();
+
+ /* We look for globs and magic files based upon the XDG Base Directory
+ * Specification
+ */
+ xdg_config_home = getenv ("XDG_CONFIG_HOME");
+ if (xdg_config_home)
+ {
+ _xdg_mime_init_from_directory (xdg_config_home);
+ }
+ else
+ {
+ const char *home;
+
+ home = getenv ("HOME");
+ if (home != NULL)
+ {
+ char *guessed_xdg_home;
+
+ guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1);
+ strcpy (guessed_xdg_home, home);
+ strcat (guessed_xdg_home, "/.local/share/");
+ _xdg_mime_init_from_directory (guessed_xdg_home);
+ free (guessed_xdg_home);
+ }
+ }
+
+ xdg_data_dirs = getenv ("XDG_DATA_DIRS");
+ if (xdg_data_dirs == NULL)
+ xdg_data_dirs = "/usr/local/share/:/usr/share/";
+
+ ptr = xdg_data_dirs;
+
+ while (*ptr != '\000')
+ {
+ const char *end_ptr;
+ char *dir;
+ int len;
+
+ end_ptr = ptr;
+ while (*end_ptr != ':' && *end_ptr != '\000')
+ end_ptr ++;
+
+ if (end_ptr == ptr)
+ {
+ ptr++;
+ continue;
+ }
+
+ if (*end_ptr == ':')
+ len = end_ptr - ptr;
+ else
+ len = end_ptr - ptr + 1;
+ dir = malloc (len);
+ strncpy (dir, ptr, len);
+ _xdg_mime_init_from_directory (dir);
+ free (dir);
+
+ ptr = end_ptr;
+ }
+ initted = 1;
+ }
+}
+
+const char *
+xdg_mime_get_mime_type_for_data (const void *data,
+ size_t len)
+{
+ const char *mime_type;
+
+ xdg_mime_init ();
+
+ mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len);
+
+ if (mime_type)
+ return mime_type;
+
+ return XDG_MIME_TYPE_UNKNOWN;
+}
+
+const char *
+xdg_mime_get_mime_type_for_file (const char *file_name)
+{
+ const char *mime_type;
+ FILE *file;
+ unsigned char *data;
+ int max_extent;
+ int bytes_read;
+ struct stat statbuf;
+ const char *base_name;
+
+ if (file_name == NULL)
+ return NULL;
+ if (! _xdg_utf8_validate (file_name))
+ return NULL;
+
+ xdg_mime_init ();
+
+ base_name = _xdg_get_base_name (file_name);
+ mime_type = xdg_mime_get_mime_type_from_file_name (base_name);
+
+ if (mime_type != XDG_MIME_TYPE_UNKNOWN)
+ return mime_type;
+
+ if (stat (file_name, &statbuf) != 0)
+ return XDG_MIME_TYPE_UNKNOWN;
+
+ if (!S_ISREG (statbuf.st_mode))
+ return XDG_MIME_TYPE_UNKNOWN;
+
+ /* FIXME: Need to make sure that max_extent isn't totally broken. This could
+ * be large and need getting from a stream instead of just reading it all
+ * in. */
+ max_extent = _xdg_mime_magic_get_buffer_extents (global_magic);
+ data = malloc (max_extent);
+ if (data == NULL)
+ return XDG_MIME_TYPE_UNKNOWN;
+
+ file = fopen (file_name, "r");
+ if (file == NULL)
+ {
+ free (data);
+ return XDG_MIME_TYPE_UNKNOWN;
+ }
+
+ bytes_read = fread (data, 1, max_extent, file);
+ if (ferror (file))
+ {
+ free (data);
+ fclose (file);
+ return XDG_MIME_TYPE_UNKNOWN;
+ }
+
+ mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read);
+
+ free (data);
+ fclose (file);
+
+ if (mime_type)
+ return mime_type;
+
+ return XDG_MIME_TYPE_UNKNOWN;
+}
+
+const char *
+xdg_mime_get_mime_type_from_file_name (const char *file_name)
+{
+ const char *mime_type;
+
+ xdg_mime_init ();
+
+ mime_type = _xdg_glob_hash_lookup_file_name (global_hash, file_name);
+ if (mime_type)
+ return mime_type;
+ else
+ return XDG_MIME_TYPE_UNKNOWN;
+}
+
+int
+xdg_mime_is_valid_mime_type (const char *mime_type)
+{
+ /* FIXME: We should make this a better test
+ */
+ return _xdg_utf8_validate (mime_type);
+}
diff --git a/gtk/xdgmime/xdgmime.h b/gtk/xdgmime/xdgmime.h
new file mode 100644
index 0000000000..7a22e8232b
--- /dev/null
+++ b/gtk/xdgmime/xdgmime.h
@@ -0,0 +1,60 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmime.h: XDG Mime Spec mime resolver. Based on version 0.11 of the spec.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#ifndef __XDG_MIME_H__
+#define __XDG_MIME_H__
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define XDG_MIME_TYPE_UNKNOWN "application/octet-stream"
+
+#ifdef XDG_PREFIX
+#define XDG_ENTRY(func) _XDG_ENTRY2(XDG_PREFIX,func)
+#define _XDG_ENTRY2(prefix,func) _XDG_ENTRY3(prefix,func)
+#define _XDG_ENTRY3(prefix,func) prefix##_##func
+
+#define xdg_mime_get_mime_type_for_data XDG_ENTRY(get_mime_type_for_data)
+#define xdg_mime_get_mime_type_for_file XDG_ENTRY(get_mime_type_for_file)
+#define xdg_mime_get_mime_type_from_file_name XDG_ENTRY(get_mime_type_from_file_name)
+#define xdg_mime_is_valid_mime_type XDG_ENTRY(is_valid_mime_type)
+#endif
+
+const char *xdg_mime_get_mime_type_for_data (const void *data,
+ size_t len);
+const char *xdg_mime_get_mime_type_for_file (const char *file_name);
+const char *xdg_mime_get_mime_type_from_file_name (const char *file_name);
+int xdg_mime_is_valid_mime_type (const char *mime_type);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __XDG_MIME_H__ */
diff --git a/gtk/xdgmime/xdgmimeglob.c b/gtk/xdgmime/xdgmimeglob.c
new file mode 100644
index 0000000000..a2b9e40ddc
--- /dev/null
+++ b/gtk/xdgmime/xdgmimeglob.c
@@ -0,0 +1,465 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmime.c: Private file. Datastructure for storing the globs.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "xdgmimeglob.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+typedef struct XdgGlobHashNode XdgGlobHashNode;
+typedef struct XdgGlobList XdgGlobList;
+
+struct XdgGlobHashNode
+{
+ xdg_unichar_t character;
+ const char *mime_type;
+ XdgGlobHashNode *next;
+ XdgGlobHashNode *child;
+};
+struct XdgGlobList
+{
+ const char *data;
+ const char *mime_type;
+ XdgGlobList *next;
+};
+
+struct XdgGlobHash
+{
+ XdgGlobList *literal_list;
+ XdgGlobHashNode *simple_node;
+ XdgGlobList *full_list;
+};
+
+
+/* XdgGlobList
+ */
+static XdgGlobList *
+_xdg_glob_list_new (void)
+{
+ XdgGlobList *new_element;
+
+ new_element = calloc (1, sizeof (XdgGlobList));
+
+ return new_element;
+}
+
+#if 0
+static void
+_xdg_glob_list_free (XdgGlobList *glob_list)
+{
+ free (glob_list);
+}
+#endif
+
+static XdgGlobList *
+_xdg_glob_list_append (XdgGlobList *glob_list,
+ void *data,
+ const char *mime_type)
+{
+ XdgGlobList *new_element;
+ XdgGlobList *tmp_element;
+
+ new_element = _xdg_glob_list_new ();
+ new_element->data = data;
+ new_element->mime_type = mime_type;
+ if (glob_list == NULL)
+ return new_element;
+
+ tmp_element = glob_list;
+ while (tmp_element->next != NULL)
+ tmp_element = tmp_element->next;
+
+ tmp_element->next = new_element;
+
+ return glob_list;
+}
+
+#if 0
+static XdgGlobList *
+_xdg_glob_list_prepend (XdgGlobList *glob_list,
+ void *data,
+ const char *mime_type)
+{
+ XdgGlobList *new_element;
+
+ new_element = _xdg_glob_list_new ();
+ new_element->data = data;
+ new_element->next = glob_list;
+ new_element->mime_type = mime_type;
+
+ return new_element;
+}
+#endif
+
+/* XdgGlobHashNode
+ */
+
+static XdgGlobHashNode *
+_xdg_glob_hash_node_new (void)
+{
+ XdgGlobHashNode *glob_hash_node;
+
+ glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
+
+ return glob_hash_node;
+}
+
+#if 0
+static void
+_xdg_glob_hash_node_free (XdgGlobHashNode *glob_hash_node)
+{
+ free (glob_hash_node);
+}
+#endif
+
+void
+_xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
+ int depth)
+{
+ int i;
+ for (i = 0; i < depth; i++)
+ printf (" ");
+
+ printf ("%c", (char)glob_hash_node->character);
+ if (glob_hash_node->mime_type)
+ printf (" - %s\n", glob_hash_node->mime_type);
+ else
+ printf ("\n");
+ if (glob_hash_node->child)
+ _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
+ if (glob_hash_node->next)
+ _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
+}
+
+static XdgGlobHashNode *
+_xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
+ const char *text,
+ const char *mime_type)
+{
+ XdgGlobHashNode *node;
+ xdg_unichar_t character;
+
+ character = _xdg_utf8_to_ucs4 (text);
+
+ if ((glob_hash_node == NULL) ||
+ (character < glob_hash_node->character))
+ {
+ node = _xdg_glob_hash_node_new ();
+ node->character = character;
+ node->next = glob_hash_node;
+ glob_hash_node = node;
+ }
+ else if (character == glob_hash_node->character)
+ {
+ node = glob_hash_node;
+ }
+ else
+ {
+ XdgGlobHashNode *prev_node;
+ int found_node = FALSE;
+
+ /* Look for the first character of text in glob_hash_node, and insert it if we
+ * have to.*/
+ prev_node = glob_hash_node;
+ node = prev_node->next;
+
+ while (node != NULL)
+ {
+ if (character < node->character)
+ {
+ node = _xdg_glob_hash_node_new ();
+ node->character = character;
+ node->next = prev_node->next;
+ prev_node->next = node;
+
+ found_node = TRUE;
+ break;
+ }
+ else if (character == node->character)
+ {
+ found_node = TRUE;
+ break;
+ }
+ prev_node = node;
+ node = node->next;
+ }
+
+ if (! found_node)
+ {
+ node = _xdg_glob_hash_node_new ();
+ node->character = character;
+ node->next = prev_node->next;
+ prev_node->next = node;
+ }
+ }
+
+ text = _xdg_utf8_next_char (text);
+ if (*text == '\000')
+ {
+ node->mime_type = mime_type;
+ }
+ else
+ {
+ node->child = _xdg_glob_hash_insert_text (node->child, text, mime_type);
+ }
+ return glob_hash_node;
+}
+
+static const char *
+_xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
+ const char *file_name,
+ int ignore_case)
+{
+ XdgGlobHashNode *node;
+ xdg_unichar_t character;
+
+ if (glob_hash_node == NULL)
+ return NULL;
+
+ character = _xdg_utf8_to_ucs4 (file_name);
+ if (ignore_case)
+ character = _xdg_ucs4_to_upper(character);
+
+ for (node = glob_hash_node;
+ node && character >= (ignore_case?_xdg_ucs4_to_upper (node->character):node->character);
+ node = node->next)
+ {
+ if (character == (ignore_case?_xdg_ucs4_to_upper (node->character):node->character))
+ {
+ file_name = _xdg_utf8_next_char (file_name);
+ if (*file_name == '\000')
+ return node->mime_type;
+ else
+ return _xdg_glob_hash_node_lookup_file_name (node->child,
+ file_name,
+ ignore_case);
+ }
+ }
+ return NULL;
+}
+
+const char *
+_xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
+ const char *file_name)
+{
+ XdgGlobList *list;
+ const char *mime_type;
+ const char *ptr;
+ /* First, check the literals */
+
+ assert (file_name != NULL);
+
+ for (list = glob_hash->literal_list; list; list = list->next)
+ if (strcmp ((const char *)list->data, file_name) == 0)
+ return list->mime_type;
+
+ for (ptr = file_name; *ptr != '\000'; ptr = _xdg_utf8_next_char (ptr))
+ {
+ if (*ptr == '.')
+ {
+ mime_type = (_xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, FALSE));
+ if (mime_type != NULL)
+ return mime_type;
+ }
+ }
+
+ for (ptr = file_name; *ptr != '\000'; ptr = _xdg_utf8_next_char (ptr))
+ {
+ if (*ptr == '.')
+ {
+ mime_type = (_xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, TRUE));
+ if (mime_type != NULL)
+ return mime_type;
+ }
+ }
+
+ /* FIXME: Not UTF-8 safe */
+ for (list = glob_hash->full_list; list; list = list->next)
+ if (fnmatch ((const char *)list->data, file_name, 0) == 0)
+ return list->mime_type;
+
+ return NULL;
+}
+
+
+
+/* XdgGlobHash
+ */
+
+XdgGlobHash *
+_xdg_glob_hash_new (void)
+{
+ XdgGlobHash *glob_hash;
+
+ glob_hash = calloc (1, sizeof (XdgGlobHash));
+
+ return glob_hash;
+}
+
+
+static void
+_xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
+{
+
+ if (node->child)
+ _xdg_glob_hash_free_nodes (node->child);
+ if (node->next)
+ _xdg_glob_hash_free_nodes (node->next);
+ free (node);
+}
+
+void
+_xdg_glob_hash_free (XdgGlobHash *glob_hash)
+{
+ _xdg_glob_hash_free_nodes (glob_hash->simple_node);
+ free (glob_hash);
+}
+
+
+
+XdgGlobType
+_xdg_glob_determine_type (const char *glob)
+{
+ const char *ptr;
+ int maybe_in_simple_glob = FALSE;
+ int first_char = TRUE;
+
+ ptr = glob;
+
+ while (*ptr != '\000')
+ {
+ if (*ptr == '*' && first_char)
+ maybe_in_simple_glob = TRUE;
+ else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
+ return XDG_GLOB_FULL;
+
+ first_char = FALSE;
+ ptr = _xdg_utf8_next_char (ptr);
+ }
+ if (maybe_in_simple_glob)
+ return XDG_GLOB_SIMPLE;
+ else
+ return XDG_GLOB_LITERAL;
+}
+
+/* glob must be valid UTF-8 */
+void
+_xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
+ const char *glob,
+ const char *mime_type)
+{
+ XdgGlobType type;
+
+ assert (glob_hash != NULL);
+ assert (glob != NULL);
+
+ type = _xdg_glob_determine_type (glob);
+
+ switch (type)
+ {
+ case XDG_GLOB_LITERAL:
+ glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type));
+ break;
+ case XDG_GLOB_SIMPLE:
+ glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, strdup (mime_type));
+ break;
+ case XDG_GLOB_FULL:
+ glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type));
+ break;
+ }
+}
+
+void
+_xdg_glob_hash_dump (XdgGlobHash *glob_hash)
+{
+ XdgGlobList *list;
+ printf ("LITERAL STRINGS\n");
+ if (glob_hash->literal_list == NULL)
+ {
+ printf (" None\n");
+ }
+ else
+ {
+ for (list = glob_hash->literal_list; list; list = list->next)
+ printf (" %s - %s\n", (char *)list->data, list->mime_type);
+ }
+ printf ("\nSIMPLE GLOBS\n");
+ _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
+
+ printf ("\nFULL GLOBS\n");
+ if (glob_hash->full_list == NULL)
+ {
+ printf (" None\n");
+ }
+ else
+ {
+ for (list = glob_hash->full_list; list; list = list->next)
+ printf (" %s - %s\n", (char *)list->data, list->mime_type);
+ }
+}
+
+
+void
+_xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
+ const char *file_name)
+{
+ FILE *glob_file;
+ char line[255];
+
+ glob_file = fopen (file_name, "r");
+
+ if (glob_file == NULL)
+ return;
+
+ /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
+ * Blah */
+ while (fgets (line, 255, glob_file) != NULL)
+ {
+ char *colon;
+ if (line[0] == '#')
+ continue;
+
+ colon = strchr (line, ':');
+ if (colon == NULL)
+ continue;
+ *(colon++) = '\000';
+ colon[strlen (colon) -1] = '\000';
+ _xdg_glob_hash_append_glob (glob_hash, colon, line);
+ }
+
+ fclose (glob_file);
+}
diff --git a/gtk/xdgmime/xdgmimeglob.h b/gtk/xdgmime/xdgmimeglob.h
new file mode 100644
index 0000000000..520ef5fe25
--- /dev/null
+++ b/gtk/xdgmime/xdgmimeglob.h
@@ -0,0 +1,53 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmime.h: Private file. Datastructure for storing the globs.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __XDG_MIME_GLOB_H__
+#define __XDG_MIME_GLOB_H__
+
+
+typedef struct XdgGlobHash XdgGlobHash;
+
+typedef enum
+{
+ XDG_GLOB_LITERAL, /* Makefile */
+ XDG_GLOB_SIMPLE, /* *.gif */
+ XDG_GLOB_FULL, /* x*.[ch] */
+} XdgGlobType;
+
+
+void _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
+ const char *file_name);
+XdgGlobHash *_xdg_glob_hash_new (void);
+void _xdg_glob_hash_free (XdgGlobHash *glob_hash);
+const char *_xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
+ const char *text);
+void _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
+ const char *glob,
+ const char *mime_type);
+XdgGlobType _xdg_glob_determine_type (const char *glob);
+void _xdg_glob_hash_dump (XdgGlobHash *glob_hash);
+
+#endif /* __XDG_MIME_GLOB_H__ */
diff --git a/gtk/xdgmime/xdgmimeint.c b/gtk/xdgmime/xdgmimeint.c
new file mode 100644
index 0000000000..75d91e0b22
--- /dev/null
+++ b/gtk/xdgmime/xdgmimeint.c
@@ -0,0 +1,124 @@
+
+#include "xdgmimeint.h"
+#include <ctype.h>
+#include <string.h>
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+const unsigned char utf8_skip_data[256] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
+};
+
+const char * const utf8_skip = utf8_skip_data;
+
+
+
+/* Returns the number of unprocessed characters. */
+xdg_unichar_t
+_xdg_utf8_to_ucs4(const char *source)
+{
+ xdg_unichar_t ucs32;
+ if( ! ( *source & 0x80 ) )
+ {
+ ucs32 = *source;
+ }
+ else
+ {
+ int bytelength = 0;
+ xdg_unichar_t result;
+ if ( ! *source & 0x40 )
+ {
+ ucs32 = *source;
+ }
+ else
+ {
+ if ( ! *source & 0x20 )
+ {
+ result = *source++ & 0x1F;
+ bytelength = 2;
+ }
+ else if ( ! *source & 0x10 )
+ {
+ result = *source++ & 0x0F;
+ bytelength = 3;
+ }
+ else if ( ! *source & 0x08 )
+ {
+ result = *source++ & 0x07;
+ bytelength = 4;
+ }
+ else if ( ! *source & 0x04 )
+ {
+ result = *source++ & 0x03;
+ bytelength = 5;
+ }
+ else if ( ! *source & 0x02 )
+ {
+ result = *source++ & 0x01;
+ bytelength = 6;
+ }
+ else
+ {
+ result = *source++;
+ bytelength = 1;
+ }
+
+ for ( bytelength --; bytelength > 0; bytelength -- )
+ {
+ result <<= 6;
+ result |= *source++ & 0x3F;
+ }
+ ucs32 = result;
+ }
+ }
+ return ucs32;
+}
+
+
+/* hullo. this is great code. don't rewrite it */
+
+xdg_unichar_t
+_xdg_ucs4_to_upper (xdg_unichar_t source)
+{
+ /* FIXME: Do a real to_upper sometime */
+ /* CaseFolding-3.2.0.txt has a table of rules. */
+ if ((source & 0xFF) == source)
+ return (xdg_unichar_t) toupper ((char) source);
+ return source;
+}
+
+int
+_xdg_utf8_validate (const char *source)
+{
+ /* FIXME: actually write */
+ return TRUE;
+}
+
+const char *
+_xdg_get_base_name (const char *file_name)
+{
+ const char *base_name;
+
+ if (file_name == NULL)
+ return NULL;
+
+ base_name = strrchr (file_name, '/');
+
+ if (base_name == NULL)
+ return file_name;
+ else
+ return base_name + 1;
+}
diff --git a/gtk/xdgmime/xdgmimeint.h b/gtk/xdgmime/xdgmimeint.h
new file mode 100644
index 0000000000..228eb5f33b
--- /dev/null
+++ b/gtk/xdgmime/xdgmimeint.h
@@ -0,0 +1,54 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeint.h: Internal defines and functions.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __XDG_MIME_INT_H__
+#define __XDG_MIME_INT_H__
+
+/* FIXME: Should be configure check */
+typedef unsigned int xdg_unichar_t;
+typedef unsigned char xdg_uchar8_t;
+typedef unsigned short xdg_uint16_t;
+typedef unsigned int xdg_uint32_t;
+
+
+#define SWAP_BE16_TO_LE16(val) (xdg_uint16_t)(((xdg_uint16_t)(val) << 8)|((xdg_uint16_t)(val) >> 8))
+
+#define SWAP_BE32_TO_LE32(val) (xdg_uint32_t)((((xdg_uint32_t)(val) & 0xFF000000U) >> 24) | \
+ (((xdg_uint32_t)(val) & 0x00FF0000U) >> 8) | \
+ (((xdg_uint32_t)(val) & 0x0000FF00U) << 8) | \
+ (((xdg_uint32_t)(val) & 0x000000FFU) << 24))
+/* UTF-8 utils
+ */
+const char *const utf8_skip;
+#define _xdg_utf8_next_char(p) (char *)((p) + utf8_skip[*(unsigned char *)(p)])
+#define _xdg_utf8_char_size(p) (int) (utf8_skip[*(unsigned char *)(p)])
+
+xdg_unichar_t _xdg_utf8_to_ucs4 (const char *source);
+xdg_unichar_t _xdg_ucs4_to_upper (xdg_unichar_t source);
+int _xdg_utf8_validate (const char *source);
+const char *_xdg_get_base_name (const char *file_name);
+
+#endif /* __XDG_MIME_INT_H__ */
diff --git a/gtk/xdgmime/xdgmimemagic.c b/gtk/xdgmime/xdgmimemagic.c
new file mode 100644
index 0000000000..98b59951ba
--- /dev/null
+++ b/gtk/xdgmime/xdgmimemagic.c
@@ -0,0 +1,733 @@
+
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimemagic.: Private file. Datastructure for storing magic.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "xdgmimemagic.h"
+#include "xdgmimeint.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+extern int errno;
+
+typedef struct XdgMimeMagicMatch XdgMimeMagicMatch;
+typedef struct XdgMimeMagicMatchlet XdgMimeMagicMatchlet;
+
+typedef enum
+{
+ XDG_MIME_MAGIC_SECTION,
+ XDG_MIME_MAGIC_MAGIC,
+ XDG_MIME_MAGIC_ERROR,
+ XDG_MIME_MAGIC_EOF
+} XdgMimeMagicState;
+
+struct XdgMimeMagicMatch
+{
+ const char *mime_type;
+ int priority;
+ XdgMimeMagicMatchlet *matchlet;
+ XdgMimeMagicMatch *next;
+};
+
+
+struct XdgMimeMagicMatchlet
+{
+ int indent;
+ int offset;
+ unsigned int value_length;
+ unsigned char *value;
+ unsigned char *mask;
+ unsigned int range_length;
+ unsigned int word_size;
+ XdgMimeMagicMatchlet *next;
+};
+
+
+struct XdgMimeMagic
+{
+ XdgMimeMagicMatch *match_list;
+ int max_extent;
+};
+
+static XdgMimeMagicMatch *
+_xdg_mime_magic_match_new (void)
+{
+ return calloc (1, sizeof (XdgMimeMagicMatch));
+}
+
+
+static XdgMimeMagicMatchlet *
+_xdg_mime_magic_matchlet_new (void)
+{
+ XdgMimeMagicMatchlet *matchlet;
+
+ matchlet = malloc (sizeof (XdgMimeMagicMatchlet));
+
+ matchlet->indent = 0;
+ matchlet->offset = 0;
+ matchlet->value_length = 0;
+ matchlet->value = NULL;
+ matchlet->mask = NULL;
+ matchlet->range_length = 1;
+ matchlet->word_size = 1;
+ matchlet->next = NULL;
+
+ return matchlet;
+}
+
+void
+_xdg_mime_magic_match_free (XdgMimeMagicMatch *mime_magic_match)
+{
+ if (mime_magic_match)
+ {
+ if (mime_magic_match->mime_type)
+ free ((char *)mime_magic_match->mime_type);
+ free (mime_magic_match);
+ }
+}
+
+
+void
+_xdg_mime_magic_matchlet_free (XdgMimeMagicMatchlet *mime_magic_matchlet)
+{
+ if (mime_magic_matchlet)
+ {
+ if (mime_magic_matchlet->next)
+ _xdg_mime_magic_matchlet_free (mime_magic_matchlet->next);
+ if (mime_magic_matchlet->value)
+ free (mime_magic_matchlet->value);
+ if (mime_magic_matchlet->mask)
+ free (mime_magic_matchlet->mask);
+ free (mime_magic_matchlet);
+ }
+}
+
+
+
+/* Reads in a hunk of data until a newline character or a '\000' is hit. The
+ * returned string is null terminated, and doesn't include the newline.
+ */
+static unsigned char *
+_xdg_mime_magic_read_to_newline (FILE *magic_file,
+ int *end_of_file)
+{
+ unsigned char *retval;
+ int c;
+ int len, pos;
+
+ len = 128;
+ pos = 0;
+ retval = malloc (len);
+ *end_of_file = FALSE;
+
+ while (TRUE)
+ {
+ c = fgetc (magic_file);
+ if (c == EOF)
+ {
+ *end_of_file = TRUE;
+ break;
+ }
+ if (c == '\n' || c == '\000')
+ break;
+ retval[pos++] = (unsigned char) c;
+ if (pos % 128 == 127)
+ {
+ len = len + 128;
+ retval = realloc (retval, len);
+ }
+ }
+
+ retval[pos] = '\000';
+ return retval;
+}
+
+/* Returns the number read from the file, or -1 if no number could be read.
+ */
+static int
+_xdg_mime_magic_read_a_number (FILE *magic_file,
+ int *end_of_file)
+{
+ /* LONG_MAX is about 20 characters on my system */
+#define MAX_NUMBER_SIZE 30
+ char number_string[MAX_NUMBER_SIZE];
+ int pos = 0;
+ int c;
+ int retval = -1;
+
+ while (TRUE)
+ {
+ c = fgetc (magic_file);
+
+ if (c == EOF)
+ {
+ *end_of_file = TRUE;
+ break;
+ }
+ if (! isdigit ((char) c))
+ {
+ ungetc (c, magic_file);
+ break;
+ }
+ number_string[pos] = (char) c;
+ pos++;
+ if (pos == MAX_NUMBER_SIZE)
+ break;
+ }
+ if (pos > 0)
+ {
+ number_string[pos] = '\000';
+ retval = strtol (number_string, NULL, 10);
+ if ((retval == LONG_MIN || retval == LONG_MAX) &&
+ (errno == ERANGE))
+ return -1;
+ }
+
+ return retval;
+}
+
+/* Headers are of the format:
+ * [<priority>:<mime-type>]
+ */
+static XdgMimeMagicState
+_xdg_mime_magic_parse_header (FILE *magic_file, XdgMimeMagicMatch *match)
+{
+ int c;
+ char *buffer;
+ char *end_ptr;
+ int end_of_file = 0;
+
+ assert (magic_file);
+ assert (match);
+
+ c = fgetc (magic_file);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+ if (c != '[')
+ return XDG_MIME_MAGIC_ERROR;
+
+ match->priority = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+ if (end_of_file)
+ return XDG_MIME_MAGIC_EOF;
+ if (match->priority == -1)
+ return XDG_MIME_MAGIC_ERROR;
+
+ c = fgetc (magic_file);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+ if (c != ':')
+ return XDG_MIME_MAGIC_ERROR;
+
+ buffer = _xdg_mime_magic_read_to_newline (magic_file, &end_of_file);
+ if (end_of_file)
+ return XDG_MIME_MAGIC_EOF;
+
+ end_ptr = buffer;
+ while (*end_ptr != ']' && *end_ptr != '\000' && *end_ptr != '\n')
+ end_ptr++;
+ if (*end_ptr != ']')
+ {
+ free (buffer);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ *end_ptr = '\000';
+
+ match->mime_type = strdup (buffer);
+ free (buffer);
+
+ return XDG_MIME_MAGIC_MAGIC;
+}
+
+static XdgMimeMagicState
+_xdg_mime_magic_parse_error (FILE *magic_file)
+{
+ int c;
+
+ while (1)
+ {
+ c = fgetc (magic_file);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+ if (c == '\n')
+ return XDG_MIME_MAGIC_SECTION;
+ }
+}
+
+/* Headers are of the format:
+ * [ indent ] ">" start-offset "=" value
+ * [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n"
+ */
+static XdgMimeMagicState
+_xdg_mime_magic_parse_magic_line (FILE *magic_file,
+ XdgMimeMagicMatch *match)
+{
+ XdgMimeMagicMatchlet *matchlet;
+ int c;
+ int end_of_file;
+ int indent = 0;
+ int bytes_read;
+
+ assert (magic_file);
+
+ /* Sniff the buffer to make sure it's a valid line */
+ c = fgetc (magic_file);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+ else if (c == '[')
+ {
+ ungetc (c, magic_file);
+ return XDG_MIME_MAGIC_SECTION;
+ }
+ else if (c == '\n')
+ return XDG_MIME_MAGIC_MAGIC;
+
+ /* At this point, it must be a digit or a '>' */
+ end_of_file = FALSE;
+ if (isdigit (c))
+ {
+ ungetc (c, magic_file);
+ indent = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+ if (end_of_file)
+ return XDG_MIME_MAGIC_EOF;
+ if (indent == -1)
+ return XDG_MIME_MAGIC_ERROR;
+ c = fgetc (magic_file);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+ }
+
+ if (c != '>')
+ return XDG_MIME_MAGIC_ERROR;
+
+ matchlet = _xdg_mime_magic_matchlet_new ();
+ matchlet->indent = indent;
+ matchlet->offset = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+ if (end_of_file)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ if (matchlet->offset == -1)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ c = fgetc (magic_file);
+ if (c == EOF)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ else if (c != '=')
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+
+ /* Next two bytes determine how long the value is */
+ matchlet->value_length = 0;
+ c = fgetc (magic_file);
+ if (c == EOF)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ matchlet->value_length = c & 0xFF;
+ matchlet->value_length = matchlet->value_length << 8;
+
+ c = fgetc (magic_file);
+ if (c == EOF)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ matchlet->value_length = matchlet->value_length + (c & 0xFF);
+
+ matchlet->value = malloc (matchlet->value_length);
+
+ /* OOM */
+ if (matchlet->value == NULL)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ bytes_read = fread (matchlet->value, 1, matchlet->value_length, magic_file);
+ if (bytes_read != matchlet->value_length)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ if (feof (magic_file))
+ return XDG_MIME_MAGIC_EOF;
+ else
+ return XDG_MIME_MAGIC_ERROR;
+ }
+
+ c = fgetc (magic_file);
+ if (c == '&')
+ {
+ matchlet->mask = malloc (matchlet->value_length);
+ /* OOM */
+ if (matchlet->mask == NULL)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ bytes_read = fread (matchlet->mask, 1, matchlet->value_length, magic_file);
+ if (bytes_read != matchlet->value_length)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ if (feof (magic_file))
+ return XDG_MIME_MAGIC_EOF;
+ else
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ c = fgetc (magic_file);
+ }
+
+ if (c == '~')
+ {
+ matchlet->word_size = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+ if (end_of_file)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ if (matchlet->word_size != 0 &&
+ matchlet->word_size != 1 &&
+ matchlet->word_size != 2 &&
+ matchlet->word_size != 4)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ c = fgetc (magic_file);
+ }
+
+ if (c == '+')
+ {
+ matchlet->range_length = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+ if (end_of_file)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ if (matchlet->range_length == -1)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ c = fgetc (magic_file);
+ }
+
+
+ if (c == '\n')
+ {
+ /* We clean up the matchlet, byte swapping if needed */
+ if (matchlet->word_size > 1)
+ {
+ int i;
+ if (matchlet->value_length % matchlet->word_size != 0)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ /* FIXME: need to get this defined in a <config.h> style file */
+#if LITTLE_ENDIAN
+ for (i = 0; i < matchlet->value_length; i = i + matchlet->word_size)
+ {
+ if (matchlet->word_size == 2)
+ *((xdg_uint16_t *) matchlet->value + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->value + i)));
+ else if (matchlet->word_size == 4)
+ *((xdg_uint32_t *) matchlet->value + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->value + i)));
+ if (matchlet->mask)
+ {
+ if (matchlet->word_size == 2)
+ *((xdg_uint16_t *) matchlet->mask + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->mask + i)));
+ else if (matchlet->word_size == 4)
+ *((xdg_uint32_t *) matchlet->mask + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->mask + i)));
+
+ }
+ }
+#endif
+ }
+
+ matchlet->next = match->matchlet;
+ match->matchlet = matchlet;
+
+
+ return XDG_MIME_MAGIC_MAGIC;
+ }
+
+ _xdg_mime_magic_matchlet_free (matchlet);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+
+ return XDG_MIME_MAGIC_ERROR;
+}
+
+static int
+_xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet,
+ const void *data,
+ size_t len)
+{
+ int i, j;
+
+ for (i = matchlet->offset; i <= matchlet->offset + matchlet->range_length; i++)
+ {
+ int valid_matchlet = TRUE;
+
+ if (i + matchlet->value_length > len)
+ return FALSE;
+
+ if (matchlet->mask)
+ {
+ for (j = 0; j < matchlet->value_length; j++)
+ {
+ if ((matchlet->value[j] & matchlet->mask[j]) !=
+ ((((unsigned char *) data)[j + i]) & matchlet->mask[j]))
+ {
+ valid_matchlet = FALSE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (j = 0; j < matchlet->value_length; j++)
+ {
+ if (matchlet->value[j] != ((unsigned char *) data)[j + i])
+ {
+ valid_matchlet = FALSE;
+ break;
+ }
+ }
+ }
+ if (valid_matchlet)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+_xdg_mime_magic_matchlet_compare_level (XdgMimeMagicMatchlet *matchlet,
+ const void *data,
+ size_t len,
+ int indent)
+{
+ while (matchlet != NULL && matchlet->indent == indent)
+ {
+ if (_xdg_mime_magic_matchlet_compare_to_data (matchlet, data, len))
+ {
+ if (matchlet->next == NULL ||
+ matchlet->next->indent <= indent)
+ return TRUE;
+
+ if (_xdg_mime_magic_matchlet_compare_level (matchlet->next,
+ data,
+ len,
+ indent + 1))
+ return TRUE;
+ }
+
+ do
+ {
+ matchlet = matchlet->next;
+ }
+ while (matchlet && matchlet->indent > indent);
+ }
+
+ return FALSE;
+}
+
+static int
+_xdg_mime_magic_match_compare_to_data (XdgMimeMagicMatch *match,
+ const void *data,
+ size_t len)
+{
+ return _xdg_mime_magic_matchlet_compare_level (match->matchlet, data, len, 0);
+}
+
+static void
+_xdg_mime_magic_insert_match (XdgMimeMagic *mime_magic,
+ XdgMimeMagicMatch *match)
+{
+ XdgMimeMagicMatch *list;
+
+ if (mime_magic->match_list == NULL)
+ {
+ mime_magic->match_list = match;
+ return;
+ }
+
+ if (match->priority < mime_magic->match_list->priority)
+ {
+ match->next = mime_magic->match_list;
+ mime_magic->match_list = match;
+ return;
+ }
+
+ list = mime_magic->match_list;
+ while (list->next != NULL)
+ {
+ if (list->next->priority > match->priority)
+ {
+ match->next = list->next;
+ list->next = match;
+ return;
+ }
+ list = list->next;
+ }
+ list->next = match;
+ match->next = NULL;
+}
+
+XdgMimeMagic *
+_xdg_mime_magic_new (void)
+{
+ return calloc (1, sizeof (XdgMimeMagic));
+}
+
+void
+_xdg_mime_magic_free (XdgMimeMagic *mime_magic)
+{
+ if (mime_magic)
+ free (mime_magic);
+}
+
+int
+_xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic)
+{
+ return mime_magic->max_extent;
+}
+
+const char *
+_xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic,
+ const void *data,
+ size_t len)
+{
+ XdgMimeMagicMatch *match;
+
+ for (match = mime_magic->match_list; match; match = match->next)
+ {
+ if (_xdg_mime_magic_match_compare_to_data (match, data, len))
+ {
+ return match->mime_type;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+_xdg_mime_update_mime_magic_extents (XdgMimeMagic *mime_magic)
+{
+ XdgMimeMagicMatch *match;
+ int max_extent = 0;
+
+ for (match = mime_magic->match_list; match; match = match->next)
+ {
+ XdgMimeMagicMatchlet *matchlet;
+
+ for (matchlet = match->matchlet; matchlet; matchlet = matchlet->next)
+ {
+ int extent;
+
+ extent = matchlet->value_length + matchlet->offset + matchlet->range_length;
+ if (max_extent < extent)
+ max_extent = extent;
+ }
+ }
+
+ mime_magic->max_extent = max_extent;
+}
+
+static void
+_xdg_mime_magic_read_magic_file (XdgMimeMagic *mime_magic,
+ FILE *magic_file)
+{
+ XdgMimeMagicState state;
+ XdgMimeMagicMatch *match = NULL; /* Quiet compiler */
+
+ state = XDG_MIME_MAGIC_SECTION;
+
+ while (state != XDG_MIME_MAGIC_EOF)
+ {
+ switch (state)
+ {
+ case XDG_MIME_MAGIC_SECTION:
+ match = _xdg_mime_magic_match_new ();
+ state = _xdg_mime_magic_parse_header (magic_file, match);
+ if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
+ _xdg_mime_magic_match_free (match);
+ break;
+ case XDG_MIME_MAGIC_MAGIC:
+ state = _xdg_mime_magic_parse_magic_line (magic_file, match);
+ if (state == XDG_MIME_MAGIC_SECTION)
+ _xdg_mime_magic_insert_match (mime_magic, match);
+ else if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
+ _xdg_mime_magic_match_free (match);
+ break;
+ case XDG_MIME_MAGIC_ERROR:
+ state = _xdg_mime_magic_parse_error (magic_file);
+ break;
+ case XDG_MIME_MAGIC_EOF:
+ default:
+ /* Make the compiler happy */
+ assert (0);
+ }
+ }
+ _xdg_mime_update_mime_magic_extents (mime_magic);
+}
+
+void
+_xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic,
+ const char *file_name)
+{
+ FILE *magic_file;
+ char header[12];
+
+ magic_file = fopen (file_name, "r");
+
+ if (magic_file == NULL)
+ return;
+
+ fread (header, 1, 12, magic_file);
+
+ if (memcmp ("MIME-Magic\0\n", header, 12) == 0)
+ _xdg_mime_magic_read_magic_file (mime_magic, magic_file);
+ fclose (magic_file);
+}
diff --git a/gtk/xdgmime/xdgmimemagic.h b/gtk/xdgmime/xdgmimemagic.h
new file mode 100644
index 0000000000..986518c105
--- /dev/null
+++ b/gtk/xdgmime/xdgmimemagic.h
@@ -0,0 +1,43 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimemagic.h: Private file. Datastructure for storing the globs.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __XDG_MIME_MAGIC_H__
+#define __XDG_MIME_MAGIC_H__
+
+#include <unistd.h>
+
+typedef struct XdgMimeMagic XdgMimeMagic;
+
+XdgMimeMagic *_xdg_mime_magic_new (void);
+void _xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic,
+ const char *file_name);
+void _xdg_mime_magic_free (XdgMimeMagic *mime_magic);
+int _xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic);
+const char *_xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic,
+ const void *data,
+ size_t len);
+
+#endif /* __XDG_MIME_MAGIC_H__ */
diff --git a/tests/testfilechooser.c b/tests/testfilechooser.c
index cec922e5e2..95696dbb8e 100644
--- a/tests/testfilechooser.c
+++ b/tests/testfilechooser.c
@@ -56,9 +56,9 @@ main (int argc, char **argv)
gtk_init (&argc, &argv);
#ifdef USE_GNOME_VFS
- file_system = _gtk_file_system_gnome_vfs_new ();
+ file_system = gtk_file_system_gnome_vfs_new ();
#else
- file_system = _gtk_file_system_unix_new ();
+ file_system = gtk_file_system_unix_new ();
#endif
dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,