Coverage Report

Created: 2025-07-01 07:09

/src/glib/gio/gresource.c
Line
Count
Source (jump to first uncovered line)
1
/* GIO - GLib Input, Output and Streaming Library
2
 *
3
 * Copyright © 2011 Red Hat, Inc
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General
16
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17
 *
18
 * Authors: Alexander Larsson <alexl@redhat.com>
19
 */
20
21
#include "config.h"
22
23
#include <string.h>
24
25
#include "gresource.h"
26
#include <gvdb/gvdb-reader.h>
27
#include <gi18n-lib.h>
28
#include <gstdio.h>
29
#include <gio/gfile.h>
30
#include <gio/gioerror.h>
31
#include <gio/gmemoryinputstream.h>
32
#include <gio/gzlibdecompressor.h>
33
#include <gio/gconverterinputstream.h>
34
35
#include "glib-private.h"
36
37
struct _GResource
38
{
39
  int ref_count;
40
41
  GvdbTable *table;
42
};
43
44
static void register_lazy_static_resources (void);
45
46
G_DEFINE_BOXED_TYPE (GResource, g_resource, g_resource_ref, g_resource_unref)
47
48
/**
49
 * SECTION:gresource
50
 * @short_description: Resource framework
51
 * @include: gio/gio.h
52
 *
53
 * Applications and libraries often contain binary or textual data that is
54
 * really part of the application, rather than user data. For instance
55
 * #GtkBuilder .ui files, splashscreen images, GMenu markup XML, CSS files,
56
 * icons, etc. These are often shipped as files in `$datadir/appname`, or
57
 * manually included as literal strings in the code.
58
 *
59
 * The #GResource API and the [glib-compile-resources][glib-compile-resources] program
60
 * provide a convenient and efficient alternative to this which has some nice properties. You
61
 * maintain the files as normal files, so its easy to edit them, but during the build the files
62
 * are combined into a binary bundle that is linked into the executable. This means that loading
63
 * the resource files are efficient (as they are already in memory, shared with other instances) and
64
 * simple (no need to check for things like I/O errors or locate the files in the filesystem). It
65
 * also makes it easier to create relocatable applications.
66
 *
67
 * Resource files can also be marked as compressed. Such files will be included in the resource bundle
68
 * in a compressed form, but will be automatically uncompressed when the resource is used. This
69
 * is very useful e.g. for larger text files that are parsed once (or rarely) and then thrown away.
70
 *
71
 * Resource files can also be marked to be preprocessed, by setting the value of the
72
 * `preprocess` attribute to a comma-separated list of preprocessing options.
73
 * The only options currently supported are:
74
 *
75
 * `xml-stripblanks` which will use the xmllint command
76
 * to strip ignorable whitespace from the XML file. For this to work,
77
 * the `XMLLINT` environment variable must be set to the full path to
78
 * the xmllint executable, or xmllint must be in the `PATH`; otherwise
79
 * the preprocessing step is skipped.
80
 *
81
 * `to-pixdata` (deprecated since gdk-pixbuf 2.32) which will use the
82
 * `gdk-pixbuf-pixdata` command to convert images to the #GdkPixdata format,
83
 * which allows you to create pixbufs directly using the data inside the
84
 * resource file, rather than an (uncompressed) copy of it. For this, the
85
 * `gdk-pixbuf-pixdata` program must be in the `PATH`, or the
86
 * `GDK_PIXBUF_PIXDATA` environment variable must be set to the full path to the
87
 * `gdk-pixbuf-pixdata` executable; otherwise the resource compiler will abort.
88
 * `to-pixdata` has been deprecated since gdk-pixbuf 2.32, as #GResource
89
 * supports embedding modern image formats just as well. Instead of using it,
90
 * embed a PNG or SVG file in your #GResource.
91
 *
92
 * `json-stripblanks` which will use the `json-glib-format` command to strip
93
 * ignorable whitespace from the JSON file. For this to work, the
94
 * `JSON_GLIB_FORMAT` environment variable must be set to the full path to the
95
 * `json-glib-format` executable, or it must be in the `PATH`;
96
 * otherwise the preprocessing step is skipped. In addition, at least version
97
 * 1.6 of `json-glib-format` is required.
98
 *
99
 * Resource files will be exported in the GResource namespace using the
100
 * combination of the given `prefix` and the filename from the `file` element.
101
 * The `alias` attribute can be used to alter the filename to expose them at a
102
 * different location in the resource namespace. Typically, this is used to
103
 * include files from a different source directory without exposing the source
104
 * directory in the resource namespace, as in the example below.
105
 *
106
 * Resource bundles are created by the [glib-compile-resources][glib-compile-resources] program
107
 * which takes an XML file that describes the bundle, and a set of files that the XML references. These
108
 * are combined into a binary resource bundle.
109
 *
110
 * An example resource description:
111
 * |[
112
 * <?xml version="1.0" encoding="UTF-8"?>
113
 * <gresources>
114
 *   <gresource prefix="/org/gtk/Example">
115
 *     <file>data/splashscreen.png</file>
116
 *     <file compressed="true">dialog.ui</file>
117
 *     <file preprocess="xml-stripblanks">menumarkup.xml</file>
118
 *     <file alias="example.css">data/example.css</file>
119
 *   </gresource>
120
 * </gresources>
121
 * ]|
122
 *
123
 * This will create a resource bundle with the following files:
124
 * |[
125
 * /org/gtk/Example/data/splashscreen.png
126
 * /org/gtk/Example/dialog.ui
127
 * /org/gtk/Example/menumarkup.xml
128
 * /org/gtk/Example/example.css
129
 * ]|
130
 *
131
 * Note that all resources in the process share the same namespace, so use Java-style
132
 * path prefixes (like in the above example) to avoid conflicts.
133
 *
134
 * You can then use [glib-compile-resources][glib-compile-resources] to compile the XML to a
135
 * binary bundle that you can load with g_resource_load(). However, its more common to use the --generate-source and
136
 * --generate-header arguments to create a source file and header to link directly into your application.
137
 * This will generate `get_resource()`, `register_resource()` and
138
 * `unregister_resource()` functions, prefixed by the `--c-name` argument passed
139
 * to [glib-compile-resources][glib-compile-resources]. `get_resource()` returns
140
 * the generated #GResource object. The register and unregister functions
141
 * register the resource so its files can be accessed using
142
 * g_resources_lookup_data().
143
 *
144
 * Once a #GResource has been created and registered all the data in it can be accessed globally in the process by
145
 * using API calls like g_resources_open_stream() to stream the data or g_resources_lookup_data() to get a direct pointer
146
 * to the data. You can also use URIs like "resource:///org/gtk/Example/data/splashscreen.png" with #GFile to access
147
 * the resource data.
148
 *
149
 * Some higher-level APIs, such as #GtkApplication, will automatically load
150
 * resources from certain well-known paths in the resource namespace as a
151
 * convenience. See the documentation for those APIs for details.
152
 *
153
 * There are two forms of the generated source, the default version uses the compiler support for constructor
154
 * and destructor functions (where available) to automatically create and register the #GResource on startup
155
 * or library load time. If you pass `--manual-register`, two functions to register/unregister the resource are created
156
 * instead. This requires an explicit initialization call in your application/library, but it works on all platforms,
157
 * even on the minor ones where constructors are not supported. (Constructor support is available for at least Win32, Mac OS and Linux.)
158
 *
159
 * Note that resource data can point directly into the data segment of e.g. a library, so if you are unloading libraries
160
 * during runtime you need to be very careful with keeping around pointers to data from a resource, as this goes away
161
 * when the library is unloaded. However, in practice this is not generally a problem, since most resource accesses
162
 * are for your own resources, and resource data is often used once, during parsing, and then released.
163
 *
164
 * When debugging a program or testing a change to an installed version, it is often useful to be able to
165
 * replace resources in the program or library, without recompiling, for debugging or quick hacking and testing
166
 * purposes. Since GLib 2.50, it is possible to use the `G_RESOURCE_OVERLAYS` environment variable to selectively overlay
167
 * resources with replacements from the filesystem.  It is a %G_SEARCHPATH_SEPARATOR-separated list of substitutions to perform
168
 * during resource lookups. It is ignored when running in a setuid process.
169
 *
170
 * A substitution has the form
171
 *
172
 * |[
173
 *    /org/gtk/libgtk=/home/desrt/gtk-overlay
174
 * ]|
175
 *
176
 * The part before the `=` is the resource subpath for which the overlay applies.  The part after is a
177
 * filesystem path which contains files and subdirectories as you would like to be loaded as resources with the
178
 * equivalent names.
179
 *
180
 * In the example above, if an application tried to load a resource with the resource path
181
 * `/org/gtk/libgtk/ui/gtkdialog.ui` then GResource would check the filesystem path
182
 * `/home/desrt/gtk-overlay/ui/gtkdialog.ui`.  If a file was found there, it would be used instead.  This is an
183
 * overlay, not an outright replacement, which means that if a file is not found at that path, the built-in
184
 * version will be used instead.  Whiteouts are not currently supported.
185
 *
186
 * Substitutions must start with a slash, and must not contain a trailing slash before the '='.  The path after
187
 * the slash should ideally be absolute, but this is not strictly required.  It is possible to overlay the
188
 * location of a single resource with an individual file.
189
 *
190
 * Since: 2.32
191
 */
