/* 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_literal (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_literal (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); }