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