mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2026-10-24 00:49:00 +03:00
g_type_ensure has a large comment saying _get_type shouldn't be marked with G_GNUC_CONST because it has side-effects on the first run, but it's OK if you consistently call g_type_ensure first. Unfortunately, this isn't accurate. GCC trunk (observed at r17-1725-ge259a182e1f100) now analyses points-to information more effectively and propagates its findings, so it sees the function modifies some global state, concludes that can't be possible (because of the const attribute), and deletes the stores. pure unfortunately is unsuitable for *_get_type too as it has a side-effect (writes to global memory). Mesa had a similar issue which I fixed recently [0]. glib and glibmm likely want the (not-yet-implemented) 'idempotent' attribute instead [1]. In fact, this exact problem is what motivated that request! :) ebassi mentions an alternative to the current setup as well, using atomics [2]. This problem has gone back many years but the compiler has finally fully outsmarted us here, so give in. [0] https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/42361 [1] https://gcc.gnu.org/PR32911 [2] https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4249#note_2583110 Fixes: https://gitlab.gnome.org/GNOME/glib/-/work_items/3984 Signed-off-by: Sam James <sam@gentoo.org>
982 lines
28 KiB
C
982 lines
28 KiB
C
/* 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);
|
||
|
||
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;
|
||
gboolean is_dir;
|
||
char *base;
|
||
|
||
/* root is always there */
|
||
if (strcmp ("/", resource->path) == 0)
|
||
is_dir = TRUE;
|
||
else
|
||
is_dir = g_resources_has_children (resource->path);
|
||
|
||
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 gboolean
|
||
g_resource_file_query_exists (GFile *file,
|
||
GCancellable *cancellable)
|
||
{
|
||
GResourceFile *resource = G_RESOURCE_FILE (file);
|
||
|
||
return g_resources_get_info (resource->path, 0, NULL, NULL, 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->query_exists = g_resource_file_query_exists;
|
||
|
||
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);
|
||
}
|