192
193
/**
194
 * GStaticResource:
195
 *
196
 * #GStaticResource is an opaque data structure and can only be accessed
197
 * using the following functions.
198
 **/
199
typedef gboolean (* CheckCandidate) (const gchar *candidate, gpointer user_data);
200
201
static gboolean
202
open_overlay_stream (const gchar *candidate,
203
                     gpointer     user_data)
204
0
{
205
0
  GInputStream **res = (GInputStream **) user_data;
206
0
  GError *error = NULL;
207
0
  GFile *file;
208
209
0
  file = g_file_new_for_path (candidate);
210
0
  *res = (GInputStream *) g_file_read (file, NULL, &error);
211
212
0
  if (*res)
213
0
    {
214
0
      g_message ("Opened file '%s' as a resource overlay", candidate);
215
0
    }
216
0
  else
217
0
    {
218
0
      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
219
0
        g_warning ("Can't open overlay file '%s': %s", candidate, error->message);
220
0
      g_error_free (error);
221
0
    }
222
223
0
  g_object_unref (file);
224
225
0
  return *res != NULL;
226
0
}
227
228
static gboolean
229
get_overlay_bytes (const gchar *candidate,
230
                   gpointer     user_data)
231
0
{
232
0
  GBytes **res = (GBytes **) user_data;
233
0
  GMappedFile *mapped_file;
234
0
  GError *error = NULL;
235
236
0
  mapped_file = g_mapped_file_new (candidate, FALSE, &error);
237
238
0
  if (mapped_file)
239
0
    {
240
0
      g_message ("Mapped file '%s' as a resource overlay", candidate);
241
0
      *res = g_mapped_file_get_bytes (mapped_file);
242
0
      g_mapped_file_unref (mapped_file);
243
0
    }
244
0
  else
245
0
    {
246
0
      if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
247
0
        g_warning ("Can't mmap overlay file '%s': %s", candidate, error->message);
248
0
      g_error_free (error);
249
0
    }
250
251
0
  return *res != NULL;
252
0
}
253
254
static gboolean
255
enumerate_overlay_dir (const gchar *candidate,
256
                       gpointer     user_data)
257
0
{
258
0
  GHashTable **hash = (GHashTable **) user_data;
259
0
  GError *error = NULL;
260
0
  GDir *dir;
261
0
  const gchar *name;
262
263
0
  dir = g_dir_open (candidate, 0, &error);
264
0
  if (dir)
265
0
    {
266
0
      if (*hash == NULL)
267
        /* note: keep in sync with same line below */
268
0
        *hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
269
270
0
      g_message ("Enumerating directory '%s' as resource overlay", candidate);
271
272
0
      while ((name = g_dir_read_name (dir)))
273
0
        {
274
0
          gchar *fullname = g_build_filename (candidate, name, NULL);
275
276
          /* match gvdb behaviour by suffixing "/" on dirs */
277
0
          if (g_file_test (fullname, G_FILE_TEST_IS_DIR))
278
0
            g_hash_table_add (*hash, g_strconcat (name, "/", NULL));
279
0
          else
280
0
            g_hash_table_add (*hash, g_strdup (name));
281
282
0
          g_free (fullname);
283
0
        }
284
285
0
      g_dir_close (dir);
286
0
    }
287
0
  else
288
0
    {
289
0
      if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
290
0
        g_warning ("Can't enumerate overlay directory '%s': %s", candidate, error->message);
291
0
      g_error_free (error);
292
0
      return FALSE;
293
0
    }
294
295
  /* We may want to enumerate results from more than one overlay
296
   * directory.
297
   */
298
0
  return FALSE;
299
0
}
300
301
typedef struct {
302
  gsize size;
303
  guint32 flags;
304
} InfoData;
305
306
static gboolean
307
get_overlay_info (const gchar *candidate,
308
                  gpointer     user_data)
309
0
{
310
0
  InfoData *info = user_data;
311
0
  GStatBuf buf;
312
313
0
  if (g_stat (candidate, &buf) < 0)
314
0
    return FALSE;
315
316
0
  info->size = buf.st_size;
317
0
  info->flags = G_RESOURCE_FLAGS_NONE;
318
319
0
  return TRUE;
320
0
}
321
322
static gboolean
323
g_resource_find_overlay (const gchar    *path,
324
                         CheckCandidate  check,
325
                         gpointer        user_data)
