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.
This commit is contained in:
Owen Taylor 2003-07-16 21:07:38 +00:00 committed by Owen Taylor
parent bc8567d488
commit 7e54248bc3
20 changed files with 2103 additions and 53 deletions

View File

@ -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)
{
@ -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,

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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)
{
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);
}
/*****************************************

View File

@ -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
*/

View File

@ -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

View File

@ -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)
{
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;
}

View File

@ -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

15
gtk/xdgmime/Makefile.am Normal file
View File

@ -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

53
gtk/xdgmime/test-mime.c Normal file
View File

@ -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;
}

235
gtk/xdgmime/xdgmime.c Normal file
View File

@ -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);
}

60
gtk/xdgmime/xdgmime.h Normal file
View File

@ -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__ */

465
gtk/xdgmime/xdgmimeglob.c Normal file
View File

@ -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);
}

53
gtk/xdgmime/xdgmimeglob.h Normal file
View File

@ -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__ */

124
gtk/xdgmime/xdgmimeint.c Normal file
View File

@ -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;
}

54
gtk/xdgmime/xdgmimeint.h Normal file
View File

@ -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__ */

733
gtk/xdgmime/xdgmimemagic.c Normal file
View File

@ -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);
}

View File

@ -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__ */

View File

@ -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,