| /* 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 <alexl@redhat.com> |
| */ |
| |
| #include "config.h" |
| |
| #include <string.h> |
| |
| #include "gresource.h" |
| #include "gresourcefile.h" |
| #include "gfileattribute.h" |
| #include <gfileattribute-priv.h> |
| #include <gfileinfo-priv.h> |
| #include "gfile.h" |
| #include "gseekable.h" |
| #include "gfileinputstream.h" |
| #include "gfileinfo.h" |
| #include "gfileenumerator.h" |
| #include "gcontenttype.h" |
| #include "gioerror.h" |
| #include <glib/gstdio.h> |
| #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; |
| |
| static GType _g_resource_file_enumerator_get_type (void); |
| |
| #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); |
| |
| 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_display_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); |
| } |