326
0
{
327
  /* This is a null-terminated array of replacement strings (with '=' inside) */
328
0
  static const gchar * const *overlay_dirs;
329
0
  gboolean res = FALSE;
330
0
  gint path_len = -1;
331
0
  gint i;
332
333
  /* We try to be very fast in case there are no overlays.  Otherwise,
334
   * we can take a bit more time...
335
   */
336
337
0
  if (g_once_init_enter (&overlay_dirs))
338
0
    {
339
0
      gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
340
0
      const gchar * const *result;
341
0
      const gchar *envvar;
342
343
      /* Don’t load overlays if setuid, as they could allow reading privileged
344
       * files. */
345
0
      envvar = !is_setuid ? g_getenv ("G_RESOURCE_OVERLAYS") : NULL;
346
0
      if (envvar != NULL)
347
0
        {
348
0
          gchar **parts;
349
0
          gint i, j;
350
351
0
          parts = g_strsplit (envvar, G_SEARCHPATH_SEPARATOR_S, 0);
352
353
          /* Sanity check the parts, dropping those that are invalid.
354
           * 'i' may grow faster than 'j'.
355
           */
356
0
          for (i = j = 0; parts[i]; i++)
357
0
            {
358
0
              gchar *part = parts[i];
359
0
              gchar *eq;
360
361
0
              eq = strchr (part, '=');
362
0
              if (eq == NULL)
363
0
                {
364
0
                  g_critical ("G_RESOURCE_OVERLAYS segment '%s' lacks '='.  Ignoring.", part);
365
0
                  g_free (part);
366
0
                  continue;
367
0
                }
368
369
0
              if (eq == part)
370
0
                {
371
0
                  g_critical ("G_RESOURCE_OVERLAYS segment '%s' lacks path before '='.  Ignoring.", part);
372
0
                  g_free (part);
373
0
                  continue;
374
0
                }
375
376
0
              if (eq[1] == '\0')
377
0
                {
378
0
                  g_critical ("G_RESOURCE_OVERLAYS segment '%s' lacks path after '='.  Ignoring", part);
379
0
                  g_free (part);
380
0
                  continue;
381
0
                }
382
383
0
              if (part[0] != '/')
384
0
                {
385
0
                  g_critical ("G_RESOURCE_OVERLAYS segment '%s' lacks leading '/'.  Ignoring.", part);
386
0
                  g_free (part);
387
0
                  continue;
388
0
                }
389
390
0
              if (eq[-1] == '/')
391
0
                {
392
0
                  g_critical ("G_RESOURCE_OVERLAYS segment '%s' has trailing '/' before '='.  Ignoring", part);
393
0
                  g_free (part);
394
0
                  continue;
395
0
                }
396
397
0
              if (!g_path_is_absolute (eq + 1))
398
0
                {
399
0
                  g_critical ("G_RESOURCE_OVERLAYS segment '%s' does not have an absolute path after '='.  Ignoring", part);
400
0
                  g_free (part);
401
0
                  continue;
402
0
                }
403
404
0
              g_message ("Adding GResources overlay '%s'", part);
405
0
              parts[j++] = part;
406
0
            }
407
408
0
          parts[j] = NULL;
409
410
0
          result = (const gchar **) parts;
411
0
        }
412
0
      else
413
0
        {
414
          /* We go out of the way to avoid malloc() in the normal case
415
           * where the environment variable is not set.
416
           */
417
0
          static const gchar * const empty_strv[0 + 1];
418
0
          result = empty_strv;
419
0
        }
420
421
0
      g_once_init_leave (&overlay_dirs, result);
422
0
    }
423
424
0
  for (i = 0; overlay_dirs[i]; i++)
425
0
    {
426
0
      const gchar *src;
427
0
      gint src_len;
428
0
      const gchar *dst;
429
0
      gint dst_len;
430
0
      gchar *candidate;
431
432
0
      {
433
0
        gchar *eq;
434
435
        /* split the overlay into src/dst */
436
0
        src = overlay_dirs[i];
437
0
        eq = strchr (src, '=');
438
0
        g_assert (eq); /* we checked this already */
439
0
        src_len = eq - src;
440
0
        dst = eq + 1;
441
        /* hold off on dst_len because we will probably fail the checks below */
442
0
      }
443
444
0
      if (path_len == -1)
445
0
        path_len = strlen (path);
446
447
      /* The entire path is too short to match the source */
448
0
      if (path_len < src_len)
449
0
        continue;
450
451
      /* It doesn't match the source */
452
0
      if (memcmp (path, src, src_len) != 0)
453
0
        continue;
454
455
      /* The prefix matches, but it's not a complete path component */
456
0
      if (path[src_len] && path[src_len] != '/')
457
0
        continue;
458
459
      /* OK.  Now we need this. */
460
0
      dst_len = strlen (dst);
461
462
      /* The candidate will be composed of:
463
       *
464
       *    dst + remaining_path + nul
465
       */
466
0
      candidate = g_malloc (dst_len + (path_len - src_len) + 1);
467
0
      memcpy (candidate, dst, dst_len);
468
0
      memcpy (candidate + dst_len, path + src_len, path_len - src_len);
469
0
      candidate[dst_len + (path_len - src_len)] = '\0';
470
471
      /* No matter what, 'r' is what we need, including the case where
472
       * we are trying to enumerate a directory.
473
       */
474
0
      res = (* check) (candidate, user_data);
475
0
      g_free (candidate);
476
477
0
      if (res)
478
0
        break;
479
0
    }
480
481
0
  return res;
482
0
}
483
484
/**
485
 * g_resource_error_quark:
486
 *
487
 * Gets the #GResource Error Quark.
488
 *
489
 * Returns: a #GQuark
490
 *
491
 * Since: 2.32
492
 */
493
G_DEFINE_QUARK (g-resource-error-quark, g_resource_error)
494
495
/**
496
 * g_resource_ref:
497
 * @resource: A #GResource
498
 *
499
 * Atomically increments the reference count of @resource by one. This
500
 * function is MT-safe and may be called from any thread.
501
 *
502
 * Returns: The passed in #GResource
503
 *
504
 * Since: 2.32
505
 **/
506
GResource *
507
g_resource_ref (GResource *resource)
508
0
{
509
0
  g_atomic_int_inc (&resource->ref_count);
510
0
  return resource;
511
0
}
512
513
/**
514
 * g_resource_unref:
515
 * @resource: A #GResource
516
 *
517
 * Atomically decrements the reference count of @resource by one. If the
518
 * reference count drops to 0, all memory allocated by the resource is
519
 * released. This function is MT-safe and may be called from any
520
 * thread.
521
 *
522
 * Since: 2.32
523
 **/
