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