Coverage Report

Created: 2025-07-23 08:13

/src/pango/subprojects/glib/gio/gresourcefile.c
Line
Count
Source (jump to first uncovered line)
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
0
#define G_TYPE_RESOURCE_FILE_ENUMERATOR         (_g_resource_file_enumerator_get_type ())
79
0
#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
0
#define G_TYPE_RESOURCE_FILE_INPUT_STREAM         (_g_resource_file_input_stream_get_type ())
86
0
#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
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
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
g_resource_file_finalize (GObject *object)
117
0
{
118
0
  GResourceFile *resource;
119
120
0
  resource = G_RESOURCE_FILE (object);
121
122
0
  g_free (resource->path);
123
124
0
  G_OBJECT_CLASS (g_resource_file_parent_class)->finalize (object);
125
0
}
126
127
static void
128
g_resource_file_class_init (GResourceFileClass *klass)
129
0
{
130
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
131
132
0
  gobject_class->finalize = g_resource_file_finalize;
133
134
0
  resource_writable_attributes = g_file_attribute_info_list_new ();
135
0
  resource_writable_namespaces = g_file_attribute_info_list_new ();
136
0
}
137
138
static void
139
g_resource_file_init (GResourceFile *resource)
140
0
{
141
0
}
142
143
static inline gchar *
144
scan_backwards (const gchar *begin,
145
                const gchar *end,
146
                gchar        c)
147
0
{
148
0
  while (end >= begin)
149
0
    {
150
0
      if (*end == c)
151
0
        return (gchar *)end;
152
0
      end--;
153
0
    }
154
155
0
  return NULL;
156
0
}
157
158
static inline void
159
pop_to_previous_part (const gchar  *begin,
160
                      gchar       **out)
161
0
{
162
0
  if (*out > begin)
163
0
    *out = scan_backwards (begin, *out - 1, '/');
164
0
}
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
canonicalize_filename (const char *in)
179
0
{
180
0
  gchar *bptr;
181
0
  char *out;
182
183
0
  bptr = out = g_malloc (strlen (in) + 2);
184
0
  *out = '/';
185
186
0
  while (*in != 0)
187
0
    {
188
0
      g_assert (*out == '/');
189
190
      /* move past slashes */
191
0
      while (*in == '/')
192
0
        in++;
193
194
      /* Handle ./ ../ .\0 ..\0 */
195
0
      if (*in == '.')
196
0
        {
197
          /* If this is ../ or ..\0 move up */
198
0
          if (in[1] == '.' && (in[2] == '/' || in[2] == 0))
199
0
            {
200
0
              pop_to_previous_part (bptr, &out);
201
0
              in += 2;
202
0
              continue;
203
0
            }
204
205
          /* If this is ./ skip past it */
206
0
          if (in[1] == '/' || in[1] == 0)
207
0
            {
208
0
              in += 1;
209
0
              continue;
210
0
            }
211
0
        }
212
213
      /* Scan to the next path piece */
214
0
      while (*in != 0 && *in != '/')
215
0
        *(++out) = *(in++);
216
217
      /* Add trailing /, compress the rest on the next go round. */
218
0
      if (*in == '/')
219
0
        *(++out) = *(in++);
220
0
    }
221
222
  /* Trim trailing / from path */
223
0
  if (out > bptr && *out == '/')
224
0
    *out = 0;
225
0
  else
226
0
    *(++out) = 0;
227
228
0
  return bptr;
229
0
}
230
231
static GFile *
232
g_resource_file_new_for_path (const char *path)
233
0
{
234
0
  GResourceFile *resource = g_object_new (G_TYPE_RESOURCE_FILE, NULL);
235
236
0
  resource->path = canonicalize_filename (path);
237
238
0
  return G_FILE (resource);
239
0
}
240
241
/* Will return %NULL if @uri is malformed */
242
GFile *
243
_g_resource_file_new (const char *uri)
244
0
{
245
0
  GFile *resource;
246
0
  char *path;
247
248
0
  path = g_uri_unescape_string (uri + strlen ("resource:"), NULL);
249
0
  if (path == NULL)
250
0
    return NULL;
251
252
0
  resource = g_resource_file_new_for_path (path);
253
0
  g_free (path);
254
255
0
  return G_FILE (resource);
256
0
}
257
258
static gboolean
259
g_resource_file_is_native (GFile *file)
260
0
{
261
0
  return FALSE;
262
0
}
263
264
static gboolean
265
g_resource_file_has_uri_scheme (GFile      *file,
266
        const char *uri_scheme)
