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