LCOV - code coverage report
Current view: top level - gio - gresource.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 87.5 % 409 358
Test Date: 2026-06-09 05:14:17 Functions: 97.3 % 37 36
Branches: - 0 0

             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                 :             : }
        

Generated by: LCOV version 2.0-1