267
0
{
268
0
  return g_ascii_strcasecmp (uri_scheme, "resource") == 0;
269
0
}
270
271
static char *
272
g_resource_file_get_uri_scheme (GFile *file)
273
0
{
274
0
  return g_strdup ("resource");
275
0
}
276
277
static char *
278
g_resource_file_get_basename (GFile *file)
279
0
{
280
0
  gchar *base;
281
282
0
  base = strrchr (G_RESOURCE_FILE (file)->path, '/');
283
0
  return g_strdup (base + 1);
284
0
}
285
286
static char *
287
g_resource_file_get_path (GFile *file)
288
0
{
289
0
  return NULL;
290
0
}
291
292
static char *
293
g_resource_file_get_uri (GFile *file)
294
0
{
295
0
  char *escaped, *res;
296
0
  escaped = g_uri_escape_string (G_RESOURCE_FILE (file)->path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
297
0
  res = g_strconcat ("resource://", escaped, NULL);
298
0
  g_free (escaped);
299
0
  return res;
300
0
}
301
302
static char *
303
g_resource_file_get_parse_name (GFile *file)
304
0
{
305
0
  return g_resource_file_get_uri (file);
306
0
}
307
308
static GFile *
309
g_resource_file_get_parent (GFile *file)
310
0
{
311
0
  GResourceFile *resource = G_RESOURCE_FILE (file);
312
0
  GResourceFile *parent;
313
0
  gchar *end;
314
315
0
  end = strrchr (resource->path, '/');
316
317
0
  if (end == G_RESOURCE_FILE (file)->path)
318
0
    return NULL;
319
320
0
  parent = g_object_new (G_TYPE_RESOURCE_FILE, NULL);
321
0
  parent->path = g_strndup (resource->path,
322
0
          end - resource->path);
323
324
0
  return G_FILE (parent);
325
0
}
326
327
static GFile *
328
g_resource_file_dup (GFile *file)
329
0
{
330
0
  GResourceFile *resource = G_RESOURCE_FILE (file);
331
332
0
  return g_resource_file_new_for_path (resource->path);
333
0
}
334
335
static guint
336
g_resource_file_hash (GFile *file)
337
0
{
338
0
  GResourceFile *resource = G_RESOURCE_FILE (file);
339
340
0
  return g_str_hash (resource->path);
341
0
}
342
343
static gboolean
344
g_resource_file_equal (GFile *file1,
345
           GFile *file2)
346
0
{
347
0
  GResourceFile *resource1 = G_RESOURCE_FILE (file1);
348
0
  GResourceFile *resource2 = G_RESOURCE_FILE (file2);
349
350
0
  return g_str_equal (resource1->path, resource2->path);
351
0
}
352
353
static const char *
354
match_prefix (const char *path,
355
        const char *prefix)
356
0
{
357
0
  size_t prefix_len;
358
359
0
  prefix_len = strlen (prefix);
360
0
  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
0
  if (prefix_len > 0 &&
366
0
      prefix[prefix_len-1] == '/')
367
0
    prefix_len--;
368
369
0
  return path + prefix_len;
370
0
}
371
372
static gboolean
373
g_resource_file_prefix_matches (GFile *parent,
374
        GFile *descendant)
375
0
{
376
0
  GResourceFile *parent_resource = G_RESOURCE_FILE (parent);
377
0
  GResourceFile *descendant_resource = G_RESOURCE_FILE (descendant);
378
0
  const char *remainder;
379
380
0
  remainder = match_prefix (descendant_resource->path, parent_resource->path);
381
0
  if (remainder != NULL && *remainder == '/')
382
0
    return TRUE;
383
0
  return FALSE;
384
0
}
385
386
static char *
387
g_resource_file_get_relative_path (GFile *parent,
388
           GFile *descendant)
389
0
{
390
0
  GResourceFile *parent_resource = G_RESOURCE_FILE (parent);
391
0
  GResourceFile *descendant_resource = G_RESOURCE_FILE (descendant);
392
0
  const char *remainder;
393
394
0
  remainder = match_prefix (descendant_resource->path, parent_resource->path);
395
396
0
  if (remainder != NULL && *remainder == '/')
397
0
    return g_strdup (remainder + 1);
398
0
  return NULL;
399
0
}
400
401
static GFile *
402
g_resource_file_resolve_relative_path (GFile      *file,
403
               const char *relative_path)
404
0
{
405
0
  GResourceFile *resource = G_RESOURCE_FILE (file);
406
0
  char *filename;
407
0
  GFile *child;
408
409
0
  if (relative_path[0] == '/')
410
0
    return g_resource_file_new_for_path (relative_path);
411
412
0
  filename = g_build_path ("/", resource->path, relative_path, NULL);
413
0
  child = g_resource_file_new_for_path (filename);
414
0
  g_free (filename);
415
416
0
  return child;
417
0
}
418
419
static GFileEnumerator *
420
g_resource_file_enumerate_children (GFile                *file,
421
            const char           *attributes,
422
            GFileQueryInfoFlags   flags,
423
            GCancellable         *cancellable,
424
            GError              **error)
425
0
{
426
0
  GResourceFile *resource = G_RESOURCE_FILE (file);
427
0
  return _g_resource_file_enumerator_new (resource,
428
0
            attributes, flags,
429
0
            cancellable, error);
430
0
}
431
432
static GFile *
433
g_resource_file_get_child_for_display_name (GFile        *file,
434
              const char   *display_name,
435
              GError      **error)
436
0
{
437
0
  GFile *new_file;
438
439
0
  new_file = g_file_get_child (file, display_name);
440
441
0
  return new_file;
442
0
}
443
444
static GFileInfo *
445
g_resource_file_query_info (GFile                *file,
446
          const char           *attributes,
447
          GFileQueryInfoFlags   flags,
448
          GCancellable         *cancellable,
449
          GError              **error)
450
0
{
451
0
  GResourceFile *resource = G_RESOURCE_FILE (file);
452
0
  GError *my_error = NULL;
453
0
  GFileInfo *info;
454
0
  GFileAttributeMatcher *matcher;
455
0
  gboolean res;
456
0
  gsize size = 0;
457
0
  guint32 resource_flags = 0;
458
0
  gboolean is_dir;
459
0
  char *base;
460
461
  /* root is always there */
462
0
  if (strcmp ("/", resource->path) == 0)
463
0
    is_dir = TRUE;
464
0
  else
465
0
    is_dir = g_resources_has_children (resource->path);
466
467
0
  if (!is_dir)
468
0
    {
469
0
      res = g_resources_get_info (resource->path, 0, &size, &resource_flags, &my_error);
470
0
      if (!res)
471
0
  {
472
0
    if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
473
0
      {
474
0
        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
475
0
         _("The resource at “%s” does not exist"),
476
0
         resource->path);
477
0
      }
478
0
    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
0
  }
484
0
    }
485
486
0
  matcher = g_file_attribute_matcher_new (attributes);
487
488
0
  info = g_file_info_new ();
489
0
  base = g_resource_file_get_basename (file);
490
0
  g_file_info_set_name (info, base);
491
0
  g_file_info_set_display_name (info, base);
492
493
0
  _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ, TRUE);
