diff --git a/gio/Makefile.am b/gio/Makefile.am index da4f0c201..0afd64ede 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -385,6 +385,8 @@ libgio_2_0_la_SOURCES = \ gproxyresolver.c \ gresolver.c \ gresource.c \ + gresourcefile.c \ + gresourcefile.h \ gseekable.c \ gsimpleasyncresult.c \ gsimplepermission.c \ diff --git a/gio/gresourcefile.c b/gio/gresourcefile.c new file mode 100644 index 000000000..829932788 --- /dev/null +++ b/gio/gresourcefile.c @@ -0,0 +1,875 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson + */ + +#include "config.h" + +#include + +#include "gresource.h" +#include "gresourcefile.h" +#include "gfileattribute.h" +#include +#include +#include "gfile.h" +#include "gseekable.h" +#include "gfileinputstream.h" +#include "gfileinfo.h" +#include "gfileenumerator.h" +#include "gcontenttype.h" +#include "gioerror.h" +#include +#include "glibintl.h" + +struct _GResourceFile +{ + GObject parent_instance; + + char *path; +}; + +struct _GResourceFileEnumerator +{ + GFileEnumerator parent; + + GFileAttributeMatcher *matcher; + char *path; + char *attributes; + GFileQueryInfoFlags flags; + int index; + + char **children; +}; + +struct _GResourceFileEnumeratorClass +{ + GFileEnumeratorClass parent_class; +}; + +typedef struct _GResourceFileEnumerator GResourceFileEnumerator; +typedef struct _GResourceFileEnumeratorClass GResourceFileEnumeratorClass; + +static void g_resource_file_file_iface_init (GFileIface *iface); + +static GFileAttributeInfoList *resource_writable_attributes = NULL; +static GFileAttributeInfoList *resource_writable_namespaces = NULL; + +#define G_TYPE_RESOURCE_FILE_ENUMERATOR (_g_resource_file_enumerator_get_type ()) +#define G_RESOURCE_FILE_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumerator)) +#define G_RESOURCE_FILE_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumeratorClass)) +#define G_IS_RESOURCE_FILE_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR)) +#define G_IS_RESOURCE_FILE_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOURCE_FILE_ENUMERATOR)) +#define G_RESOURCE_FILE_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumeratorClass)) + +#define G_TYPE_RESOURCE_FILE_INPUT_STREAM (_g_resource_file_input_stream_get_type ()) +#define G_RESOURCE_FILE_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStream)) +#define G_RESOURCE_FILE_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStreamClass)) +#define G_IS_RESOURCE_FILE_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM)) +#define G_IS_RESOURCE_FILE_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOURCE_FILE_INPUT_STREAM)) +#define G_RESOURCE_FILE_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStreamClass)) + +typedef struct _GResourceFileInputStream GResourceFileInputStream; +typedef struct _GResourceFileInputStreamClass GResourceFileInputStreamClass; + +#define g_resource_file_get_type _g_resource_file_get_type +G_DEFINE_TYPE_WITH_CODE (GResourceFile, g_resource_file, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_FILE, + g_resource_file_file_iface_init)) + +#define g_resource_file_enumerator_get_type _g_resource_file_enumerator_get_type +G_DEFINE_TYPE (GResourceFileEnumerator, g_resource_file_enumerator, G_TYPE_FILE_ENUMERATOR); + +static GFileEnumerator *_g_resource_file_enumerator_new (GResourceFile *file, + const char *attributes, + GFileQueryInfoFlags flags, + GCancellable *cancellable, + GError **error); + + +static GType _g_resource_file_input_stream_get_type (void) G_GNUC_CONST; + +static GFileInputStream *_g_resource_file_input_stream_new (GInputStream *stream, GFile *file); + + +static void +g_resource_file_finalize (GObject *object) +{ + GResourceFile *resource; + + resource = G_RESOURCE_FILE (object); + + g_free (resource->path); + + G_OBJECT_CLASS (g_resource_file_parent_class)->finalize (object); +} + +static void +g_resource_file_class_init (GResourceFileClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_resource_file_finalize; + + resource_writable_attributes = g_file_attribute_info_list_new (); + resource_writable_namespaces = g_file_attribute_info_list_new (); +} + +static void +g_resource_file_init (GResourceFile *resource) +{ +} + +static char * +canonicalize_filename (const char *filename) +{ + char *canon, *start, *p, *q; + + /* Skip multiple inital slashes */ + while (filename[0] == '/' && filename[1] == '/') + filename++; + + if (*filename != '/') + canon = g_strconcat ("/", filename, NULL); + else + canon = g_strdup (filename); + + start = canon + 1; + + p = start; + while (*p != 0) + { + if (p[0] == '.' && (p[1] == 0 || p[1] == '/')) + { + memmove (p, p+1, strlen (p+1)+1); + } + else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || p[2] == '/')) + { + q = p + 2; + /* Skip previous separator */ + p = p - 2; + if (p < start) + p = start; + while (p > start && *p != '/') + p--; + if (*p == '/') + *p++ = '/'; + memmove (p, q, strlen (q)+1); + } + else + { + /* Skip until next separator */ + while (*p != 0 && *p != '/') + p++; + + if (*p != 0) + { + /* Canonicalize one separator */ + *p++ = '/'; + } + } + + /* Remove additional separators */ + q = p; + while (*q && *q == '/') + q++; + + if (p != q) + memmove (p, q, strlen (q)+1); + } + + /* Remove trailing slashes */ + if (p > start && *(p-1) == '/') + *(p-1) = 0; + + return canon; +} + +static GFile * +g_resource_file_new_for_path (const char *path) +{ + GResourceFile *resource = g_object_new (G_TYPE_RESOURCE_FILE, NULL); + + resource->path = canonicalize_filename (path); + + return G_FILE (resource); +} + +GFile * +_g_resource_file_new (const char *uri) +{ + GFile *resource; + char *path; + + path = g_uri_unescape_string (uri + strlen ("resource:"), NULL); + resource = g_resource_file_new_for_path (path); + g_free (path); + + return G_FILE (resource); +} + +static gboolean +g_resource_file_is_native (GFile *file) +{ + return FALSE; +} + +static gboolean +g_resource_file_has_uri_scheme (GFile *file, + const char *uri_scheme) +{ + return g_ascii_strcasecmp (uri_scheme, "resource") == 0; +} + +static char * +g_resource_file_get_uri_scheme (GFile *file) +{ + return g_strdup ("resource"); +} + +static char * +g_resource_file_get_basename (GFile *file) +{ + gchar *base; + + base = strrchr (G_RESOURCE_FILE (file)->path, '/'); + return g_strdup (base + 1); +} + +static char * +g_resource_file_get_path (GFile *file) +{ + return NULL; +} + +static char * +g_resource_file_get_uri (GFile *file) +{ + char *escaped, *res; + escaped = g_uri_escape_string (G_RESOURCE_FILE (file)->path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE); + res = g_strconcat ("resource://", escaped, NULL); + g_free (escaped); + return res; +} + +static char * +g_resource_file_get_parse_name (GFile *file) +{ + return g_resource_file_get_uri (file); +} + +static GFile * +g_resource_file_get_parent (GFile *file) +{ + GResourceFile *resource = G_RESOURCE_FILE (file); + GResourceFile *parent; + gchar *end; + + end = strrchr (resource->path, '/'); + + if (end == G_RESOURCE_FILE (file)->path) + return NULL; + + parent = g_object_new (G_TYPE_RESOURCE_FILE, NULL); + parent->path = g_strndup (resource->path, + end - resource->path - 1); + + return G_FILE (parent); +} + +static GFile * +g_resource_file_dup (GFile *file) +{ + GResourceFile *resource = G_RESOURCE_FILE (file); + + return g_resource_file_new_for_path (resource->path); +} + +static guint +g_resource_file_hash (GFile *file) +{ + GResourceFile *resource = G_RESOURCE_FILE (file); + + return g_str_hash (resource->path); +} + +static gboolean +g_resource_file_equal (GFile *file1, + GFile *file2) +{ + GResourceFile *resource1 = G_RESOURCE_FILE (file1); + GResourceFile *resource2 = G_RESOURCE_FILE (file2); + + return g_str_equal (resource1->path, resource2->path); +} + +static const char * +match_prefix (const char *path, + const char *prefix) +{ + int prefix_len; + + prefix_len = strlen (prefix); + if (strncmp (path, prefix, prefix_len) != 0) + return NULL; + + /* Handle the case where prefix is the root, so that + * the IS_DIR_SEPRARATOR check below works */ + if (prefix_len > 0 && + prefix[prefix_len-1] == '/') + prefix_len--; + + return path + prefix_len; +} + +static gboolean +g_resource_file_prefix_matches (GFile *parent, + GFile *descendant) +{ + GResourceFile *parent_resource = G_RESOURCE_FILE (parent); + GResourceFile *descendant_resource = G_RESOURCE_FILE (descendant); + const char *remainder; + + remainder = match_prefix (descendant_resource->path, parent_resource->path); + if (remainder != NULL && *remainder == '/') + return TRUE; + return FALSE; +} + +static char * +g_resource_file_get_relative_path (GFile *parent, + GFile *descendant) +{ + GResourceFile *parent_resource = G_RESOURCE_FILE (parent); + GResourceFile *descendant_resource = G_RESOURCE_FILE (descendant); + const char *remainder; + + remainder = match_prefix (descendant_resource->path, parent_resource->path); + + if (remainder != NULL && *remainder == '/') + return g_strdup (remainder + 1); + return NULL; +} + +static GFile * +g_resource_file_resolve_relative_path (GFile *file, + const char *relative_path) +{ + GResourceFile *resource = G_RESOURCE_FILE (file); + char *filename; + GFile *child; + + if (relative_path[0] == '/') + return g_resource_file_new_for_path (relative_path); + + filename = g_build_path ("/", resource->path, relative_path, NULL); + child = g_resource_file_new_for_path (filename); + g_free (filename); + + return child; +} + +static GFileEnumerator * +g_resource_file_enumerate_children (GFile *file, + const char *attributes, + GFileQueryInfoFlags flags, + GCancellable *cancellable, + GError **error) +{ + GResourceFile *resource = G_RESOURCE_FILE (file); + return _g_resource_file_enumerator_new (resource, + attributes, flags, + cancellable, error); +} + +static GFile * +g_resource_file_get_child_for_display_name (GFile *file, + const char *display_name, + GError **error) +{ + GFile *new_file; + + new_file = g_file_get_child (file, display_name); + + return new_file; +} + +static GFileInfo * +g_resource_file_query_info (GFile *file, + const char *attributes, + GFileQueryInfoFlags flags, + GCancellable *cancellable, + GError **error) +{ + GResourceFile *resource = G_RESOURCE_FILE (file); + GError *my_error = NULL; + GFileInfo *info; + GFileAttributeMatcher *matcher; + gboolean res; + gsize size; + guint32 resource_flags; + char **children; + gboolean is_dir; + char *base; + + is_dir = FALSE; + children = g_resources_enumerate_children (resource->path, 0, NULL); + if (children != NULL) + { + g_strfreev (children); + is_dir = TRUE; + } + + /* root is always there */ + if (strcmp ("/", resource->path) == 0) + is_dir = TRUE; + + if (!is_dir) + { + res = g_resources_get_info (resource->path, 0, &size, &resource_flags, &my_error); + if (!res) + { + if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("The resource at '%s' does not exist"), + resource->path); + } + else + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + my_error->message); + g_clear_error (&my_error); + return FALSE; + } + } + + matcher = g_file_attribute_matcher_new (attributes); + + info = g_file_info_new (); + base = g_resource_file_get_basename (file); + g_file_info_set_name (info, base); + + _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ, TRUE); + _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE, FALSE); + _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE, FALSE); + _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME, FALSE); + _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE, FALSE); + _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH, FALSE); + + if (is_dir) + { + g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); + } + else + { + GBytes *bytes; + char *content_type; + + g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR); + g_file_info_set_size (info, size); + + if ((_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) || + ((~resource_flags & G_RESOURCE_FLAGS_COMPRESSED) && + _g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))) && + (bytes = g_resources_lookup_data (resource->path, 0, NULL))) + { + const guchar *data; + gsize data_size; + + data = g_bytes_get_data (bytes, &data_size); + content_type = g_content_type_guess (base, data, data_size, NULL); + + g_bytes_unref (bytes); + } + else + content_type = NULL; + + if (content_type) + { + _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE, content_type); + _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, content_type); + + g_free (content_type); + } + } + + g_free (base); + g_file_attribute_matcher_unref (matcher); + + return info; +} + +static GFileAttributeInfoList * +g_resource_file_query_settable_attributes (GFile *file, + GCancellable *cancellable, + GError **error) +{ + return g_file_attribute_info_list_ref (resource_writable_attributes); +} + +static GFileAttributeInfoList * +g_resource_file_query_writable_namespaces (GFile *file, + GCancellable *cancellable, + GError **error) +{ + return g_file_attribute_info_list_ref (resource_writable_namespaces); +} + +static GFileInputStream * +g_resource_file_read (GFile *file, + GCancellable *cancellable, + GError **error) +{ + GResourceFile *resource = G_RESOURCE_FILE (file); + GError *my_error = NULL; + GInputStream *stream; + GFileInputStream *res; + + stream = g_resources_open_stream (resource->path, 0, &my_error); + + if (stream == NULL) + { + if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("The resource at '%s' does not exist"), + resource->path); + } + else + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + my_error->message); + g_clear_error (&my_error); + return NULL; + } + + res = _g_resource_file_input_stream_new (stream, file); + g_object_unref (stream); + return res; +} + +static void +g_resource_file_file_iface_init (GFileIface *iface) +{ + iface->dup = g_resource_file_dup; + iface->hash = g_resource_file_hash; + iface->equal = g_resource_file_equal; + iface->is_native = g_resource_file_is_native; + iface->has_uri_scheme = g_resource_file_has_uri_scheme; + iface->get_uri_scheme = g_resource_file_get_uri_scheme; + iface->get_basename = g_resource_file_get_basename; + iface->get_path = g_resource_file_get_path; + iface->get_uri = g_resource_file_get_uri; + iface->get_parse_name = g_resource_file_get_parse_name; + iface->get_parent = g_resource_file_get_parent; + iface->prefix_matches = g_resource_file_prefix_matches; + iface->get_relative_path = g_resource_file_get_relative_path; + iface->resolve_relative_path = g_resource_file_resolve_relative_path; + iface->get_child_for_display_name = g_resource_file_get_child_for_display_name; + iface->enumerate_children = g_resource_file_enumerate_children; + iface->query_info = g_resource_file_query_info; + iface->query_settable_attributes = g_resource_file_query_settable_attributes; + iface->query_writable_namespaces = g_resource_file_query_writable_namespaces; + iface->read_fn = g_resource_file_read; + + iface->supports_thread_contexts = TRUE; +} + +static GFileInfo *g_resource_file_enumerator_next_file (GFileEnumerator *enumerator, + GCancellable *cancellable, + GError **error); +static gboolean g_resource_file_enumerator_close (GFileEnumerator *enumerator, + GCancellable *cancellable, + GError **error); + +static void +g_resource_file_enumerator_finalize (GObject *object) +{ + GResourceFileEnumerator *resource; + + resource = G_RESOURCE_FILE_ENUMERATOR (object); + + g_strfreev (resource->children); + g_free (resource->path); + g_free (resource->attributes); + + G_OBJECT_CLASS (g_resource_file_enumerator_parent_class)->finalize (object); +} + +static void +g_resource_file_enumerator_class_init (GResourceFileEnumeratorClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass); + + gobject_class->finalize = g_resource_file_enumerator_finalize; + + enumerator_class->next_file = g_resource_file_enumerator_next_file; + enumerator_class->close_fn = g_resource_file_enumerator_close; +} + +static void +g_resource_file_enumerator_init (GResourceFileEnumerator *resource) +{ +} + +static GFileEnumerator * +_g_resource_file_enumerator_new (GResourceFile *file, + const char *attributes, + GFileQueryInfoFlags flags, + GCancellable *cancellable, + GError **error) +{ + GResourceFileEnumerator *resource; + char **children; + gboolean res; + + children = g_resources_enumerate_children (file->path, 0, NULL); + if (children == NULL && + strcmp ("/", file->path) != 0) + { + res = g_resources_get_info (file->path, 0, NULL, NULL, NULL); + if (res) + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, + _("The resource at '%s' is not a directory"), + file->path); + else + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("The resource at '%s' does not exist"), + file->path); + return NULL; + } + + resource = g_object_new (G_TYPE_RESOURCE_FILE_ENUMERATOR, + "container", file, + NULL); + + resource->children = children; + resource->path = g_strdup (file->path); + resource->attributes = g_strdup (attributes); + resource->flags = flags; + + return G_FILE_ENUMERATOR (resource); +} + +static GFileInfo * +g_resource_file_enumerator_next_file (GFileEnumerator *enumerator, + GCancellable *cancellable, + GError **error) +{ + GResourceFileEnumerator *resource = G_RESOURCE_FILE_ENUMERATOR (enumerator); + char *path; + GFileInfo *info; + GFile *file; + + if (resource->children == NULL || + resource->children[resource->index] == NULL) + return NULL; + + path = g_build_path ("/", resource->path, resource->children[resource->index++], NULL); + file = g_resource_file_new_for_path (path); + g_free (path); + + info = g_file_query_info (file, + resource->attributes, + resource->flags, + cancellable, + error); + + g_object_unref (file); + + return info; +} + +static gboolean +g_resource_file_enumerator_close (GFileEnumerator *enumerator, + GCancellable *cancellable, + GError **error) +{ + return TRUE; +} + + +struct _GResourceFileInputStream +{ + GFileInputStream parent_instance; + GInputStream *stream; + GFile *file; +}; + +struct _GResourceFileInputStreamClass +{ + GFileInputStreamClass parent_class; +}; + +#define g_resource_file_input_stream_get_type _g_resource_file_input_stream_get_type +G_DEFINE_TYPE (GResourceFileInputStream, g_resource_file_input_stream, G_TYPE_FILE_INPUT_STREAM); + +static gssize g_resource_file_input_stream_read (GInputStream *stream, + void *buffer, + gsize count, + GCancellable *cancellable, + GError **error); +static gssize g_resource_file_input_stream_skip (GInputStream *stream, + gsize count, + GCancellable *cancellable, + GError **error); +static gboolean g_resource_file_input_stream_close (GInputStream *stream, + GCancellable *cancellable, + GError **error); +static goffset g_resource_file_input_stream_tell (GFileInputStream *stream); +static gboolean g_resource_file_input_stream_can_seek (GFileInputStream *stream); +static gboolean g_resource_file_input_stream_seek (GFileInputStream *stream, + goffset offset, + GSeekType type, + GCancellable *cancellable, + GError **error); +static GFileInfo *g_resource_file_input_stream_query_info (GFileInputStream *stream, + const char *attributes, + GCancellable *cancellable, + GError **error); + +static void +g_resource_file_input_stream_finalize (GObject *object) +{ + GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (object); + + g_object_unref (file->stream); + g_object_unref (file->file); + G_OBJECT_CLASS (g_resource_file_input_stream_parent_class)->finalize (object); +} + +static void +g_resource_file_input_stream_class_init (GResourceFileInputStreamClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); + GFileInputStreamClass *file_stream_class = G_FILE_INPUT_STREAM_CLASS (klass); + + gobject_class->finalize = g_resource_file_input_stream_finalize; + + stream_class->read_fn = g_resource_file_input_stream_read; + stream_class->skip = g_resource_file_input_stream_skip; + stream_class->close_fn = g_resource_file_input_stream_close; + file_stream_class->tell = g_resource_file_input_stream_tell; + file_stream_class->can_seek = g_resource_file_input_stream_can_seek; + file_stream_class->seek = g_resource_file_input_stream_seek; + file_stream_class->query_info = g_resource_file_input_stream_query_info; +} + +static void +g_resource_file_input_stream_init (GResourceFileInputStream *info) +{ +} + +static GFileInputStream * +_g_resource_file_input_stream_new (GInputStream *in_stream, GFile *file) +{ + GResourceFileInputStream *stream; + + stream = g_object_new (G_TYPE_RESOURCE_FILE_INPUT_STREAM, NULL); + stream->stream = g_object_ref (in_stream); + stream->file = g_object_ref (file); + + return G_FILE_INPUT_STREAM (stream); +} + +static gssize +g_resource_file_input_stream_read (GInputStream *stream, + void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream); + return g_input_stream_read (file->stream, + buffer, count, cancellable, error); +} + +static gssize +g_resource_file_input_stream_skip (GInputStream *stream, + gsize count, + GCancellable *cancellable, + GError **error) +{ + GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream); + return g_input_stream_skip (file->stream, + count, cancellable, error); +} + +static gboolean +g_resource_file_input_stream_close (GInputStream *stream, + GCancellable *cancellable, + GError **error) +{ + GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream); + return g_input_stream_close (file->stream, + cancellable, error); +} + + +static goffset +g_resource_file_input_stream_tell (GFileInputStream *stream) +{ + GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);; + + if (!G_IS_SEEKABLE (file->stream)); + return 0; + + return g_seekable_tell (G_SEEKABLE (file->stream)); +} + +static gboolean +g_resource_file_input_stream_can_seek (GFileInputStream *stream) +{ + GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream); + + return G_IS_SEEKABLE (file->stream) && g_seekable_can_seek (G_SEEKABLE (file->stream)); +} + +static gboolean +g_resource_file_input_stream_seek (GFileInputStream *stream, + goffset offset, + GSeekType type, + GCancellable *cancellable, + GError **error) +{ + GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream); + + if (!G_IS_SEEKABLE (file->stream)) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Input stream doesn't implement seek")); + return FALSE; + } + + return g_seekable_seek (G_SEEKABLE (file->stream), + offset, type, cancellable, error); +} + +static GFileInfo * +g_resource_file_input_stream_query_info (GFileInputStream *stream, + const char *attributes, + GCancellable *cancellable, + GError **error) +{ + GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream); + + return g_file_query_info (file->file, attributes, 0, cancellable, error); +} diff --git a/gio/gresourcefile.h b/gio/gresourcefile.h new file mode 100644 index 000000000..2c792b00f --- /dev/null +++ b/gio/gresourcefile.h @@ -0,0 +1,51 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2011 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson + */ + +#ifndef __G_RESOURCE_FILE_H__ +#define __G_RESOURCE_FILE_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_RESOURCE_FILE (_g_resource_file_get_type ()) +#define G_RESOURCE_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_RESOURCE_FILE, GResourceFile)) +#define G_RESOURCE_FILE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_RESOURCE_FILE, GResourceFileClass)) +#define G_IS_RESOURCE_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_RESOURCE_FILE)) +#define G_IS_RESOURCE_FILE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOURCE_FILE)) +#define G_RESOURCE_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOURCE_FILE, GResourceFileClass)) + +typedef struct _GResourceFile GResourceFile; +typedef struct _GResourceFileClass GResourceFileClass; + +struct _GResourceFileClass +{ + GObjectClass parent_class; +}; + +GType _g_resource_file_get_type (void) G_GNUC_CONST; + +GFile * _g_resource_file_new (const char *uri); + +G_END_DECLS + +#endif /* __G_RESOURCE_FILE_H__ */ diff --git a/gio/gvfs.c b/gio/gvfs.c index cbe7f2f33..dda8afb73 100644 --- a/gio/gvfs.c +++ b/gio/gvfs.c @@ -24,6 +24,7 @@ #include #include "gvfs.h" #include "glocalvfs.h" +#include "gresourcefile.h" #include "giomodule-priv.h" #include "glibintl.h" @@ -119,6 +120,13 @@ g_vfs_get_file_for_uri (GVfs *vfs, class = G_VFS_GET_CLASS (vfs); + /* This is an unfortunate placement, but we really + need to check this before chaining to the vfs, + because we want to support resource uris for + all vfs:es, even those that predate resources. */ + if (g_str_has_prefix (uri, "resource:")) + return _g_resource_file_new (uri); + return (* class->get_file_for_uri) (vfs, uri); } @@ -167,6 +175,9 @@ g_vfs_parse_name (GVfs *vfs, class = G_VFS_GET_CLASS (vfs); + if (g_str_has_prefix (parse_name, "resource:")) + return _g_resource_file_new (parse_name); + return (* class->parse_name) (vfs, parse_name); } diff --git a/gio/tests/resources.c b/gio/tests/resources.c index ed1adc6e5..3da0e0a30 100644 --- a/gio/tests/resources.c +++ b/gio/tests/resources.c @@ -412,6 +412,45 @@ test_resource_module (void) } } +static void +test_uri_query_info (void) +{ + GResource *resource; + GError *error = NULL; + gboolean loaded_file; + char *content; + gsize content_size; + GBytes *data; + GFile *file; + GFileInfo *info; + const char *content_type; + + loaded_file = g_file_get_contents ("test.gresource", &content, &content_size, + NULL); + g_assert (loaded_file); + + data = g_bytes_new_take (content, content_size); + resource = g_resource_new_from_data (data, &error); + g_assert (resource != NULL); + g_assert_no_error (error); + + g_resources_register (resource); + + file = g_file_new_for_uri ("resource://" "/a_prefix/test2-alias.txt"); + + info = g_file_query_info (file, "*", 0, NULL, &error); + g_assert_no_error (error); + g_object_unref (file); + + content_type = g_file_info_get_content_type (info); + g_assert (content_type); + g_assert_cmpstr (content_type, ==, "text/plain"); + + g_object_unref (info); + + g_resources_unregister (resource); + g_resource_unref (resource); +} int main (int argc, @@ -431,6 +470,7 @@ main (int argc, /* This only uses automatic resources too, so it tests the constructors and destructors */ g_test_add_func ("/resource/module", test_resource_module); #endif + g_test_add_func ("/resource/uri/query-info", test_uri_query_info); return g_test_run(); }