524
void
525
g_resource_unref (GResource *resource)
526
0
{
527
0
  if (g_atomic_int_dec_and_test (&resource->ref_count))
528
0
    {
529
0
      gvdb_table_free (resource->table);
530
0
      g_free (resource);
531
0
    }
532
0
}
533
534
/*< internal >
535
 * g_resource_new_from_table:
536
 * @table: (transfer full): a GvdbTable
537
 *
538
 * Returns: (transfer full): a new #GResource for @table
539
 */
540
static GResource *
541
g_resource_new_from_table (GvdbTable *table)
542
0
{
543
0
  GResource *resource;
544
545
0
  resource = g_new (GResource, 1);
546
0
  resource->ref_count = 1;
547
0
  resource->table = table;
548
549
0
  return resource;
550
0
}
551
552
static void
553
g_resource_error_from_gvdb_table_error (GError **g_resource_error,
554
                                        GError  *gvdb_table_error  /* (transfer full) */)
555
0
{
556
0
  if (g_error_matches (gvdb_table_error, G_FILE_ERROR, G_FILE_ERROR_INVAL))
557
0
    g_set_error_literal (g_resource_error,
558
0
                         G_RESOURCE_ERROR, G_RESOURCE_ERROR_INTERNAL,
559
0
                         gvdb_table_error->message);
560
0
  else
561
0
    g_propagate_error (g_resource_error, g_steal_pointer (&gvdb_table_error));
562
0
  g_clear_error (&gvdb_table_error);
563
0
}
564
565
/**
566
 * g_resource_new_from_data:
567
 * @data: A #GBytes
568
 * @error: return location for a #GError, or %NULL
569
 *
570
 * Creates a GResource from a reference to the binary resource bundle.
571
 * This will keep a reference to @data while the resource lives, so
572
 * the data should not be modified or freed.
573
 *
574
 * If you want to use this resource in the global resource namespace you need
575
 * to register it with g_resources_register().
576
 *
577
 * Note: @data must be backed by memory that is at least pointer aligned.
578
 * Otherwise this function will internally create a copy of the memory since
579
 * GLib 2.56, or in older versions fail and exit the process.
580
 *
581
 * If @data is empty or corrupt, %G_RESOURCE_ERROR_INTERNAL will be returned.
582
 *
583
 * Returns: (transfer full): a new #GResource, or %NULL on error
584
 *
585
 * Since: 2.32
586
 **/
587
GResource *
588
g_resource_new_from_data (GBytes  *data,
589
                          GError **error)
590
0
{
591
0
  GvdbTable *table;
592
0
  gboolean unref_data = FALSE;
593
0
  GError *local_error = NULL;
594
595
0
  if (((guintptr) g_bytes_get_data (data, NULL)) % sizeof (gpointer) != 0)
596
0
    {
597
0
      data = g_bytes_new (g_bytes_get_data (data, NULL),
598
0
                          g_bytes_get_size (data));
599
0
      unref_data = TRUE;
600
0
    }
601
602
0
  table = gvdb_table_new_from_bytes (data, TRUE, &local_error);
603
604
0
  if (unref_data)
605
0
    g_bytes_unref (data);
606
607
0
  if (table == NULL)
608
0
    {
609
0
      g_resource_error_from_gvdb_table_error (error, g_steal_pointer (&local_error));
610
0
      return NULL;
611
0
    }
612
613
0
  return g_resource_new_from_table (table);
614
0
}
615
616
/**
617
 * g_resource_load:
618
 * @filename: (type filename): the path of a filename to load, in the GLib filename encoding
619
 * @error: return location for a #GError, or %NULL
620
 *
621
 * Loads a binary resource bundle and creates a #GResource representation of it, allowing
622
 * you to query it for data.
623
 *
624
 * If you want to use this resource in the global resource namespace you need
625
 * to register it with g_resources_register().
626
 *
627
 * If @filename is empty or the data in it is corrupt,
628
 * %G_RESOURCE_ERROR_INTERNAL will be returned. If @filename doesn’t exist, or
629
 * there is an error in reading it, an error from g_mapped_file_new() will be
630
 * returned.
631
 *
632
 * Returns: (transfer full): a new #GResource, or %NULL on error
633
 *
634
 * Since: 2.32
635
 **/
636
GResource *
637
g_resource_load (const gchar  *filename,
638
                 GError      **error)
639
0
{
640
0
  GvdbTable *table;
641
0
  GError *local_error = NULL;
642
643
0
  table = gvdb_table_new (filename, FALSE, &local_error);
644
0
  if (table == NULL)
645
0
    {
646
0
      g_resource_error_from_gvdb_table_error (error, g_steal_pointer (&local_error));
647
0
      return NULL;
648
0
    }
649
650
0
  return g_resource_new_from_table (table);
651
0
}
652
653
static gboolean
654
do_lookup (GResource             *resource,
655
           const gchar           *path,
656
           GResourceLookupFlags   lookup_flags,
657
           gsize                 *size,
658
           guint32               *flags,
659
           const void           **data,
660
           gsize                 *data_size,
661
           GError               **error)
662
0
{
663
0
  char *free_path = NULL;
664
0
  gsize path_len;
665
0
  gboolean res = FALSE;
666
0
  GVariant *value;
667
668
  /* Drop any trailing slash. */
669
0
  path_len = strlen (path);
670
0
  if (path_len >= 1 && path[path_len-1] == '/')
671
0
    {
672
0
      path = free_path = g_strdup (path);
673
0
      free_path[path_len-1] = 0;
674
0
    }
675
676
0
  value = gvdb_table_get_raw_value (resource->table, path);
677
678
0
  if (value == NULL)
679
0
    {
680
0
      g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
681
0
                   _("The resource at “%s” does not exist"),
682
0
                   path);
683
0
    }
684
0
  else
685
0
    {
686
0
      guint32 _size, _flags;
687
0
      GVariant *array;
688
689
0
      g_variant_get (value, "(uu@ay)",
690
0
                     &_size,
691
0
                     &_flags,
692
0
                     &array);
693
694
0
      _size = GUINT32_FROM_LE (_size);
695
0
      _flags = GUINT32_FROM_LE (_flags);
696
697
0
      if (size)
698
0
        *size = _size;
699
0
      if (flags)
700
0
        *flags = _flags;
701
0
      if (data)
702
0
        *data = g_variant_get_data (array);
703
0
      if (data_size)
704
0
        {
705
          /* Don't report trailing newline that non-compressed files has */
706
0
          if (_flags & G_RESOURCE_FLAGS_COMPRESSED)
707
0
            *data_size = g_variant_get_size (array);
708
0
          else
709
0
            *data_size = g_variant_get_size (array) - 1;
710
0
        }
711
0
      g_variant_unref (array);
712
0
      g_variant_unref (value);
713
714
0
      res = TRUE;
715
0
    }