494
0
  _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE, FALSE);
495
0
  _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE, FALSE);
496
0
  _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME, FALSE);
497
0
  _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE, FALSE);
498
0
  _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH, FALSE);
499
500
0
  if (is_dir)
501
0
    {
502
0
      g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
503
0
    }
504
0
  else
505
0
    {
506
0
      GBytes *bytes;
507
0
      char *content_type;
508
509
0
      g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
510
0
      g_file_info_set_size (info, size);
511
512
0
      if ((_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
513
0
           ((~resource_flags & G_RESOURCE_FLAGS_COMPRESSED) && 
514
0
            _g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))) &&
515
0
          (bytes = g_resources_lookup_data (resource->path, 0, NULL)))
516
0
        {
517
0
          const guchar *data;
518
0
          gsize data_size;
519
520
0
          data = g_bytes_get_data (bytes, &data_size);
521
0
          content_type = g_content_type_guess (base, data, data_size, NULL);
522
523
0
          g_bytes_unref (bytes);
524
0
        }
525
0
      else
526
0
        content_type = NULL;
527
528
0
      if (content_type)
529
0
        {
530
0
          _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE, content_type);
531
0
          _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, content_type);
532
533
0
          g_free (content_type);
534
0
        }
535
0
    }
536
537
0
  g_free (base);
538
0
  g_file_attribute_matcher_unref (matcher);
539
540
0
  return info;
541
0
}
542
543
static GFileInfo *
544
g_resource_file_query_filesystem_info (GFile         *file,
545
                                       const char    *attributes,
546
                                       GCancellable  *cancellable,
547
                                       GError       **error)
