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
37struct _GResource
38{
39 int ref_count;
40
41 GvdbTable *table;
42};
43
44static void register_lazy_static_resources (void);
45
46G_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 **/
199typedef gboolean (* CheckCandidate) (const gchar *candidate, gpointer user_data);
200
201static gboolean
202open_overlay_stream (const gchar *candidate,
203 gpointer user_data)
204{
205 GInputStream **res = (GInputStream **) user_data;
206 GError *error = NULL;
207 GFile *file;
208
209 file = g_file_new_for_path (path: candidate);
210 *res = (GInputStream *) g_file_read (file, NULL, error: &error);
211
212 if (*res)
213 {
214 g_message ("Opened file '%s' as a resource overlay", candidate);
215 }
216 else
217 {
218 if (!g_error_matches (error, G_IO_ERROR, code: G_IO_ERROR_NOT_FOUND))
219 g_warning ("Can't open overlay file '%s': %s", candidate, error->message);
220 g_error_free (error);
221 }
222
223 g_object_unref (object: file);
224
225 return *res != NULL;
226}
227
228static gboolean
229get_overlay_bytes (const gchar *candidate,
230 gpointer user_data)
231{
232 GBytes **res = (GBytes **) user_data;
233 GMappedFile *mapped_file;
234 GError *error = NULL;
235
236 mapped_file = g_mapped_file_new (filename: candidate, FALSE, error: &error);
237
238 if (mapped_file)
239 {
240 g_message ("Mapped file '%s' as a resource overlay", candidate);
241 *res = g_mapped_file_get_bytes (file: mapped_file);
242 g_mapped_file_unref (file: mapped_file);
243 }
244 else
245 {
246 if (!g_error_matches (error, G_FILE_ERROR, code: G_FILE_ERROR_NOENT))
247 g_warning ("Can't mmap overlay file '%s': %s", candidate, error->message);
248 g_error_free (error);
249 }
250
251 return *res != NULL;
252}
253
254static gboolean
255enumerate_overlay_dir (const gchar *candidate,
256 gpointer user_data)
257{
258 GHashTable **hash = (GHashTable **) user_data;
259 GError *error = NULL;
260 GDir *dir;
261 const gchar *name;
262
263 dir = g_dir_open (path: candidate, flags: 0, error: &error);
264 if (dir)
265 {
266 if (*hash == NULL)
267 /* note: keep in sync with same line below */
268 *hash = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, NULL);
269
270 g_message ("Enumerating directory '%s' as resource overlay", candidate);
271
272 while ((name = g_dir_read_name (dir)))
273 {
274 gchar *fullname = g_build_filename (first_element: candidate, name, NULL);
275
276 /* match gvdb behaviour by suffixing "/" on dirs */
277 if (g_file_test (filename: fullname, test: G_FILE_TEST_IS_DIR))
278 g_hash_table_add (hash_table: *hash, key: g_strconcat (string1: name, "/", NULL));
279 else
280 g_hash_table_add (hash_table: *hash, key: g_strdup (str: name));
281
282 g_free (mem: fullname);
283 }
284
285 g_dir_close (dir);
286 }
287 else
288 {
289 if (!g_error_matches (error, G_FILE_ERROR, code: G_FILE_ERROR_NOENT))
290 g_warning ("Can't enumerate overlay directory '%s': %s", candidate, error->message);
291 g_error_free (error);
292 return FALSE;
293 }
294
295 /* We may want to enumerate results from more than one overlay
296 * directory.
297 */
298 return FALSE;
299}
300
301typedef struct {
302 gsize size;
303 guint32 flags;
304} InfoData;
305
306static gboolean
307get_overlay_info (const gchar *candidate,
308 gpointer user_data)
309{
310 InfoData *info = user_data;
311 GStatBuf buf;
312
313 if (g_stat (file: candidate, buf: &buf) < 0)
314 return FALSE;
315
316 info->size = buf.st_size;
317 info->flags = G_RESOURCE_FLAGS_NONE;
318
319 return TRUE;
320}
321
322static gboolean
323g_resource_find_overlay (const gchar *path,
324 CheckCandidate check,
325 gpointer user_data)
326{
327 /* This is a null-terminated array of replacement strings (with '=' inside) */
328 static const gchar * const *overlay_dirs;
329 gboolean res = FALSE;
330 gint path_len = -1;
331 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 if (g_once_init_enter (&overlay_dirs))
338 {
339 gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
340 const gchar * const *result;
341 const gchar *envvar;
342
343 /* Don’t load overlays if setuid, as they could allow reading privileged
344 * files. */
345 envvar = !is_setuid ? g_getenv (variable: "G_RESOURCE_OVERLAYS") : NULL;
346 if (envvar != NULL)
347 {
348 gchar **parts;
349 gint i, j;
350
351 parts = g_strsplit (string: envvar, G_SEARCHPATH_SEPARATOR_S, max_tokens: 0);
352
353 /* Sanity check the parts, dropping those that are invalid.
354 * 'i' may grow faster than 'j'.
355 */
356 for (i = j = 0; parts[i]; i++)
357 {
358 gchar *part = parts[i];
359 gchar *eq;
360
361 eq = strchr (s: part, c: '=');
362 if (eq == NULL)
363 {
364 g_critical ("G_RESOURCE_OVERLAYS segment '%s' lacks '='. Ignoring.", part);
365 g_free (mem: part);
366 continue;
367 }
368
369 if (eq == part)
370 {
371 g_critical ("G_RESOURCE_OVERLAYS segment '%s' lacks path before '='. Ignoring.", part);
372 g_free (mem: part);
373 continue;
374 }
375
376 if (eq[1] == '\0')
377 {
378 g_critical ("G_RESOURCE_OVERLAYS segment '%s' lacks path after '='. Ignoring", part);
379 g_free (mem: part);
380 continue;
381 }
382
383 if (part[0] != '/')
384 {
385 g_critical ("G_RESOURCE_OVERLAYS segment '%s' lacks leading '/'. Ignoring.", part);
386 g_free (mem: part);
387 continue;
388 }
389
390 if (eq[-1] == '/')
391 {
392 g_critical ("G_RESOURCE_OVERLAYS segment '%s' has trailing '/' before '='. Ignoring", part);
393 g_free (mem: part);
394 continue;
395 }
396
397 if (!g_path_is_absolute (file_name: eq + 1))
398 {
399 g_critical ("G_RESOURCE_OVERLAYS segment '%s' does not have an absolute path after '='. Ignoring", part);
400 g_free (mem: part);
401 continue;
402 }
403
404 g_message ("Adding GResources overlay '%s'", part);
405 parts[j++] = part;
406 }
407
408 parts[j] = NULL;
409
410 result = (const gchar **) parts;
411 }
412 else
413 {
414 /* We go out of the way to avoid malloc() in the normal case
415 * where the environment variable is not set.
416 */
417 static const gchar * const empty_strv[0 + 1];
418 result = empty_strv;
419 }
420
421 g_once_init_leave (&overlay_dirs, result);
422 }
423
424 for (i = 0; overlay_dirs[i]; i++)
425 {
426 const gchar *src;
427 gint src_len;
428 const gchar *dst;
429 gint dst_len;
430 gchar *candidate;
431
432 {
433 gchar *eq;
434
435 /* split the overlay into src/dst */
436 src = overlay_dirs[i];
437 eq = strchr (s: src, c: '=');
438 g_assert (eq); /* we checked this already */
439 src_len = eq - src;
440 dst = eq + 1;
441 /* hold off on dst_len because we will probably fail the checks below */
442 }
443
444 if (path_len == -1)
445 path_len = strlen (s: path);
446
447 /* The entire path is too short to match the source */
448 if (path_len < src_len)
449 continue;
450
451 /* It doesn't match the source */
452 if (memcmp (s1: path, s2: src, n: src_len) != 0)
453 continue;
454
455 /* The prefix matches, but it's not a complete path component */
456 if (path[src_len] && path[src_len] != '/')
457 continue;
458
459 /* OK. Now we need this. */
460 dst_len = strlen (s: dst);
461
462 /* The candidate will be composed of:
463 *
464 * dst + remaining_path + nul
465 */
466 candidate = g_malloc (n_bytes: dst_len + (path_len - src_len) + 1);
467 memcpy (dest: candidate, src: dst, n: dst_len);
468 memcpy (dest: candidate + dst_len, src: path + src_len, n: path_len - src_len);
469 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 res = (* check) (candidate, user_data);
475 g_free (mem: candidate);
476
477 if (res)
478 break;
479 }
480
481 return res;
482}
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 */
493G_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 **/
506GResource *
507g_resource_ref (GResource *resource)
508{
509 g_atomic_int_inc (&resource->ref_count);
510 return resource;
511}
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 **/
524void
525g_resource_unref (GResource *resource)
526{
527 if (g_atomic_int_dec_and_test (&resource->ref_count))
528 {
529 gvdb_table_free (table: resource->table);
530 g_free (mem: resource);
531 }
532}
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 */
540static GResource *
541g_resource_new_from_table (GvdbTable *table)
542{
543 GResource *resource;
544
545 resource = g_new (GResource, 1);
546 resource->ref_count = 1;
547 resource->table = table;
548
549 return resource;
550}
551
552static void
553g_resource_error_from_gvdb_table_error (GError **g_resource_error,
554 GError *gvdb_table_error /* (transfer full) */)
555{
556 if (g_error_matches (error: gvdb_table_error, G_FILE_ERROR, code: G_FILE_ERROR_INVAL))
557 g_set_error_literal (err: g_resource_error,
558 G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_INTERNAL,
559 message: gvdb_table_error->message);
560 else
561 g_propagate_error (dest: g_resource_error, g_steal_pointer (&gvdb_table_error));
562 g_clear_error (err: &gvdb_table_error);
563}
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 **/
587GResource *
588g_resource_new_from_data (GBytes *data,
589 GError **error)
590{
591 GvdbTable *table;
592 gboolean unref_data = FALSE;
593 GError *local_error = NULL;
594
595 if (((guintptr) g_bytes_get_data (bytes: data, NULL)) % sizeof (gpointer) != 0)
596 {
597 data = g_bytes_new (data: g_bytes_get_data (bytes: data, NULL),
598 size: g_bytes_get_size (bytes: data));
599 unref_data = TRUE;
600 }
601
602 table = gvdb_table_new_from_bytes (bytes: data, TRUE, error: &local_error);
603
604 if (unref_data)
605 g_bytes_unref (bytes: data);
606
607 if (table == NULL)
608 {
609 g_resource_error_from_gvdb_table_error (g_resource_error: error, g_steal_pointer (&local_error));
610 return NULL;
611 }
612
613 return g_resource_new_from_table (table);
614}
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 **/
636GResource *
637g_resource_load (const gchar *filename,
638 GError **error)
639{
640 GvdbTable *table;
641 GError *local_error = NULL;
642
643 table = gvdb_table_new (filename, FALSE, error: &local_error);
644 if (table == NULL)
645 {
646 g_resource_error_from_gvdb_table_error (g_resource_error: error, g_steal_pointer (&local_error));
647 return NULL;
648 }
649
650 return g_resource_new_from_table (table);
651}
652
653static gboolean
654do_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{
663 char *free_path = NULL;
664 gsize path_len;
665 gboolean res = FALSE;
666 GVariant *value;
667
668 /* Drop any trailing slash. */
669 path_len = strlen (s: path);
670 if (path_len >= 1 && path[path_len-1] == '/')
671 {
672 path = free_path = g_strdup (str: path);
673 free_path[path_len-1] = 0;
674 }
675
676 value = gvdb_table_get_raw_value (table: resource->table, key: path);
677
678 if (value == NULL)
679 {
680 g_set_error (err: error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND,
681 _("The resource at “%s” does not exist"),
682 path);
683 }
684 else
685 {
686 guint32 _size, _flags;
687 GVariant *array;
688
689 g_variant_get (value, format_string: "(uu@ay)",
690 &_size,
691 &_flags,
692 &array);
693
694 _size = GUINT32_FROM_LE (_size);
695 _flags = GUINT32_FROM_LE (_flags);
696
697 if (size)
698 *size = _size;
699 if (flags)
700 *flags = _flags;
701 if (data)
702 *data = g_variant_get_data (value: array);
703 if (data_size)
704 {
705 /* Don't report trailing newline that non-compressed files has */
706 if (_flags & G_RESOURCE_FLAGS_COMPRESSED)
707 *data_size = g_variant_get_size (value: array);
708 else
709 *data_size = g_variant_get_size (value: array) - 1;
710 }
711 g_variant_unref (value: array);
712 g_variant_unref (value);
713
714 res = TRUE;
715 }
716
717 g_free (mem: free_path);
718 return res;
719}
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 **/
738GInputStream *
739g_resource_open_stream (GResource *resource,
740 const gchar *path,
741 GResourceLookupFlags lookup_flags,
742 GError **error)
743{
744 const void *data;
745 gsize data_size;
746 guint32 flags;
747 GInputStream *stream, *stream2;
748
749 if (!do_lookup (resource, path, lookup_flags, NULL, flags: &flags, data: &data, data_size: &data_size, error))
750 return NULL;
751
752 stream = g_memory_input_stream_new_from_data (data, len: data_size, NULL);
753 g_object_set_data_full (G_OBJECT (stream), key: "g-resource",
754 data: g_resource_ref (resource),
755 destroy: (GDestroyNotify)g_resource_unref);
756
757 if (flags & G_RESOURCE_FLAGS_COMPRESSED)
758 {
759 GZlibDecompressor *decompressor =
760 g_zlib_decompressor_new (format: G_ZLIB_COMPRESSOR_FORMAT_ZLIB);
761
762 stream2 = g_converter_input_stream_new (base_stream: stream, G_CONVERTER (decompressor));
763 g_object_unref (object: decompressor);
764 g_object_unref (object: stream);
765 stream = stream2;
766 }
767
768 return stream;
769}
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 **/
798GBytes *
799g_resource_lookup_data (GResource *resource,
800 const gchar *path,
801 GResourceLookupFlags lookup_flags,
802 GError **error)
803{
804 const void *data;
805 guint32 flags;
806 gsize data_size;
807 gsize size;
808
809 if (!do_lookup (resource, path, lookup_flags, size: &size, flags: &flags, data: &data, data_size: &data_size, error))
810 return NULL;
811
812 if (size == 0)
813 return g_bytes_new_with_free_func (data: "", size: 0, free_func: (GDestroyNotify) g_resource_unref, user_data: g_resource_ref (resource));
814 else if (flags & G_RESOURCE_FLAGS_COMPRESSED)
815 {
816 char *uncompressed, *d;
817 const char *s;
818 GConverterResult res;
819 gsize d_size, s_size;
820 gsize bytes_read, bytes_written;
821
822
823 GZlibDecompressor *decompressor =
824 g_zlib_decompressor_new (format: G_ZLIB_COMPRESSOR_FORMAT_ZLIB);
825
826 uncompressed = g_malloc (n_bytes: size + 1);
827
828 s = data;
829 s_size = data_size;
830 d = uncompressed;
831 d_size = size;
832
833 do
834 {
835 res = g_converter_convert (G_CONVERTER (decompressor),
836 inbuf: s, inbuf_size: s_size,
837 outbuf: d, outbuf_size: d_size,
838 flags: G_CONVERTER_INPUT_AT_END,
839 bytes_read: &bytes_read,
840 bytes_written: &bytes_written,
841 NULL);
842 if (res == G_CONVERTER_ERROR)
843 {
844 g_free (mem: uncompressed);
845 g_object_unref (object: decompressor);
846
847 g_set_error (err: error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_INTERNAL,
848 _("The resource at “%s” failed to decompress"),
849 path);
850 return NULL;
851
852 }
853 s += bytes_read;
854 s_size -= bytes_read;
855 d += bytes_written;
856 d_size -= bytes_written;
857 }
858 while (res != G_CONVERTER_FINISHED);
859
860 uncompressed[size] = 0; /* Zero terminate */
861
862 g_object_unref (object: decompressor);
863
864 return g_bytes_new_take (data: uncompressed, size);
865 }
866 else
867 return g_bytes_new_with_free_func (data, size: data_size, free_func: (GDestroyNotify)g_resource_unref, user_data: g_resource_ref (resource));
868}
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 **/
890gboolean
891g_resource_get_info (GResource *resource,
892 const gchar *path,
893 GResourceLookupFlags lookup_flags,
894 gsize *size,
895 guint32 *flags,
896 GError **error)
897{
898 return do_lookup (resource, path, lookup_flags, size, flags, NULL, NULL, error);
899}
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 **/
921gchar **
922g_resource_enumerate_children (GResource *resource,
923 const gchar *path,
924 GResourceLookupFlags lookup_flags,
925 GError **error)
926{
927 gchar local_str[256];
928 const gchar *path_with_slash;
929 gchar **children;
930 gchar *free_path = NULL;
931 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 if (*path == 0)
940 {
941 if (error)
942 g_set_error (err: error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND,
943 _("The resource at “%s” does not exist"),
944 path);
945 return NULL;
946 }
947
948 path_len = strlen (s: path);
949
950 if G_UNLIKELY (path[path_len-1] != '/')
951 {
952 if (path_len < sizeof (local_str) - 2)
953 {
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 memcpy (dest: local_str, src: path, n: path_len);
962 local_str[path_len] = '/';
963 local_str[path_len+1] = 0;
964 path_with_slash = local_str;
965 }
966 else
967 {
968 path_with_slash = free_path = g_strconcat (string1: path, "/", NULL);
969 }
970 }
971 else
972 {
973 path_with_slash = path;
974 }
975
976 children = gvdb_table_list (table: resource->table, key: path_with_slash);
977 g_free (mem: free_path);
978
979 if (children == NULL)
980 {
981 if (error)
982 g_set_error (err: error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND,
983 _("The resource at “%s” does not exist"),
984 path);
985 return NULL;
986 }
987
988 return children;
989}
990
991static GRWLock resources_lock;
992static 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 */
996static GStaticResource *lazy_register_resources;
997
998static void
999g_resources_register_unlocked (GResource *resource)
1000{
1001 registered_resources = g_list_prepend (list: registered_resources, data: g_resource_ref (resource));
1002}
1003
1004static void
1005g_resources_unregister_unlocked (GResource *resource)
1006{
1007 if (g_list_find (list: registered_resources, data: resource) == NULL)
1008 {
1009 g_warning ("Tried to remove not registered resource");
1010 }
1011 else
1012 {
1013 registered_resources = g_list_remove (list: registered_resources, data: resource);
1014 g_resource_unref (resource);
1015 }
1016}
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 **/
1028void
1029g_resources_register (GResource *resource)
1030{
1031 g_rw_lock_writer_lock (rw_lock: &resources_lock);
1032 g_resources_register_unlocked (resource);
1033 g_rw_lock_writer_unlock (rw_lock: &resources_lock);
1034}
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 **/
1044void
1045g_resources_unregister (GResource *resource)
1046{
1047 g_rw_lock_writer_lock (rw_lock: &resources_lock);
1048 g_resources_unregister_unlocked (resource);
1049 g_rw_lock_writer_unlock (rw_lock: &resources_lock);
1050}
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 **/
1069GInputStream *
1070g_resources_open_stream (const gchar *path,
1071 GResourceLookupFlags lookup_flags,
1072 GError **error)
1073{
1074 GInputStream *res = NULL;
1075 GList *l;
1076 GInputStream *stream;
1077
1078 if (g_resource_find_overlay (path, check: open_overlay_stream, user_data: &res))
1079 return res;
1080
1081 register_lazy_static_resources ();
1082
1083 g_rw_lock_reader_lock (rw_lock: &resources_lock);
1084
1085 for (l = registered_resources; l != NULL; l = l->next)
1086 {
1087 GResource *r = l->data;
1088 GError *my_error = NULL;
1089
1090 stream = g_resource_open_stream (resource: r, path, lookup_flags, error: &my_error);
1091 if (stream == NULL &&
1092 g_error_matches (error: my_error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND))
1093 {
1094 g_clear_error (err: &my_error);
1095 }
1096 else
1097 {
1098 if (stream == NULL)
1099 g_propagate_error (dest: error, src: my_error);
1100 res = stream;
1101 break;
1102 }
1103 }
1104
1105 if (l == NULL)
1106 g_set_error (err: error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND,
1107 _("The resource at “%s” does not exist"),
1108 path);
1109
1110 g_rw_lock_reader_unlock (rw_lock: &resources_lock);
1111
1112 return res;
1113}
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 **/
1141GBytes *
1142g_resources_lookup_data (const gchar *path,
1143 GResourceLookupFlags lookup_flags,
1144 GError **error)
1145{
1146 GBytes *res = NULL;
1147 GList *l;
1148 GBytes *data;
1149
1150 if (g_resource_find_overlay (path, check: get_overlay_bytes, user_data: &res))
1151 return res;
1152
1153 register_lazy_static_resources ();
1154
1155 g_rw_lock_reader_lock (rw_lock: &resources_lock);
1156
1157 for (l = registered_resources; l != NULL; l = l->next)
1158 {
1159 GResource *r = l->data;
1160 GError *my_error = NULL;
1161
1162 data = g_resource_lookup_data (resource: r, path, lookup_flags, error: &my_error);
1163 if (data == NULL &&
1164 g_error_matches (error: my_error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND))
1165 {
1166 g_clear_error (err: &my_error);
1167 }
1168 else
1169 {
1170 if (data == NULL)
1171 g_propagate_error (dest: error, src: my_error);
1172 res = data;
1173 break;
1174 }
1175 }
1176
1177 if (l == NULL)
1178 g_set_error (err: error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND,
1179 _("The resource at “%s” does not exist"),
1180 path);
1181
1182 g_rw_lock_reader_unlock (rw_lock: &resources_lock);
1183
1184 return res;
1185}
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 **/
1204gchar **
1205g_resources_enumerate_children (const gchar *path,
1206 GResourceLookupFlags lookup_flags,
1207 GError **error)
1208{
1209 GHashTable *hash = NULL;
1210 GList *l;
1211 char **children;
1212 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 g_resource_find_overlay (path, check: enumerate_overlay_dir, user_data: &hash);
1224
1225 register_lazy_static_resources ();
1226
1227 g_rw_lock_reader_lock (rw_lock: &resources_lock);
1228
1229 for (l = registered_resources; l != NULL; l = l->next)
1230 {
1231 GResource *r = l->data;
1232
1233 children = g_resource_enumerate_children (resource: r, path, lookup_flags: 0, NULL);
1234
1235 if (children != NULL)
1236 {
1237 if (hash == NULL)
1238 /* note: keep in sync with same line above */
1239 hash = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, NULL);
1240
1241 for (i = 0; children[i] != NULL; i++)
1242 g_hash_table_add (hash_table: hash, key: children[i]);
1243 g_free (mem: children);
1244 }
1245 }
1246
1247 g_rw_lock_reader_unlock (rw_lock: &resources_lock);
1248
1249 if (hash == NULL)
1250 {
1251 if (error)
1252 g_set_error (err: error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND,
1253 _("The resource at “%s” does not exist"),
1254 path);
1255 return NULL;
1256 }
1257 else
1258 {
1259 children = (gchar **) g_hash_table_get_keys_as_array (hash_table: hash, NULL);
1260 g_hash_table_steal_all (hash_table: hash);
1261 g_hash_table_destroy (hash_table: hash);
1262
1263 return children;
1264 }
1265}
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 **/
1286gboolean
1287g_resources_get_info (const gchar *path,
1288 GResourceLookupFlags lookup_flags,
1289 gsize *size,
1290 guint32 *flags,
1291 GError **error)
1292{
1293 gboolean res = FALSE;
1294 GList *l;
1295 gboolean r_res;
1296 InfoData info;
1297
1298 if (g_resource_find_overlay (path, check: get_overlay_info, user_data: &info))
1299 {
1300 if (size)
1301 *size = info.size;
1302 if (flags)
1303 *flags = info.flags;
1304
1305 return TRUE;
1306 }
1307
1308 register_lazy_static_resources ();
1309
1310 g_rw_lock_reader_lock (rw_lock: &resources_lock);
1311
1312 for (l = registered_resources; l != NULL; l = l->next)
1313 {
1314 GResource *r = l->data;
1315 GError *my_error = NULL;
1316
1317 r_res = g_resource_get_info (resource: r, path, lookup_flags, size, flags, error: &my_error);
1318 if (!r_res &&
1319 g_error_matches (error: my_error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND))
1320 {
1321 g_clear_error (err: &my_error);
1322 }
1323 else
1324 {
1325 if (!r_res)
1326 g_propagate_error (dest: error, src: my_error);
1327 res = r_res;
1328 break;
1329 }
1330 }
1331
1332 if (l == NULL)
1333 g_set_error (err: error, G_RESOURCE_ERROR, code: G_RESOURCE_ERROR_NOT_FOUND,
1334 _("The resource at “%s” does not exist"),
1335 path);
1336
1337 g_rw_lock_reader_unlock (rw_lock: &resources_lock);
1338
1339 return res;
1340}
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
1362static void
1363register_lazy_static_resources_unlocked (void)
1364{
1365 GStaticResource *list;
1366
1367 do
1368 list = lazy_register_resources;
1369 while (!g_atomic_pointer_compare_and_exchange (&lazy_register_resources, list, NULL));
1370
1371 while (list != NULL)
1372 {
1373 GBytes *bytes = g_bytes_new_static (data: list->data, size: list->data_len);
1374 GResource *resource = g_resource_new_from_data (data: bytes, NULL);
1375 if (resource)
1376 {
1377 g_resources_register_unlocked (resource);
1378 g_atomic_pointer_set (&list->resource, resource);
1379 }
1380 g_bytes_unref (bytes);
1381
1382 list = list->next;
1383 }
1384}
1385
1386static void
1387register_lazy_static_resources (void)
1388{
1389 if (g_atomic_pointer_get (&lazy_register_resources) == NULL)
1390 return;
1391
1392 g_rw_lock_writer_lock (rw_lock: &resources_lock);
1393 register_lazy_static_resources_unlocked ();
1394 g_rw_lock_writer_unlock (rw_lock: &resources_lock);
1395}
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 **/
1410void
1411g_static_resource_init (GStaticResource *static_resource)
1412{
1413 GStaticResource *next;
1414
1415 do
1416 {
1417 next = lazy_register_resources;
1418 static_resource->next = next;
1419 }
1420 while (!g_atomic_pointer_compare_and_exchange (&lazy_register_resources, next, static_resource));
1421}
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 **/
1435void
1436g_static_resource_fini (GStaticResource *static_resource)
1437{
1438 GResource *resource;
1439
1440 g_rw_lock_writer_lock (rw_lock: &resources_lock);
1441
1442 register_lazy_static_resources_unlocked ();
1443
1444 resource = g_atomic_pointer_get (&static_resource->resource);
1445 if (resource)
1446 {
1447 g_atomic_pointer_set (&static_resource->resource, NULL);
1448 g_resources_unregister_unlocked (resource);
1449 g_resource_unref (resource);
1450 }
1451
1452 g_rw_lock_writer_unlock (rw_lock: &resources_lock);
1453}
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 **/
1469GResource *
1470g_static_resource_get_resource (GStaticResource *static_resource)
1471{
1472 register_lazy_static_resources ();
1473
1474 return g_atomic_pointer_get (&static_resource->resource);
1475}
1476

source code of gtk/subprojects/glib/gio/gresource.c