mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-23 20:46:14 +01:00
Support resource:/// uris
This commit is contained in:
parent
75439298f8
commit
e041843b3e
@ -385,6 +385,8 @@ libgio_2_0_la_SOURCES = \
|
||||
gproxyresolver.c \
|
||||
gresolver.c \
|
||||
gresource.c \
|
||||
gresourcefile.c \
|
||||
gresourcefile.h \
|
||||
gseekable.c \
|
||||
gsimpleasyncresult.c \
|
||||
gsimplepermission.c \
|
||||
|
875
gio/gresourcefile.c
Normal file
875
gio/gresourcefile.c
Normal file
@ -0,0 +1,875 @@
|
||||
/* 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;
|
||||
|
||||
#define G_TYPE_RESOURCE_FILE_ENUMERATOR (_g_resource_file_enumerator_get_type ())
|
||||
#define G_RESOURCE_FILE_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumerator))
|
||||
#define G_RESOURCE_FILE_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumeratorClass))
|
||||
#define G_IS_RESOURCE_FILE_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR))
|
||||
#define G_IS_RESOURCE_FILE_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOURCE_FILE_ENUMERATOR))
|
||||
#define G_RESOURCE_FILE_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumeratorClass))
|
||||
|
||||
#define G_TYPE_RESOURCE_FILE_INPUT_STREAM (_g_resource_file_input_stream_get_type ())
|
||||
#define G_RESOURCE_FILE_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStream))
|
||||
#define G_RESOURCE_FILE_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStreamClass))
|
||||
#define G_IS_RESOURCE_FILE_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM))
|
||||
#define G_IS_RESOURCE_FILE_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOURCE_FILE_INPUT_STREAM))
|
||||
#define G_RESOURCE_FILE_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStreamClass))
|
||||
|
||||
typedef struct _GResourceFileInputStream GResourceFileInputStream;
|
||||
typedef struct _GResourceFileInputStreamClass GResourceFileInputStreamClass;
|
||||
|
||||
#define g_resource_file_get_type _g_resource_file_get_type
|
||||
G_DEFINE_TYPE_WITH_CODE (GResourceFile, g_resource_file, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
|
||||
g_resource_file_file_iface_init))
|
||||
|
||||
#define g_resource_file_enumerator_get_type _g_resource_file_enumerator_get_type
|
||||
G_DEFINE_TYPE (GResourceFileEnumerator, g_resource_file_enumerator, G_TYPE_FILE_ENUMERATOR);
|
||||
|
||||
static GFileEnumerator *_g_resource_file_enumerator_new (GResourceFile *file,
|
||||
const char *attributes,
|
||||
GFileQueryInfoFlags flags,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
|
||||
static GType _g_resource_file_input_stream_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GFileInputStream *_g_resource_file_input_stream_new (GInputStream *stream, GFile *file);
|
||||
|
||||
|
||||
static void
|
||||
g_resource_file_finalize (GObject *object)
|
||||
{
|
||||
GResourceFile *resource;
|
||||
|
||||
resource = G_RESOURCE_FILE (object);
|
||||
|
||||
g_free (resource->path);
|
||||
|
||||
G_OBJECT_CLASS (g_resource_file_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_resource_file_class_init (GResourceFileClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = g_resource_file_finalize;
|
||||
|
||||
resource_writable_attributes = g_file_attribute_info_list_new ();
|
||||
resource_writable_namespaces = g_file_attribute_info_list_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
g_resource_file_init (GResourceFile *resource)
|
||||
{
|
||||
}
|
||||
|
||||
static char *
|
||||
canonicalize_filename (const char *filename)
|
||||
{
|
||||
char *canon, *start, *p, *q;
|
||||
|
||||
/* Skip multiple inital slashes */
|
||||
while (filename[0] == '/' && filename[1] == '/')
|
||||
filename++;
|
||||
|
||||
if (*filename != '/')
|
||||
canon = g_strconcat ("/", filename, NULL);
|
||||
else
|
||||
canon = g_strdup (filename);
|
||||
|
||||
start = canon + 1;
|
||||
|
||||
p = start;
|
||||
while (*p != 0)
|
||||
{
|
||||
if (p[0] == '.' && (p[1] == 0 || p[1] == '/'))
|
||||
{
|
||||
memmove (p, p+1, strlen (p+1)+1);
|
||||
}
|
||||
else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || p[2] == '/'))
|
||||
{
|
||||
q = p + 2;
|
||||
/* Skip previous separator */
|
||||
p = p - 2;
|
||||
if (p < start)
|
||||
p = start;
|
||||
while (p > start && *p != '/')
|
||||
p--;
|
||||
if (*p == '/')
|
||||
*p++ = '/';
|
||||
memmove (p, q, strlen (q)+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Skip until next separator */
|
||||
while (*p != 0 && *p != '/')
|
||||
p++;
|
||||
|
||||
if (*p != 0)
|
||||
{
|
||||
/* Canonicalize one separator */
|
||||
*p++ = '/';
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove additional separators */
|
||||
q = p;
|
||||
while (*q && *q == '/')
|
||||
q++;
|
||||
|
||||
if (p != q)
|
||||
memmove (p, q, strlen (q)+1);
|
||||
}
|
||||
|
||||
/* Remove trailing slashes */
|
||||
if (p > start && *(p-1) == '/')
|
||||
*(p-1) = 0;
|
||||
|
||||
return canon;
|
||||
}
|
||||
|
||||
static GFile *
|
||||
g_resource_file_new_for_path (const char *path)
|
||||
{
|
||||
GResourceFile *resource = g_object_new (G_TYPE_RESOURCE_FILE, NULL);
|
||||
|
||||
resource->path = canonicalize_filename (path);
|
||||
|
||||
return G_FILE (resource);
|
||||
}
|
||||
|
||||
GFile *
|
||||
_g_resource_file_new (const char *uri)
|
||||
{
|
||||
GFile *resource;
|
||||
char *path;
|
||||
|
||||
path = g_uri_unescape_string (uri + strlen ("resource:"), NULL);
|
||||
resource = g_resource_file_new_for_path (path);
|
||||
g_free (path);
|
||||
|
||||
return G_FILE (resource);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_resource_file_is_native (GFile *file)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_resource_file_has_uri_scheme (GFile *file,
|
||||
const char *uri_scheme)
|
||||
{
|
||||
return g_ascii_strcasecmp (uri_scheme, "resource") == 0;
|
||||
}
|
||||
|
||||
static char *
|
||||
g_resource_file_get_uri_scheme (GFile *file)
|
||||
{
|
||||
return g_strdup ("resource");
|
||||
}
|
||||
|
||||
static char *
|
||||
g_resource_file_get_basename (GFile *file)
|
||||
{
|
||||
gchar *base;
|
||||
|
||||
base = strrchr (G_RESOURCE_FILE (file)->path, '/');
|
||||
return g_strdup (base + 1);
|
||||
}
|
||||
|
||||
static char *
|
||||
g_resource_file_get_path (GFile *file)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *
|
||||
g_resource_file_get_uri (GFile *file)
|
||||
{
|
||||
char *escaped, *res;
|
||||
escaped = g_uri_escape_string (G_RESOURCE_FILE (file)->path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
|
||||
res = g_strconcat ("resource://", escaped, NULL);
|
||||
g_free (escaped);
|
||||
return res;
|
||||
}
|
||||
|
||||
static char *
|
||||
g_resource_file_get_parse_name (GFile *file)
|
||||
{
|
||||
return g_resource_file_get_uri (file);
|
||||
}
|
||||
|
||||
static GFile *
|
||||
g_resource_file_get_parent (GFile *file)
|
||||
{
|
||||
GResourceFile *resource = G_RESOURCE_FILE (file);
|
||||
GResourceFile *parent;
|
||||
gchar *end;
|
||||
|
||||
end = strrchr (resource->path, '/');
|
||||
|
||||
if (end == G_RESOURCE_FILE (file)->path)
|
||||
return NULL;
|
||||
|
||||
parent = g_object_new (G_TYPE_RESOURCE_FILE, NULL);
|
||||
parent->path = g_strndup (resource->path,
|
||||
end - resource->path - 1);
|
||||
|
||||
return G_FILE (parent);
|
||||
}
|
||||
|
||||
static GFile *
|
||||
g_resource_file_dup (GFile *file)
|
||||
{
|
||||
GResourceFile *resource = G_RESOURCE_FILE (file);
|
||||
|
||||
return g_resource_file_new_for_path (resource->path);
|
||||
}
|
||||
|
||||
static guint
|
||||
g_resource_file_hash (GFile *file)
|
||||
{
|
||||
GResourceFile *resource = G_RESOURCE_FILE (file);
|
||||
|
||||
return g_str_hash (resource->path);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_resource_file_equal (GFile *file1,
|
||||
GFile *file2)
|
||||
{
|
||||
GResourceFile *resource1 = G_RESOURCE_FILE (file1);
|
||||
GResourceFile *resource2 = G_RESOURCE_FILE (file2);
|
||||
|
||||
return g_str_equal (resource1->path, resource2->path);
|
||||
}
|
||||
|
||||
static const char *
|
||||
match_prefix (const char *path,
|
||||
const char *prefix)
|
||||
{
|
||||
int prefix_len;
|
||||
|
||||
prefix_len = strlen (prefix);
|
||||
if (strncmp (path, prefix, prefix_len) != 0)
|
||||
return NULL;
|
||||
|
||||
/* Handle the case where prefix is the root, so that
|
||||
* the IS_DIR_SEPRARATOR check below works */
|
||||
if (prefix_len > 0 &&
|
||||
prefix[prefix_len-1] == '/')
|
||||
prefix_len--;
|
||||
|
||||
return path + prefix_len;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_resource_file_prefix_matches (GFile *parent,
|
||||
GFile *descendant)
|
||||
{
|
||||
GResourceFile *parent_resource = G_RESOURCE_FILE (parent);
|
||||
GResourceFile *descendant_resource = G_RESOURCE_FILE (descendant);
|
||||
const char *remainder;
|
||||
|
||||
remainder = match_prefix (descendant_resource->path, parent_resource->path);
|
||||
if (remainder != NULL && *remainder == '/')
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static char *
|
||||
g_resource_file_get_relative_path (GFile *parent,
|
||||
GFile *descendant)
|
||||
{
|
||||
GResourceFile *parent_resource = G_RESOURCE_FILE (parent);
|
||||
GResourceFile *descendant_resource = G_RESOURCE_FILE (descendant);
|
||||
const char *remainder;
|
||||
|
||||
remainder = match_prefix (descendant_resource->path, parent_resource->path);
|
||||
|
||||
if (remainder != NULL && *remainder == '/')
|
||||
return g_strdup (remainder + 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GFile *
|
||||
g_resource_file_resolve_relative_path (GFile *file,
|
||||
const char *relative_path)
|
||||
{
|
||||
GResourceFile *resource = G_RESOURCE_FILE (file);
|
||||
char *filename;
|
||||
GFile *child;
|
||||
|
||||
if (relative_path[0] == '/')
|
||||
return g_resource_file_new_for_path (relative_path);
|
||||
|
||||
filename = g_build_path ("/", resource->path, relative_path, NULL);
|
||||
child = g_resource_file_new_for_path (filename);
|
||||
g_free (filename);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
static GFileEnumerator *
|
||||
g_resource_file_enumerate_children (GFile *file,
|
||||
const char *attributes,
|
||||
GFileQueryInfoFlags flags,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GResourceFile *resource = G_RESOURCE_FILE (file);
|
||||
return _g_resource_file_enumerator_new (resource,
|
||||
attributes, flags,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
static GFile *
|
||||
g_resource_file_get_child_for_display_name (GFile *file,
|
||||
const char *display_name,
|
||||
GError **error)
|
||||
{
|
||||
GFile *new_file;
|
||||
|
||||
new_file = g_file_get_child (file, display_name);
|
||||
|
||||
return new_file;
|
||||
}
|
||||
|
||||
static GFileInfo *
|
||||
g_resource_file_query_info (GFile *file,
|
||||
const char *attributes,
|
||||
GFileQueryInfoFlags flags,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GResourceFile *resource = G_RESOURCE_FILE (file);
|
||||
GError *my_error = NULL;
|
||||
GFileInfo *info;
|
||||
GFileAttributeMatcher *matcher;
|
||||
gboolean res;
|
||||
gsize size;
|
||||
guint32 resource_flags;
|
||||
char **children;
|
||||
gboolean is_dir;
|
||||
char *base;
|
||||
|
||||
is_dir = FALSE;
|
||||
children = g_resources_enumerate_children (resource->path, 0, NULL);
|
||||
if (children != NULL)
|
||||
{
|
||||
g_strfreev (children);
|
||||
is_dir = TRUE;
|
||||
}
|
||||
|
||||
/* root is always there */
|
||||
if (strcmp ("/", resource->path) == 0)
|
||||
is_dir = TRUE;
|
||||
|
||||
if (!is_dir)
|
||||
{
|
||||
res = g_resources_get_info (resource->path, 0, &size, &resource_flags, &my_error);
|
||||
if (!res)
|
||||
{
|
||||
if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
_("The resource at '%s' does not exist"),
|
||||
resource->path);
|
||||
}
|
||||
else
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
my_error->message);
|
||||
g_clear_error (&my_error);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
matcher = g_file_attribute_matcher_new (attributes);
|
||||
|
||||
info = g_file_info_new ();
|
||||
base = g_resource_file_get_basename (file);
|
||||
g_file_info_set_name (info, base);
|
||||
|
||||
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ, TRUE);
|
||||
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE, FALSE);
|
||||
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE, FALSE);
|
||||
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME, FALSE);
|
||||
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE, FALSE);
|
||||
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH, FALSE);
|
||||
|
||||
if (is_dir)
|
||||
{
|
||||
g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
|
||||
}
|
||||
else
|
||||
{
|
||||
GBytes *bytes;
|
||||
char *content_type;
|
||||
|
||||
g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
|
||||
g_file_info_set_size (info, size);
|
||||
|
||||
if ((_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
|
||||
((~resource_flags & G_RESOURCE_FLAGS_COMPRESSED) &&
|
||||
_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))) &&
|
||||
(bytes = g_resources_lookup_data (resource->path, 0, NULL)))
|
||||
{
|
||||
const guchar *data;
|
||||
gsize data_size;
|
||||
|
||||
data = g_bytes_get_data (bytes, &data_size);
|
||||
content_type = g_content_type_guess (base, data, data_size, NULL);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
else
|
||||
content_type = NULL;
|
||||
|
||||
if (content_type)
|
||||
{
|
||||
_g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE, content_type);
|
||||
_g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, content_type);
|
||||
|
||||
g_free (content_type);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (base);
|
||||
g_file_attribute_matcher_unref (matcher);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static GFileAttributeInfoList *
|
||||
g_resource_file_query_settable_attributes (GFile *file,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
return g_file_attribute_info_list_ref (resource_writable_attributes);
|
||||
}
|
||||
|
||||
static GFileAttributeInfoList *
|
||||
g_resource_file_query_writable_namespaces (GFile *file,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
return g_file_attribute_info_list_ref (resource_writable_namespaces);
|
||||
}
|
||||
|
||||
static GFileInputStream *
|
||||
g_resource_file_read (GFile *file,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GResourceFile *resource = G_RESOURCE_FILE (file);
|
||||
GError *my_error = NULL;
|
||||
GInputStream *stream;
|
||||
GFileInputStream *res;
|
||||
|
||||
stream = g_resources_open_stream (resource->path, 0, &my_error);
|
||||
|
||||
if (stream == NULL)
|
||||
{
|
||||
if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
_("The resource at '%s' does not exist"),
|
||||
resource->path);
|
||||
}
|
||||
else
|
||||
g_set_error (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);
|
||||
}
|
51
gio/gresourcefile.h
Normal file
51
gio/gresourcefile.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2011 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>
|
||||
*/
|
||||
|
||||
#ifndef __G_RESOURCE_FILE_H__
|
||||
#define __G_RESOURCE_FILE_H__
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_RESOURCE_FILE (_g_resource_file_get_type ())
|
||||
#define G_RESOURCE_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_RESOURCE_FILE, GResourceFile))
|
||||
#define G_RESOURCE_FILE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_RESOURCE_FILE, GResourceFileClass))
|
||||
#define G_IS_RESOURCE_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_RESOURCE_FILE))
|
||||
#define G_IS_RESOURCE_FILE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOURCE_FILE))
|
||||
#define G_RESOURCE_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOURCE_FILE, GResourceFileClass))
|
||||
|
||||
typedef struct _GResourceFile GResourceFile;
|
||||
typedef struct _GResourceFileClass GResourceFileClass;
|
||||
|
||||
struct _GResourceFileClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType _g_resource_file_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GFile * _g_resource_file_new (const char *uri);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_RESOURCE_FILE_H__ */
|
11
gio/gvfs.c
11
gio/gvfs.c
@ -24,6 +24,7 @@
|
||||
#include <string.h>
|
||||
#include "gvfs.h"
|
||||
#include "glocalvfs.h"
|
||||
#include "gresourcefile.h"
|
||||
#include "giomodule-priv.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
@ -119,6 +120,13 @@ g_vfs_get_file_for_uri (GVfs *vfs,
|
||||
|
||||
class = G_VFS_GET_CLASS (vfs);
|
||||
|
||||
/* This is an unfortunate placement, but we really
|
||||
need to check this before chaining to the vfs,
|
||||
because we want to support resource uris for
|
||||
all vfs:es, even those that predate resources. */
|
||||
if (g_str_has_prefix (uri, "resource:"))
|
||||
return _g_resource_file_new (uri);
|
||||
|
||||
return (* class->get_file_for_uri) (vfs, uri);
|
||||
}
|
||||
|
||||
@ -167,6 +175,9 @@ g_vfs_parse_name (GVfs *vfs,
|
||||
|
||||
class = G_VFS_GET_CLASS (vfs);
|
||||
|
||||
if (g_str_has_prefix (parse_name, "resource:"))
|
||||
return _g_resource_file_new (parse_name);
|
||||
|
||||
return (* class->parse_name) (vfs, parse_name);
|
||||
}
|
||||
|
||||
|
@ -412,6 +412,45 @@ test_resource_module (void)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_uri_query_info (void)
|
||||
{
|
||||
GResource *resource;
|
||||
GError *error = NULL;
|
||||
gboolean loaded_file;
|
||||
char *content;
|
||||
gsize content_size;
|
||||
GBytes *data;
|
||||
GFile *file;
|
||||
GFileInfo *info;
|
||||
const char *content_type;
|
||||
|
||||
loaded_file = g_file_get_contents ("test.gresource", &content, &content_size,
|
||||
NULL);
|
||||
g_assert (loaded_file);
|
||||
|
||||
data = g_bytes_new_take (content, content_size);
|
||||
resource = g_resource_new_from_data (data, &error);
|
||||
g_assert (resource != NULL);
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_resources_register (resource);
|
||||
|
||||
file = g_file_new_for_uri ("resource://" "/a_prefix/test2-alias.txt");
|
||||
|
||||
info = g_file_query_info (file, "*", 0, NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
g_object_unref (file);
|
||||
|
||||
content_type = g_file_info_get_content_type (info);
|
||||
g_assert (content_type);
|
||||
g_assert_cmpstr (content_type, ==, "text/plain");
|
||||
|
||||
g_object_unref (info);
|
||||
|
||||
g_resources_unregister (resource);
|
||||
g_resource_unref (resource);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
@ -431,6 +470,7 @@ main (int argc,
|
||||
/* This only uses automatic resources too, so it tests the constructors and destructors */
|
||||
g_test_add_func ("/resource/module", test_resource_module);
|
||||
#endif
|
||||
g_test_add_func ("/resource/uri/query-info", test_uri_query_info);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user