548
0
{
549
0
  GFileInfo *info;
550
0
  GFileAttributeMatcher *matcher;
551
552
0
  info = g_file_info_new ();
553
554
0
  matcher = g_file_attribute_matcher_new (attributes);
555
0
  if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE))
556
0
    g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "resource");
557
558
0
  if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
559
0
    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
560
561
0
  g_file_attribute_matcher_unref (matcher);
562
563
0
  return info;
564
0
}
565
566
static GFileAttributeInfoList *
567
g_resource_file_query_settable_attributes (GFile         *file,
568
             GCancellable  *cancellable,
569
             GError       **error)
570
0
{
571
0
  return g_file_attribute_info_list_ref (resource_writable_attributes);
572
0
}
573
574
static GFileAttributeInfoList *
575
g_resource_file_query_writable_namespaces (GFile         *file,
576
             GCancellable  *cancellable,
577
             GError       **error)
578
0
{
579
0
  return g_file_attribute_info_list_ref (resource_writable_namespaces);
580
0
}
581
582
static GFileInputStream *
583
g_resource_file_read (GFile         *file,
584
          GCancellable  *cancellable,
585
          GError       **error)
586
0
{
587
0
  GResourceFile *resource = G_RESOURCE_FILE (file);
588
0
  GError *my_error = NULL;
589
0
  GInputStream *stream;
590
0
  GFileInputStream *res;
591
592
0
  stream = g_resources_open_stream (resource->path, 0, &my_error);
593
594
0
  if (stream == NULL)
595
0
    {
596
0
      if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
597
0
  {
598
0
    g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
599
0
           _("The resource at “%s” does not exist"),
600
0
           resource->path);
601
0
  }
602
0
      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
0
    }
608
609
0
  res = _g_resource_file_input_stream_new (stream, file);
610
0
  g_object_unref (stream);
611
0
  return res;
612
0
}
613
614
typedef GFileMonitor GResourceFileMonitor;
615
typedef GFileMonitorClass GResourceFileMonitorClass;
616
617
GType g_resource_file_monitor_get_type (void);
618
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
0
{
624
0
  return TRUE;
625
0
}
626
627
static void
628
g_resource_file_monitor_init (GResourceFileMonitor *monitor)
629
0
{
630
0
}
631
632
static void
633
g_resource_file_monitor_class_init (GResourceFileMonitorClass *class)
634
0
{
635
0
  class->cancel = g_resource_file_monitor_cancel;
636
0
}
637
638
static GFileMonitor *
639
g_resource_file_monitor_file (GFile              *file,
640
                              GFileMonitorFlags   flags,
641
                              GCancellable       *cancellable,
642
                              GError            **error)
643
0
{
644
0
  return g_object_new (g_resource_file_monitor_get_type (), NULL);
645
0
}
646
647
static GFile *
648
g_resource_file_set_display_name (GFile         *file,
649
                                  const char    *display_name,
650
                                  GCancellable  *cancellable,
651
                                  GError       **error)
652
0
{
653
0
  g_set_error_literal (error,
654
0
                       G_IO_ERROR,
655
0
                       G_IO_ERROR_NOT_SUPPORTED,
656
0
                       _("Resource files cannot be renamed"));
657
0
  return NULL;
658
0
}
659
660
static gboolean
661
g_resource_file_query_exists (GFile        *file,
662
                              GCancellable *cancellable)