716
717
0
  g_free (free_path);
718
0
  return res;
719
0
}
720
721
/**
722
 * g_resource_open_stream:
723
 * @resource: A #GResource
724
 * @path: A pathname inside the resource
725
 * @lookup_flags: A #GResourceLookupFlags
726
 * @error: return location for a #GError, or %NULL
727
 *
728
 * Looks for a file at the specified @path in the resource and
729
 * returns a #GInputStream that lets you read the data.
730
 *
731
 * @lookup_flags controls the behaviour of the lookup.
732
 *
733
 * Returns: (transfer full): #GInputStream or %NULL on error.
734
 *     Free the returned object with g_object_unref()
735
 *
736
 * Since: 2.32
737
 **/
738
GInputStream *
739
g_resource_open_stream (GResource             *resource,
740
                        const gchar           *path,
741
                        GResourceLookupFlags   lookup_flags,
742
                        GError               **error)
743
0
{
744
0
  const void *data;
745
0
  gsize data_size;
746
0
  guint32 flags;
747
0
  GInputStream *stream, *stream2;
748
749
0
  if (!do_lookup (resource, path, lookup_flags, NULL, &flags, &data, &data_size, error))
750
0
    return NULL;
751
752
0
  stream = g_memory_input_stream_new_from_data (data, data_size, NULL);
753
0
  g_object_set_data_full (G_OBJECT (stream), "g-resource",
754
0
                          g_resource_ref (resource),
755
0
                          (GDestroyNotify)g_resource_unref);
756
757
0
  if (flags & G_RESOURCE_FLAGS_COMPRESSED)
758
0
    {
759
0
      GZlibDecompressor *decompressor =
760
0
        g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_ZLIB);
761
762
0
      stream2 = g_converter_input_stream_new (stream, G_CONVERTER (decompressor));
763
0
      g_object_unref (decompressor);
764
0
      g_object_unref (stream);
765
0
      stream = stream2;
766
0
    }
767
768
0
  return stream;
769
0
}
770
771
/**
772
 * g_resource_lookup_data:
773
 * @resource: A #GResource
774
 * @path: A pathname inside the resource
775
 * @lookup_flags: A #GResourceLookupFlags
776
 * @error: return location for a #GError, or %NULL
777
 *
778
 * Looks for a file at the specified @path in the resource and
779
 * returns a #GBytes that lets you directly access the data in
780
 * memory.
781
 *
782
 * The data is always followed by a zero byte, so you
783
 * can safely use the data as a C string. However, that byte
784
 * is not included in the size of the GBytes.
785
 *
786
 * For uncompressed resource files this is a pointer directly into
787
 * the resource bundle, which is typically in some readonly data section
788
 * in the program binary. For compressed files we allocate memory on
789
 * the heap and automatically uncompress the data.
790
 *
791
 * @lookup_flags controls the behaviour of the lookup.
792
 *
793
 * Returns: (transfer full): #GBytes or %NULL on error.
794
 *     Free the returned object with g_bytes_unref()
795
 *
796
 * Since: 2.32
797
 **/
798
GBytes *
799
g_resource_lookup_data (GResource             *resource,
800
                        const gchar           *path,
801
                        GResourceLookupFlags   lookup_flags,
802
                        GError               **error)
803
0
{
804
0
  const void *data;
805
0
  guint32 flags;
806
0
  gsize data_size;
807
0
  gsize size;
808
809
0
  if (!do_lookup (resource, path, lookup_flags, &size, &flags, &data, &data_size, error))
810
0
    return NULL;
811
812
0
  if (size == 0)
813
0
    return g_bytes_new_with_free_func ("", 0, (GDestroyNotify) g_resource_unref, g_resource_ref (resource));
814
0
  else if (flags & G_RESOURCE_FLAGS_COMPRESSED)
815
0
    {
816
0
      char *uncompressed, *d;
817
0
      const char *s;
818
0
      GConverterResult res;
819
0
      gsize d_size, s_size;
820
0
      gsize bytes_read, bytes_written;
821
822
823
0
      GZlibDecompressor *decompressor =
824
0
        g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_ZLIB);
825
826
0
      uncompressed = g_malloc (size + 1);
827
828
0
      s = data;
829
0
      s_size = data_size;
830
0
      d = uncompressed;
831
0
      d_size = size;
832
833
0
      do
834
0
        {
835
0
          res = g_converter_convert (G_CONVERTER (decompressor),
836
0
                                     s, s_size,
837
0
                                     d, d_size,
838
0
                                     G_CONVERTER_INPUT_AT_END,
839
0
                                     &bytes_read,
840
0
                                     &bytes_written,
841
0
                                     NULL);
842
0
          if (res == G_CONVERTER_ERROR)
843
0
            {
844
0
              g_free (uncompressed);
845
0
              g_object_unref (decompressor);
846
847
0
              g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_INTERNAL,
848
0
                           _("The resource at “%s” failed to decompress"),
849
0
                           path);
850
0
              return NULL;
851
852
0
            }
853
0
          s += bytes_read;
854
0
          s_size -= bytes_read;
855
0
          d += bytes_written;
856
0
          d_size -= bytes_written;
857
0
        }
858
0
      while (res != G_CONVERTER_FINISHED);
859
860
0
      uncompressed[size] = 0; /* Zero terminate */
861
862
0
      g_object_unref (decompressor);
863
864
0
      return g_bytes_new_take (uncompressed, size);
865
0
    }
866
0
  else
867
0
    return g_bytes_new_with_free_func (data, data_size, (GDestroyNotify)g_resource_unref, g_resource_ref (resource));
868
0
}
869
870
/**
871
 * g_resource_get_info:
872
 * @resource: A #GResource
873
 * @path: A pathname inside the resource
874
 * @lookup_flags: A #GResourceLookupFlags
875
 * @size:  (out) (optional): a location to place the length of the contents of the file,
876
 *    or %NULL if the length is not needed
877
 * @flags:  (out) (optional): a location to place the flags about the file,
878
 *    or %NULL if the length is not needed
879
 * @error: return location for a #GError, or %NULL
880
 *
881
 * Looks for a file at the specified @path in the resource and
882
 * if found returns information about it.
883
 *
884
 * @lookup_flags controls the behaviour of the lookup.
885
 *
886
 * Returns: %TRUE if the file was found. %FALSE if there were errors
887
 *
888
 * Since: 2.32
889
 **/
890
gboolean
891
g_resource_get_info (GResource             *resource,
892
                     const gchar           *path,
893
                     GResourceLookupFlags   lookup_flags,
894
                     gsize                 *size,
895
                     guint32               *flags,
896
                     GError               **error)
