1/* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: Alexander Larsson <alexl@redhat.com>
19 */
20
21#include "config.h"
22
23#include <string.h>
24
25#include "gresource.h"
26#include "gresourcefile.h"
27#include "gfileattribute.h"
28#include <gfileattribute-priv.h>
29#include <gfileinfo-priv.h>
30#include "gfile.h"
31#include "gfilemonitor.h"
32#include "gseekable.h"
33#include "gfileinputstream.h"
34#include "gfileinfo.h"
35#include "gfileenumerator.h"
36#include "gcontenttype.h"
37#include "gioerror.h"
38#include <glib/gstdio.h>
39#include "glibintl.h"
40
41struct _GResourceFile
42{
43 GObject parent_instance;
44
45 char *path;
46};
47
48struct _GResourceFileEnumerator
49{
50 GFileEnumerator parent;
51
52 GFileAttributeMatcher *matcher;
53 char *path;
54 char *attributes;
55 GFileQueryInfoFlags flags;
56 int index;
57
58 char **children;
59};
60
61struct _GResourceFileEnumeratorClass
62{
63 GFileEnumeratorClass parent_class;
64};
65
66typedef struct _GResourceFileEnumerator GResourceFileEnumerator;
67typedef struct _GResourceFileEnumeratorClass GResourceFileEnumeratorClass;
68
69static void g_resource_file_file_iface_init (GFileIface *iface);
70
71static GFileAttributeInfoList *resource_writable_attributes = NULL;
72static GFileAttributeInfoList *resource_writable_namespaces = NULL;
73
74static GType _g_resource_file_enumerator_get_type (void);
75
76#define G_TYPE_RESOURCE_FILE_ENUMERATOR (_g_resource_file_enumerator_get_type ())
77#define G_RESOURCE_FILE_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumerator))
78#define G_RESOURCE_FILE_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumeratorClass))
79#define G_IS_RESOURCE_FILE_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR))
80#define G_IS_RESOURCE_FILE_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOURCE_FILE_ENUMERATOR))
81#define G_RESOURCE_FILE_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumeratorClass))
82
83#define G_TYPE_RESOURCE_FILE_INPUT_STREAM (_g_resource_file_input_stream_get_type ())
84#define G_RESOURCE_FILE_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStream))
85#define G_RESOURCE_FILE_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStreamClass))
86#define G_IS_RESOURCE_FILE_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM))
87#define G_IS_RESOURCE_FILE_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOURCE_FILE_INPUT_STREAM))
88#define G_RESOURCE_FILE_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStreamClass))
89
90typedef struct _GResourceFileInputStream GResourceFileInputStream;
91typedef struct _GResourceFileInputStreamClass GResourceFileInputStreamClass;
92
93#define g_resource_file_get_type _g_resource_file_get_type
94G_DEFINE_TYPE_WITH_CODE (GResourceFile, g_resource_file, G_TYPE_OBJECT,
95 G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
96 g_resource_file_file_iface_init))
97
98#define g_resource_file_enumerator_get_type _g_resource_file_enumerator_get_type
99G_DEFINE_TYPE (GResourceFileEnumerator, g_resource_file_enumerator, G_TYPE_FILE_ENUMERATOR)
100
101static GFileEnumerator *_g_resource_file_enumerator_new (GResourceFile *file,
102 const char *attributes,
103 GFileQueryInfoFlags flags,
104 GCancellable *cancellable,
105 GError **error);
106
107
108static GType _g_resource_file_input_stream_get_type (void) G_GNUC_CONST;
109
110static GFileInputStream *_g_resource_file_input_stream_new (GInputStream *stream, GFile *file);
111
112
113static void
114g_resource_file_finalize (GObject *object)
115{
116 GResourceFile *resource;
117
118 resource = G_RESOURCE_FILE (object);
119
120 g_free (mem: resource->path);
121
122 G_OBJECT_CLASS (g_resource_file_parent_class)->finalize (object);
123}
124
125static void
126g_resource_file_class_init (GResourceFileClass *klass)
127{
128 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
129
130 gobject_class->finalize = g_resource_file_finalize;
131
132 resource_writable_attributes = g_file_attribute_info_list_new ();
133 resource_writable_namespaces = g_file_attribute_info_list_new ();
134}
135
136static void
137g_resource_file_init (GResourceFile *resource)
138{
139}
140
141static inline gchar *
142scan_backwards (const gchar *begin,
143 const gchar *end,
144 gchar c)
145{
146 while (end >= begin)
147 {
148 if (*end == c)
149 return (gchar *)end;
150 end--;
151 }
152
153 return NULL;
154}
155
156static inline void
157pop_to_previous_part (const gchar *begin,
158 gchar **out)
159{
160 if (*out > begin)
161 *out = scan_backwards (begin, end: *out - 1, c: '/');
162}
163
164/*
165 * canonicalize_filename:
166 * @in: the path to be canonicalized
167 *
168 * The path @in may contain non-canonical path pieces such as "../"
169 * or duplicated "/". This will resolve those into a form that only
170 * contains a single / at a time and resolves all "../". The resulting
171 * path must also start with a /.
172 *
173 * Returns: the canonical form of the path
174 */
175static char *
176canonicalize_filename (const char *in)
177{
178 gchar *bptr;
179 char *out;
180
181 bptr = out = g_malloc (n_bytes: strlen (s: in) + 2);
182 *out = '/';
183
184 while (*in != 0)
185 {
186 g_assert (*out == '/');
187
188 /* move past slashes */
189 while (*in == '/')
190 in++;
191
192 /* Handle ./ ../ .\0 ..\0 */
193 if (*in == '.')
194 {
195 /* If this is ../ or ..\0 move up */
196 if (in[1] == '.' && (in[2] == '/' || in[2] == 0))
197 {
198 pop_to_previous_part (begin: bptr, out: &out);
199 in += 2;
200 continue;
201 }
202
203 /* If this is ./ skip past it */
204 if (in[1] == '/' || in[1] == 0)
205 {
206 in += 1;
207 continue;
208 }
209 }
210
211 /* Scan to the next path piece */
212 while (*in != 0 && *in != '/')
213 *(++out) = *(in++);
214
215 /* Add trailing /, compress the rest on the next go round. */
216 if (*in == '/')
217 *(++out) = *(in++);
218 }
219
220 /* Trim trailing / from path */
221 if (out > bptr && *out == '/')
222 *out = 0;
223 else
224 *(++out) = 0;
225
226 return bptr;
227}
228
229static GFile *
230g_resource_file_new_for_path (const char *path)
231{
232 GResourceFile *resource = g_object_new (G_TYPE_RESOURCE_FILE, NULL);
233
234 resource->path = canonicalize_filename (in: path);
235
236 return G_FILE (resource);
237}
238
239GFile *
240_g_resource_file_new (const char *uri)
241{
242 GFile *resource;
243 char *path;
244
245 path = g_uri_unescape_string (escaped_string: uri + strlen (s: "resource:"), NULL);
246 resource = g_resource_file_new_for_path (path);
247 g_free (mem: path);
248
249 return G_FILE (resource);
250}
251
252static gboolean
253g_resource_file_is_native (GFile *file)
254{
255 return FALSE;
256}
257
258static gboolean
259g_resource_file_has_uri_scheme (GFile *file,
260 const char *uri_scheme)
261{
262 return g_ascii_strcasecmp (s1: uri_scheme, s2: "resource") == 0;
263}
264
265static char *
266g_resource_file_get_uri_scheme (GFile *file)
267{
268 return g_strdup (str: "resource");
269}
270
271static char *
272g_resource_file_get_basename (GFile *file)
273{
274 gchar *base;
275
276 base = strrchr (G_RESOURCE_FILE (file)->path, c: '/');
277 return g_strdup (str: base + 1);
278}
279
280static char *
281g_resource_file_get_path (GFile *file)
282{
283 return NULL;
284}
285
286static char *
287g_resource_file_get_uri (GFile *file)
288{
289 char *escaped, *res;
290 escaped = g_uri_escape_string (G_RESOURCE_FILE (file)->path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
291 res = g_strconcat (string1: "resource://", escaped, NULL);
292 g_free (mem: escaped);
293 return res;
294}
295
296static char *
297g_resource_file_get_parse_name (GFile *file)
298{
299 return g_resource_file_get_uri (file);
300}
301
302static GFile *
303g_resource_file_get_parent (GFile *file)
304{
305 GResourceFile *resource = G_RESOURCE_FILE (file);
306 GResourceFile *parent;
307 gchar *end;
308
309 end = strrchr (s: resource->path, c: '/');
310
311 if (end == G_RESOURCE_FILE (file)->path)
312 return NULL;
313
314 parent = g_object_new (G_TYPE_RESOURCE_FILE, NULL);
315 parent->path = g_strndup (str: resource->path,
316 n: end - resource->path);
317
318 return G_FILE (parent);
319}
320
321static GFile *
322g_resource_file_dup (GFile *file)
323{
324 GResourceFile *resource = G_RESOURCE_FILE (file);
325
326 return g_resource_file_new_for_path (path: resource->path);
327}
328
329static guint
330g_resource_file_hash (GFile *file)
331{
332 GResourceFile *resource = G_RESOURCE_FILE (file);
333
334 return g_str_hash (v: resource->path);
335}
336
337static gboolean
338g_resource_file_equal (GFile *file1,
339 GFile *file2)
340{
341 GResourceFile *resource1 = G_RESOURCE_FILE (file1);
342 GResourceFile *resource2 = G_RESOURCE_FILE (file2);
343
344 return g_str_equal (v1: resource1->path, v2: resource2->path);
345}
346
347static const char *
348match_prefix (const char *path,
349 const char *prefix)
350{
351 int prefix_len;
352
353 prefix_len = strlen (s: prefix);
354 if (strncmp (s1: path, s2: prefix, n: prefix_len) != 0)
355 return NULL;
356
357 /* Handle the case where prefix is the root, so that
358 * the IS_DIR_SEPRARATOR check below works */
359 if (prefix_len > 0 &&
360 prefix[prefix_len-1] == '/')
361 prefix_len--;
362
363 return path + prefix_len;
364}
365
366static gboolean
367g_resource_file_prefix_matches (GFile *parent,
368 GFile *descendant)
369{
370 GResourceFile *parent_resource = G_RESOURCE_FILE (parent);
371 GResourceFile *descendant_resource = G_RESOURCE_FILE (descendant);
372 const char *remainder;
373
374 remainder = match_prefix (path: descendant_resource->path, prefix: parent_resource->path);
375 if (remainder != NULL && *remainder == '/')
376 return TRUE;
377 return FALSE;
378}
379
380static char *
381g_resource_file_get_relative_path (GFile *parent,
382 GFile *descendant)
383{
384 GResourceFile *parent_resource = G_RESOURCE_FILE (parent);
385 GResourceFile *descendant_resource = G_RESOURCE_FILE (descendant);
386 const char *remainder;
387
388 remainder = match_prefix (path: descendant_resource->path, prefix: parent_resource->path);
389
390 if (remainder != NULL && *remainder == '/')
391 return g_strdup (str: remainder + 1);
392 return NULL;
393}
394
395static GFile *
396g_resource_file_resolve_relative_path (GFile *file,
397 const char *relative_path)
398{
399 GResourceFile *resource = G_RESOURCE_FILE (file);
400 char *filename;
401 GFile *child;
402
403 if (relative_path[0] == '/')
404 return g_resource_file_new_for_path (path: relative_path);
405
406 filename = g_build_path (separator: "/", first_element: resource->path, relative_path, NULL);
407 child = g_resource_file_new_for_path (path: filename);
408 g_free (mem: filename);
409
410 return child;
411}
412
413static GFileEnumerator *
414g_resource_file_enumerate_children (GFile *file,
415 const char *attributes,
416 GFileQueryInfoFlags flags,
417 GCancellable *cancellable,
418 GError **error)
419{
420 GResourceFile *resource = G_RESOURCE_FILE (file);
421 return _g_resource_file_enumerator_new (file: resource,
422 attributes, flags,
423 cancellable, error);
424}
425
426static GFile *
427g_resource_file_get_child_for_display_name (GFile *file,
428 const char *display_name,
429 GError **error)
430{
431 GFile *new_file;
432
433 new_file = g_file_get_child (file, name: display_name);
434
435 return new_file;
436}
437
438static GFileInfo *
439g_resource_file_query_info (GFile *file,
440 const char *attributes,
441 GFileQueryInfoFlags flags,
442 GCancellable *cancellable,
443 GError **error)
444{
445 GResourceFile *resource = G_RESOURCE_FILE (file);
446 GError *my_error = NULL;
447 GFileInfo *info;
448 GFileAttributeMatcher *matcher;
449 gboolean res;
450 gsize size;
451 guint32 resource_flags;
452 char **children;
453 gboolean is_dir;
454 char *base;
455
456 is_dir = FALSE;
457 children = g_resources_enumerate_children (path: resource->path, lookup_flags: 0, NULL);
458 if (children != NULL)
459 {
460 g_strfreev (str_array: children);
461 is_dir = TRUE;
462 }
463
464 /* root is always there */
465 if (strcmp (s1: "/", s2: resource->path) == 0)
466 is_dir = TRUE;
467
468 if (!is_dir)
469 {
470 res = g_resources_get_info (path: resource->path, lookup_flags: 0, size: &size, flags: &resource_flags, error: &my_error);
471 if (!res)
472 {
473 if (g_error_matches (error: my_error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND))
474 {
475 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_NOT_FOUND,
476 _("The resource at “%s” does not exist"),
477 resource->path);
478 }
479 else
480 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED,
481 message: my_error->message);
482 g_clear_error (err: &my_error);
483 return FALSE;
484 }
485 }
486
487 matcher = g_file_attribute_matcher_new (attributes);
488
489 info = g_file_info_new ();
490 base = g_resource_file_get_basename (file);
491 g_file_info_set_name (info, name: base);
492 g_file_info_set_display_name (info, display_name: base);
493
494 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ, TRUE);
495 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE, FALSE);
496 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE, FALSE);
497 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME, FALSE);
498 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE, FALSE);
499 _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH, FALSE);
500
501 if (is_dir)
502 {
503 g_file_info_set_file_type (info, type: G_FILE_TYPE_DIRECTORY);
504 }
505 else
506 {
507 GBytes *bytes;
508 char *content_type;
509
510 g_file_info_set_file_type (info, type: G_FILE_TYPE_REGULAR);
511 g_file_info_set_size (info, size);
512
513 if ((_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
514 ((~resource_flags & G_RESOURCE_FLAGS_COMPRESSED) &&
515 _g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))) &&
516 (bytes = g_resources_lookup_data (path: resource->path, lookup_flags: 0, NULL)))
517 {
518 const guchar *data;
519 gsize data_size;
520
521 data = g_bytes_get_data (bytes, size: &data_size);
522 content_type = g_content_type_guess (filename: base, data, data_size, NULL);
523
524 g_bytes_unref (bytes);
525 }
526 else
527 content_type = NULL;
528
529 if (content_type)
530 {
531 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE, attr_value: content_type);
532 _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, attr_value: content_type);
533
534 g_free (mem: content_type);
535 }
536 }
537
538 g_free (mem: base);
539 g_file_attribute_matcher_unref (matcher);
540
541 return info;
542}
543
544static GFileInfo *
545g_resource_file_query_filesystem_info (GFile *file,
546 const char *attributes,
547 GCancellable *cancellable,
548 GError **error)
549{
550 GFileInfo *info;
551 GFileAttributeMatcher *matcher;
552
553 info = g_file_info_new ();
554
555 matcher = g_file_attribute_matcher_new (attributes);
556 if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE))
557 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, attr_value: "resource");
558
559 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);
560
561 g_file_attribute_matcher_unref (matcher);
562
563 return info;
564}
565
566static GFileAttributeInfoList *
567g_resource_file_query_settable_attributes (GFile *file,
568 GCancellable *cancellable,
569 GError **error)
570{
571 return g_file_attribute_info_list_ref (list: resource_writable_attributes);
572}
573
574static GFileAttributeInfoList *
575g_resource_file_query_writable_namespaces (GFile *file,
576 GCancellable *cancellable,
577 GError **error)
578{
579 return g_file_attribute_info_list_ref (list: resource_writable_namespaces);
580}
581
582static GFileInputStream *
583g_resource_file_read (GFile *file,
584 GCancellable *cancellable,
585 GError **error)
586{
587 GResourceFile *resource = G_RESOURCE_FILE (file);
588 GError *my_error = NULL;
589 GInputStream *stream;
590 GFileInputStream *res;
591
592 stream = g_resources_open_stream (path: resource->path, lookup_flags: 0, error: &my_error);
593
594 if (stream == NULL)
595 {
596 if (g_error_matches (error: my_error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND))
597 {
598 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_NOT_FOUND,
599 _("The resource at “%s” does not exist"),
600 resource->path);
601 }
602 else
603 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED,
604 message: my_error->message);
605 g_clear_error (err: &my_error);
606 return NULL;
607 }
608
609 res = _g_resource_file_input_stream_new (stream, file);
610 g_object_unref (object: stream);
611 return res;
612}
613
614typedef GFileMonitor GResourceFileMonitor;
615typedef GFileMonitorClass GResourceFileMonitorClass;
616
617GType g_resource_file_monitor_get_type (void);
618
619G_DEFINE_TYPE (GResourceFileMonitor, g_resource_file_monitor, G_TYPE_FILE_MONITOR)
620
621static gboolean
622g_resource_file_monitor_cancel (GFileMonitor *monitor)
623{
624 return TRUE;
625}
626
627static void
628g_resource_file_monitor_init (GResourceFileMonitor *monitor)
629{
630}
631
632static void
633g_resource_file_monitor_class_init (GResourceFileMonitorClass *class)
634{
635 class->cancel = g_resource_file_monitor_cancel;
636}
637
638static GFileMonitor *
639g_resource_file_monitor_file (GFile *file,
640 GFileMonitorFlags flags,
641 GCancellable *cancellable,
642 GError **error)
643{
644 return g_object_new (object_type: g_resource_file_monitor_get_type (), NULL);
645}
646
647static void
648g_resource_file_file_iface_init (GFileIface *iface)
649{
650 iface->dup = g_resource_file_dup;
651 iface->hash = g_resource_file_hash;
652 iface->equal = g_resource_file_equal;
653 iface->is_native = g_resource_file_is_native;
654 iface->has_uri_scheme = g_resource_file_has_uri_scheme;
655 iface->get_uri_scheme = g_resource_file_get_uri_scheme;
656 iface->get_basename = g_resource_file_get_basename;
657 iface->get_path = g_resource_file_get_path;
658 iface->get_uri = g_resource_file_get_uri;
659 iface->get_parse_name = g_resource_file_get_parse_name;
660 iface->get_parent = g_resource_file_get_parent;
661 iface->prefix_matches = g_resource_file_prefix_matches;
662 iface->get_relative_path = g_resource_file_get_relative_path;
663 iface->resolve_relative_path = g_resource_file_resolve_relative_path;
664 iface->get_child_for_display_name = g_resource_file_get_child_for_display_name;
665 iface->enumerate_children = g_resource_file_enumerate_children;
666 iface->query_info = g_resource_file_query_info;
667 iface->query_filesystem_info = g_resource_file_query_filesystem_info;
668 iface->query_settable_attributes = g_resource_file_query_settable_attributes;
669 iface->query_writable_namespaces = g_resource_file_query_writable_namespaces;
670 iface->read_fn = g_resource_file_read;
671 iface->monitor_file = g_resource_file_monitor_file;
672
673 iface->supports_thread_contexts = TRUE;
674}
675
676static GFileInfo *g_resource_file_enumerator_next_file (GFileEnumerator *enumerator,
677 GCancellable *cancellable,
678 GError **error);
679static gboolean g_resource_file_enumerator_close (GFileEnumerator *enumerator,
680 GCancellable *cancellable,
681 GError **error);
682
683static void
684g_resource_file_enumerator_finalize (GObject *object)
685{
686 GResourceFileEnumerator *resource;
687
688 resource = G_RESOURCE_FILE_ENUMERATOR (object);
689
690 g_strfreev (str_array: resource->children);
691 g_free (mem: resource->path);
692 g_free (mem: resource->attributes);
693
694 G_OBJECT_CLASS (g_resource_file_enumerator_parent_class)->finalize (object);
695}
696
697static void
698g_resource_file_enumerator_class_init (GResourceFileEnumeratorClass *klass)
699{
700 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
701 GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
702
703 gobject_class->finalize = g_resource_file_enumerator_finalize;
704
705 enumerator_class->next_file = g_resource_file_enumerator_next_file;
706 enumerator_class->close_fn = g_resource_file_enumerator_close;
707}
708
709static void
710g_resource_file_enumerator_init (GResourceFileEnumerator *resource)
711{
712}
713
714static GFileEnumerator *
715_g_resource_file_enumerator_new (GResourceFile *file,
716 const char *attributes,
717 GFileQueryInfoFlags flags,
718 GCancellable *cancellable,
719 GError **error)
720{
721 GResourceFileEnumerator *resource;
722 char **children;
723 gboolean res;
724
725 children = g_resources_enumerate_children (path: file->path, lookup_flags: 0, NULL);
726 if (children == NULL &&
727 strcmp (s1: "/", s2: file->path) != 0)
728 {
729 res = g_resources_get_info (path: file->path, lookup_flags: 0, NULL, NULL, NULL);
730 if (res)
731 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_NOT_DIRECTORY,
732 _("The resource at “%s” is not a directory"),
733 file->path);
734 else
735 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_NOT_FOUND,
736 _("The resource at “%s” does not exist"),
737 file->path);
738 return NULL;
739 }
740
741 resource = g_object_new (G_TYPE_RESOURCE_FILE_ENUMERATOR,
742 first_property_name: "container", file,
743 NULL);
744
745 resource->children = children;
746 resource->path = g_strdup (str: file->path);
747 resource->attributes = g_strdup (str: attributes);
748 resource->flags = flags;
749
750 return G_FILE_ENUMERATOR (resource);
751}
752
753static GFileInfo *
754g_resource_file_enumerator_next_file (GFileEnumerator *enumerator,
755 GCancellable *cancellable,
756 GError **error)
757{
758 GResourceFileEnumerator *resource = G_RESOURCE_FILE_ENUMERATOR (enumerator);
759 char *path;
760 GFileInfo *info;
761 GFile *file;
762
763 if (resource->children == NULL ||
764 resource->children[resource->index] == NULL)
765 return NULL;
766
767 path = g_build_path (separator: "/", first_element: resource->path, resource->children[resource->index++], NULL);
768 file = g_resource_file_new_for_path (path);
769 g_free (mem: path);
770
771 info = g_file_query_info (file,
772 attributes: resource->attributes,
773 flags: resource->flags,
774 cancellable,
775 error);
776
777 g_object_unref (object: file);
778
779 return info;
780}
781
782static gboolean
783g_resource_file_enumerator_close (GFileEnumerator *enumerator,
784 GCancellable *cancellable,
785 GError **error)
786{
787 return TRUE;
788}
789
790
791struct _GResourceFileInputStream
792{
793 GFileInputStream parent_instance;
794 GInputStream *stream;
795 GFile *file;
796};
797
798struct _GResourceFileInputStreamClass
799{
800 GFileInputStreamClass parent_class;
801};
802
803#define g_resource_file_input_stream_get_type _g_resource_file_input_stream_get_type
804G_DEFINE_TYPE (GResourceFileInputStream, g_resource_file_input_stream, G_TYPE_FILE_INPUT_STREAM)
805
806static gssize g_resource_file_input_stream_read (GInputStream *stream,
807 void *buffer,
808 gsize count,
809 GCancellable *cancellable,
810 GError **error);
811static gssize g_resource_file_input_stream_skip (GInputStream *stream,
812 gsize count,
813 GCancellable *cancellable,
814 GError **error);
815static gboolean g_resource_file_input_stream_close (GInputStream *stream,
816 GCancellable *cancellable,
817 GError **error);
818static goffset g_resource_file_input_stream_tell (GFileInputStream *stream);
819static gboolean g_resource_file_input_stream_can_seek (GFileInputStream *stream);
820static gboolean g_resource_file_input_stream_seek (GFileInputStream *stream,
821 goffset offset,
822 GSeekType type,
823 GCancellable *cancellable,
824 GError **error);
825static GFileInfo *g_resource_file_input_stream_query_info (GFileInputStream *stream,
826 const char *attributes,
827 GCancellable *cancellable,
828 GError **error);
829
830static void
831g_resource_file_input_stream_finalize (GObject *object)
832{
833 GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (object);
834
835 g_object_unref (object: file->stream);
836 g_object_unref (object: file->file);
837 G_OBJECT_CLASS (g_resource_file_input_stream_parent_class)->finalize (object);
838}
839
840static void
841g_resource_file_input_stream_class_init (GResourceFileInputStreamClass *klass)
842{
843 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
844 GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
845 GFileInputStreamClass *file_stream_class = G_FILE_INPUT_STREAM_CLASS (klass);
846
847 gobject_class->finalize = g_resource_file_input_stream_finalize;
848
849 stream_class->read_fn = g_resource_file_input_stream_read;
850 stream_class->skip = g_resource_file_input_stream_skip;
851 stream_class->close_fn = g_resource_file_input_stream_close;
852 file_stream_class->tell = g_resource_file_input_stream_tell;
853 file_stream_class->can_seek = g_resource_file_input_stream_can_seek;
854 file_stream_class->seek = g_resource_file_input_stream_seek;
855 file_stream_class->query_info = g_resource_file_input_stream_query_info;
856}
857
858static void
859g_resource_file_input_stream_init (GResourceFileInputStream *info)
860{
861}
862
863static GFileInputStream *
864_g_resource_file_input_stream_new (GInputStream *in_stream, GFile *file)
865{
866 GResourceFileInputStream *stream;
867
868 stream = g_object_new (G_TYPE_RESOURCE_FILE_INPUT_STREAM, NULL);
869 stream->stream = g_object_ref (in_stream);
870 stream->file = g_object_ref (file);
871
872 return G_FILE_INPUT_STREAM (stream);
873}
874
875static gssize
876g_resource_file_input_stream_read (GInputStream *stream,
877 void *buffer,
878 gsize count,
879 GCancellable *cancellable,
880 GError **error)
881{
882 GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
883 return g_input_stream_read (stream: file->stream,
884 buffer, count, cancellable, error);
885}
886
887static gssize
888g_resource_file_input_stream_skip (GInputStream *stream,
889 gsize count,
890 GCancellable *cancellable,
891 GError **error)
892{
893 GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
894 return g_input_stream_skip (stream: file->stream,
895 count, cancellable, error);
896}
897
898static gboolean
899g_resource_file_input_stream_close (GInputStream *stream,
900 GCancellable *cancellable,
901 GError **error)
902{
903 GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
904 return g_input_stream_close (stream: file->stream,
905 cancellable, error);
906}
907
908
909static goffset
910g_resource_file_input_stream_tell (GFileInputStream *stream)
911{
912 GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
913
914 if (!G_IS_SEEKABLE (file->stream))
915 return 0;
916
917 return g_seekable_tell (G_SEEKABLE (file->stream));
918}
919
920static gboolean
921g_resource_file_input_stream_can_seek (GFileInputStream *stream)
922{
923 GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
924
925 return G_IS_SEEKABLE (file->stream) && g_seekable_can_seek (G_SEEKABLE (file->stream));
926}
927
928static gboolean
929g_resource_file_input_stream_seek (GFileInputStream *stream,
930 goffset offset,
931 GSeekType type,
932 GCancellable *cancellable,
933 GError **error)
934{
935 GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
936
937 if (!G_IS_SEEKABLE (file->stream))
938 {
939 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED,
940 _("Input stream doesn’t implement seek"));
941 return FALSE;
942 }
943
944 return g_seekable_seek (G_SEEKABLE (file->stream),
945 offset, type, cancellable, error);
946}
947
948static GFileInfo *
949g_resource_file_input_stream_query_info (GFileInputStream *stream,
950 const char *attributes,
951 GCancellable *cancellable,
952 GError **error)
953{
954 GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
955
956 return g_file_query_info (file: file->file, attributes, flags: 0, cancellable, error);
957}
958

source code of gtk/subprojects/glib/gio/gresourcefile.c