663
0
{
664
0
  GResourceFile *resource = G_RESOURCE_FILE (file);
665
666
0
  return g_resources_get_info (resource->path, 0, NULL, NULL, NULL);
667
0
}
668
669
static void
670
g_resource_file_file_iface_init (GFileIface *iface)
671
0
{
672
0
  iface->dup = g_resource_file_dup;
673
0
  iface->hash = g_resource_file_hash;
674
0
  iface->equal = g_resource_file_equal;
675
0
  iface->is_native = g_resource_file_is_native;
676
0
  iface->has_uri_scheme = g_resource_file_has_uri_scheme;
677
0
  iface->get_uri_scheme = g_resource_file_get_uri_scheme;
678
0
  iface->get_basename = g_resource_file_get_basename;
679
0
  iface->get_path = g_resource_file_get_path;
680
0
  iface->get_uri = g_resource_file_get_uri;
681
0
  iface->get_parse_name = g_resource_file_get_parse_name;
682
0
  iface->get_parent = g_resource_file_get_parent;
683
0
  iface->prefix_matches = g_resource_file_prefix_matches;
684
0
  iface->get_relative_path = g_resource_file_get_relative_path;
685
0
  iface->resolve_relative_path = g_resource_file_resolve_relative_path;
686
0
  iface->get_child_for_display_name = g_resource_file_get_child_for_display_name;
687
0
  iface->set_display_name = g_resource_file_set_display_name;
688
0
  iface->enumerate_children = g_resource_file_enumerate_children;
689
0
  iface->query_info = g_resource_file_query_info;
690
0
  iface->query_filesystem_info = g_resource_file_query_filesystem_info;
691
0
  iface->query_settable_attributes = g_resource_file_query_settable_attributes;
692
0
  iface->query_writable_namespaces = g_resource_file_query_writable_namespaces;
693
0
  iface->read_fn = g_resource_file_read;
694
0
  iface->monitor_file = g_resource_file_monitor_file;
695
0
  iface->query_exists = g_resource_file_query_exists;
696
697
0
  iface->supports_thread_contexts = TRUE;
698
0
}
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
g_resource_file_enumerator_finalize (GObject *object)
709
0
{
710
0
  GResourceFileEnumerator *resource;
711
712
0
  resource = G_RESOURCE_FILE_ENUMERATOR (object);
713
714
0
  g_strfreev (resource->children);
715
0
  g_free (resource->path);
716
0
  g_free (resource->attributes);
717
718
0
  G_OBJECT_CLASS (g_resource_file_enumerator_parent_class)->finalize (object);
719
0
}
720
721
static void
722
g_resource_file_enumerator_class_init (GResourceFileEnumeratorClass *klass)
723
0
{
724
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
725
0
  GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
726
727
0
  gobject_class->finalize = g_resource_file_enumerator_finalize;
728
729
0
  enumerator_class->next_file = g_resource_file_enumerator_next_file;
730
0
  enumerator_class->close_fn = g_resource_file_enumerator_close;
731
0
}
732
733
static void
734
g_resource_file_enumerator_init (GResourceFileEnumerator *resource)
735
0
{
736
0
}
737
738
static GFileEnumerator *
739
_g_resource_file_enumerator_new (GResourceFile *file,
740
         const char           *attributes,
741
         GFileQueryInfoFlags   flags,
742
         GCancellable         *cancellable,
743
         GError              **error)
744
0
{
745
0
  GResourceFileEnumerator *resource;
746
0
  char **children;
747
0
  gboolean res;
748
749
0
  children = g_resources_enumerate_children (file->path, 0, NULL);
750
0
  if (children == NULL &&
751
0
      strcmp ("/", file->path) != 0)
752
0
    {
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
0
         _("The resource at “%s” is not a directory"),
757
0
         file->path);
758
0
      else
759
0
  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
760
0
         _("The resource at “%s” does not exist"),
761
0
         file->path);
762
0
      return NULL;
763
0
    }
764
765
0
  resource = g_object_new (G_TYPE_RESOURCE_FILE_ENUMERATOR,
766
0
         "container", file,
767
0
         NULL);
768
769
0
  resource->children = children;
770
0
  resource->path = g_strdup (file->path);
771
0
  resource->attributes = g_strdup (attributes);
772
0
  resource->flags = flags;
773
774
0
  return G_FILE_ENUMERATOR (resource);
775
0
}
776
777
static GFileInfo *
778
g_resource_file_enumerator_next_file (GFileEnumerator  *enumerator,
779
              GCancellable     *cancellable,
780
              GError          **error)
781
0
{
782
0
  GResourceFileEnumerator *resource = G_RESOURCE_FILE_ENUMERATOR (enumerator);
783
0
  char *path;
784
0
  GFileInfo *info;
785
0
  GFile *file;
786
787
0
  if (resource->children == NULL ||
788
0
      resource->children[resource->index] == NULL)
789
0
    return NULL;
790
791
0
  path = g_build_path ("/", resource->path, resource->children[resource->index++], NULL);
792
0
  file = g_resource_file_new_for_path (path);
793
0
  g_free (path);
794
795
0
  info = g_file_query_info (file,
796
0
          resource->attributes,
797
0
          resource->flags,
798
0
          cancellable,
799
0
          error);
800
801
0
  g_object_unref (file);
802
803
0
  return info;
804
0
}
805
806
static gboolean
807
g_resource_file_enumerator_close (GFileEnumerator  *enumerator,
808
             GCancellable     *cancellable,
809
             GError          **error)
