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 #include #include #include #include +#include #include #include #include @@ -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) { @@ -946,6 +964,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, 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 + #include "gtkfilesystem.h" #include @@ -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) -{ - g_return_val_if_fail (info != NULL, NULL); - - return info->icon; -} - void -gtk_file_info_set_icon (GtkFileInfo *info, - GdkPixbuf *icon) +gtk_file_info_set_icon_type (GtkFileInfo *info, + GtkFileIconType icon_type) { g_return_if_fail (info != NULL); - g_return_if_fail (icon == NULL || GDK_IS_PIXBUF (icon)); - if (icon != info->icon) + info->icon_type = icon_type; +} + +GtkFileIconType +gtk_file_info_get_icon_type (const GtkFileInfo *info) +{ + g_return_val_if_fail (info != NULL, GTK_FILE_ICON_REGULAR); + + return info->icon_type; +} + +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) { - if (info->icon) - g_object_unref (info->icon); - - info->icon = icon; + cache = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify)g_free, + (GDestroyNotify)icon_cache_element_free); - if (info->icon) - g_object_ref (info->icon); + 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) + { + 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); + } + + 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 */ + + 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 -#include +#include /* 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 #include #include @@ -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) { - gtk_file_info_set_mime_type (info, "application/octet-stream"); + 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)) + { + 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 +#include + + +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 + * + * 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 +#include +#include +#include +#include + +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 + * + * 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 + +#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 + * + * 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 +#include +#include +#include +#include + +#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 + * + * 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 +#include + +#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 + * + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#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: + * [:] + */ +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 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 + * + * 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 + +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,