897
0
{
898
0
  return do_lookup (resource, path, lookup_flags, size, flags, NULL, NULL, error);
899
0
}
900
901
/**
902
 * g_resource_enumerate_children:
903
 * @resource: A #GResource
904
 * @path: A pathname inside the resource
905
 * @lookup_flags: A #GResourceLookupFlags
906
 * @error: return location for a #GError, or %NULL
907
 *
908
 * Returns all the names of children at the specified @path in the resource.
909
 * The return result is a %NULL terminated list of strings which should
910
 * be released with g_strfreev().
911
 *
912
 * If @path is invalid or does not exist in the #GResource,
913
 * %G_RESOURCE_ERROR_NOT_FOUND will be returned.
914
 *
915
 * @lookup_flags controls the behaviour of the lookup.
916
 *
917
 * Returns: (array zero-terminated=1) (transfer full): an array of constant strings
918
 *
919
 * Since: 2.32
920
 **/
921
gchar **
922
g_resource_enumerate_children (GResource             *resource,
923
                               const gchar           *path,
924
                               GResourceLookupFlags   lookup_flags,
925
                               GError               **error)
926
0
{
927
0
  gchar local_str[256];
928
0
  const gchar *path_with_slash;
929
0
  gchar **children;
930
0
  gchar *free_path = NULL;
931
0
  gsize path_len;
932
933
  /*
934
   * Size of 256 is arbitrarily chosen based on being large enough
935
   * for pretty much everything we come across, but not cumbersome
936
   * on the stack. It also matches common cacheline sizes.
937
   */
938
939
0
  if (*path == 0)
940
0
    {
941
0
      if (error)
942
0
        g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
943
0
                     _("The resource at “%s” does not exist"),
944
0
                     path);
945
0
      return NULL;
946
0
    }
947
948
0
  path_len = strlen (path);
949
950
0
  if G_UNLIKELY (path[path_len-1] != '/')
951
0
    {
952
0
      if (path_len < sizeof (local_str) - 2)
953
0
        {
954
          /*
955
           * We got a path that does not have a trailing /. It is not the
956
           * ideal use of this API as we require trailing / for our lookup
957
           * into gvdb. Some degenerate application configurations can hit
958
           * this code path quite a bit, so we try to avoid using the
959
           * g_strconcat()/g_free().
960
           */
961
0
          memcpy (local_str, path, path_len);
962
0
          local_str[path_len] = '/';
963
0
          local_str[path_len+1] = 0;
964
0
          path_with_slash = local_str;
965
0
        }
966
0
      else
967
0
        {
968
0
          path_with_slash = free_path = g_strconcat (path, "/", NULL);
969
0
        }
970
0
    }
971
0
  else
972
0
    {
973
0
      path_with_slash = path;
974
0
    }
975
976
0
  children = gvdb_table_list (resource->table, path_with_slash);
977
0
  g_free (free_path);
978
979
0
  if (children == NULL)
980
0
    {
981
0
      if (error)
982
0
        g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
983
0
                     _("The resource at “%s” does not exist"),
984
0
                     path);
985
0
      return NULL;
986
0
    }
987
988
0
  return children;
989
0
}
990
991
static GRWLock resources_lock;
992
static GList *registered_resources;
993
994
/* This is updated atomically, so we can append to it and check for NULL outside the
995
   lock, but all other accesses are done under the write lock */
996
static GStaticResource *lazy_register_resources;
997
998
static void
999
g_resources_register_unlocked (GResource *resource)
1000
0
{
1001
0
  registered_resources = g_list_prepend (registered_resources, g_resource_ref (resource));
1002
0
}
1003
1004
static void
1005
g_resources_unregister_unlocked (GResource *resource)
1006
0
{
1007
0
  if (g_list_find (registered_resources, resource) == NULL)
1008
0
    {
1009
0
      g_warning ("Tried to remove not registered resource");
1010
0
    }
1011
0
  else
1012
0
    {
1013
0
      registered_resources = g_list_remove (registered_resources, resource);
1014
0
      g_resource_unref (resource);
1015
0
    }
1016
0
}
1017
1018
/**
1019
 * g_resources_register:
1020
 * @resource: A #GResource
1021
 *
1022
 * Registers the resource with the process-global set of resources.
1023
 * Once a resource is registered the files in it can be accessed
1024
 * with the global resource lookup functions like g_resources_lookup_data().
1025
 *
1026
 * Since: 2.32
1027
 **/
1028
void
1029
g_resources_register (GResource *resource)
1030
0
{
1031
0
  g_rw_lock_writer_lock (&resources_lock);
1032
0
  g_resources_register_unlocked (resource);
1033
0
  g_rw_lock_writer_unlock (&resources_lock);
1034
0
}
1035
1036
/**
1037
 * g_resources_unregister:
1038
 * @resource: A #GResource
1039
 *
1040
 * Unregisters the resource from the process-global set of resources.
1041
 *
1042
 * Since: 2.32
1043
 **/
1044
void
1045
g_resources_unregister (GResource *resource)
1046
0
{
1047
0
  g_rw_lock_writer_lock (&resources_lock);
1048
0
  g_resources_unregister_unlocked (resource);
1049
0
  g_rw_lock_writer_unlock (&resources_lock);
1050
0
}
1051
1052
/**
1053
 * g_resources_open_stream:
1054
 * @path: A pathname inside the resource
1055
 * @lookup_flags: A #GResourceLookupFlags
1056
 * @error: return location for a #GError, or %NULL
1057
 *
1058
 * Looks for a file at the specified @path in the set of
1059
 * globally registered resources and returns a #GInputStream
1060
 * that lets you read the data.
1061
 *
1062
 * @lookup_flags controls the behaviour of the lookup.
1063
 *
1064
 * Returns: (transfer full): #GInputStream or %NULL on error.
1065
 *     Free the returned object with g_object_unref()
1066
 *
1067
 * Since: 2.32
1068
 **/
1069
GInputStream *
1070
g_resources_open_stream (const gchar           *path,
1071
                         GResourceLookupFlags   lookup_flags,
1072
                         GError               **error)