810
0
{
811
0
  return TRUE;
812
0
}
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
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
g_resource_file_input_stream_finalize (GObject *object)
856
0
{
857
0
  GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (object);
858
859
0
  g_object_unref (file->stream);
860
0
  g_object_unref (file->file);
861
0
  G_OBJECT_CLASS (g_resource_file_input_stream_parent_class)->finalize (object);
862
0
}
863
864
static void
865
g_resource_file_input_stream_class_init (GResourceFileInputStreamClass *klass)
866
0
{
867
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
868
0
  GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
869
0
  GFileInputStreamClass *file_stream_class = G_FILE_INPUT_STREAM_CLASS (klass);
870
871
0
  gobject_class->finalize = g_resource_file_input_stream_finalize;
872
873
0
  stream_class->read_fn = g_resource_file_input_stream_read;
874
0
  stream_class->skip = g_resource_file_input_stream_skip;
875
0
  stream_class->close_fn = g_resource_file_input_stream_close;
876
0
  file_stream_class->tell = g_resource_file_input_stream_tell;
877
0
  file_stream_class->can_seek = g_resource_file_input_stream_can_seek;
878
0
  file_stream_class->seek = g_resource_file_input_stream_seek;
879
0
  file_stream_class->query_info = g_resource_file_input_stream_query_info;
880
0
}
881
882
static void
883
g_resource_file_input_stream_init (GResourceFileInputStream *info)
884
0
{
885
0
}
886
887
static GFileInputStream *
888
_g_resource_file_input_stream_new (GInputStream *in_stream, GFile *file)
889
0
{
890
0
  GResourceFileInputStream *stream;
891
892
0
  stream = g_object_new (G_TYPE_RESOURCE_FILE_INPUT_STREAM, NULL);
893
0
  stream->stream = g_object_ref (in_stream);
894
0
  stream->file = g_object_ref (file);
895
896
0
  return G_FILE_INPUT_STREAM (stream);
897
0
}
898
899
static gssize
900
g_resource_file_input_stream_read (GInputStream  *stream,
901
           void          *buffer,
902
           gsize          count,
903
           GCancellable  *cancellable,
904
           GError       **error)
905
0
{
906
0
  GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
907
0
  return g_input_stream_read (file->stream,
908
0
            buffer, count, cancellable, error);
909
0
}
910
911
static gssize
912
g_resource_file_input_stream_skip (GInputStream  *stream,
913
           gsize          count,
914
           GCancellable  *cancellable,
915
           GError       **error)
916
0
{
917
0
  GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
918
0
  return g_input_stream_skip (file->stream,
919
0
            count, cancellable, error);
920
0
}
921
922
static gboolean
923
g_resource_file_input_stream_close (GInputStream  *stream,
924
            GCancellable  *cancellable,
925
            GError       **error)
926
0
{
927
0
  GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
928
0
  return g_input_stream_close (file->stream,
929
0
             cancellable, error);
930
0
}
931
932
933
static goffset
934
g_resource_file_input_stream_tell (GFileInputStream *stream)
935
0
{
936
0
  GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
937
938
0
  if (!G_IS_SEEKABLE (file->stream))
939
0
      return 0;
940
941
0
  return g_seekable_tell (G_SEEKABLE (file->stream));
942
0
}
943
944
static gboolean
945
g_resource_file_input_stream_can_seek (GFileInputStream *stream)
946
0
{
947
0
  GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
948
949
0
  return G_IS_SEEKABLE (file->stream) && g_seekable_can_seek (G_SEEKABLE (file->stream));
950
0
}
951
952
static gboolean
953
g_resource_file_input_stream_seek (GFileInputStream  *stream,
954
           goffset            offset,
955
           GSeekType          type,
956
           GCancellable      *cancellable,
957
           GError           **error)
958
0
{
959
0
  GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
960
961
0
  if (!G_IS_SEEKABLE (file->stream))
962
0
    {
963
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
964
0
         _("Input stream doesn’t implement seek"));
965
0
      return FALSE;
966
0
    }
967
968
0
  return g_seekable_seek (G_SEEKABLE (file->stream),
969
0
        offset, type, cancellable, error);
970
0
}
971
972
static GFileInfo *
973
g_resource_file_input_stream_query_info (GFileInputStream  *stream,
974
           const char        *attributes,
975
           GCancellable      *cancellable,
976
           GError           **error)
977
0
{
978
0
  GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
979
980
0
  return g_file_query_info (file->file, attributes, 0, cancellable, error);
981
0
}