Coverage Report

Created: 2025-06-13 06:55

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