• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
41 struct _GResourceFile
42 {
43   GObject parent_instance;
44 
45   char *path;
46 };
47 
48 struct _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 
61 struct _GResourceFileEnumeratorClass
62 {
63   GFileEnumeratorClass parent_class;
64 };
65 
66 typedef struct _GResourceFileEnumerator        GResourceFileEnumerator;
67 typedef struct _GResourceFileEnumeratorClass   GResourceFileEnumeratorClass;
68 
69 static void g_resource_file_file_iface_init (GFileIface *iface);
70 
71 static GFileAttributeInfoList *resource_writable_attributes = NULL;
72 static GFileAttributeInfoList *resource_writable_namespaces = NULL;
73 
74 static 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 
90 typedef struct _GResourceFileInputStream         GResourceFileInputStream;
91 typedef struct _GResourceFileInputStreamClass    GResourceFileInputStreamClass;
92 
93 #define g_resource_file_get_type _g_resource_file_get_type
94 G_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
99 G_DEFINE_TYPE (GResourceFileEnumerator, g_resource_file_enumerator, G_TYPE_FILE_ENUMERATOR)
100 
101 static GFileEnumerator *_g_resource_file_enumerator_new (GResourceFile *file,
102 							 const char           *attributes,
103 							 GFileQueryInfoFlags   flags,
104 							 GCancellable         *cancellable,
105 							 GError              **error);
106 
107 
108 static GType              _g_resource_file_input_stream_get_type (void) G_GNUC_CONST;
109 
110 static GFileInputStream *_g_resource_file_input_stream_new (GInputStream *stream, GFile *file);
111 
112 
113 static void
g_resource_file_finalize(GObject * object)114 g_resource_file_finalize (GObject *object)
115 {
116   GResourceFile *resource;
117 
118   resource = G_RESOURCE_FILE (object);
119 
120   g_free (resource->path);
121 
122   G_OBJECT_CLASS (g_resource_file_parent_class)->finalize (object);
123 }
124 
125 static void
g_resource_file_class_init(GResourceFileClass * klass)126 g_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 
136 static void
g_resource_file_init(GResourceFile * resource)137 g_resource_file_init (GResourceFile *resource)
138 {
139 }
140 
141 static inline gchar *
scan_backwards(const gchar * begin,const gchar * end,gchar c)142 scan_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 
156 static inline void
pop_to_previous_part(const gchar * begin,gchar ** out)157 pop_to_previous_part (const gchar  *begin,
158                       gchar       **out)
159 {
160   if (*out > begin)
161     *out = scan_backwards (begin, *out - 1, '/');
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  */
175 static char *
canonicalize_filename(const char * in)176 canonicalize_filename (const char *in)
177 {
178   gchar *bptr;
179   char *out;
180 
181   bptr = out = g_malloc (strlen (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 (bptr, &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 
229 static GFile *
g_resource_file_new_for_path(const char * path)230 g_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 (path);
235 
236   return G_FILE (resource);
237 }
238 
239 GFile *
_g_resource_file_new(const char * uri)240 _g_resource_file_new (const char *uri)
241 {
242   GFile *resource;
243   char *path;
244 
245   path = g_uri_unescape_string (uri + strlen ("resource:"), NULL);
246   resource = g_resource_file_new_for_path (path);
247   g_free (path);
248 
249   return G_FILE (resource);
250 }
251 
252 static gboolean
g_resource_file_is_native(GFile * file)253 g_resource_file_is_native (GFile *file)
254 {
255   return FALSE;
256 }
257 
258 static gboolean
g_resource_file_has_uri_scheme(GFile * file,const char * uri_scheme)259 g_resource_file_has_uri_scheme (GFile      *file,
260 				const char *uri_scheme)
261 {
262   return g_ascii_strcasecmp (uri_scheme, "resource") == 0;
263 }
264 
265 static char *
g_resource_file_get_uri_scheme(GFile * file)266 g_resource_file_get_uri_scheme (GFile *file)
267 {
268   return g_strdup ("resource");
269 }
270 
271 static char *
g_resource_file_get_basename(GFile * file)272 g_resource_file_get_basename (GFile *file)
273 {
274   gchar *base;
275 
276   base = strrchr (G_RESOURCE_FILE (file)->path, '/');
277   return g_strdup (base + 1);
278 }
279 
280 static char *
g_resource_file_get_path(GFile * file)281 g_resource_file_get_path (GFile *file)
282 {
283   return NULL;
284 }
285 
286 static char *
g_resource_file_get_uri(GFile * file)287 g_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 ("resource://", escaped, NULL);
292   g_free (escaped);
293   return res;
294 }
295 
296 static char *
g_resource_file_get_parse_name(GFile * file)297 g_resource_file_get_parse_name (GFile *file)
298 {
299   return g_resource_file_get_uri (file);
300 }
301 
302 static GFile *
g_resource_file_get_parent(GFile * file)303 g_resource_file_get_parent (GFile *file)
304 {
305   GResourceFile *resource = G_RESOURCE_FILE (file);
306   GResourceFile *parent;
307   gchar *end;
308 
309   end = strrchr (resource->path, '/');
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 (resource->path,
316 			    end - resource->path);
317 
318   return G_FILE (parent);
319 }
320 
321 static GFile *
g_resource_file_dup(GFile * file)322 g_resource_file_dup (GFile *file)
323 {
324   GResourceFile *resource = G_RESOURCE_FILE (file);
325 
326   return g_resource_file_new_for_path (resource->path);
327 }
328 
329 static guint
g_resource_file_hash(GFile * file)330 g_resource_file_hash (GFile *file)
331 {
332   GResourceFile *resource = G_RESOURCE_FILE (file);
333 
334   return g_str_hash (resource->path);
335 }
336 
337 static gboolean
g_resource_file_equal(GFile * file1,GFile * file2)338 g_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 (resource1->path, resource2->path);
345 }
346 
347 static const char *
match_prefix(const char * path,const char * prefix)348 match_prefix (const char *path,
349 	      const char *prefix)
350 {
351   int prefix_len;
352 
353   prefix_len = strlen (prefix);
354   if (strncmp (path, prefix, 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 
366 static gboolean
g_resource_file_prefix_matches(GFile * parent,GFile * descendant)367 g_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 (descendant_resource->path, parent_resource->path);
375   if (remainder != NULL && *remainder == '/')
376     return TRUE;
377   return FALSE;
378 }
379 
380 static char *
g_resource_file_get_relative_path(GFile * parent,GFile * descendant)381 g_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 (descendant_resource->path, parent_resource->path);
389 
390   if (remainder != NULL && *remainder == '/')
391     return g_strdup (remainder + 1);
392   return NULL;
393 }
394 
395 static GFile *
g_resource_file_resolve_relative_path(GFile * file,const char * relative_path)396 g_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 (relative_path);
405 
406   filename = g_build_path ("/", resource->path, relative_path, NULL);
407   child = g_resource_file_new_for_path (filename);
408   g_free (filename);
409 
410   return child;
411 }
412 
413 static GFileEnumerator *
g_resource_file_enumerate_children(GFile * file,const char * attributes,GFileQueryInfoFlags flags,GCancellable * cancellable,GError ** error)414 g_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 (resource,
422 					  attributes, flags,
423 					  cancellable, error);
424 }
425 
426 static GFile *
g_resource_file_get_child_for_display_name(GFile * file,const char * display_name,GError ** error)427 g_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, display_name);
434 
435   return new_file;
436 }
437 
438 static GFileInfo *
g_resource_file_query_info(GFile * file,const char * attributes,GFileQueryInfoFlags flags,GCancellable * cancellable,GError ** error)439 g_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 (resource->path, 0, NULL);
458   if (children != NULL)
459     {
460       g_strfreev (children);
461       is_dir = TRUE;
462     }
463 
464   /* root is always there */
465   if (strcmp ("/", resource->path) == 0)
466     is_dir = TRUE;
467 
468   if (!is_dir)
469     {
470       res = g_resources_get_info (resource->path, 0, &size, &resource_flags, &my_error);
471       if (!res)
472 	{
473 	  if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
474 	    {
475 	      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
476 			   _("The resource at “%s” does not exist"),
477 			   resource->path);
478 	    }
479 	  else
480 	    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
481                                  my_error->message);
482 	  g_clear_error (&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, base);
492   g_file_info_set_display_name (info, 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, G_FILE_TYPE_DIRECTORY);
504     }
505   else
506     {
507       GBytes *bytes;
508       char *content_type;
509 
510       g_file_info_set_file_type (info, 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 (resource->path, 0, NULL)))
517         {
518           const guchar *data;
519           gsize data_size;
520 
521           data = g_bytes_get_data (bytes, &data_size);
522           content_type = g_content_type_guess (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, content_type);
532           _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, content_type);
533 
534           g_free (content_type);
535         }
536     }
537 
538   g_free (base);
539   g_file_attribute_matcher_unref (matcher);
540 
541   return info;
542 }
543 
544 static GFileInfo *
g_resource_file_query_filesystem_info(GFile * file,const char * attributes,GCancellable * cancellable,GError ** error)545 g_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, "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 
566 static GFileAttributeInfoList *
g_resource_file_query_settable_attributes(GFile * file,GCancellable * cancellable,GError ** error)567 g_resource_file_query_settable_attributes (GFile         *file,
568 					   GCancellable  *cancellable,
569 					   GError       **error)
570 {
571   return g_file_attribute_info_list_ref (resource_writable_attributes);
572 }
573 
574 static GFileAttributeInfoList *
g_resource_file_query_writable_namespaces(GFile * file,GCancellable * cancellable,GError ** error)575 g_resource_file_query_writable_namespaces (GFile         *file,
576 					   GCancellable  *cancellable,
577 					   GError       **error)
578 {
579   return g_file_attribute_info_list_ref (resource_writable_namespaces);
580 }
581 
582 static GFileInputStream *
g_resource_file_read(GFile * file,GCancellable * cancellable,GError ** error)583 g_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 (resource->path, 0, &my_error);
593 
594   if (stream == NULL)
595     {
596       if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
597 	{
598 	  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
599 		       _("The resource at “%s” does not exist"),
600 		       resource->path);
601 	}
602       else
603 	g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
604                              my_error->message);
605       g_clear_error (&my_error);
606       return NULL;
607     }
608 
609   res = _g_resource_file_input_stream_new (stream, file);
610   g_object_unref (stream);
611   return res;
612 }
613 
614 typedef GFileMonitor GResourceFileMonitor;
615 typedef GFileMonitorClass GResourceFileMonitorClass;
616 
617 GType g_resource_file_monitor_get_type (void);
618 
G_DEFINE_TYPE(GResourceFileMonitor,g_resource_file_monitor,G_TYPE_FILE_MONITOR)619 G_DEFINE_TYPE (GResourceFileMonitor, g_resource_file_monitor, G_TYPE_FILE_MONITOR)
620 
621 static gboolean
622 g_resource_file_monitor_cancel (GFileMonitor *monitor)
623 {
624   return TRUE;
625 }
626 
627 static void
g_resource_file_monitor_init(GResourceFileMonitor * monitor)628 g_resource_file_monitor_init (GResourceFileMonitor *monitor)
629 {
630 }
631 
632 static void
g_resource_file_monitor_class_init(GResourceFileMonitorClass * class)633 g_resource_file_monitor_class_init (GResourceFileMonitorClass *class)
634 {
635   class->cancel = g_resource_file_monitor_cancel;
636 }
637 
638 static GFileMonitor *
g_resource_file_monitor_file(GFile * file,GFileMonitorFlags flags,GCancellable * cancellable,GError ** error)639 g_resource_file_monitor_file (GFile              *file,
640                               GFileMonitorFlags   flags,
641                               GCancellable       *cancellable,
642                               GError            **error)
643 {
644   return g_object_new (g_resource_file_monitor_get_type (), NULL);
645 }
646 
647 static void
g_resource_file_file_iface_init(GFileIface * iface)648 g_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 
676 static GFileInfo *g_resource_file_enumerator_next_file (GFileEnumerator  *enumerator,
677 							GCancellable     *cancellable,
678 							GError          **error);
679 static gboolean   g_resource_file_enumerator_close     (GFileEnumerator  *enumerator,
680 							GCancellable     *cancellable,
681 							GError          **error);
682 
683 static void
g_resource_file_enumerator_finalize(GObject * object)684 g_resource_file_enumerator_finalize (GObject *object)
685 {
686   GResourceFileEnumerator *resource;
687 
688   resource = G_RESOURCE_FILE_ENUMERATOR (object);
689 
690   g_strfreev (resource->children);
691   g_free (resource->path);
692   g_free (resource->attributes);
693 
694   G_OBJECT_CLASS (g_resource_file_enumerator_parent_class)->finalize (object);
695 }
696 
697 static void
g_resource_file_enumerator_class_init(GResourceFileEnumeratorClass * klass)698 g_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 
709 static void
g_resource_file_enumerator_init(GResourceFileEnumerator * resource)710 g_resource_file_enumerator_init (GResourceFileEnumerator *resource)
711 {
712 }
713 
714 static GFileEnumerator *
_g_resource_file_enumerator_new(GResourceFile * file,const char * attributes,GFileQueryInfoFlags flags,GCancellable * cancellable,GError ** error)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 (file->path, 0, NULL);
726   if (children == NULL &&
727       strcmp ("/", file->path) != 0)
728     {
729       res = g_resources_get_info (file->path, 0, NULL, NULL, NULL);
730       if (res)
731 	g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
732 		     _("The resource at “%s” is not a directory"),
733 		     file->path);
734       else
735 	g_set_error (error, G_IO_ERROR, 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 			   "container", file,
743 			   NULL);
744 
745   resource->children = children;
746   resource->path = g_strdup (file->path);
747   resource->attributes = g_strdup (attributes);
748   resource->flags = flags;
749 
750   return G_FILE_ENUMERATOR (resource);
751 }
752 
753 static GFileInfo *
g_resource_file_enumerator_next_file(GFileEnumerator * enumerator,GCancellable * cancellable,GError ** error)754 g_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 ("/", resource->path, resource->children[resource->index++], NULL);
768   file = g_resource_file_new_for_path (path);
769   g_free (path);
770 
771   info = g_file_query_info (file,
772 			    resource->attributes,
773 			    resource->flags,
774 			    cancellable,
775 			    error);
776 
777   g_object_unref (file);
778 
779   return info;
780 }
781 
782 static gboolean
g_resource_file_enumerator_close(GFileEnumerator * enumerator,GCancellable * cancellable,GError ** error)783 g_resource_file_enumerator_close (GFileEnumerator  *enumerator,
784 			       GCancellable     *cancellable,
785 			       GError          **error)
786 {
787   return TRUE;
788 }
789 
790 
791 struct _GResourceFileInputStream
792 {
793   GFileInputStream parent_instance;
794   GInputStream *stream;
795   GFile *file;
796 };
797 
798 struct _GResourceFileInputStreamClass
799 {
800   GFileInputStreamClass parent_class;
801 };
802 
803 #define g_resource_file_input_stream_get_type _g_resource_file_input_stream_get_type
804 G_DEFINE_TYPE (GResourceFileInputStream, g_resource_file_input_stream, G_TYPE_FILE_INPUT_STREAM)
805 
806 static gssize     g_resource_file_input_stream_read       (GInputStream      *stream,
807 							   void              *buffer,
808 							   gsize              count,
809 							   GCancellable      *cancellable,
810 							   GError           **error);
811 static gssize     g_resource_file_input_stream_skip       (GInputStream      *stream,
812 							   gsize              count,
813 							   GCancellable      *cancellable,
814 							   GError           **error);
815 static gboolean   g_resource_file_input_stream_close      (GInputStream      *stream,
816 							   GCancellable      *cancellable,
817 							   GError           **error);
818 static goffset    g_resource_file_input_stream_tell       (GFileInputStream  *stream);
819 static gboolean   g_resource_file_input_stream_can_seek   (GFileInputStream  *stream);
820 static gboolean   g_resource_file_input_stream_seek       (GFileInputStream  *stream,
821 							   goffset            offset,
822 							   GSeekType          type,
823 							   GCancellable      *cancellable,
824 							   GError           **error);
825 static GFileInfo *g_resource_file_input_stream_query_info (GFileInputStream  *stream,
826 							   const char        *attributes,
827 							   GCancellable      *cancellable,
828 							   GError           **error);
829 
830 static void
g_resource_file_input_stream_finalize(GObject * object)831 g_resource_file_input_stream_finalize (GObject *object)
832 {
833   GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (object);
834 
835   g_object_unref (file->stream);
836   g_object_unref (file->file);
837   G_OBJECT_CLASS (g_resource_file_input_stream_parent_class)->finalize (object);
838 }
839 
840 static void
g_resource_file_input_stream_class_init(GResourceFileInputStreamClass * klass)841 g_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 
858 static void
g_resource_file_input_stream_init(GResourceFileInputStream * info)859 g_resource_file_input_stream_init (GResourceFileInputStream *info)
860 {
861 }
862 
863 static GFileInputStream *
_g_resource_file_input_stream_new(GInputStream * in_stream,GFile * file)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 
875 static gssize
g_resource_file_input_stream_read(GInputStream * stream,void * buffer,gsize count,GCancellable * cancellable,GError ** error)876 g_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 (file->stream,
884 			      buffer, count, cancellable, error);
885 }
886 
887 static gssize
g_resource_file_input_stream_skip(GInputStream * stream,gsize count,GCancellable * cancellable,GError ** error)888 g_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 (file->stream,
895 			      count, cancellable, error);
896 }
897 
898 static gboolean
g_resource_file_input_stream_close(GInputStream * stream,GCancellable * cancellable,GError ** error)899 g_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 (file->stream,
905 			       cancellable, error);
906 }
907 
908 
909 static goffset
g_resource_file_input_stream_tell(GFileInputStream * stream)910 g_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 
920 static gboolean
g_resource_file_input_stream_can_seek(GFileInputStream * stream)921 g_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 
928 static gboolean
g_resource_file_input_stream_seek(GFileInputStream * stream,goffset offset,GSeekType type,GCancellable * cancellable,GError ** error)929 g_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 (error, G_IO_ERROR, 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 
948 static GFileInfo *
g_resource_file_input_stream_query_info(GFileInputStream * stream,const char * attributes,GCancellable * cancellable,GError ** error)949 g_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, attributes, 0, cancellable, error);
957 }
958