| /* GIO - GLib Input, Output and Streaming Library |
| * |
| * Copyright (C) 2006-2007 Red Hat, Inc. |
| * |
| * SPDX-License-Identifier: LGPL-2.1-or-later |
| * |
| * 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.1 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, see <http://www.gnu.org/licenses/>. |
| * |
| * 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 "gfilemonitor.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 inline gchar * |
| scan_backwards (const gchar *begin, |
| const gchar *end, |
| gchar c) |
| { |
| while (end >= begin) |
| { |
| if (*end == c) |
| return (gchar *)end; |
| end--; |
| } |
| |
| return NULL; |
| } |
| |
| static inline void |
| pop_to_previous_part (const gchar *begin, |
| gchar **out) |
| { |
| if (*out > begin) |
| *out = scan_backwards (begin, *out - 1, '/'); |
| } |
| |
| /* |
| * canonicalize_filename: |
| * @in: the path to be canonicalized |
| * |
| * The path @in may contain non-canonical path pieces such as "../" |
| * or duplicated "/". This will resolve those into a form that only |
| * contains a single / at a time and resolves all "../". The resulting |
| * path must also start with a /. |
| * |
| * Returns: the canonical form of the path |
| */ |
| static char * |
| canonicalize_filename (const char *in) |
| { |
| gchar *bptr; |
| char *out; |
| |
| bptr = out = g_malloc (strlen (in) + 2); |
| *out = '/'; |
| |
| while (*in != 0) |
| { |
| g_assert (*out == '/'); |
| |
| /* move past slashes */ |
| while (*in == '/') |
| in++; |
| |
| /* Handle ./ ../ .\0 ..\0 */ |
| if (*in == '.') |
| { |
| /* If this is ../ or ..\0 move up */ |
| if (in[1] == '.' && (in[2] == '/' || in[2] == 0)) |
| { |
| pop_to_previous_part (bptr, &out); |
| in += 2; |
| continue; |
| } |
| |
| /* If this is ./ skip past it */ |
| if (in[1] == '/' || in[1] == 0) |
| { |
| in += 1; |
| continue; |
| } |
| } |
| |
| /* Scan to the next path piece */ |
| while (*in != 0 && *in != '/') |
| *(++out) = *(in++); |
| |
| /* Add trailing /, compress the rest on the next go round. */ |
| if (*in == '/') |
| *(++out) = *(in++); |
| } |
| |
| /* Trim trailing / from path */ |
| if (out > bptr && *out == '/') |
| *out = 0; |
| else |
| *(++out) = 0; |
| |
| return bptr; |
| } |
| |
| 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); |
| } |
| |
| /* Will return %NULL if @uri is malformed */ |
| GFile * |
| _g_resource_file_new (const char *uri) |
| { |
| GFile *resource; |
| char *path; |
| |
| path = g_uri_unescape_string (uri + strlen ("resource:"), NULL); |
| if (path == NULL) |
| return 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 = 0; |
| guint32 resource_flags = 0; |
| 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 GFileInfo * |
| g_resource_file_query_filesystem_info (GFile *file, |
| const char *attributes, |
| GCancellable *cancellable, |
| GError **error) |
| { |
| GFileInfo *info; |
| GFileAttributeMatcher *matcher; |
| |
| info = g_file_info_new (); |
| |
| matcher = g_file_attribute_matcher_new (attributes); |
| if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE)) |
| g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "resource"); |
| |
| if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY)) |
| g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE); |
| |
| 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; |
| } |
| |
| typedef GFileMonitor GResourceFileMonitor; |
| typedef GFileMonitorClass GResourceFileMonitorClass; |
| |
| GType g_resource_file_monitor_get_type (void); |
| |
| G_DEFINE_TYPE (GResourceFileMonitor, g_resource_file_monitor, G_TYPE_FILE_MONITOR) |
| |
| static gboolean |
| g_resource_file_monitor_cancel (GFileMonitor *monitor) |
| { |
| return TRUE; |
| } |
| |
| static void |
| g_resource_file_monitor_init (GResourceFileMonitor *monitor) |
| { |
| } |
| |
| static void |
| g_resource_file_monitor_class_init (GResourceFileMonitorClass *class) |
| { |
| class->cancel = g_resource_file_monitor_cancel; |
| } |
| |
| static GFileMonitor * |
| g_resource_file_monitor_file (GFile *file, |
| GFileMonitorFlags flags, |
| GCancellable *cancellable, |
| GError **error) |
| { |
| return g_object_new (g_resource_file_monitor_get_type (), NULL); |
| } |
| |
| static GFile * |
| g_resource_file_set_display_name (GFile *file, |
| const char *display_name, |
| GCancellable *cancellable, |
| GError **error) |
| { |
| g_set_error_literal (error, |
| G_IO_ERROR, |
| G_IO_ERROR_NOT_SUPPORTED, |
| _("Resource files cannot be renamed")); |
| return NULL; |
| } |
| |
| 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->set_display_name = g_resource_file_set_display_name; |
| iface->enumerate_children = g_resource_file_enumerate_children; |
| iface->query_info = g_resource_file_query_info; |
| iface->query_filesystem_info = g_resource_file_query_filesystem_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->monitor_file = g_resource_file_monitor_file; |
| |
| 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); |
| } |