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