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

Generated by: LCOV version 2.0-1