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