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