1073
0
{
1074
0
  GInputStream *res = NULL;
1075
0
  GList *l;
1076
0
  GInputStream *stream;
1077
1078
0
  if (g_resource_find_overlay (path, open_overlay_stream, &res))
1079
0
    return res;
1080
1081
0
  register_lazy_static_resources ();
1082
1083
0
  g_rw_lock_reader_lock (&resources_lock);
1084
1085
0
  for (l = registered_resources; l != NULL; l = l->next)
1086
0
    {
1087
0
      GResource *r = l->data;
1088
0
      GError *my_error = NULL;
1089
1090
0
      stream = g_resource_open_stream (r, path, lookup_flags, &my_error);
1091
0
      if (stream == NULL &&
1092
0
          g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
1093
0
        {
1094
0
          g_clear_error (&my_error);
1095
0
        }
1096
0
      else
1097
0
        {
1098
0
          if (stream == NULL)
1099
0
            g_propagate_error (error, my_error);
1100
0
          res = stream;
1101
0
          break;
1102
0
        }
1103
0
    }
1104
1105
0
  if (l == NULL)
1106
0
    g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
1107
0
                 _("The resource at “%s” does not exist"),
1108
0
                 path);
1109
1110
0
  g_rw_lock_reader_unlock (&resources_lock);
1111
1112
0
  return res;
1113
0
}
1114
1115
/**
1116
 * g_resources_lookup_data:
1117
 * @path: A pathname inside the resource
1118
 * @lookup_flags: A #GResourceLookupFlags
1119
 * @error: return location for a #GError, or %NULL
1120
 *
1121
 * Looks for a file at the specified @path in the set of
1122
 * globally registered resources and returns a #GBytes that
1123
 * lets you directly access the data in memory.
1124
 *
1125
 * The data is always followed by a zero byte, so you
1126
 * can safely use the data as a C string. However, that byte
1127
 * is not included in the size of the GBytes.
1128
 *
1129
 * For uncompressed resource files this is a pointer directly into
1130
 * the resource bundle, which is typically in some readonly data section
1131
 * in the program binary. For compressed files we allocate memory on
1132
 * the heap and automatically uncompress the data.
1133
 *
1134
 * @lookup_flags controls the behaviour of the lookup.
1135
 *
1136
 * Returns: (transfer full): #GBytes or %NULL on error.
1137
 *     Free the returned object with g_bytes_unref()
1138
 *
1139
 * Since: 2.32
1140
 **/
1141
GBytes *
1142
g_resources_lookup_data (const gchar           *path,
1143
                         GResourceLookupFlags   lookup_flags,
1144
                         GError               **error)
1145
0
{
1146
0
  GBytes *res = NULL;
1147
0
  GList *l;
1148
0
  GBytes *data;
1149
1150
0
  if (g_resource_find_overlay (path, get_overlay_bytes, &res))
1151
0
    return res;
1152
1153
0
  register_lazy_static_resources ();
1154
1155
0
  g_rw_lock_reader_lock (&resources_lock);
1156
1157
0
  for (l = registered_resources; l != NULL; l = l->next)
1158
0
    {
1159
0
      GResource *r = l->data;
1160
0
      GError *my_error = NULL;
1161
1162
0
      data = g_resource_lookup_data (r, path, lookup_flags, &my_error);
1163
0
      if (data == NULL &&
1164
0
          g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
1165
0
        {
1166
0
          g_clear_error (&my_error);
1167
0
        }
1168
0
      else
1169
0
        {
1170
0
          if (data == NULL)
1171
0
            g_propagate_error (error, my_error);
1172
0
          res = data;
1173
0
          break;
1174
0
        }
1175
0
    }
1176
1177
0
  if (l == NULL)
1178
0
    g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
1179
0
                 _("The resource at “%s” does not exist"),
1180
0
                 path);
1181
1182
0
  g_rw_lock_reader_unlock (&resources_lock);
1183
1184
0
  return res;
1185
0
}
1186
1187
/**
1188
 * g_resources_enumerate_children:
1189
 * @path: A pathname inside the resource
1190
 * @lookup_flags: A #GResourceLookupFlags
1191
 * @error: return location for a #GError, or %NULL
1192
 *
1193
 * Returns all the names of children at the specified @path in the set of
1194
 * globally registered resources.
1195
 * The return result is a %NULL terminated list of strings which should
1196
 * be released with g_strfreev().
1197
 *
1198
 * @lookup_flags controls the behaviour of the lookup.
1199
 *
1200
 * Returns: (array zero-terminated=1) (transfer full): an array of constant strings
1201
 *
1202
 * Since: 2.32
1203
 **/
1204
gchar **
1205
g_resources_enumerate_children (const gchar           *path,
1206
                                GResourceLookupFlags   lookup_flags,
1207
                                GError               **error)
1208
0
{
1209
0
  GHashTable *hash = NULL;
1210
0
  GList *l;
1211
0
  char **children;
1212
0
  int i;
1213
1214
  /* This will enumerate actual files found in overlay directories but
1215
   * will not enumerate the overlays themselves.  For example, if we
1216
   * have an overlay "/org/gtk=/path/to/files" and we enumerate "/org"
1217
   * then we will not see "gtk" in the result set unless it is provided
1218
   * by another resource file.
1219
   *
1220
   * This is probably not going to be a problem since if we are doing
1221
   * such an overlay, we probably will already have that path.
1222
   */
1223
0
  g_resource_find_overlay (path, enumerate_overlay_dir, &hash);
1224
1225
0
  register_lazy_static_resources ();
1226
1227
0
  g_rw_lock_reader_lock (&resources_lock);
1228
1229
0
  for (l = registered_resources; l != NULL; l = l->next)
1230
0
    {
1231
0
      GResource *r = l->data;
1232
1233
0
      children = g_resource_enumerate_children (r, path, 0, NULL);
1234
1235
0
      if (children != NULL)
1236
0
        {
1237
0
          if (hash == NULL)
1238
            /* note: keep in sync with same line above */
1239
0
            hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1240
1241
0
          for (i = 0; children[i] != NULL; i++)
1242
0
            g_hash_table_add (hash, children[i]);
1243
0
          g_free (children);
1244
0
        }
1245
0
    }
1246
1247
0
  g_rw_lock_reader_unlock (&resources_lock);
1248
1249
0
  if (hash == NULL)
1250
0
    {
1251
0
      if (error)
1252
0
        g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
1253
0
                     _("The resource at “%s” does not exist"),
1254
0
                     path);
1255
0
      return NULL;
1256
0
    }
1257
0
  else
1258
0
    {
1259
0
      children = (gchar **) g_hash_table_get_keys_as_array (hash, NULL);
1260
0
      g_hash_table_steal_all (hash);
1261
0
      g_hash_table_destroy (hash);
1262
1263
0
      return children;
1264
0
    }
1265
0
}
1266
1267
/**
1268
 * g_resources_get_info:
1269
 * @path: A pathname inside the resource
1270
 * @lookup_flags: A #GResourceLookupFlags
1271
 * @size:  (out) (optional): a location to place the length of the contents of the file,
1272
 *    or %NULL if the length is not needed
1273
 * @flags:  (out) (optional): a location to place the #GResourceFlags about the file,
1274
 *    or %NULL if the flags are not needed
1275
 * @error: return location for a #GError, or %NULL
1276
 *
1277
 * Looks for a file at the specified @path in the set of
1278
 * globally registered resources and if found returns information about it.
1279
 *
1280
 * @lookup_flags controls the behaviour of the lookup.
1281
 *
1282
 * Returns: %TRUE if the file was found. %FALSE if there were errors
1283
 *
1284
 * Since: 2.32
1285
 **/
