LCOV - code coverage report
Current view: top level - glib/gio - gresource.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 315 396 79.5 %
Date: 2024-04-23 05:16:05 Functions: 29 32 90.6 %
Branches: 123 186 66.1 %

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

Generated by: LCOV version 1.14