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 : : int 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 : : char **children;
459 : : gboolean is_dir;
460 : : char *base;
461 : :
462 : 4 : is_dir = FALSE;
463 : 4 : children = g_resources_enumerate_children (resource->path, 0, NULL);
464 [ - + ]: 4 : if (children != NULL)
465 : : {
466 : 0 : g_strfreev (children);
467 : 0 : is_dir = TRUE;
468 : : }
469 : :
470 : : /* root is always there */
471 [ - + ]: 4 : if (strcmp ("/", resource->path) == 0)
472 : 0 : is_dir = TRUE;
473 : :
474 [ + - ]: 4 : if (!is_dir)
475 : : {
476 : 4 : res = g_resources_get_info (resource->path, 0, &size, &resource_flags, &my_error);
477 [ - + ]: 4 : if (!res)
478 : : {
479 [ # # ]: 0 : if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
480 : : {
481 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
482 : : _("The resource at “%s” does not exist"),
483 : : resource->path);
484 : : }
485 : : else
486 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
487 : 0 : my_error->message);
488 : 0 : g_clear_error (&my_error);
489 : 0 : return FALSE;
490 : : }
491 : : }
492 : :
493 : 4 : matcher = g_file_attribute_matcher_new (attributes);
494 : :
495 : 4 : info = g_file_info_new ();
496 : 4 : base = g_resource_file_get_basename (file);
497 : 4 : g_file_info_set_name (info, base);
498 : 4 : g_file_info_set_display_name (info, base);
499 : :
500 : 4 : _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ, TRUE);
501 : 4 : _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE, FALSE);
502 : 4 : _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE, FALSE);
503 : 4 : _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME, FALSE);
504 : 4 : _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE, FALSE);
505 : 4 : _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH, FALSE);
506 : :
507 [ - + ]: 4 : if (is_dir)
508 : : {
509 : 0 : g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
510 : : }
511 : : else
512 : : {
513 : : GBytes *bytes;
514 : : char *content_type;
515 : :
516 : 4 : g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
517 : 4 : g_file_info_set_size (info, size);
518 : :
519 [ + + ]: 4 : if ((_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
520 [ + - - + ]: 6 : ((~resource_flags & G_RESOURCE_FLAGS_COMPRESSED) &&
521 [ + - ]: 4 : _g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))) &&
522 : 1 : (bytes = g_resources_lookup_data (resource->path, 0, NULL)))
523 : 1 : {
524 : : const guchar *data;
525 : : gsize data_size;
526 : :
527 : 1 : data = g_bytes_get_data (bytes, &data_size);
528 : 1 : content_type = g_content_type_guess (base, data, data_size, NULL);
529 : :
530 : 1 : g_bytes_unref (bytes);
531 : : }
532 : : else
533 : 3 : content_type = NULL;
534 : :
535 [ + + ]: 4 : if (content_type)
536 : : {
537 : 1 : _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE, content_type);
538 : 1 : _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, content_type);
539 : :
540 : 1 : g_free (content_type);
541 : : }
542 : : }
543 : :
544 : 4 : g_free (base);
545 : 4 : g_file_attribute_matcher_unref (matcher);
546 : :
547 : 4 : return info;
548 : : }
549 : :
550 : : static GFileInfo *
551 : 1 : g_resource_file_query_filesystem_info (GFile *file,
552 : : const char *attributes,
553 : : GCancellable *cancellable,
554 : : GError **error)
555 : : {
556 : : GFileInfo *info;
557 : : GFileAttributeMatcher *matcher;
558 : :
559 : 1 : info = g_file_info_new ();
560 : :
561 : 1 : matcher = g_file_attribute_matcher_new (attributes);
562 [ + - ]: 1 : if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE))
563 : 1 : g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "resource");
564 : :
565 [ + - ]: 1 : if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
566 : 1 : g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
567 : :
568 : 1 : g_file_attribute_matcher_unref (matcher);
569 : :
570 : 1 : return info;
571 : : }
572 : :
573 : : static GFileAttributeInfoList *
574 : 1 : g_resource_file_query_settable_attributes (GFile *file,
575 : : GCancellable *cancellable,
576 : : GError **error)
577 : : {
578 : 1 : return g_file_attribute_info_list_ref (resource_writable_attributes);
579 : : }
580 : :
581 : : static GFileAttributeInfoList *
582 : 1 : g_resource_file_query_writable_namespaces (GFile *file,
583 : : GCancellable *cancellable,
584 : : GError **error)
585 : : {
586 : 1 : return g_file_attribute_info_list_ref (resource_writable_namespaces);
587 : : }
588 : :
589 : : static GFileInputStream *
590 : 1 : g_resource_file_read (GFile *file,
591 : : GCancellable *cancellable,
592 : : GError **error)
593 : : {
594 : 1 : GResourceFile *resource = G_RESOURCE_FILE (file);
595 : 1 : GError *my_error = NULL;
596 : : GInputStream *stream;
597 : : GFileInputStream *res;
598 : :
599 : 1 : stream = g_resources_open_stream (resource->path, 0, &my_error);
600 : :
601 [ - + ]: 1 : if (stream == NULL)
602 : : {
603 [ # # ]: 0 : if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
604 : : {
605 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
606 : : _("The resource at “%s” does not exist"),
607 : : resource->path);
608 : : }
609 : : else
610 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
611 : 0 : my_error->message);
612 : 0 : g_clear_error (&my_error);
613 : 0 : return NULL;
614 : : }
615 : :
616 : 1 : res = _g_resource_file_input_stream_new (stream, file);
617 : 1 : g_object_unref (stream);
618 : 1 : return res;
619 : : }
620 : :
621 : : typedef GFileMonitor GResourceFileMonitor;
622 : : typedef GFileMonitorClass GResourceFileMonitorClass;
623 : :
624 : : GType g_resource_file_monitor_get_type (void);
625 : :
626 [ # # # # : 0 : G_DEFINE_TYPE (GResourceFileMonitor, g_resource_file_monitor, G_TYPE_FILE_MONITOR)
# # ]
627 : :
628 : : static gboolean
629 : 0 : g_resource_file_monitor_cancel (GFileMonitor *monitor)
630 : : {
631 : 0 : return TRUE;
632 : : }
633 : :
634 : : static void
635 : 0 : g_resource_file_monitor_init (GResourceFileMonitor *monitor)
636 : : {
637 : 0 : }
638 : :
639 : : static void
640 : 0 : g_resource_file_monitor_class_init (GResourceFileMonitorClass *class)
641 : : {
642 : 0 : class->cancel = g_resource_file_monitor_cancel;
643 : 0 : }
644 : :
645 : : static GFileMonitor *
646 : 0 : g_resource_file_monitor_file (GFile *file,
647 : : GFileMonitorFlags flags,
648 : : GCancellable *cancellable,
649 : : GError **error)
650 : : {
651 : 0 : return g_object_new (g_resource_file_monitor_get_type (), NULL);
652 : : }
653 : :
654 : : static GFile *
655 : 0 : g_resource_file_set_display_name (GFile *file,
656 : : const char *display_name,
657 : : GCancellable *cancellable,
658 : : GError **error)
659 : : {
660 : 0 : g_set_error_literal (error,
661 : : G_IO_ERROR,
662 : : G_IO_ERROR_NOT_SUPPORTED,
663 : : _("Resource files cannot be renamed"));
664 : 0 : return NULL;
665 : : }
666 : :
667 : : static void
668 : 1 : g_resource_file_file_iface_init (GFileIface *iface)
669 : : {
670 : 1 : iface->dup = g_resource_file_dup;
671 : 1 : iface->hash = g_resource_file_hash;
672 : 1 : iface->equal = g_resource_file_equal;
673 : 1 : iface->is_native = g_resource_file_is_native;
674 : 1 : iface->has_uri_scheme = g_resource_file_has_uri_scheme;
675 : 1 : iface->get_uri_scheme = g_resource_file_get_uri_scheme;
676 : 1 : iface->get_basename = g_resource_file_get_basename;
677 : 1 : iface->get_path = g_resource_file_get_path;
678 : 1 : iface->get_uri = g_resource_file_get_uri;
679 : 1 : iface->get_parse_name = g_resource_file_get_parse_name;
680 : 1 : iface->get_parent = g_resource_file_get_parent;
681 : 1 : iface->prefix_matches = g_resource_file_prefix_matches;
682 : 1 : iface->get_relative_path = g_resource_file_get_relative_path;
683 : 1 : iface->resolve_relative_path = g_resource_file_resolve_relative_path;
684 : 1 : iface->get_child_for_display_name = g_resource_file_get_child_for_display_name;
685 : 1 : iface->set_display_name = g_resource_file_set_display_name;
686 : 1 : iface->enumerate_children = g_resource_file_enumerate_children;
687 : 1 : iface->query_info = g_resource_file_query_info;
688 : 1 : iface->query_filesystem_info = g_resource_file_query_filesystem_info;
689 : 1 : iface->query_settable_attributes = g_resource_file_query_settable_attributes;
690 : 1 : iface->query_writable_namespaces = g_resource_file_query_writable_namespaces;
691 : 1 : iface->read_fn = g_resource_file_read;
692 : 1 : iface->monitor_file = g_resource_file_monitor_file;
693 : :
694 : 1 : iface->supports_thread_contexts = TRUE;
695 : 1 : }
696 : :
697 : : static GFileInfo *g_resource_file_enumerator_next_file (GFileEnumerator *enumerator,
698 : : GCancellable *cancellable,
699 : : GError **error);
700 : : static gboolean g_resource_file_enumerator_close (GFileEnumerator *enumerator,
701 : : GCancellable *cancellable,
702 : : GError **error);
703 : :
704 : : static void
705 : 1 : g_resource_file_enumerator_finalize (GObject *object)
706 : : {
707 : : GResourceFileEnumerator *resource;
708 : :
709 : 1 : resource = G_RESOURCE_FILE_ENUMERATOR (object);
710 : :
711 : 1 : g_strfreev (resource->children);
712 : 1 : g_free (resource->path);
713 : 1 : g_free (resource->attributes);
714 : :
715 : 1 : G_OBJECT_CLASS (g_resource_file_enumerator_parent_class)->finalize (object);
716 : 1 : }
717 : :
718 : : static void
719 : 1 : g_resource_file_enumerator_class_init (GResourceFileEnumeratorClass *klass)
720 : : {
721 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
722 : 1 : GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
723 : :
724 : 1 : gobject_class->finalize = g_resource_file_enumerator_finalize;
725 : :
726 : 1 : enumerator_class->next_file = g_resource_file_enumerator_next_file;
727 : 1 : enumerator_class->close_fn = g_resource_file_enumerator_close;
728 : 1 : }
729 : :
730 : : static void
731 : 1 : g_resource_file_enumerator_init (GResourceFileEnumerator *resource)
732 : : {
733 : 1 : }
734 : :
735 : : static GFileEnumerator *
736 : 1 : _g_resource_file_enumerator_new (GResourceFile *file,
737 : : const char *attributes,
738 : : GFileQueryInfoFlags flags,
739 : : GCancellable *cancellable,
740 : : GError **error)
741 : : {
742 : : GResourceFileEnumerator *resource;
743 : : char **children;
744 : : gboolean res;
745 : :
746 : 1 : children = g_resources_enumerate_children (file->path, 0, NULL);
747 [ - + ]: 1 : if (children == NULL &&
748 [ # # ]: 0 : strcmp ("/", file->path) != 0)
749 : : {
750 : 0 : res = g_resources_get_info (file->path, 0, NULL, NULL, NULL);
751 [ # # ]: 0 : if (res)
752 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
753 : : _("The resource at “%s” is not a directory"),
754 : : file->path);
755 : : else
756 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
757 : : _("The resource at “%s” does not exist"),
758 : : file->path);
759 : 0 : return NULL;
760 : : }
761 : :
762 : 1 : resource = g_object_new (G_TYPE_RESOURCE_FILE_ENUMERATOR,
763 : : "container", file,
764 : : NULL);
765 : :
766 : 1 : resource->children = children;
767 : 2 : resource->path = g_strdup (file->path);
768 : 1 : resource->attributes = g_strdup (attributes);
769 : 1 : resource->flags = flags;
770 : :
771 : 1 : return G_FILE_ENUMERATOR (resource);
772 : : }
773 : :
774 : : static GFileInfo *
775 : 3 : g_resource_file_enumerator_next_file (GFileEnumerator *enumerator,
776 : : GCancellable *cancellable,
777 : : GError **error)
778 : : {
779 : 3 : GResourceFileEnumerator *resource = G_RESOURCE_FILE_ENUMERATOR (enumerator);
780 : : char *path;
781 : : GFileInfo *info;
782 : : GFile *file;
783 : :
784 [ + - ]: 3 : if (resource->children == NULL ||
785 [ + + ]: 3 : resource->children[resource->index] == NULL)
786 : 1 : return NULL;
787 : :
788 : 2 : path = g_build_path ("/", resource->path, resource->children[resource->index++], NULL);
789 : 2 : file = g_resource_file_new_for_path (path);
790 : 2 : g_free (path);
791 : :
792 : 2 : info = g_file_query_info (file,
793 : 2 : resource->attributes,
794 : : resource->flags,
795 : : cancellable,
796 : : error);
797 : :
798 : 2 : g_object_unref (file);
799 : :
800 : 2 : return info;
801 : : }
802 : :
803 : : static gboolean
804 : 1 : g_resource_file_enumerator_close (GFileEnumerator *enumerator,
805 : : GCancellable *cancellable,
806 : : GError **error)
807 : : {
808 : 1 : return TRUE;
809 : : }
810 : :
811 : :
812 : : struct _GResourceFileInputStream
813 : : {
814 : : GFileInputStream parent_instance;
815 : : GInputStream *stream;
816 : : GFile *file;
817 : : };
818 : :
819 : : struct _GResourceFileInputStreamClass
820 : : {
821 : : GFileInputStreamClass parent_class;
822 : : };
823 : :
824 : : #define g_resource_file_input_stream_get_type _g_resource_file_input_stream_get_type
825 [ + + + - : 12 : G_DEFINE_TYPE (GResourceFileInputStream, g_resource_file_input_stream, G_TYPE_FILE_INPUT_STREAM)
+ + ]
826 : :
827 : : static gssize g_resource_file_input_stream_read (GInputStream *stream,
828 : : void *buffer,
829 : : gsize count,
830 : : GCancellable *cancellable,
831 : : GError **error);
832 : : static gssize g_resource_file_input_stream_skip (GInputStream *stream,
833 : : gsize count,
834 : : GCancellable *cancellable,
835 : : GError **error);
836 : : static gboolean g_resource_file_input_stream_close (GInputStream *stream,
837 : : GCancellable *cancellable,
838 : : GError **error);
839 : : static goffset g_resource_file_input_stream_tell (GFileInputStream *stream);
840 : : static gboolean g_resource_file_input_stream_can_seek (GFileInputStream *stream);
841 : : static gboolean g_resource_file_input_stream_seek (GFileInputStream *stream,
842 : : goffset offset,
843 : : GSeekType type,
844 : : GCancellable *cancellable,
845 : : GError **error);
846 : : static GFileInfo *g_resource_file_input_stream_query_info (GFileInputStream *stream,
847 : : const char *attributes,
848 : : GCancellable *cancellable,
849 : : GError **error);
850 : :
851 : : static void
852 : 1 : g_resource_file_input_stream_finalize (GObject *object)
853 : : {
854 : 1 : GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (object);
855 : :
856 : 1 : g_object_unref (file->stream);
857 : 1 : g_object_unref (file->file);
858 : 1 : G_OBJECT_CLASS (g_resource_file_input_stream_parent_class)->finalize (object);
859 : 1 : }
860 : :
861 : : static void
862 : 1 : g_resource_file_input_stream_class_init (GResourceFileInputStreamClass *klass)
863 : : {
864 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
865 : 1 : GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
866 : 1 : GFileInputStreamClass *file_stream_class = G_FILE_INPUT_STREAM_CLASS (klass);
867 : :
868 : 1 : gobject_class->finalize = g_resource_file_input_stream_finalize;
869 : :
870 : 1 : stream_class->read_fn = g_resource_file_input_stream_read;
871 : 1 : stream_class->skip = g_resource_file_input_stream_skip;
872 : 1 : stream_class->close_fn = g_resource_file_input_stream_close;
873 : 1 : file_stream_class->tell = g_resource_file_input_stream_tell;
874 : 1 : file_stream_class->can_seek = g_resource_file_input_stream_can_seek;
875 : 1 : file_stream_class->seek = g_resource_file_input_stream_seek;
876 : 1 : file_stream_class->query_info = g_resource_file_input_stream_query_info;
877 : 1 : }
878 : :
879 : : static void
880 : 1 : g_resource_file_input_stream_init (GResourceFileInputStream *info)
881 : : {
882 : 1 : }
883 : :
884 : : static GFileInputStream *
885 : 1 : _g_resource_file_input_stream_new (GInputStream *in_stream, GFile *file)
886 : : {
887 : : GResourceFileInputStream *stream;
888 : :
889 : 1 : stream = g_object_new (G_TYPE_RESOURCE_FILE_INPUT_STREAM, NULL);
890 : 1 : stream->stream = g_object_ref (in_stream);
891 : 1 : stream->file = g_object_ref (file);
892 : :
893 : 1 : return G_FILE_INPUT_STREAM (stream);
894 : : }
895 : :
896 : : static gssize
897 : 2 : g_resource_file_input_stream_read (GInputStream *stream,
898 : : void *buffer,
899 : : gsize count,
900 : : GCancellable *cancellable,
901 : : GError **error)
902 : : {
903 : 2 : GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
904 : 2 : return g_input_stream_read (file->stream,
905 : : buffer, count, cancellable, error);
906 : : }
907 : :
908 : : static gssize
909 : 1 : g_resource_file_input_stream_skip (GInputStream *stream,
910 : : gsize count,
911 : : GCancellable *cancellable,
912 : : GError **error)
913 : : {
914 : 1 : GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
915 : 1 : return g_input_stream_skip (file->stream,
916 : : count, cancellable, error);
917 : : }
918 : :
919 : : static gboolean
920 : 1 : g_resource_file_input_stream_close (GInputStream *stream,
921 : : GCancellable *cancellable,
922 : : GError **error)
923 : : {
924 : 1 : GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
925 : 1 : return g_input_stream_close (file->stream,
926 : : cancellable, error);
927 : : }
928 : :
929 : :
930 : : static goffset
931 : 1 : g_resource_file_input_stream_tell (GFileInputStream *stream)
932 : : {
933 : 1 : GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
934 : :
935 [ - + + - : 1 : if (!G_IS_SEEKABLE (file->stream))
- + - + ]
936 : 0 : return 0;
937 : :
938 : 1 : return g_seekable_tell (G_SEEKABLE (file->stream));
939 : : }
940 : :
941 : : static gboolean
942 : 1 : g_resource_file_input_stream_can_seek (GFileInputStream *stream)
943 : : {
944 : 1 : GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
945 : :
946 [ - + + - : 1 : return G_IS_SEEKABLE (file->stream) && g_seekable_can_seek (G_SEEKABLE (file->stream));
- + + - +
- ]
947 : : }
948 : :
949 : : static gboolean
950 : 1 : g_resource_file_input_stream_seek (GFileInputStream *stream,
951 : : goffset offset,
952 : : GSeekType type,
953 : : GCancellable *cancellable,
954 : : GError **error)
955 : : {
956 : 1 : GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
957 : :
958 [ - + + - : 1 : if (!G_IS_SEEKABLE (file->stream))
- + - + ]
959 : : {
960 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
961 : : _("Input stream doesn’t implement seek"));
962 : 0 : return FALSE;
963 : : }
964 : :
965 : 1 : return g_seekable_seek (G_SEEKABLE (file->stream),
966 : : offset, type, cancellable, error);
967 : : }
968 : :
969 : : static GFileInfo *
970 : 1 : g_resource_file_input_stream_query_info (GFileInputStream *stream,
971 : : const char *attributes,
972 : : GCancellable *cancellable,
973 : : GError **error)
974 : : {
975 : 1 : GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
976 : :
977 : 1 : return g_file_query_info (file->file, attributes, 0, cancellable, error);
978 : : }
|