1286
gboolean
1287
g_resources_get_info (const gchar           *path,
1288
                      GResourceLookupFlags   lookup_flags,
1289
                      gsize                 *size,
1290
                      guint32               *flags,
1291
                      GError               **error)
1292
0
{
1293
0
  gboolean res = FALSE;
1294
0
  GList *l;
1295
0
  gboolean r_res;
1296
0
  InfoData info;
1297
1298
0
  if (g_resource_find_overlay (path, get_overlay_info, &info))
1299
0
    {
1300
0
      if (size)
1301
0
        *size = info.size;
1302
0
      if (flags)
1303
0
        *flags = info.flags;
1304
1305
0
      return TRUE;
1306
0
    }
1307
1308
0
  register_lazy_static_resources ();
1309
1310
0
  g_rw_lock_reader_lock (&resources_lock);
1311
1312
0
  for (l = registered_resources; l != NULL; l = l->next)
1313
0
    {
1314
0
      GResource *r = l->data;
1315
0
      GError *my_error = NULL;
1316
1317
0
      r_res = g_resource_get_info (r, path, lookup_flags, size, flags, &my_error);
1318
0
      if (!r_res &&
1319
0
          g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
1320
0
        {
1321
0
          g_clear_error (&my_error);
1322
0
        }
1323
0
      else
1324
0
        {
1325
0
          if (!r_res)
1326
0
            g_propagate_error (error, my_error);
1327
0
          res = r_res;
1328
0
          break;
1329
0
        }
1330
0
    }
1331
1332
0
  if (l == NULL)
1333
0
    g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
1334
0
                 _("The resource at “%s” does not exist"),
1335
0
                 path);
1336
1337
0
  g_rw_lock_reader_unlock (&resources_lock);
1338
1339
0
  return res;
1340
0
}
1341
1342
/* This code is to handle registration of resources very early, from a constructor.
1343
 * At that point we'd like to do minimal work, to avoid ordering issues. For instance,
1344
 * we're not allowed to use g_malloc, as the user need to be able to call g_mem_set_vtable
1345
 * before the first call to g_malloc.
1346
 *
1347
 * So, what we do at construction time is that we just register a static structure on
1348
 * a list of resources that need to be initialized, and then later, when doing any lookups
1349
 * in the global list of registered resources, or when getting a reference to the
1350
 * lazily initialized resource we lazily create and register all the GResources on
1351
 * the lazy list.
1352
 *
1353
 * To avoid having to use locks in the constructor, and having to grab the writer lock
1354
 * when checking the lazy registering list we update lazy_register_resources in
1355
 * a lock-less fashion (atomic prepend-only, atomic replace with NULL). However, all
1356
 * operations except:
1357
 *  * check if there are any resources to lazily initialize
1358
 *  * Add a static resource to the lazy init list
1359
 * Do use the full writer lock for protection.
1360
 */
1361
1362
static void
1363
register_lazy_static_resources_unlocked (void)
1364
0
{
1365
0
  GStaticResource *list;
1366
1367
0
  do
1368
0
    list = lazy_register_resources;
1369
0
  while (!g_atomic_pointer_compare_and_exchange (&lazy_register_resources, list, NULL));
1370
1371
0
  while (list != NULL)
1372
0
    {
1373
0
      GBytes *bytes = g_bytes_new_static (list->data, list->data_len);
1374
0
      GResource *resource = g_resource_new_from_data (bytes, NULL);
1375
0
      if (resource)
1376
0
        {
1377
0
          g_resources_register_unlocked (resource);
1378
0
          g_atomic_pointer_set (&list->resource, resource);
1379
0
        }
1380
0
      g_bytes_unref (bytes);
1381
1382
0
      list = list->next;
1383
0
    }
1384
0
}
1385
1386
static void
1387
register_lazy_static_resources (void)
1388
0
{
1389
0
  if (g_atomic_pointer_get (&lazy_register_resources) == NULL)
1390
0
    return;
1391
1392
0
  g_rw_lock_writer_lock (&resources_lock);
1393
0
  register_lazy_static_resources_unlocked ();
1394
0
  g_rw_lock_writer_unlock (&resources_lock);
1395
0
}
1396
1397
/**
1398
 * g_static_resource_init:
1399
 * @static_resource: pointer to a static #GStaticResource
1400
 *
1401
 * Initializes a GResource from static data using a
1402
 * GStaticResource.
1403
 *
1404
 * This is normally used by code generated by
1405
 * [glib-compile-resources][glib-compile-resources]
1406
 * and is not typically used by other code.
1407
 *
1408
 * Since: 2.32
1409
 **/
1410
void
1411
g_static_resource_init (GStaticResource *static_resource)
1412
0
{
1413
0
  GStaticResource *next;
1414
1415
0
  do
1416
0
    {
1417
0
      next = lazy_register_resources;
1418
0
      static_resource->next = next;
1419
0
    }
1420
0
  while (!g_atomic_pointer_compare_and_exchange (&lazy_register_resources, next, static_resource));
1421
0
}
1422
1423
/**
1424
 * g_static_resource_fini:
1425
 * @static_resource: pointer to a static #GStaticResource
1426
 *
1427
 * Finalized a GResource initialized by g_static_resource_init().
1428
 *
1429
 * This is normally used by code generated by
1430
 * [glib-compile-resources][glib-compile-resources]
1431
 * and is not typically used by other code.
1432
 *
1433
 * Since: 2.32
1434
 **/
1435
void
1436
g_static_resource_fini (GStaticResource *static_resource)
1437
0
{
1438
0
  GResource *resource;
1439
1440
0
  g_rw_lock_writer_lock (&resources_lock);
1441
1442
0
  register_lazy_static_resources_unlocked ();
1443
1444
0
  resource = g_atomic_pointer_get (&static_resource->resource);
1445
0
  if (resource)
1446
0
    {
1447
0
      g_atomic_pointer_set (&static_resource->resource, NULL);
1448
0
      g_resources_unregister_unlocked (resource);
1449
0
      g_resource_unref (resource);
1450
0
    }
1451
1452
0
  g_rw_lock_writer_unlock (&resources_lock);
1453
0
}
1454
1455
/**
1456
 * g_static_resource_get_resource:
1457
 * @static_resource: pointer to a static #GStaticResource
1458
 *
1459
 * Gets the GResource that was registered by a call to g_static_resource_init().
1460
 *
1461
 * This is normally used by code generated by
1462
 * [glib-compile-resources][glib-compile-resources]
1463
 * and is not typically used by other code.
1464
 *
1465
 * Returns:  (transfer none): a #GResource
1466
 *
1467
 * Since: 2.32
1468
 **/
1469
GResource *
1470
g_static_resource_get_resource (GStaticResource *static_resource)
1471
0
{
1472
0
  register_lazy_static_resources ();
1473
1474
0
  return g_atomic_pointer_get (&static_resource->resource);
1475
0
}