LCOV - code coverage report
Current view: top level - girepository - girepository.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 68.9 % 704 485
Test Date: 2024-11-26 05:23:01 Functions: 80.7 % 57 46
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
       2                 :             :  * GObject introspection: Repository implementation
       3                 :             :  *
       4                 :             :  * Copyright (C) 2005 Matthias Clasen
       5                 :             :  * Copyright (C) 2008 Colin Walters <walters@verbum.org>
       6                 :             :  * Copyright (C) 2008 Red Hat, Inc.
       7                 :             :  *
       8                 :             :  * SPDX-License-Identifier: LGPL-2.1-or-later
       9                 :             :  *
      10                 :             :  * This library is free software; you can redistribute it and/or
      11                 :             :  * modify it under the terms of the GNU Lesser General Public
      12                 :             :  * License as published by the Free Software Foundation; either
      13                 :             :  * version 2 of the License, or (at your option) any later version.
      14                 :             :  *
      15                 :             :  * This library is distributed in the hope that it will be useful,
      16                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      17                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      18                 :             :  * Lesser General Public License for more details.
      19                 :             :  *
      20                 :             :  * You should have received a copy of the GNU Lesser General Public
      21                 :             :  * License along with this library; if not, write to the
      22                 :             :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      23                 :             :  * Boston, MA 02111-1307, USA.
      24                 :             :  */
      25                 :             : 
      26                 :             : #include "config.h"
      27                 :             : 
      28                 :             : #include <stdio.h>
      29                 :             : #include <string.h>
      30                 :             : #include <stdlib.h>
      31                 :             : 
      32                 :             : #include <glib.h>
      33                 :             : #include <glib-private.h>
      34                 :             : #include <glib/gprintf.h>
      35                 :             : #include <gmodule.h>
      36                 :             : #include "gibaseinfo-private.h"
      37                 :             : #include "girepository.h"
      38                 :             : #include "gitypelib-internal.h"
      39                 :             : #include "girepository-private.h"
      40                 :             : 
      41                 :             : /**
      42                 :             :  * GIRepository:
      43                 :             :  *
      44                 :             :  * `GIRepository` is used to manage repositories of namespaces. Namespaces
      45                 :             :  * are represented on disk by type libraries (`.typelib` files).
      46                 :             :  *
      47                 :             :  * The individual pieces of API within a type library are represented by
      48                 :             :  * subclasses of [class@GIRepository.BaseInfo]. These can be found using
      49                 :             :  * methods like [method@GIRepository.Repository.find_by_name] or
      50                 :             :  * [method@GIRepository.Repository.get_info].
      51                 :             :  *
      52                 :             :  * You are responsible for ensuring that the lifetime of the
      53                 :             :  * [class@GIRepository.Repository] exceeds that of the lifetime of any of its
      54                 :             :  * [class@GIRepository.BaseInfo]s. This cannot be guaranteed by using internal
      55                 :             :  * references within libgirepository as that would affect performance.
      56                 :             :  *
      57                 :             :  * ### Discovery of type libraries
      58                 :             :  *
      59                 :             :  * `GIRepository` will typically look for a `girepository-1.0` directory
      60                 :             :  * under the library directory used when compiling gobject-introspection. On a
      61                 :             :  * standard Linux system this will end up being `/usr/lib/girepository-1.0`.
      62                 :             :  *
      63                 :             :  * It is possible to control the search paths programmatically, using
      64                 :             :  * [method@GIRepository.Repository.prepend_search_path]. It is also possible to
      65                 :             :  * modify the search paths by using the `GI_TYPELIB_PATH` environment variable.
      66                 :             :  * The environment variable takes precedence over the default search path
      67                 :             :  * and the [method@GIRepository.Repository.prepend_search_path] calls.
      68                 :             :  *
      69                 :             :  * ### Namespace ordering
      70                 :             :  *
      71                 :             :  * In situations where namespaces may be searched in order, or returned in a
      72                 :             :  * list, the namespaces will be returned in alphabetical order, with all fully
      73                 :             :  * loaded namespaces being returned before any lazily loaded ones (those loaded
      74                 :             :  * with `GI_REPOSITORY_LOAD_FLAG_LAZY`). This allows for deterministic and
      75                 :             :  * reproducible results.
      76                 :             :  *
      77                 :             :  * Similarly, if a symbol (such as a `GType` or error domain) is being searched
      78                 :             :  * for in the set of loaded namespaces, the namespaces will be searched in that
      79                 :             :  * order. In particular, this means that a symbol which exists in two namespaces
      80                 :             :  * will always be returned from the alphabetically-higher namespace. This should
      81                 :             :  * only happen in the case of `Gio` and `GioUnix`/`GioWin32`, which all refer to
      82                 :             :  * the same `.so` file and expose overlapping sets of symbols. Symbols should
      83                 :             :  * always end up being resolved to `GioUnix` or `GioWin32` if they are platform
      84                 :             :  * dependent, rather than `Gio` itself.
      85                 :             :  *
      86                 :             :  * Since: 2.80
      87                 :             :  */
      88                 :             : 
      89                 :             : /* The namespace and version corresponding to libgirepository itself, so
      90                 :             :  * that we can refuse to load typelibs corresponding to the older,
      91                 :             :  * incompatible version of this same library in gobject-introspection. */
      92                 :             : #define GIREPOSITORY_TYPELIB_NAME "GIRepository"
      93                 :             : #define GIREPOSITORY_TYPELIB_VERSION "3.0"
      94                 :             : #define GIREPOSITORY_TYPELIB_FILENAME \
      95                 :             :   GIREPOSITORY_TYPELIB_NAME "-" GIREPOSITORY_TYPELIB_VERSION ".typelib"
      96                 :             : 
      97                 :             : typedef struct {
      98                 :             :   size_t n_interfaces;
      99                 :             :   GIBaseInfo *interfaces[];
     100                 :             : } GTypeInterfaceCache;
     101                 :             : 
     102                 :             : static void
     103                 :           1 : gtype_interface_cache_free (gpointer data)
     104                 :             : {
     105                 :           1 :   GTypeInterfaceCache *cache = data;
     106                 :             : 
     107                 :           3 :   for (size_t i = 0; i < cache->n_interfaces; i++)
     108                 :           2 :     gi_base_info_unref ((GIBaseInfo*) cache->interfaces[i]);
     109                 :           1 :   g_free (cache);
     110                 :           1 : }
     111                 :             : 
     112                 :             : struct _GIRepository
     113                 :             : {
     114                 :             :   GObject parent;
     115                 :             : 
     116                 :             :   GPtrArray *typelib_search_path;  /* (element-type filename) (owned) */
     117                 :             :   GPtrArray *library_paths;  /* (element-type filename) (owned) */
     118                 :             : 
     119                 :             :   /* Certain operations require iterating over the typelibs and the iteration
     120                 :             :    * order may affect the results. So keep an ordered list of the typelibs,
     121                 :             :    * alongside the hash table which keep the canonical strong reference to them. */
     122                 :             :   GHashTable *typelibs; /* (string) namespace -> GITypelib */
     123                 :             :   GPtrArray *ordered_typelibs;  /* (element-type unowned GITypelib) (owned) (not nullable) */
     124                 :             :   GHashTable *lazy_typelibs; /* (string) namespace-version -> GITypelib */
     125                 :             :   GPtrArray *ordered_lazy_typelibs;  /* (element-type unowned GITypelib) (owned) (not nullable) */
     126                 :             : 
     127                 :             :   GHashTable *info_by_gtype; /* GType -> GIBaseInfo */
     128                 :             :   GHashTable *info_by_error_domain; /* GQuark -> GIBaseInfo */
     129                 :             :   GHashTable *interfaces_for_gtype; /* GType -> GTypeInterfaceCache */
     130                 :             :   GHashTable *unknown_gtypes; /* hashset of GType */
     131                 :             : 
     132                 :             :   char **cached_shared_libraries;  /* (owned) (nullable) (array zero-terminated=1) */
     133                 :             :   size_t cached_n_shared_libraries;  /* length of @cached_shared_libraries, not including NULL terminator */
     134                 :             : };
     135                 :             : 
     136                 :        5590 : G_DEFINE_TYPE (GIRepository, gi_repository, G_TYPE_OBJECT);
     137                 :             : 
     138                 :             : #ifdef G_PLATFORM_WIN32
     139                 :             : #include <windows.h>
     140                 :             : 
     141                 :             : static HMODULE girepository_dll = NULL;
     142                 :             : 
     143                 :             : #ifdef DLL_EXPORT
     144                 :             : 
     145                 :             : BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
     146                 :             : 
     147                 :             : BOOL WINAPI
     148                 :             : DllMain (HINSTANCE hinstDLL,
     149                 :             :          DWORD     fdwReason,
     150                 :             :          LPVOID    lpvReserved)
     151                 :             : {
     152                 :             :   if (fdwReason == DLL_PROCESS_ATTACH)
     153                 :             :       girepository_dll = hinstDLL;
     154                 :             : 
     155                 :             :   return TRUE;
     156                 :             : }
     157                 :             : 
     158                 :             : #endif /* DLL_EXPORT */
     159                 :             : #endif /* G_PLATFORM_WIN32 */
     160                 :             : 
     161                 :             : #ifdef __APPLE__
     162                 :             : #include <mach-o/dyld.h>
     163                 :             : 
     164                 :             : /* This function returns the file path of the loaded libgirepository1.0.dylib.
     165                 :             :  * It iterates over all the loaded images to find the one with the
     166                 :             :  * gi_repository_init symbol and returns its file path.
     167                 :             :   */
     168                 :             : static const char *
     169                 :             : gi_repository_get_library_path_macos (void)
     170                 :             : {
     171                 :             :   /*
     172                 :             :    * Relevant documentation:
     173                 :             :    * https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/0-Introduction/introduction.html
     174                 :             :    * https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dyld.3.html
     175                 :             :    * https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h
     176                 :             :   */
     177                 :             :   const void *ptr = gi_repository_init;
     178                 :             :   const struct mach_header *header;
     179                 :             :   intptr_t offset;
     180                 :             :   uint32_t i, count;
     181                 :             : 
     182                 :             :   /* Iterate over all the loaded images */
     183                 :             :   count = _dyld_image_count ();
     184                 :             :   for (i = 0; i < count; i++)
     185                 :             :     {
     186                 :             :       header = _dyld_get_image_header (i);
     187                 :             :       offset = _dyld_get_image_vmaddr_slide (i);
     188                 :             : 
     189                 :             :       /* Locate the first `load` command */
     190                 :             :       struct load_command *cmd = (struct load_command *) ((char *) header + sizeof (struct mach_header));
     191                 :             :       if (header->magic == MH_MAGIC_64)
     192                 :             :         cmd = (struct load_command *) ((char *) header + sizeof (struct mach_header_64));
     193                 :             : 
     194                 :             :       /* Find the first `segment` command iterating over all the `load` commands.
     195                 :             :        * Then, check if the gi_repository_init symbol is in this image by checking
     196                 :             :        * if the pointer is in the segment's memory address range.
     197                 :             :        */
     198                 :             :       uint32_t j = 0;
     199                 :             :       while (j < header->ncmds)
     200                 :             :         {
     201                 :             :           if (cmd->cmd == LC_SEGMENT)
     202                 :             :             {
     203                 :             :               struct segment_command *seg = (struct segment_command *) cmd;
     204                 :             :               if (((intptr_t) ptr >= (seg->vmaddr + offset)) && ((intptr_t) ptr < (seg->vmaddr + offset + seg->vmsize)))
     205                 :             :                 return _dyld_get_image_name (i);
     206                 :             :            }
     207                 :             :           if (cmd->cmd == LC_SEGMENT_64)
     208                 :             :             {
     209                 :             :               struct segment_command_64 *seg = (struct segment_command_64 *) cmd;
     210                 :             :               if (((uintptr_t ) ptr >= (seg->vmaddr + offset)) && ((uintptr_t ) ptr < (seg->vmaddr + offset + seg->vmsize)))
     211                 :             :                 return _dyld_get_image_name (i);
     212                 :             :             }
     213                 :             :           /* Jump to the next command */
     214                 :             :           j++;
     215                 :             :           cmd = (struct load_command *) ((char *) cmd + cmd->cmdsize);
     216                 :             :         }
     217                 :             :     }
     218                 :             :   return NULL;
     219                 :             : }
     220                 :             : #endif /* __APPLE__ */
     221                 :             : 
     222                 :             : /*
     223                 :             :  * gi_repository_get_libdir:
     224                 :             :  *
     225                 :             :  * Returns the directory where the typelib files are installed.
     226                 :             :  *
     227                 :             :  * In platforms without relocation support, this functions returns the
     228                 :             :  * `GOBJECT_INTROSPECTION_LIBDIR` directory defined at build time .
     229                 :             :  *
     230                 :             :  * On Windows and macOS this function returns the directory
     231                 :             :  * relative to the installation directory detected at runtime.
     232                 :             :  *
     233                 :             :  * On macOS, if the library is installed in
     234                 :             :  * `/Applications/MyApp.app/Contents/Home/lib/libgirepository-1.0.dylib`, it returns
     235                 :             :  * `/Applications/MyApp.app/Contents/Home/lib/girepository-1.0`
     236                 :             :  *
     237                 :             :  * On Windows, if the application is installed in
     238                 :             :  * `C:/Program Files/MyApp/bin/MyApp.exe`, it returns
     239                 :             :  * `C:/Program Files/MyApp/lib/girepository-1.0`
     240                 :             : */
     241                 :             : static const gchar *
     242                 :          67 : gi_repository_get_libdir (void)
     243                 :             : {
     244                 :             :   static gchar *static_libdir;
     245                 :             : 
     246                 :          67 :   if (g_once_init_enter_pointer (&static_libdir))
     247                 :             :     {
     248                 :             :       gchar *libdir;
     249                 :             : #if defined(G_PLATFORM_WIN32)
     250                 :             :       const char *toplevel = g_win32_get_package_installation_directory_of_module (girepository_dll);
     251                 :             :       libdir = g_build_filename (toplevel, GOBJECT_INTROSPECTION_RELATIVE_LIBDIR, NULL);
     252                 :             :       g_ignore_leak (libdir);
     253                 :             : #elif defined(__APPLE__)
     254                 :             :       const char *libpath = gi_repository_get_library_path_macos ();
     255                 :             :       if (libpath != NULL)
     256                 :             :         {
     257                 :             :           libdir = g_path_get_dirname (libpath);
     258                 :             :           g_ignore_leak (libdir);
     259                 :             :         } else {
     260                 :             :           libdir = GOBJECT_INTROSPECTION_LIBDIR;
     261                 :             :         }
     262                 :             : #else /* !G_PLATFORM_WIN32 && !__APPLE__ */
     263                 :          10 :         libdir = GOBJECT_INTROSPECTION_LIBDIR;
     264                 :             : #endif
     265                 :          10 :       g_once_init_leave_pointer (&static_libdir, libdir);
     266                 :             :     }
     267                 :          67 :   return static_libdir;
     268                 :             : }
     269                 :             : 
     270                 :             : static void
     271                 :          67 : gi_repository_init (GIRepository *repository)
     272                 :             : {
     273                 :             :   /* typelib search path */
     274                 :             :     {
     275                 :             :       const char *libdir;
     276                 :             :       char *typelib_dir;
     277                 :             :       const char *type_lib_path_env;
     278                 :             : 
     279                 :             :       /* This variable is intended to take precedence over both:
     280                 :             :        *   - the default search path;
     281                 :             :        *   - all gi_repository_prepend_search_path() calls.
     282                 :             :        */
     283                 :          67 :       type_lib_path_env = g_getenv ("GI_TYPELIB_PATH");
     284                 :             : 
     285                 :          67 :       if (type_lib_path_env)
     286                 :             :         {
     287                 :             :           char **custom_dirs;
     288                 :             : 
     289                 :          67 :           custom_dirs = g_strsplit (type_lib_path_env, G_SEARCHPATH_SEPARATOR_S, 0);
     290                 :          67 :           repository->typelib_search_path =
     291                 :          67 :             g_ptr_array_new_take_null_terminated ((gpointer) g_steal_pointer (&custom_dirs), g_free);
     292                 :             :         }
     293                 :             :       else
     294                 :             :         {
     295                 :           0 :           repository->typelib_search_path = g_ptr_array_new_null_terminated (1, g_free, TRUE);
     296                 :             :         }
     297                 :             : 
     298                 :          67 :       libdir = gi_repository_get_libdir ();
     299                 :             : 
     300                 :          67 :       typelib_dir = g_build_filename (libdir, "girepository-1.0", NULL);
     301                 :             : 
     302                 :          67 :       g_ptr_array_add (repository->typelib_search_path, g_steal_pointer (&typelib_dir));
     303                 :             :     }
     304                 :             : 
     305                 :          67 :   repository->library_paths = g_ptr_array_new_null_terminated (1, g_free, TRUE);
     306                 :             : 
     307                 :             :   repository->typelibs
     308                 :          67 :     = g_hash_table_new_full (g_str_hash, g_str_equal,
     309                 :             :                              (GDestroyNotify) g_free,
     310                 :             :                              (GDestroyNotify) gi_typelib_unref);
     311                 :          67 :   repository->ordered_typelibs = g_ptr_array_new_with_free_func (NULL);
     312                 :             :   repository->lazy_typelibs
     313                 :          67 :     = g_hash_table_new_full (g_str_hash, g_str_equal,
     314                 :             :                              (GDestroyNotify) g_free,
     315                 :             :                              (GDestroyNotify) gi_typelib_unref);
     316                 :          67 :   repository->ordered_lazy_typelibs = g_ptr_array_new_with_free_func (NULL);
     317                 :             : 
     318                 :             :   repository->info_by_gtype
     319                 :          67 :     = g_hash_table_new_full (g_direct_hash, g_direct_equal,
     320                 :             :                              (GDestroyNotify) NULL,
     321                 :             :                              (GDestroyNotify) gi_base_info_unref);
     322                 :             :   repository->info_by_error_domain
     323                 :          67 :     = g_hash_table_new_full (g_direct_hash, g_direct_equal,
     324                 :             :                              (GDestroyNotify) NULL,
     325                 :             :                              (GDestroyNotify) gi_base_info_unref);
     326                 :             :   repository->interfaces_for_gtype
     327                 :          67 :     = g_hash_table_new_full (g_direct_hash, g_direct_equal,
     328                 :             :                              (GDestroyNotify) NULL,
     329                 :             :                              (GDestroyNotify) gtype_interface_cache_free);
     330                 :          67 :   repository->unknown_gtypes = g_hash_table_new (NULL, NULL);
     331                 :          67 : }
     332                 :             : 
     333                 :             : static void
     334                 :          67 : gi_repository_finalize (GObject *object)
     335                 :             : {
     336                 :          67 :   GIRepository *repository = GI_REPOSITORY (object);
     337                 :             : 
     338                 :          67 :   g_hash_table_destroy (repository->typelibs);
     339                 :          67 :   g_ptr_array_unref (repository->ordered_typelibs);
     340                 :          67 :   g_hash_table_destroy (repository->lazy_typelibs);
     341                 :          67 :   g_ptr_array_unref (repository->ordered_lazy_typelibs);
     342                 :             : 
     343                 :          67 :   g_hash_table_destroy (repository->info_by_gtype);
     344                 :          67 :   g_hash_table_destroy (repository->info_by_error_domain);
     345                 :          67 :   g_hash_table_destroy (repository->interfaces_for_gtype);
     346                 :          67 :   g_hash_table_destroy (repository->unknown_gtypes);
     347                 :             : 
     348                 :          67 :   g_clear_pointer (&repository->cached_shared_libraries, g_strfreev);
     349                 :             : 
     350                 :          67 :   g_clear_pointer (&repository->library_paths, g_ptr_array_unref);
     351                 :          67 :   g_clear_pointer (&repository->typelib_search_path, g_ptr_array_unref);
     352                 :             : 
     353                 :          67 :   (* G_OBJECT_CLASS (gi_repository_parent_class)->finalize) (G_OBJECT (repository));
     354                 :          67 : }
     355                 :             : 
     356                 :             : static void
     357                 :          11 : gi_repository_class_init (GIRepositoryClass *class)
     358                 :             : {
     359                 :             :   GObjectClass *gobject_class;
     360                 :             : 
     361                 :          11 :   gobject_class = G_OBJECT_CLASS (class);
     362                 :             : 
     363                 :          11 :   gobject_class->finalize = gi_repository_finalize;
     364                 :          11 : }
     365                 :             : 
     366                 :             : /**
     367                 :             :  * gi_repository_prepend_search_path:
     368                 :             :  * @repository: A #GIRepository
     369                 :             :  * @directory: (type filename): directory name to prepend to the typelib
     370                 :             :  *   search path
     371                 :             :  *
     372                 :             :  * Prepends @directory to the typelib search path.
     373                 :             :  *
     374                 :             :  * See also: gi_repository_get_search_path().
     375                 :             :  *
     376                 :             :  * Since: 2.80
     377                 :             :  */
     378                 :             : void
     379                 :          64 : gi_repository_prepend_search_path (GIRepository *repository,
     380                 :             :                                    const char   *directory)
     381                 :             : {
     382                 :          64 :   g_return_if_fail (GI_IS_REPOSITORY (repository));
     383                 :             : 
     384                 :          64 :   g_ptr_array_insert (repository->typelib_search_path, 0, g_strdup (directory));
     385                 :             : }
     386                 :             : 
     387                 :             : /**
     388                 :             :  * gi_repository_get_search_path:
     389                 :             :  * @repository: A #GIRepository
     390                 :             :  * @n_paths_out: (optional) (out): The number of search paths returned.
     391                 :             :  *
     392                 :             :  * Returns the current search path [class@GIRepository.Repository] will use when
     393                 :             :  * loading typelib files.
     394                 :             :  *
     395                 :             :  * The list is internal to [class@GIRepository.Repository] and should not be
     396                 :             :  * freed, nor should its string elements.
     397                 :             :  *
     398                 :             :  * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
     399                 :             :  * counted in @n_paths_out.
     400                 :             :  *
     401                 :             :  * Returns: (element-type filename) (transfer none) (array length=n_paths_out): list of search paths, most
     402                 :             :  *   important first
     403                 :             :  * Since: 2.80
     404                 :             :  */
     405                 :             : const char * const *
     406                 :           4 : gi_repository_get_search_path (GIRepository *repository,
     407                 :             :                                size_t       *n_paths_out)
     408                 :             : {
     409                 :           4 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
     410                 :             : 
     411                 :           4 :   if G_UNLIKELY (!repository->typelib_search_path ||
     412                 :             :                  !repository->typelib_search_path->pdata)
     413                 :             :     {
     414                 :             :       static const char * const empty_search_path[] = {NULL};
     415                 :             : 
     416                 :           0 :       if (n_paths_out)
     417                 :           0 :         *n_paths_out = 0;
     418                 :             : 
     419                 :           0 :       return empty_search_path;
     420                 :             :     }
     421                 :             : 
     422                 :           4 :   if (n_paths_out)
     423                 :           3 :     *n_paths_out = repository->typelib_search_path->len;
     424                 :             : 
     425                 :           4 :   return (const char * const *) repository->typelib_search_path->pdata;
     426                 :             : }
     427                 :             : 
     428                 :             : /**
     429                 :             :  * gi_repository_prepend_library_path:
     430                 :             :  * @repository: A #GIRepository
     431                 :             :  * @directory: (type filename): a single directory to scan for shared libraries
     432                 :             :  *
     433                 :             :  * Prepends @directory to the search path that is used to
     434                 :             :  * search shared libraries referenced by imported namespaces.
     435                 :             :  *
     436                 :             :  * Multiple calls to this function all contribute to the final
     437                 :             :  * list of paths.
     438                 :             :  *
     439                 :             :  * The list of paths is unique to @repository. When a typelib is loaded by the
     440                 :             :  * repository, the list of paths from the @repository at that instant is used
     441                 :             :  * by the typelib for loading its modules.
     442                 :             :  *
     443                 :             :  * If the library is not found in the directories configured
     444                 :             :  * in this way, loading will fall back to the system library
     445                 :             :  * path (i.e. `LD_LIBRARY_PATH` and `DT_RPATH` in ELF systems).
     446                 :             :  * See the documentation of your dynamic linker for full details.
     447                 :             :  *
     448                 :             :  * Since: 2.80
     449                 :             :  */
     450                 :             : void
     451                 :           2 : gi_repository_prepend_library_path (GIRepository *repository,
     452                 :             :                                     const char   *directory)
     453                 :             : {
     454                 :           2 :   g_return_if_fail (GI_IS_REPOSITORY (repository));
     455                 :             : 
     456                 :           2 :   g_ptr_array_insert (repository->library_paths, 0, g_strdup (directory));
     457                 :             : }
     458                 :             : 
     459                 :             : /**
     460                 :             :  * gi_repository_get_library_path:
     461                 :             :  * @repository: A #GIRepository
     462                 :             :  * @n_paths_out: (optional) (out): The number of library paths returned.
     463                 :             :  *
     464                 :             :  * Returns the current search path [class@GIRepository.Repository] will use when
     465                 :             :  * loading shared libraries referenced by imported namespaces.
     466                 :             :  *
     467                 :             :  * The list is internal to [class@GIRepository.Repository] and should not be
     468                 :             :  * freed, nor should its string elements.
     469                 :             :  *
     470                 :             :  * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
     471                 :             :  * counted in @n_paths_out.
     472                 :             :  *
     473                 :             :  * Returns: (element-type filename) (transfer none) (array length=n_paths_out): list of search paths, most
     474                 :             :  *   important first
     475                 :             :  * Since: 2.80
     476                 :             :  */
     477                 :             : const char * const *
     478                 :           3 : gi_repository_get_library_path (GIRepository *repository,
     479                 :             :                                 size_t       *n_paths_out)
     480                 :             : {
     481                 :           3 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
     482                 :             : 
     483                 :           3 :   if G_UNLIKELY (!repository->library_paths || !repository->library_paths->pdata)
     484                 :             :     {
     485                 :             :       static const char * const empty_search_path[] = {NULL};
     486                 :             : 
     487                 :           0 :       if (n_paths_out)
     488                 :           0 :         *n_paths_out = 0;
     489                 :             : 
     490                 :           0 :       return empty_search_path;
     491                 :             :     }
     492                 :             : 
     493                 :           3 :   if (n_paths_out)
     494                 :           3 :     *n_paths_out = repository->library_paths->len;
     495                 :             : 
     496                 :           3 :   return (const char * const *) repository->library_paths->pdata;
     497                 :             : }
     498                 :             : 
     499                 :             : static char *
     500                 :         194 : build_typelib_key (const char *name, const char *source)
     501                 :             : {
     502                 :         194 :   GString *str = g_string_new (name);
     503                 :             :   g_string_append_c (str, '\0');
     504                 :             :   g_string_append (str, source);
     505                 :         194 :   return g_string_free (str, FALSE);
     506                 :             : }
     507                 :             : 
     508                 :             : /* Note: Returns %NULL (not an empty %NULL-terminated array) if there are no
     509                 :             :  * dependencies. */
     510                 :             : static char **
     511                 :         196 : get_typelib_dependencies (GITypelib *typelib)
     512                 :             : {
     513                 :             :   Header *header;
     514                 :             :   const char *dependencies_glob;
     515                 :             : 
     516                 :         196 :   header = (Header *)typelib->data;
     517                 :             : 
     518                 :         196 :   if (header->dependencies == 0)
     519                 :          63 :     return NULL;
     520                 :             : 
     521                 :         133 :   dependencies_glob = gi_typelib_get_string (typelib, header->dependencies);
     522                 :         133 :   return g_strsplit (dependencies_glob, "|", 0);
     523                 :             : }
     524                 :             : 
     525                 :             : static GITypelib *
     526                 :        2104 : check_version_conflict (GITypelib *typelib,
     527                 :             :                         const char  *namespace,
     528                 :             :                         const char  *expected_version,
     529                 :             :                         char       **version_conflict)
     530                 :             : {
     531                 :             :   Header *header;
     532                 :             :   const char *loaded_version;
     533                 :             : 
     534                 :        2104 :   if (expected_version == NULL)
     535                 :             :     {
     536                 :        2018 :       if (version_conflict)
     537                 :           0 :         *version_conflict = NULL;
     538                 :        2018 :       return typelib;
     539                 :             :     }
     540                 :             : 
     541                 :          86 :   header = (Header*)typelib->data;
     542                 :          86 :   loaded_version = gi_typelib_get_string (typelib, header->nsversion);
     543                 :          86 :   g_assert (loaded_version != NULL);
     544                 :             : 
     545                 :          86 :   if (strcmp (expected_version, loaded_version) != 0)
     546                 :             :     {
     547                 :           0 :       if (version_conflict)
     548                 :           0 :         *version_conflict = (char*)loaded_version;
     549                 :           0 :       return NULL;
     550                 :             :     }
     551                 :          86 :   if (version_conflict)
     552                 :          86 :     *version_conflict = NULL;
     553                 :          86 :   return typelib;
     554                 :             : }
     555                 :             : 
     556                 :             : static GITypelib *
     557                 :        2298 : get_registered_status (GIRepository *repository,
     558                 :             :                        const char   *namespace,
     559                 :             :                        const char   *version,
     560                 :             :                        gboolean      allow_lazy,
     561                 :             :                        gboolean     *lazy_status,
     562                 :             :                        char        **version_conflict)
     563                 :             : {
     564                 :             :   GITypelib *typelib;
     565                 :             : 
     566                 :        2298 :   if (lazy_status)
     567                 :         280 :     *lazy_status = FALSE;
     568                 :        2298 :   typelib = g_hash_table_lookup (repository->typelibs, namespace);
     569                 :        2298 :   if (typelib)
     570                 :        2104 :     return check_version_conflict (typelib, namespace, version, version_conflict);
     571                 :         194 :   typelib = g_hash_table_lookup (repository->lazy_typelibs, namespace);
     572                 :         194 :   if (!typelib)
     573                 :         194 :     return NULL;
     574                 :           0 :   if (lazy_status)
     575                 :           0 :     *lazy_status = TRUE;
     576                 :           0 :   if (!allow_lazy)
     577                 :           0 :     return NULL;
     578                 :           0 :   return check_version_conflict (typelib, namespace, version, version_conflict);
     579                 :             : }
     580                 :             : 
     581                 :             : static GITypelib *
     582                 :        2018 : get_registered (GIRepository *repository,
     583                 :             :                 const char   *namespace,
     584                 :             :                 const char   *version)
     585                 :             : {
     586                 :        2018 :   return get_registered_status (repository, namespace, version, TRUE, NULL, NULL);
     587                 :             : }
     588                 :             : 
     589                 :             : static gboolean
     590                 :         194 : load_dependencies_recurse (GIRepository *repository,
     591                 :             :                            GITypelib     *typelib,
     592                 :             :                            GError      **error)
     593                 :             : {
     594                 :             :   char **dependencies;
     595                 :             : 
     596                 :         194 :   dependencies = get_typelib_dependencies (typelib);
     597                 :             : 
     598                 :         194 :   if (dependencies != NULL)
     599                 :             :     {
     600                 :             :       int i;
     601                 :             : 
     602                 :         349 :       for (i = 0; dependencies[i]; i++)
     603                 :             :         {
     604                 :         217 :           char *dependency = dependencies[i];
     605                 :             :           const char *last_dash;
     606                 :             :           char *dependency_namespace;
     607                 :             :           const char *dependency_version;
     608                 :             : 
     609                 :         217 :           last_dash = strrchr (dependency, '-');
     610                 :         217 :           dependency_namespace = g_strndup (dependency, last_dash - dependency);
     611                 :         217 :           dependency_version = last_dash+1;
     612                 :             : 
     613                 :         217 :           if (!gi_repository_require (repository, dependency_namespace, dependency_version,
     614                 :             :                                       0, error))
     615                 :             :             {
     616                 :           0 :               g_free (dependency_namespace);
     617                 :           0 :               g_strfreev (dependencies);
     618                 :           0 :               return FALSE;
     619                 :             :             }
     620                 :         217 :           g_free (dependency_namespace);
     621                 :             :         }
     622                 :         132 :       g_strfreev (dependencies);
     623                 :             :     }
     624                 :         194 :   return TRUE;
     625                 :             : }
     626                 :             : 
     627                 :             : /* Sort typelibs by namespace. The main requirement here is just to make iteration
     628                 :             :  * deterministic, otherwise results can change as a lot of the code here would
     629                 :             :  * just iterate over a `GHashTable`.
     630                 :             :  *
     631                 :             :  * A sub-requirement of this is that namespaces are sorted such that if a GType
     632                 :             :  * or symbol is found in multiple namespaces where one is a prefix of the other,
     633                 :             :  * the longest namespace wins. In practice, this only happens in
     634                 :             :  * Gio/GioUnix/GioWin32, as all three of those namespaces refer to the same
     635                 :             :  * `.so` file and overlapping sets of the same symbols, but we want the platform
     636                 :             :  * specific namespace to be returned in preference to anything else (even though
     637                 :             :  * either namespace is valid).
     638                 :             :  * See https://gitlab.gnome.org/GNOME/glib/-/issues/3303 */
     639                 :             : static int
     640                 :         296 : sort_typelibs_cb (const void *a,
     641                 :             :                   const void *b)
     642                 :             : {
     643                 :         296 :   GITypelib *typelib_a = *(GITypelib **) a;
     644                 :         296 :   GITypelib *typelib_b = *(GITypelib **) b;
     645                 :             : 
     646                 :         296 :   return strcmp (gi_typelib_get_namespace (typelib_a),
     647                 :             :                  gi_typelib_get_namespace (typelib_b));
     648                 :             : }
     649                 :             : 
     650                 :             : static const char *
     651                 :         194 : register_internal (GIRepository *repository,
     652                 :             :                    const char   *source,
     653                 :             :                    gboolean      lazy,
     654                 :             :                    GITypelib     *typelib,
     655                 :             :                    GError      **error)
     656                 :             : {
     657                 :             :   Header *header;
     658                 :             :   const char *namespace;
     659                 :             : 
     660                 :         194 :   g_return_val_if_fail (typelib != NULL, NULL);
     661                 :             : 
     662                 :         194 :   header = (Header *)typelib->data;
     663                 :             : 
     664                 :         194 :   g_return_val_if_fail (header != NULL, NULL);
     665                 :             : 
     666                 :         194 :   namespace = gi_typelib_get_string (typelib, header->namespace);
     667                 :             : 
     668                 :         194 :   if (lazy)
     669                 :             :     {
     670                 :           0 :       g_assert (!g_hash_table_lookup (repository->lazy_typelibs,
     671                 :             :                                       namespace));
     672                 :           0 :       g_hash_table_insert (repository->lazy_typelibs,
     673                 :           0 :                            build_typelib_key (namespace, source), gi_typelib_ref (typelib));
     674                 :           0 :       g_ptr_array_add (repository->ordered_lazy_typelibs, typelib);
     675                 :           0 :       g_ptr_array_sort (repository->ordered_lazy_typelibs, sort_typelibs_cb);
     676                 :             :     }
     677                 :             :   else
     678                 :             :     {
     679                 :             :       gpointer value;
     680                 :             :       char *key;
     681                 :             : 
     682                 :             :       /* First, try loading all the dependencies */
     683                 :         194 :       if (!load_dependencies_recurse (repository, typelib, error))
     684                 :           0 :         return NULL;
     685                 :             : 
     686                 :             :       /* Check if we are transitioning from lazily loaded state */
     687                 :         194 :       if (g_hash_table_lookup_extended (repository->lazy_typelibs,
     688                 :             :                                         namespace,
     689                 :             :                                         (gpointer)&key, &value))
     690                 :             :         {
     691                 :           0 :           g_hash_table_remove (repository->lazy_typelibs, key);
     692                 :           0 :           g_ptr_array_remove (repository->ordered_lazy_typelibs, typelib);
     693                 :             :         }
     694                 :             :       else
     695                 :             :         {
     696                 :         194 :           key = build_typelib_key (namespace, source);
     697                 :             :         }
     698                 :             : 
     699                 :         194 :       g_hash_table_insert (repository->typelibs,
     700                 :             :                            g_steal_pointer (&key),
     701                 :         194 :                            gi_typelib_ref (typelib));
     702                 :         194 :       g_ptr_array_add (repository->ordered_typelibs, typelib);
     703                 :         194 :       g_ptr_array_sort (repository->ordered_typelibs, sort_typelibs_cb);
     704                 :             :     }
     705                 :             : 
     706                 :             :   /* These types might be resolved now, clear the cache */
     707                 :         194 :   g_hash_table_remove_all (repository->unknown_gtypes);
     708                 :             : 
     709                 :         194 :   return namespace;
     710                 :             : }
     711                 :             : 
     712                 :             : /**
     713                 :             :  * gi_repository_get_immediate_dependencies:
     714                 :             :  * @repository: A #GIRepository
     715                 :             :  * @namespace_: Namespace of interest
     716                 :             :  * @n_dependencies_out: (optional) (out): Return location for the number of
     717                 :             :  *   dependencies
     718                 :             :  *
     719                 :             :  * Return an array of the immediate versioned dependencies for @namespace_.
     720                 :             :  * Returned strings are of the form `namespace-version`.
     721                 :             :  *
     722                 :             :  * Note: @namespace_ must have already been loaded using a function
     723                 :             :  * such as [method@GIRepository.Repository.require] before calling this
     724                 :             :  * function.
     725                 :             :  *
     726                 :             :  * To get the transitive closure of dependencies for @namespace_, use
     727                 :             :  * [method@GIRepository.Repository.get_dependencies].
     728                 :             :  *
     729                 :             :  * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
     730                 :             :  * counted in @n_dependencies_out.
     731                 :             :  *
     732                 :             :  * Returns: (transfer full) (array length=n_dependencies_out): String array of
     733                 :             :  *   immediate versioned dependencies
     734                 :             :  * Since: 2.80
     735                 :             :  */
     736                 :             : char **
     737                 :           0 : gi_repository_get_immediate_dependencies (GIRepository *repository,
     738                 :             :                                           const char   *namespace,
     739                 :             :                                           size_t       *n_dependencies_out)
     740                 :             : {
     741                 :             :   GITypelib *typelib;
     742                 :             :   char **deps;
     743                 :             : 
     744                 :           0 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
     745                 :           0 :   g_return_val_if_fail (namespace != NULL, NULL);
     746                 :             : 
     747                 :           0 :   typelib = get_registered (repository, namespace, NULL);
     748                 :           0 :   g_return_val_if_fail (typelib != NULL, NULL);
     749                 :             : 
     750                 :             :   /* Ensure we always return a non-%NULL vector. */
     751                 :           0 :   deps = get_typelib_dependencies (typelib);
     752                 :           0 :   if (deps == NULL)
     753                 :           0 :       deps = g_strsplit ("", "|", 0);
     754                 :             : 
     755                 :           0 :   if (n_dependencies_out != NULL)
     756                 :           0 :     *n_dependencies_out = g_strv_length (deps);
     757                 :             : 
     758                 :           0 :   return deps;
     759                 :             : }
     760                 :             : 
     761                 :             : /* Load the transitive closure of dependency namespace-version strings for the
     762                 :             :  * given @typelib. @repository must be non-%NULL. @transitive_dependencies must
     763                 :             :  * be a pre-existing GHashTable<owned utf8, owned utf8> set for storing the
     764                 :             :  * dependencies. */
     765                 :             : static void
     766                 :           2 : get_typelib_dependencies_transitive (GIRepository *repository,
     767                 :             :                                      GITypelib    *typelib,
     768                 :             :                                      GHashTable   *transitive_dependencies)
     769                 :             : {
     770                 :             :   char **immediate_dependencies;
     771                 :             : 
     772                 :           2 :   immediate_dependencies = get_typelib_dependencies (typelib);
     773                 :             : 
     774                 :           3 :   for (size_t i = 0; immediate_dependencies != NULL && immediate_dependencies[i]; i++)
     775                 :             :     {
     776                 :             :       char *dependency;
     777                 :             :       const char *last_dash;
     778                 :             :       char *dependency_namespace;
     779                 :             : 
     780                 :           1 :       dependency = immediate_dependencies[i];
     781                 :             : 
     782                 :             :       /* Steal from the strv. */
     783                 :           1 :       g_hash_table_add (transitive_dependencies, dependency);
     784                 :           1 :       immediate_dependencies[i] = NULL;
     785                 :             : 
     786                 :             :       /* Recurse for this namespace. */
     787                 :           1 :       last_dash = strrchr (dependency, '-');
     788                 :           1 :       dependency_namespace = g_strndup (dependency, last_dash - dependency);
     789                 :             : 
     790                 :           1 :       typelib = get_registered (repository, dependency_namespace, NULL);
     791                 :           1 :       g_return_if_fail (typelib != NULL);
     792                 :           1 :       get_typelib_dependencies_transitive (repository, typelib,
     793                 :             :                                            transitive_dependencies);
     794                 :             : 
     795                 :           1 :       g_free (dependency_namespace);
     796                 :             :     }
     797                 :             : 
     798                 :           2 :   g_free (immediate_dependencies);
     799                 :             : }
     800                 :             : 
     801                 :             : /**
     802                 :             :  * gi_repository_get_dependencies:
     803                 :             :  * @repository: A #GIRepository
     804                 :             :  * @namespace_: Namespace of interest
     805                 :             :  * @n_dependencies_out: (optional) (out): Return location for the number of
     806                 :             :  *   dependencies
     807                 :             :  *
     808                 :             :  * Retrieves all (transitive) versioned dependencies for
     809                 :             :  * @namespace_.
     810                 :             :  *
     811                 :             :  * The returned strings are of the form `namespace-version`.
     812                 :             :  *
     813                 :             :  * Note: @namespace_ must have already been loaded using a function
     814                 :             :  * such as [method@GIRepository.Repository.require] before calling this
     815                 :             :  * function.
     816                 :             :  *
     817                 :             :  * To get only the immediate dependencies for @namespace_, use
     818                 :             :  * [method@GIRepository.Repository.get_immediate_dependencies].
     819                 :             :  *
     820                 :             :  * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
     821                 :             :  * counted in @n_dependencies_out.
     822                 :             :  *
     823                 :             :  * Returns: (transfer full) (array length=n_dependencies_out): String array of
     824                 :             :  *   all versioned dependencies
     825                 :             :  * Since: 2.80
     826                 :             :  */
     827                 :             : char **
     828                 :           1 : gi_repository_get_dependencies (GIRepository *repository,
     829                 :             :                                 const char   *namespace,
     830                 :             :                                 size_t       *n_dependencies_out)
     831                 :             : {
     832                 :             :   GITypelib *typelib;
     833                 :             :   GHashTable *transitive_dependencies;  /* set of owned utf8 */
     834                 :             :   GHashTableIter iter;
     835                 :             :   char *dependency;
     836                 :             :   GPtrArray *out;  /* owned utf8 elements */
     837                 :             : 
     838                 :           1 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
     839                 :           1 :   g_return_val_if_fail (namespace != NULL, NULL);
     840                 :             : 
     841                 :           1 :   typelib = get_registered (repository, namespace, NULL);
     842                 :           1 :   g_return_val_if_fail (typelib != NULL, NULL);
     843                 :             : 
     844                 :             :   /* Load the dependencies. */
     845                 :           1 :   transitive_dependencies = g_hash_table_new_full (g_str_hash, g_str_equal,
     846                 :             :                                                    g_free, NULL);
     847                 :           1 :   get_typelib_dependencies_transitive (repository, typelib,
     848                 :             :                                        transitive_dependencies);
     849                 :             : 
     850                 :             :   /* Convert to a string array. */
     851                 :           1 :   out = g_ptr_array_new_null_terminated (g_hash_table_size (transitive_dependencies),
     852                 :             :                                          g_free, TRUE);
     853                 :           1 :   g_hash_table_iter_init (&iter, transitive_dependencies);
     854                 :             : 
     855                 :           2 :   while (g_hash_table_iter_next (&iter, (gpointer) &dependency, NULL))
     856                 :             :     {
     857                 :           1 :       g_ptr_array_add (out, dependency);
     858                 :           1 :       g_hash_table_iter_steal (&iter);
     859                 :             :     }
     860                 :             : 
     861                 :           1 :   g_hash_table_unref (transitive_dependencies);
     862                 :             : 
     863                 :           1 :   if (n_dependencies_out != NULL)
     864                 :           1 :     *n_dependencies_out = out->len;
     865                 :             : 
     866                 :           1 :   return (char **) g_ptr_array_free (out, FALSE);
     867                 :             : }
     868                 :             : 
     869                 :             : /**
     870                 :             :  * gi_repository_load_typelib:
     871                 :             :  * @repository: A #GIRepository
     872                 :             :  * @typelib: (transfer none): the typelib to load
     873                 :             :  * @flags: flags affecting the loading operation
     874                 :             :  * @error: return location for a [type@GLib.Error], or `NULL`
     875                 :             :  *
     876                 :             :  * Load the given @typelib into the repository.
     877                 :             :  *
     878                 :             :  * Returns: namespace of the loaded typelib
     879                 :             :  * Since: 2.80
     880                 :             :  */
     881                 :             : const char *
     882                 :           0 : gi_repository_load_typelib (GIRepository           *repository,
     883                 :             :                             GITypelib              *typelib,
     884                 :             :                             GIRepositoryLoadFlags   flags,
     885                 :             :                             GError                **error)
     886                 :             : {
     887                 :             :   Header *header;
     888                 :             :   const char *namespace;
     889                 :             :   const char *nsversion;
     890                 :           0 :   gboolean allow_lazy = flags & GI_REPOSITORY_LOAD_FLAG_LAZY;
     891                 :             :   gboolean is_lazy;
     892                 :             :   char *version_conflict;
     893                 :             : 
     894                 :           0 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
     895                 :             : 
     896                 :           0 :   header = (Header *) typelib->data;
     897                 :           0 :   namespace = gi_typelib_get_string (typelib, header->namespace);
     898                 :           0 :   nsversion = gi_typelib_get_string (typelib, header->nsversion);
     899                 :             : 
     900                 :           0 :   if (get_registered_status (repository, namespace, nsversion, allow_lazy,
     901                 :             :                              &is_lazy, &version_conflict))
     902                 :             :     {
     903                 :           0 :       if (version_conflict != NULL)
     904                 :             :         {
     905                 :           0 :           g_set_error (error, GI_REPOSITORY_ERROR,
     906                 :             :                        GI_REPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
     907                 :             :                        "Attempting to load namespace '%s', version '%s', but '%s' is already loaded",
     908                 :             :                        namespace, nsversion, version_conflict);
     909                 :           0 :           return NULL;
     910                 :             :         }
     911                 :           0 :       return namespace;
     912                 :             :     }
     913                 :           0 :   return register_internal (repository, "<builtin>",
     914                 :             :                             allow_lazy, typelib, error);
     915                 :             : }
     916                 :             : 
     917                 :             : /**
     918                 :             :  * gi_repository_is_registered:
     919                 :             :  * @repository: A #GIRepository
     920                 :             :  * @namespace_: Namespace of interest
     921                 :             :  * @version: (nullable): Required version, may be `NULL` for latest
     922                 :             :  *
     923                 :             :  * Check whether a particular namespace (and optionally, a specific
     924                 :             :  * version thereof) is currently loaded.
     925                 :             :  *
     926                 :             :  * This function is likely to only be useful in unusual circumstances; in order
     927                 :             :  * to act upon metadata in the namespace, you should call
     928                 :             :  * [method@GIRepository.Repository.require] instead which will ensure the
     929                 :             :  * namespace is loaded, and return as quickly as this function will if it has
     930                 :             :  * already been loaded.
     931                 :             :  *
     932                 :             :  * Returns: `TRUE` if namespace-version is loaded, `FALSE` otherwise
     933                 :             :  * Since: 2.80
     934                 :             :  */
     935                 :             : gboolean
     936                 :           1 : gi_repository_is_registered (GIRepository *repository,
     937                 :             :                              const char   *namespace,
     938                 :             :                              const char   *version)
     939                 :             : {
     940                 :           1 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), FALSE);
     941                 :             : 
     942                 :           1 :   return get_registered (repository, namespace, version) != NULL;
     943                 :             : }
     944                 :             : 
     945                 :             : /**
     946                 :             :  * gi_repository_new:
     947                 :             :  *
     948                 :             :  * Create a new [class@GIRepository.Repository].
     949                 :             :  *
     950                 :             :  * Returns: (transfer full): a new [class@GIRepository.Repository]
     951                 :             :  * Since: 2.80
     952                 :             :  */
     953                 :             : GIRepository *
     954                 :          67 : gi_repository_new (void)
     955                 :             : {
     956                 :          67 :   return g_object_new (GI_TYPE_REPOSITORY, NULL);
     957                 :             : }
     958                 :             : 
     959                 :             : /**
     960                 :             :  * gi_repository_get_n_infos:
     961                 :             :  * @repository: A #GIRepository
     962                 :             :  * @namespace_: Namespace to inspect
     963                 :             :  *
     964                 :             :  * This function returns the number of metadata entries in
     965                 :             :  * given namespace @namespace_.
     966                 :             :  *
     967                 :             :  * The namespace must have already been loaded before calling this function.
     968                 :             :  *
     969                 :             :  * Returns: number of metadata entries
     970                 :             :  * Since: 2.80
     971                 :             :  */
     972                 :             : unsigned int
     973                 :           2 : gi_repository_get_n_infos (GIRepository *repository,
     974                 :             :                            const char   *namespace)
     975                 :             : {
     976                 :             :   GITypelib *typelib;
     977                 :           2 :   unsigned int n_interfaces = 0;
     978                 :             : 
     979                 :           2 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), -1);
     980                 :           2 :   g_return_val_if_fail (namespace != NULL, -1);
     981                 :             : 
     982                 :           2 :   typelib = get_registered (repository, namespace, NULL);
     983                 :             : 
     984                 :           2 :   g_return_val_if_fail (typelib != NULL, -1);
     985                 :             : 
     986                 :           2 :   n_interfaces = ((Header *)typelib->data)->n_local_entries;
     987                 :             : 
     988                 :           2 :   return n_interfaces;
     989                 :             : }
     990                 :             : 
     991                 :             : /**
     992                 :             :  * gi_repository_get_info:
     993                 :             :  * @repository: A #GIRepository
     994                 :             :  * @namespace_: Namespace to inspect
     995                 :             :  * @idx: 0-based offset into namespace metadata for entry
     996                 :             :  *
     997                 :             :  * This function returns a particular metadata entry in the
     998                 :             :  * given namespace @namespace_.
     999                 :             :  *
    1000                 :             :  * The namespace must have already been loaded before calling this function.
    1001                 :             :  * See [method@GIRepository.Repository.get_n_infos] to find the maximum number
    1002                 :             :  * of entries. It is an error to pass an invalid @idx to this function.
    1003                 :             :  *
    1004                 :             :  * Returns: (transfer full) (not nullable): [class@GIRepository.BaseInfo]
    1005                 :             :  *   containing metadata
    1006                 :             :  * Since: 2.80
    1007                 :             :  */
    1008                 :             : GIBaseInfo *
    1009                 :        1952 : gi_repository_get_info (GIRepository *repository,
    1010                 :             :                         const char   *namespace,
    1011                 :             :                         unsigned int  idx)
    1012                 :             : {
    1013                 :             :   GITypelib *typelib;
    1014                 :             :   DirEntry *entry;
    1015                 :             : 
    1016                 :        1952 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
    1017                 :        1952 :   g_return_val_if_fail (namespace != NULL, NULL);
    1018                 :        1952 :   g_return_val_if_fail (idx < G_MAXUINT16, NULL);
    1019                 :             : 
    1020                 :        1952 :   typelib = get_registered (repository, namespace, NULL);
    1021                 :             : 
    1022                 :        1952 :   g_return_val_if_fail (typelib != NULL, NULL);
    1023                 :             : 
    1024                 :        1952 :   entry = gi_typelib_get_dir_entry (typelib, idx + 1);
    1025                 :        1952 :   g_return_val_if_fail (entry != NULL, NULL);
    1026                 :             : 
    1027                 :        1952 :   return gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
    1028                 :             :                            repository,
    1029                 :             :                            NULL, typelib, entry->offset);
    1030                 :             : }
    1031                 :             : 
    1032                 :             : static DirEntry *
    1033                 :           9 : find_by_gtype (GPtrArray   *ordered_table,
    1034                 :             :                const char  *gtype_name,
    1035                 :             :                gboolean     check_prefix,
    1036                 :             :                GITypelib  **out_result_typelib)
    1037                 :             : {
    1038                 :             :   /* Search in reverse order as the longest namespaces will be listed last, and
    1039                 :             :    * those are the ones we want to search first. */
    1040                 :          21 :   for (guint i = ordered_table->len; i > 0; i--)
    1041                 :             :     {
    1042                 :          17 :       GITypelib *typelib = g_ptr_array_index (ordered_table, i - 1);
    1043                 :             :       DirEntry *ret;
    1044                 :             : 
    1045                 :          17 :       if (check_prefix)
    1046                 :             :         {
    1047                 :          12 :           if (!gi_typelib_matches_gtype_name_prefix (typelib, gtype_name))
    1048                 :           0 :             continue;
    1049                 :             :         }
    1050                 :             : 
    1051                 :          17 :       ret = gi_typelib_get_dir_entry_by_gtype_name (typelib, gtype_name);
    1052                 :          17 :       if (ret)
    1053                 :             :         {
    1054                 :           5 :           *out_result_typelib = typelib;
    1055                 :           5 :           return ret;
    1056                 :             :         }
    1057                 :             :     }
    1058                 :             : 
    1059                 :           4 :   return NULL;
    1060                 :             : }
    1061                 :             : 
    1062                 :             : /**
    1063                 :             :  * gi_repository_find_by_gtype:
    1064                 :             :  * @repository: A #GIRepository
    1065                 :             :  * @gtype: [type@GObject.Type] to search for
    1066                 :             :  *
    1067                 :             :  * Searches all loaded namespaces for a particular [type@GObject.Type].
    1068                 :             :  *
    1069                 :             :  * Note that in order to locate the metadata, the namespace corresponding to
    1070                 :             :  * the type must first have been loaded.  There is currently no
    1071                 :             :  * mechanism for determining the namespace which corresponds to an
    1072                 :             :  * arbitrary [type@GObject.Type] — thus, this function will operate most
    1073                 :             :  * reliably when you know the [type@GObject.Type] is from a loaded namespace.
    1074                 :             :  *
    1075                 :             :  * Returns: (transfer full) (nullable): [class@GIRepository.BaseInfo]
    1076                 :             :  *   representing metadata about @type, or `NULL` if none found
    1077                 :             :  * Since: 2.80
    1078                 :             :  */
    1079                 :             : GIBaseInfo *
    1080                 :           8 : gi_repository_find_by_gtype (GIRepository *repository,
    1081                 :             :                              GType         gtype)
    1082                 :             : {
    1083                 :             :   const char *gtype_name;
    1084                 :           8 :   GITypelib *result_typelib = NULL;
    1085                 :             :   GIBaseInfo *cached;
    1086                 :             :   DirEntry *entry;
    1087                 :             : 
    1088                 :           8 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
    1089                 :           8 :   g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL);
    1090                 :             : 
    1091                 :           8 :   cached = g_hash_table_lookup (repository->info_by_gtype,
    1092                 :             :                                 (gpointer)gtype);
    1093                 :             : 
    1094                 :           8 :   if (cached != NULL)
    1095                 :           1 :     return gi_base_info_ref (cached);
    1096                 :             : 
    1097                 :           7 :   if (g_hash_table_contains (repository->unknown_gtypes, (gpointer)gtype))
    1098                 :           1 :     return NULL;
    1099                 :             : 
    1100                 :           6 :   gtype_name = g_type_name (gtype);
    1101                 :             : 
    1102                 :             :   /* Inside each typelib, we include the "C prefix" which acts as
    1103                 :             :    * a namespace mechanism.  For GtkTreeView, the C prefix is Gtk.
    1104                 :             :    * Given the assumption that GTypes for a library also use the
    1105                 :             :    * C prefix, we know we can skip examining a typelib if our
    1106                 :             :    * target type does not have this typelib's C prefix. Use this
    1107                 :             :    * assumption as our first attempt at locating the DirEntry.
    1108                 :             :    */
    1109                 :           6 :   entry = find_by_gtype (repository->ordered_typelibs, gtype_name, TRUE, &result_typelib);
    1110                 :           6 :   if (entry == NULL)
    1111                 :           1 :     entry = find_by_gtype (repository->ordered_lazy_typelibs, gtype_name, TRUE, &result_typelib);
    1112                 :             : 
    1113                 :             :   /* Not every class library necessarily specifies a correct c_prefix,
    1114                 :             :    * so take a second pass. This time we will try a global lookup,
    1115                 :             :    * ignoring prefixes.
    1116                 :             :    * See http://bugzilla.gnome.org/show_bug.cgi?id=564016
    1117                 :             :    */
    1118                 :           6 :   if (entry == NULL)
    1119                 :           1 :     entry = find_by_gtype (repository->ordered_typelibs, gtype_name, FALSE, &result_typelib);
    1120                 :           6 :   if (entry == NULL)
    1121                 :           1 :     entry = find_by_gtype (repository->ordered_lazy_typelibs, gtype_name, FALSE, &result_typelib);
    1122                 :             : 
    1123                 :           6 :   if (entry != NULL)
    1124                 :             :     {
    1125                 :           5 :       cached = gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
    1126                 :             :                                  repository,
    1127                 :             :                                  NULL, result_typelib, entry->offset);
    1128                 :             : 
    1129                 :           5 :       g_hash_table_insert (repository->info_by_gtype,
    1130                 :             :                            (gpointer) gtype,
    1131                 :           5 :                            gi_base_info_ref (cached));
    1132                 :           5 :       return cached;
    1133                 :             :     }
    1134                 :             :   else
    1135                 :             :     {
    1136                 :           1 :       g_hash_table_add (repository->unknown_gtypes, (gpointer) gtype);
    1137                 :           1 :       return NULL;
    1138                 :             :     }
    1139                 :             : }
    1140                 :             : 
    1141                 :             : /**
    1142                 :             :  * gi_repository_find_by_name:
    1143                 :             :  * @repository: A #GIRepository
    1144                 :             :  * @namespace_: Namespace which will be searched
    1145                 :             :  * @name: Entry name to find
    1146                 :             :  *
    1147                 :             :  * Searches for a particular entry in a namespace.
    1148                 :             :  *
    1149                 :             :  * Before calling this function for a particular namespace, you must call
    1150                 :             :  * [method@GIRepository.Repository.require] to load the namespace, or otherwise
    1151                 :             :  * ensure the namespace has already been loaded.
    1152                 :             :  *
    1153                 :             :  * Returns: (transfer full) (nullable): [class@GIRepository.BaseInfo]
    1154                 :             :  *   representing metadata about @name, or `NULL` if none found
    1155                 :             :  * Since: 2.80
    1156                 :             :  */
    1157                 :             : GIBaseInfo *
    1158                 :          59 : gi_repository_find_by_name (GIRepository *repository,
    1159                 :             :                             const char   *namespace,
    1160                 :             :                             const char   *name)
    1161                 :             : {
    1162                 :             :   GITypelib *typelib;
    1163                 :             :   DirEntry *entry;
    1164                 :             : 
    1165                 :          59 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
    1166                 :          59 :   g_return_val_if_fail (namespace != NULL, NULL);
    1167                 :             : 
    1168                 :          59 :   typelib = get_registered (repository, namespace, NULL);
    1169                 :          59 :   g_return_val_if_fail (typelib != NULL, NULL);
    1170                 :             : 
    1171                 :          59 :   entry = gi_typelib_get_dir_entry_by_name (typelib, name);
    1172                 :          59 :   if (entry == NULL)
    1173                 :           1 :     return NULL;
    1174                 :          58 :   return gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
    1175                 :             :                            repository,
    1176                 :             :                            NULL, typelib, entry->offset);
    1177                 :             : }
    1178                 :             : 
    1179                 :             : static DirEntry *
    1180                 :           5 : find_by_error_domain (GPtrArray  *ordered_typelibs,
    1181                 :             :                       GQuark      target_domain,
    1182                 :             :                       GITypelib **out_typelib)
    1183                 :             : {
    1184                 :             :   /* Search in reverse order as the longest namespaces will be listed last, and
    1185                 :             :    * those are the ones we want to search first. */
    1186                 :          16 :   for (guint i = ordered_typelibs->len; i > 0; i--)
    1187                 :             :     {
    1188                 :          12 :       GITypelib *typelib = g_ptr_array_index (ordered_typelibs, i - 1);
    1189                 :             :       DirEntry *entry;
    1190                 :             : 
    1191                 :          12 :       entry = gi_typelib_get_dir_entry_by_error_domain (typelib, target_domain);
    1192                 :          12 :       if (entry != NULL)
    1193                 :             :         {
    1194                 :           1 :           *out_typelib = typelib;
    1195                 :           1 :           return entry;
    1196                 :             :         }
    1197                 :             :     }
    1198                 :             : 
    1199                 :           4 :   return NULL;
    1200                 :             : }
    1201                 :             : 
    1202                 :             : /**
    1203                 :             :  * gi_repository_find_by_error_domain:
    1204                 :             :  * @repository: A #GIRepository
    1205                 :             :  * @domain: a [type@GLib.Error] domain
    1206                 :             :  *
    1207                 :             :  * Searches for the enum type corresponding to the given [type@GLib.Error]
    1208                 :             :  * domain.
    1209                 :             :  *
    1210                 :             :  * Before calling this function for a particular namespace, you must call
    1211                 :             :  * [method@GIRepository.Repository.require] to load the namespace, or otherwise
    1212                 :             :  * ensure the namespace has already been loaded.
    1213                 :             :  *
    1214                 :             :  * Returns: (transfer full) (nullable): [class@GIRepository.EnumInfo]
    1215                 :             :  *   representing metadata about @domain’s enum type, or `NULL` if none found
    1216                 :             :  * Since: 2.80
    1217                 :             :  */
    1218                 :             : GIEnumInfo *
    1219                 :           4 : gi_repository_find_by_error_domain (GIRepository *repository,
    1220                 :             :                                     GQuark        domain)
    1221                 :             : {
    1222                 :             :   GIEnumInfo *cached;
    1223                 :           4 :   DirEntry *result = NULL;
    1224                 :           4 :   GITypelib *result_typelib = NULL;
    1225                 :             : 
    1226                 :           4 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
    1227                 :             : 
    1228                 :           4 :   cached = g_hash_table_lookup (repository->info_by_error_domain,
    1229                 :           4 :                                 GUINT_TO_POINTER (domain));
    1230                 :             : 
    1231                 :           4 :   if (cached != NULL)
    1232                 :           1 :     return (GIEnumInfo *) gi_base_info_ref ((GIBaseInfo *)cached);
    1233                 :             : 
    1234                 :           3 :   result = find_by_error_domain (repository->ordered_typelibs, domain, &result_typelib);
    1235                 :           3 :   if (result == NULL)
    1236                 :           2 :     result = find_by_error_domain (repository->ordered_lazy_typelibs, domain, &result_typelib);
    1237                 :             : 
    1238                 :           3 :   if (result != NULL)
    1239                 :             :     {
    1240                 :           1 :       cached = (GIEnumInfo *) gi_info_new_full (gi_typelib_blob_type_to_info_type (result->blob_type),
    1241                 :             :                                                 repository,
    1242                 :             :                                                 NULL, result_typelib, result->offset);
    1243                 :             : 
    1244                 :           1 :       g_hash_table_insert (repository->info_by_error_domain,
    1245                 :           1 :                            GUINT_TO_POINTER (domain),
    1246                 :           1 :                            gi_base_info_ref ((GIBaseInfo *) cached));
    1247                 :           1 :       return cached;
    1248                 :             :     }
    1249                 :           2 :   return NULL;
    1250                 :             : }
    1251                 :             : 
    1252                 :             : /**
    1253                 :             :  * gi_repository_get_object_gtype_interfaces:
    1254                 :             :  * @repository: a #GIRepository
    1255                 :             :  * @gtype: a [type@GObject.Type] whose fundamental type is `G_TYPE_OBJECT`
    1256                 :             :  * @n_interfaces_out: (out): Number of interfaces
    1257                 :             :  * @interfaces_out: (out) (transfer none) (array length=n_interfaces_out): Interfaces for @gtype
    1258                 :             :  *
    1259                 :             :  * Look up the implemented interfaces for @gtype.
    1260                 :             :  *
    1261                 :             :  * This function cannot fail per se; but for a totally ‘unknown’
    1262                 :             :  * [type@GObject.Type], it may return 0 implemented interfaces.
    1263                 :             :  *
    1264                 :             :  * The semantics of this function are designed for a dynamic binding,
    1265                 :             :  * where in certain cases (such as a function which returns an
    1266                 :             :  * interface which may have ‘hidden’ implementation classes), not all
    1267                 :             :  * data may be statically known, and will have to be determined from
    1268                 :             :  * the [type@GObject.Type] of the object.  An example is
    1269                 :             :  * [func@Gio.File.new_for_path] returning a concrete class of
    1270                 :             :  * `GLocalFile`, which is a [type@GObject.Type] we see at runtime, but
    1271                 :             :  * not statically.
    1272                 :             :  *
    1273                 :             :  * Since: 2.80
    1274                 :             :  */
    1275                 :             : void
    1276                 :           1 : gi_repository_get_object_gtype_interfaces (GIRepository      *repository,
    1277                 :             :                                            GType              gtype,
    1278                 :             :                                            size_t            *n_interfaces_out,
    1279                 :             :                                            GIInterfaceInfo ***interfaces_out)
    1280                 :             : {
    1281                 :             :   GTypeInterfaceCache *cache;
    1282                 :             : 
    1283                 :           1 :   g_return_if_fail (GI_IS_REPOSITORY (repository));
    1284                 :           1 :   g_return_if_fail (g_type_fundamental (gtype) == G_TYPE_OBJECT);
    1285                 :             : 
    1286                 :           1 :   cache = g_hash_table_lookup (repository->interfaces_for_gtype,
    1287                 :             :                                (void *) gtype);
    1288                 :           1 :   if (cache == NULL)
    1289                 :             :     {
    1290                 :             :       GType *interfaces;
    1291                 :             :       unsigned int i;
    1292                 :             :       unsigned int n_interfaces;
    1293                 :           1 :       GList *interface_infos = NULL, *iter;
    1294                 :             : 
    1295                 :           1 :       interfaces = g_type_interfaces (gtype, &n_interfaces);
    1296                 :           3 :       for (i = 0; i < n_interfaces; i++)
    1297                 :             :         {
    1298                 :             :           GIBaseInfo *base_info;
    1299                 :             : 
    1300                 :           2 :           base_info = gi_repository_find_by_gtype (repository, interfaces[i]);
    1301                 :           2 :           if (base_info == NULL)
    1302                 :           0 :             continue;
    1303                 :             : 
    1304                 :           2 :           if (gi_base_info_get_info_type (base_info) != GI_INFO_TYPE_INTERFACE)
    1305                 :             :             {
    1306                 :             :               /* FIXME - could this really happen? */
    1307                 :           0 :               gi_base_info_unref (base_info);
    1308                 :           0 :               continue;
    1309                 :             :             }
    1310                 :             : 
    1311                 :           2 :           if (!g_list_find (interface_infos, base_info))
    1312                 :           2 :             interface_infos = g_list_prepend (interface_infos, base_info);
    1313                 :             :         }
    1314                 :             : 
    1315                 :           1 :       cache = g_malloc (sizeof (GTypeInterfaceCache)
    1316                 :           1 :                         + sizeof (GIBaseInfo*) * g_list_length (interface_infos));
    1317                 :           1 :       cache->n_interfaces = g_list_length (interface_infos);
    1318                 :           3 :       for (iter = interface_infos, i = 0; iter; iter = iter->next, i++)
    1319                 :           2 :         cache->interfaces[i] = iter->data;
    1320                 :           1 :       g_list_free (interface_infos);
    1321                 :             : 
    1322                 :           1 :       g_hash_table_insert (repository->interfaces_for_gtype, (gpointer) gtype,
    1323                 :             :                            cache);
    1324                 :             : 
    1325                 :           1 :       g_free (interfaces);
    1326                 :             :     }
    1327                 :             : 
    1328                 :           1 :   *n_interfaces_out = cache->n_interfaces;
    1329                 :           1 :   *interfaces_out = (GIInterfaceInfo**)&cache->interfaces[0];
    1330                 :             : }
    1331                 :             : 
    1332                 :             : static void
    1333                 :           6 : collect_namespaces (GPtrArray  *ordered_typelibs,
    1334                 :             :                     char      **names,
    1335                 :             :                     size_t     *inout_i)
    1336                 :             : {
    1337                 :          17 :   for (guint j = 0; j < ordered_typelibs->len; j++)
    1338                 :             :     {
    1339                 :          11 :       GITypelib *typelib = g_ptr_array_index (ordered_typelibs, j);
    1340                 :          11 :       const char *namespace = gi_typelib_get_namespace (typelib);
    1341                 :          22 :       names[(*inout_i)++] = g_strdup (namespace);
    1342                 :             :     }
    1343                 :           6 : }
    1344                 :             : 
    1345                 :             : /**
    1346                 :             :  * gi_repository_get_loaded_namespaces:
    1347                 :             :  * @repository: A #GIRepository
    1348                 :             :  * @n_namespaces_out: (optional) (out): Return location for the number of
    1349                 :             :  *   namespaces
    1350                 :             :  *
    1351                 :             :  * Return the list of currently loaded namespaces.
    1352                 :             :  *
    1353                 :             :  * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
    1354                 :             :  * counted in @n_namespaces_out.
    1355                 :             :  *
    1356                 :             :  * Returns: (element-type utf8) (transfer full) (array length=n_namespaces_out):
    1357                 :             :  *   list of namespaces
    1358                 :             :  * Since: 2.80
    1359                 :             :  */
    1360                 :             : char **
    1361                 :           3 : gi_repository_get_loaded_namespaces (GIRepository *repository,
    1362                 :             :                                      size_t       *n_namespaces_out)
    1363                 :             : {
    1364                 :             :   char **names;
    1365                 :             :   size_t i;
    1366                 :             :   size_t n_typelibs;
    1367                 :             : 
    1368                 :           3 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
    1369                 :             : 
    1370                 :           3 :   n_typelibs = repository->ordered_typelibs->len + repository->ordered_lazy_typelibs->len;
    1371                 :           3 :   names = g_malloc0 (sizeof (char *) * (n_typelibs + 1));
    1372                 :           3 :   i = 0;
    1373                 :             : 
    1374                 :           3 :   collect_namespaces (repository->ordered_typelibs, names, &i);
    1375                 :           3 :   collect_namespaces (repository->ordered_lazy_typelibs, names, &i);
    1376                 :             : 
    1377                 :           3 :   if (n_namespaces_out != NULL)
    1378                 :           2 :     *n_namespaces_out = i;
    1379                 :             : 
    1380                 :           3 :   return names;
    1381                 :             : }
    1382                 :             : 
    1383                 :             : /**
    1384                 :             :  * gi_repository_get_version:
    1385                 :             :  * @repository: A #GIRepository
    1386                 :             :  * @namespace_: Namespace to inspect
    1387                 :             :  *
    1388                 :             :  * This function returns the loaded version associated with the given
    1389                 :             :  * namespace @namespace_.
    1390                 :             :  *
    1391                 :             :  * Note: The namespace must have already been loaded using a function
    1392                 :             :  * such as [method@GIRepository.Repository.require] before calling this
    1393                 :             :  * function.
    1394                 :             :  *
    1395                 :             :  * Returns: Loaded version
    1396                 :             :  * Since: 2.80
    1397                 :             :  */
    1398                 :             : const char *
    1399                 :           1 : gi_repository_get_version (GIRepository *repository,
    1400                 :             :                            const char   *namespace)
    1401                 :             : {
    1402                 :             :   GITypelib *typelib;
    1403                 :             :   Header *header;
    1404                 :             : 
    1405                 :           1 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
    1406                 :           1 :   g_return_val_if_fail (namespace != NULL, NULL);
    1407                 :             : 
    1408                 :           1 :   typelib = get_registered (repository, namespace, NULL);
    1409                 :             : 
    1410                 :           1 :   g_return_val_if_fail (typelib != NULL, NULL);
    1411                 :             : 
    1412                 :           1 :   header = (Header *) typelib->data;
    1413                 :           1 :   return gi_typelib_get_string (typelib, header->nsversion);
    1414                 :             : }
    1415                 :             : 
    1416                 :             : /**
    1417                 :             :  * gi_repository_get_shared_libraries:
    1418                 :             :  * @repository: A #GIRepository
    1419                 :             :  * @namespace_: Namespace to inspect
    1420                 :             :  * @out_n_elements: (out) (optional): Return location for the number of elements
    1421                 :             :  *   in the returned array
    1422                 :             :  *
    1423                 :             :  * This function returns an array of paths to the
    1424                 :             :  * shared C libraries associated with the given namespace @namespace_.
    1425                 :             :  *
    1426                 :             :  * There may be no shared library path associated, in which case this
    1427                 :             :  * function will return `NULL`.
    1428                 :             :  *
    1429                 :             :  * Note: The namespace must have already been loaded using a function
    1430                 :             :  * such as [method@GIRepository.Repository.require] before calling this
    1431                 :             :  * function.
    1432                 :             :  *
    1433                 :             :  * The list is internal to [class@GIRepository.Repository] and should not be
    1434                 :             :  * freed, nor should its string elements.
    1435                 :             :  *
    1436                 :             :  * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
    1437                 :             :  * counted in @out_n_elements.
    1438                 :             :  *
    1439                 :             :  * Returns: (nullable) (array length=out_n_elements) (transfer none): Array of
    1440                 :             :  *   paths to shared libraries, or `NULL` if none are associated
    1441                 :             :  * Since: 2.80
    1442                 :             :  */
    1443                 :             : const char * const *
    1444                 :           0 : gi_repository_get_shared_libraries (GIRepository *repository,
    1445                 :             :                                     const char   *namespace,
    1446                 :             :                                     size_t       *out_n_elements)
    1447                 :             : {
    1448                 :             :   GITypelib *typelib;
    1449                 :             :   Header *header;
    1450                 :             : 
    1451                 :           0 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
    1452                 :           0 :   g_return_val_if_fail (namespace != NULL, NULL);
    1453                 :             : 
    1454                 :           0 :   typelib = get_registered (repository, namespace, NULL);
    1455                 :             : 
    1456                 :           0 :   g_return_val_if_fail (typelib != NULL, NULL);
    1457                 :             : 
    1458                 :           0 :   header = (Header *) typelib->data;
    1459                 :           0 :   if (!header->shared_library)
    1460                 :             :     {
    1461                 :           0 :       if (out_n_elements != NULL)
    1462                 :           0 :         *out_n_elements = 0;
    1463                 :           0 :       return NULL;
    1464                 :             :     }
    1465                 :             : 
    1466                 :             :   /* Populate the cache. */
    1467                 :           0 :   if (repository->cached_shared_libraries == NULL)
    1468                 :             :     {
    1469                 :           0 :       const char *comma_separated = gi_typelib_get_string (typelib, header->shared_library);
    1470                 :             : 
    1471                 :           0 :       if (comma_separated != NULL && *comma_separated != '\0')
    1472                 :             :         {
    1473                 :           0 :           repository->cached_shared_libraries = g_strsplit (comma_separated, ",", -1);
    1474                 :           0 :           repository->cached_n_shared_libraries = g_strv_length (repository->cached_shared_libraries);
    1475                 :             :         }
    1476                 :             :     }
    1477                 :             : 
    1478                 :           0 :   if (out_n_elements != NULL)
    1479                 :           0 :     *out_n_elements = repository->cached_n_shared_libraries;
    1480                 :             : 
    1481                 :           0 :   return (const char * const *) repository->cached_shared_libraries;
    1482                 :             : }
    1483                 :             : 
    1484                 :             : /**
    1485                 :             :  * gi_repository_get_c_prefix:
    1486                 :             :  * @repository: A #GIRepository
    1487                 :             :  * @namespace_: Namespace to inspect
    1488                 :             :  *
    1489                 :             :  * This function returns the ‘C prefix’, or the C level namespace
    1490                 :             :  * associated with the given introspection namespace.
    1491                 :             :  *
    1492                 :             :  * Each C symbol starts with this prefix, as well each [type@GObject.Type] in
    1493                 :             :  * the library.
    1494                 :             :  *
    1495                 :             :  * Note: The namespace must have already been loaded using a function
    1496                 :             :  * such as [method@GIRepository.Repository.require] before calling this
    1497                 :             :  * function.
    1498                 :             :  *
    1499                 :             :  * Returns: (nullable): C namespace prefix, or `NULL` if none associated
    1500                 :             :  * Since: 2.80
    1501                 :             :  */
    1502                 :             : const char *
    1503                 :           1 : gi_repository_get_c_prefix (GIRepository *repository,
    1504                 :             :                             const char   *namespace_)
    1505                 :             : {
    1506                 :             :   GITypelib *typelib;
    1507                 :             :   Header *header;
    1508                 :             : 
    1509                 :           1 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
    1510                 :           1 :   g_return_val_if_fail (namespace_ != NULL, NULL);
    1511                 :             : 
    1512                 :           1 :   typelib = get_registered (repository, namespace_, NULL);
    1513                 :             : 
    1514                 :           1 :   g_return_val_if_fail (typelib != NULL, NULL);
    1515                 :             : 
    1516                 :           1 :   header = (Header *) typelib->data;
    1517                 :           1 :   if (header->c_prefix)
    1518                 :           1 :     return gi_typelib_get_string (typelib, header->c_prefix);
    1519                 :             :   else
    1520                 :           0 :     return NULL;
    1521                 :             : }
    1522                 :             : 
    1523                 :             : /**
    1524                 :             :  * gi_repository_get_typelib_path:
    1525                 :             :  * @repository: A #GIRepository
    1526                 :             :  * @namespace_: GI namespace to use, e.g. `Gtk`
    1527                 :             :  *
    1528                 :             :  * If namespace @namespace_ is loaded, return the full path to the
    1529                 :             :  * .typelib file it was loaded from.
    1530                 :             :  *
    1531                 :             :  * If the typelib for namespace @namespace_ was included in a shared library,
    1532                 :             :  * return the special string `<builtin>`.
    1533                 :             :  *
    1534                 :             :  * Returns: (type filename) (nullable): Filesystem path (or `<builtin>`) if
    1535                 :             :  *   successful, `NULL` if namespace is not loaded
    1536                 :             :  * Since: 2.80
    1537                 :             :  */
    1538                 :             : const char *
    1539                 :           0 : gi_repository_get_typelib_path (GIRepository *repository,
    1540                 :             :                                 const char   *namespace)
    1541                 :             : {
    1542                 :             :   gpointer orig_key, value;
    1543                 :             : 
    1544                 :           0 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
    1545                 :             : 
    1546                 :           0 :   if (!g_hash_table_lookup_extended (repository->typelibs, namespace,
    1547                 :             :                                      &orig_key, &value))
    1548                 :             :     {
    1549                 :           0 :       if (!g_hash_table_lookup_extended (repository->lazy_typelibs, namespace,
    1550                 :             :                                          &orig_key, &value))
    1551                 :             : 
    1552                 :           0 :         return NULL;
    1553                 :             :     }
    1554                 :           0 :   return ((char*)orig_key) + strlen ((char *) orig_key) + 1;
    1555                 :             : }
    1556                 :             : 
    1557                 :             : /* This simple search function looks for a specified namespace-version;
    1558                 :             :    it's faster than the full directory listing required for latest version. */
    1559                 :             : static GMappedFile *
    1560                 :         194 : find_namespace_version (const char          *namespace,
    1561                 :             :                         const char          *version,
    1562                 :             :                         const char * const  *search_paths,
    1563                 :             :                         size_t               n_search_paths,
    1564                 :             :                         char               **path_ret)
    1565                 :             : {
    1566                 :         194 :   GError *error = NULL;
    1567                 :         194 :   GMappedFile *mfile = NULL;
    1568                 :             :   char *fname;
    1569                 :             : 
    1570                 :         194 :   if (g_str_equal (namespace, GIREPOSITORY_TYPELIB_NAME) &&
    1571                 :           0 :       !g_str_equal (version, GIREPOSITORY_TYPELIB_VERSION))
    1572                 :             :     {
    1573                 :           0 :       g_debug ("Ignoring %s-%s.typelib because this libgirepository "
    1574                 :             :                "corresponds to %s-%s",
    1575                 :             :                namespace, version,
    1576                 :             :                namespace, GIREPOSITORY_TYPELIB_VERSION);
    1577                 :           0 :       return NULL;
    1578                 :             :     }
    1579                 :             : 
    1580                 :         194 :   fname = g_strdup_printf ("%s-%s.typelib", namespace, version);
    1581                 :             : 
    1582                 :         194 :   for (size_t i = 0; i < n_search_paths; ++i)
    1583                 :             :     {
    1584                 :         194 :       char *path = g_build_filename (search_paths[i], fname, NULL);
    1585                 :             : 
    1586                 :         194 :       mfile = g_mapped_file_new (path, FALSE, &error);
    1587                 :         194 :       if (error)
    1588                 :             :         {
    1589                 :           0 :           g_free (path);
    1590                 :           0 :           g_clear_error (&error);
    1591                 :           0 :           continue;
    1592                 :             :         }
    1593                 :         194 :       *path_ret = path;
    1594                 :         194 :       break;
    1595                 :             :     }
    1596                 :         194 :   g_free (fname);
    1597                 :         194 :   return mfile;
    1598                 :             : }
    1599                 :             : 
    1600                 :             : static gboolean
    1601                 :           1 : parse_version (const char *version,
    1602                 :             :                int *major,
    1603                 :             :                int *minor)
    1604                 :             : {
    1605                 :             :   const char *dot;
    1606                 :             :   char *end;
    1607                 :             : 
    1608                 :           1 :   *major = strtol (version, &end, 10);
    1609                 :           1 :   dot = strchr (version, '.');
    1610                 :           1 :   if (dot == NULL)
    1611                 :             :     {
    1612                 :           0 :       *minor = 0;
    1613                 :           0 :       return TRUE;
    1614                 :             :     }
    1615                 :           1 :   if (dot != end)
    1616                 :           0 :     return FALSE;
    1617                 :           1 :   *minor = strtol (dot+1, &end, 10);
    1618                 :           1 :   if (end != (version + strlen (version)))
    1619                 :           0 :     return FALSE;
    1620                 :           1 :   return TRUE;
    1621                 :             : }
    1622                 :             : 
    1623                 :             : static int
    1624                 :           0 : compare_version (const char *v1,
    1625                 :             :                  const char *v2)
    1626                 :             : {
    1627                 :             :   gboolean success;
    1628                 :             :   int v1_major, v1_minor;
    1629                 :             :   int v2_major, v2_minor;
    1630                 :             : 
    1631                 :           0 :   success = parse_version (v1, &v1_major, &v1_minor);
    1632                 :           0 :   g_assert (success);
    1633                 :             : 
    1634                 :           0 :   success = parse_version (v2, &v2_major, &v2_minor);
    1635                 :           0 :   g_assert (success);
    1636                 :             : 
    1637                 :             :   /* Avoid a compiler warning about `success` being unused with G_DISABLE_ASSERT */
    1638                 :             :   (void) success;
    1639                 :             : 
    1640                 :           0 :   if (v1_major > v2_major)
    1641                 :           0 :     return 1;
    1642                 :           0 :   else if (v2_major > v1_major)
    1643                 :           0 :     return -1;
    1644                 :           0 :   else if (v1_minor > v2_minor)
    1645                 :           0 :     return 1;
    1646                 :           0 :   else if (v2_minor > v1_minor)
    1647                 :           0 :     return -1;
    1648                 :           0 :   return 0;
    1649                 :             : }
    1650                 :             : 
    1651                 :             : struct NamespaceVersionCandidadate
    1652                 :             : {
    1653                 :             :   GMappedFile *mfile;
    1654                 :             :   int path_index;
    1655                 :             :   char *path;
    1656                 :             :   char *version;
    1657                 :             : };
    1658                 :             : 
    1659                 :             : static int
    1660                 :           0 : compare_candidate_reverse (struct NamespaceVersionCandidadate *c1,
    1661                 :             :                            struct NamespaceVersionCandidadate *c2)
    1662                 :             : {
    1663                 :           0 :   int result = compare_version (c1->version, c2->version);
    1664                 :             :   /* First, check the version */
    1665                 :           0 :   if (result > 0)
    1666                 :           0 :     return -1;
    1667                 :           0 :   else if (result < 0)
    1668                 :           0 :     return 1;
    1669                 :             :   else
    1670                 :             :     {
    1671                 :             :       /* Now check the path index, which says how early in the search path
    1672                 :             :        * we found it.  This ensures that of equal version targets, we
    1673                 :             :        * pick the earlier one.
    1674                 :             :        */
    1675                 :           0 :       if (c1->path_index == c2->path_index)
    1676                 :           0 :         return 0;
    1677                 :           0 :       else if (c1->path_index > c2->path_index)
    1678                 :           0 :         return 1;
    1679                 :             :       else
    1680                 :           0 :         return -1;
    1681                 :             :     }
    1682                 :             : }
    1683                 :             : 
    1684                 :             : static void
    1685                 :           1 : free_candidate (struct NamespaceVersionCandidadate *candidate)
    1686                 :             : {
    1687                 :           1 :   g_mapped_file_unref (candidate->mfile);
    1688                 :           1 :   g_free (candidate->path);
    1689                 :           1 :   g_free (candidate->version);
    1690                 :           1 :   g_slice_free (struct NamespaceVersionCandidadate, candidate);
    1691                 :           1 : }
    1692                 :             : 
    1693                 :             : static GSList *
    1694                 :           2 : enumerate_namespace_versions (const char         *namespace,
    1695                 :             :                               const char * const *search_paths,
    1696                 :             :                               size_t              n_search_paths)
    1697                 :             : {
    1698                 :           2 :   GSList *candidates = NULL;
    1699                 :           2 :   GHashTable *found_versions = g_hash_table_new (g_str_hash, g_str_equal);
    1700                 :             :   char *namespace_dash;
    1701                 :             :   char *namespace_typelib;
    1702                 :           2 :   GError *error = NULL;
    1703                 :             :   int index;
    1704                 :             : 
    1705                 :           2 :   namespace_dash = g_strdup_printf ("%s-", namespace);
    1706                 :           2 :   namespace_typelib = g_strdup_printf ("%s.typelib", namespace);
    1707                 :             : 
    1708                 :           2 :   index = 0;
    1709                 :           8 :   for (size_t i = 0; i < n_search_paths; ++i)
    1710                 :             :     {
    1711                 :             :       GDir *dir;
    1712                 :             :       const char *dirname;
    1713                 :             :       const char *entry;
    1714                 :             : 
    1715                 :           6 :       dirname = search_paths[i];
    1716                 :           6 :       dir = g_dir_open (dirname, 0, NULL);
    1717                 :           6 :       if (dir == NULL)
    1718                 :           4 :         continue;
    1719                 :          30 :       while ((entry = g_dir_read_name (dir)) != NULL)
    1720                 :             :         {
    1721                 :             :           GMappedFile *mfile;
    1722                 :             :           char *path, *version;
    1723                 :             :           struct NamespaceVersionCandidadate *candidate;
    1724                 :             : 
    1725                 :          28 :           if (!g_str_has_suffix (entry, ".typelib"))
    1726                 :          14 :             continue;
    1727                 :             : 
    1728                 :          14 :           if (g_str_has_prefix (entry, namespace_dash))
    1729                 :             :             {
    1730                 :             :               const char *last_dash;
    1731                 :             :               const char *name_end;
    1732                 :             :               int major, minor;
    1733                 :             : 
    1734                 :           1 :               if (g_str_equal (namespace, GIREPOSITORY_TYPELIB_NAME) &&
    1735                 :           0 :                   !g_str_equal (entry, GIREPOSITORY_TYPELIB_FILENAME))
    1736                 :             :                 {
    1737                 :           0 :                   g_debug ("Ignoring %s because this libgirepository "
    1738                 :             :                            "corresponds to %s",
    1739                 :             :                            entry, GIREPOSITORY_TYPELIB_FILENAME);
    1740                 :           0 :                   continue;
    1741                 :             :                 }
    1742                 :             : 
    1743                 :           1 :               name_end = strrchr (entry, '.');
    1744                 :           1 :               last_dash = strrchr (entry, '-');
    1745                 :           1 :               version = g_strndup (last_dash+1, name_end-(last_dash+1));
    1746                 :           1 :               if (!parse_version (version, &major, &minor))
    1747                 :             :                 {
    1748                 :           0 :                   g_free (version);
    1749                 :           0 :                   continue;
    1750                 :             :                 }
    1751                 :             :             }
    1752                 :             :           else
    1753                 :          13 :             continue;
    1754                 :             : 
    1755                 :           1 :           if (g_hash_table_lookup (found_versions, version) != NULL)
    1756                 :             :             {
    1757                 :           0 :               g_free (version);
    1758                 :           0 :               continue;
    1759                 :             :             }
    1760                 :             : 
    1761                 :           1 :           path = g_build_filename (dirname, entry, NULL);
    1762                 :           1 :           mfile = g_mapped_file_new (path, FALSE, &error);
    1763                 :           1 :           if (mfile == NULL)
    1764                 :             :             {
    1765                 :           0 :               g_free (path);
    1766                 :           0 :               g_free (version);
    1767                 :           0 :               g_clear_error (&error);
    1768                 :           0 :               continue;
    1769                 :             :             }
    1770                 :           1 :           candidate = g_slice_new0 (struct NamespaceVersionCandidadate);
    1771                 :           1 :           candidate->mfile = mfile;
    1772                 :           1 :           candidate->path_index = index;
    1773                 :           1 :           candidate->path = path;
    1774                 :           1 :           candidate->version = version;
    1775                 :           1 :           candidates = g_slist_prepend (candidates, candidate);
    1776                 :           1 :           g_hash_table_add (found_versions, version);
    1777                 :             :         }
    1778                 :           2 :       g_dir_close (dir);
    1779                 :           2 :       index++;
    1780                 :             :     }
    1781                 :             : 
    1782                 :           2 :   g_free (namespace_dash);
    1783                 :           2 :   g_free (namespace_typelib);
    1784                 :           2 :   g_hash_table_destroy (found_versions);
    1785                 :             : 
    1786                 :           2 :   return candidates;
    1787                 :             : }
    1788                 :             : 
    1789                 :             : static GMappedFile *
    1790                 :           0 : find_namespace_latest (const char          *namespace,
    1791                 :             :                        const char * const  *search_paths,
    1792                 :             :                        size_t               n_search_paths,
    1793                 :             :                        char               **version_ret,
    1794                 :             :                        char               **path_ret)
    1795                 :             : {
    1796                 :             :   GSList *candidates;
    1797                 :           0 :   GMappedFile *result = NULL;
    1798                 :             : 
    1799                 :           0 :   *version_ret = NULL;
    1800                 :           0 :   *path_ret = NULL;
    1801                 :             : 
    1802                 :           0 :   candidates = enumerate_namespace_versions (namespace, search_paths, n_search_paths);
    1803                 :             : 
    1804                 :           0 :   if (candidates != NULL)
    1805                 :             :     {
    1806                 :             :       struct NamespaceVersionCandidadate *elected;
    1807                 :           0 :       candidates = g_slist_sort (candidates, (GCompareFunc) compare_candidate_reverse);
    1808                 :             : 
    1809                 :           0 :       elected = (struct NamespaceVersionCandidadate *) candidates->data;
    1810                 :             :       /* Remove the elected one so we don't try to free its contents */
    1811                 :           0 :       candidates = g_slist_delete_link (candidates, candidates);
    1812                 :             : 
    1813                 :           0 :       result = elected->mfile;
    1814                 :           0 :       *path_ret = elected->path;
    1815                 :           0 :       *version_ret = elected->version;
    1816                 :           0 :       g_slice_free (struct NamespaceVersionCandidadate, elected); /* just free the container */
    1817                 :           0 :       g_slist_foreach (candidates, (GFunc) (void *) free_candidate, NULL);
    1818                 :           0 :       g_slist_free (candidates);
    1819                 :             :     }
    1820                 :           0 :   return result;
    1821                 :             : }
    1822                 :             : 
    1823                 :             : /**
    1824                 :             :  * gi_repository_enumerate_versions:
    1825                 :             :  * @repository: A #GIRepository
    1826                 :             :  * @namespace_: GI namespace, e.g. `Gtk`
    1827                 :             :  * @n_versions_out: (optional) (out): The number of versions returned.
    1828                 :             :  *
    1829                 :             :  * Obtain an unordered list of versions (either currently loaded or
    1830                 :             :  * available) for @namespace_ in this @repository.
    1831                 :             :  *
    1832                 :             :  * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
    1833                 :             :  * counted in @n_versions_out.
    1834                 :             :  *
    1835                 :             :  * Returns: (element-type utf8) (transfer full) (array length=n_versions_out): the array of versions.
    1836                 :             :  * Since: 2.80
    1837                 :             :  */
    1838                 :             : char **
    1839                 :           2 : gi_repository_enumerate_versions (GIRepository *repository,
    1840                 :             :                                   const char   *namespace_,
    1841                 :             :                                   size_t       *n_versions_out)
    1842                 :             : {
    1843                 :             :   GPtrArray *versions;
    1844                 :             :   GSList *candidates, *link;
    1845                 :             :   const char *loaded_version;
    1846                 :             :   char **ret;
    1847                 :             : 
    1848                 :           2 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
    1849                 :             : 
    1850                 :           2 :   candidates = enumerate_namespace_versions (namespace_,
    1851                 :           2 :                                              (const char * const *) repository->typelib_search_path->pdata,
    1852                 :           2 :                                              repository->typelib_search_path->len);
    1853                 :             : 
    1854                 :           2 :   if (!candidates)
    1855                 :             :     {
    1856                 :           1 :       if (n_versions_out)
    1857                 :           1 :         *n_versions_out = 0;
    1858                 :           1 :       return g_strdupv ((char *[]) {NULL});
    1859                 :             :     }
    1860                 :             : 
    1861                 :           1 :   versions = g_ptr_array_new_null_terminated (1, g_free, TRUE);
    1862                 :           2 :   for (link = candidates; link; link = link->next)
    1863                 :             :     {
    1864                 :           1 :       struct NamespaceVersionCandidadate *candidate = link->data;
    1865                 :           1 :       g_ptr_array_add (versions, g_steal_pointer (&candidate->version));
    1866                 :           1 :       free_candidate (candidate);
    1867                 :             :     }
    1868                 :           1 :   g_slist_free (candidates);
    1869                 :             : 
    1870                 :             :   /* The currently loaded version of a namespace is also part of the
    1871                 :             :    * available versions, as it could have been loaded using
    1872                 :             :    * require_private().
    1873                 :             :    */
    1874                 :           1 :   if (gi_repository_is_registered (repository, namespace_, NULL))
    1875                 :             :     {
    1876                 :           1 :       loaded_version = gi_repository_get_version (repository, namespace_);
    1877                 :           2 :       if (loaded_version &&
    1878                 :           1 :           !g_ptr_array_find_with_equal_func (versions, loaded_version, g_str_equal, NULL))
    1879                 :           0 :         g_ptr_array_add (versions, g_strdup (loaded_version));
    1880                 :             :     }
    1881                 :             : 
    1882                 :           1 :   ret = (char **) g_ptr_array_steal (versions, n_versions_out);
    1883                 :           1 :   g_ptr_array_unref (g_steal_pointer (&versions));
    1884                 :             : 
    1885                 :           1 :   return ret;
    1886                 :             : }
    1887                 :             : 
    1888                 :             : static GITypelib *
    1889                 :         280 : require_internal (GIRepository           *repository,
    1890                 :             :                   const char             *namespace,
    1891                 :             :                   const char             *version,
    1892                 :             :                   GIRepositoryLoadFlags   flags,
    1893                 :             :                   const char * const     *search_paths,
    1894                 :             :                   size_t                  n_search_paths,
    1895                 :             :                   GError                **error)
    1896                 :             : {
    1897                 :             :   GMappedFile *mfile;
    1898                 :         280 :   GITypelib *ret = NULL;
    1899                 :             :   Header *header;
    1900                 :         280 :   GITypelib *typelib = NULL;
    1901                 :         280 :   GITypelib *typelib_owned = NULL;
    1902                 :             :   const char *typelib_namespace, *typelib_version;
    1903                 :         280 :   gboolean allow_lazy = (flags & GI_REPOSITORY_LOAD_FLAG_LAZY) > 0;
    1904                 :             :   gboolean is_lazy;
    1905                 :         280 :   char *version_conflict = NULL;
    1906                 :         280 :   char *path = NULL;
    1907                 :         280 :   char *tmp_version = NULL;
    1908                 :             : 
    1909                 :         280 :   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
    1910                 :         280 :   g_return_val_if_fail (namespace != NULL, NULL);
    1911                 :             : 
    1912                 :         280 :   typelib = get_registered_status (repository, namespace, version, allow_lazy,
    1913                 :             :                                    &is_lazy, &version_conflict);
    1914                 :         280 :   if (typelib)
    1915                 :          86 :     return typelib;
    1916                 :             : 
    1917                 :         194 :   if (version_conflict != NULL)
    1918                 :             :     {
    1919                 :           0 :       g_set_error (error, GI_REPOSITORY_ERROR,
    1920                 :             :                    GI_REPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
    1921                 :             :                    "Requiring namespace '%s' version '%s', but '%s' is already loaded",
    1922                 :             :                    namespace, version, version_conflict);
    1923                 :           0 :       return NULL;
    1924                 :             :     }
    1925                 :             : 
    1926                 :         194 :   if (version != NULL)
    1927                 :             :     {
    1928                 :         194 :       mfile = find_namespace_version (namespace, version, search_paths,
    1929                 :             :                                       n_search_paths, &path);
    1930                 :         194 :       tmp_version = g_strdup (version);
    1931                 :             :     }
    1932                 :             :   else
    1933                 :             :     {
    1934                 :           0 :       mfile = find_namespace_latest (namespace, search_paths, n_search_paths,
    1935                 :             :                                      &tmp_version, &path);
    1936                 :             :     }
    1937                 :             : 
    1938                 :         194 :   if (mfile == NULL)
    1939                 :             :     {
    1940                 :           0 :       if (version != NULL)
    1941                 :           0 :         g_set_error (error, GI_REPOSITORY_ERROR,
    1942                 :             :                      GI_REPOSITORY_ERROR_TYPELIB_NOT_FOUND,
    1943                 :             :                      "Typelib file for namespace '%s', version '%s' not found",
    1944                 :             :                      namespace, version);
    1945                 :             :       else
    1946                 :           0 :         g_set_error (error, GI_REPOSITORY_ERROR,
    1947                 :             :                      GI_REPOSITORY_ERROR_TYPELIB_NOT_FOUND,
    1948                 :             :                      "Typelib file for namespace '%s' (any version) not found",
    1949                 :             :                      namespace);
    1950                 :           0 :       goto out;
    1951                 :             :     }
    1952                 :             : 
    1953                 :             :   {
    1954                 :         194 :     GError *temp_error = NULL;
    1955                 :         194 :     GBytes *bytes = NULL;
    1956                 :             : 
    1957                 :         194 :     bytes = g_mapped_file_get_bytes (mfile);
    1958                 :         194 :     typelib_owned = typelib = gi_typelib_new_from_bytes (bytes, &temp_error);
    1959                 :         194 :     g_bytes_unref (bytes);
    1960                 :         194 :     g_clear_pointer (&mfile, g_mapped_file_unref);
    1961                 :             : 
    1962                 :         194 :     if (!typelib)
    1963                 :             :       {
    1964                 :           0 :         g_set_error (error, GI_REPOSITORY_ERROR,
    1965                 :             :                      GI_REPOSITORY_ERROR_TYPELIB_NOT_FOUND,
    1966                 :             :                      "Failed to load typelib file '%s' for namespace '%s': %s",
    1967                 :           0 :                      path, namespace, temp_error->message);
    1968                 :           0 :         g_clear_error (&temp_error);
    1969                 :           0 :         goto out;
    1970                 :             :       }
    1971                 :             : 
    1972                 :         194 :     typelib->library_paths = (repository->library_paths != NULL) ? g_ptr_array_ref (repository->library_paths) : NULL;
    1973                 :             :   }
    1974                 :         194 :   header = (Header *) typelib->data;
    1975                 :         194 :   typelib_namespace = gi_typelib_get_string (typelib, header->namespace);
    1976                 :         194 :   typelib_version = gi_typelib_get_string (typelib, header->nsversion);
    1977                 :             : 
    1978                 :         194 :   if (strcmp (typelib_namespace, namespace) != 0)
    1979                 :             :     {
    1980                 :           0 :       g_set_error (error, GI_REPOSITORY_ERROR,
    1981                 :             :                    GI_REPOSITORY_ERROR_NAMESPACE_MISMATCH,
    1982                 :             :                    "Typelib file %s for namespace '%s' contains "
    1983                 :             :                    "namespace '%s' which doesn't match the file name",
    1984                 :             :                    path, namespace, typelib_namespace);
    1985                 :           0 :       goto out;
    1986                 :             :     }
    1987                 :         194 :   if (version != NULL && strcmp (typelib_version, version) != 0)
    1988                 :             :     {
    1989                 :           0 :       g_set_error (error, GI_REPOSITORY_ERROR,
    1990                 :             :                    GI_REPOSITORY_ERROR_NAMESPACE_MISMATCH,
    1991                 :             :                    "Typelib file %s for namespace '%s' contains "
    1992                 :             :                    "version '%s' which doesn't match the expected version '%s'",
    1993                 :             :                    path, namespace, typelib_version, version);
    1994                 :           0 :       goto out;
    1995                 :             :     }
    1996                 :             : 
    1997                 :         194 :   if (!register_internal (repository, path, allow_lazy,
    1998                 :             :                           typelib, error))
    1999                 :           0 :     goto out;
    2000                 :         194 :   ret = typelib;
    2001                 :         194 :  out:
    2002                 :         194 :   g_clear_pointer (&typelib_owned, gi_typelib_unref);
    2003                 :         194 :   g_free (tmp_version);
    2004                 :         194 :   g_free (path);
    2005                 :         194 :   return ret;
    2006                 :             : }
    2007                 :             : 
    2008                 :             : /**
    2009                 :             :  * gi_repository_require:
    2010                 :             :  * @repository: A #GIRepository
    2011                 :             :  * @namespace_: GI namespace to use, e.g. `Gtk`
    2012                 :             :  * @version: (nullable): Version of namespace, may be `NULL` for latest
    2013                 :             :  * @flags: Set of [flags@GIRepository.RepositoryLoadFlags], may be 0
    2014                 :             :  * @error: a [type@GLib.Error].
    2015                 :             :  *
    2016                 :             :  * Force the namespace @namespace_ to be loaded if it isn’t already.
    2017                 :             :  *
    2018                 :             :  * If @namespace_ is not loaded, this function will search for a
    2019                 :             :  * `.typelib` file using the repository search path.  In addition, a
    2020                 :             :  * version @version of namespace may be specified.  If @version is
    2021                 :             :  * not specified, the latest will be used.
    2022                 :             :  *
    2023                 :             :  * Returns: (transfer none): a pointer to the [type@GIRepository.Typelib] if
    2024                 :             :  *   successful, `NULL` otherwise
    2025                 :             :  * Since: 2.80
    2026                 :             :  */
    2027                 :             : GITypelib *
    2028                 :         280 : gi_repository_require (GIRepository  *repository,
    2029                 :             :                        const char    *namespace,
    2030                 :             :                        const char    *version,
    2031                 :             :                        GIRepositoryLoadFlags flags,
    2032                 :             :                        GError       **error)
    2033                 :             : {
    2034                 :             :   GITypelib *typelib;
    2035                 :             : 
    2036                 :         280 :   typelib = require_internal (repository, namespace, version, flags,
    2037                 :         280 :                               (const char * const *) repository->typelib_search_path->pdata,
    2038                 :         280 :                               repository->typelib_search_path->len, error);
    2039                 :             : 
    2040                 :         280 :   return typelib;
    2041                 :             : }
    2042                 :             : 
    2043                 :             : /**
    2044                 :             :  * gi_repository_require_private:
    2045                 :             :  * @repository: A #GIRepository
    2046                 :             :  * @typelib_dir: (type filename): Private directory where to find the requested
    2047                 :             :  *   typelib
    2048                 :             :  * @namespace_: GI namespace to use, e.g. `Gtk`
    2049                 :             :  * @version: (nullable): Version of namespace, may be `NULL` for latest
    2050                 :             :  * @flags: Set of [flags@GIRepository.RepositoryLoadFlags], may be 0
    2051                 :             :  * @error: a [type@GLib.Error].
    2052                 :             :  *
    2053                 :             :  * Force the namespace @namespace_ to be loaded if it isn’t already.
    2054                 :             :  *
    2055                 :             :  * If @namespace_ is not loaded, this function will search for a
    2056                 :             :  * `.typelib` file within the private directory only. In addition, a
    2057                 :             :  * version @version of namespace should be specified.  If @version is
    2058                 :             :  * not specified, the latest will be used.
    2059                 :             :  *
    2060                 :             :  * Returns: (transfer none): a pointer to the [type@GIRepository.Typelib] if
    2061                 :             :  *   successful, `NULL` otherwise
    2062                 :             :  * Since: 2.80
    2063                 :             :  */
    2064                 :             : GITypelib *
    2065                 :           0 : gi_repository_require_private (GIRepository           *repository,
    2066                 :             :                                const char             *typelib_dir,
    2067                 :             :                                const char             *namespace,
    2068                 :             :                                const char             *version,
    2069                 :             :                                GIRepositoryLoadFlags   flags,
    2070                 :             :                                GError                **error)
    2071                 :             : {
    2072                 :           0 :   const char * const search_path[] = { typelib_dir, NULL };
    2073                 :             : 
    2074                 :           0 :   return require_internal (repository, namespace, version, flags,
    2075                 :             :                            search_path, 1, error);
    2076                 :             : }
    2077                 :             : 
    2078                 :             : static gboolean
    2079                 :           0 : gi_repository_introspect_cb (const char *option_name,
    2080                 :             :                              const char *value,
    2081                 :             :                              gpointer data,
    2082                 :             :                              GError **error)
    2083                 :             : {
    2084                 :           0 :   GError *tmp_error = NULL;
    2085                 :             :   char **args;
    2086                 :             : 
    2087                 :           0 :   args = g_strsplit (value, ",", 2);
    2088                 :             : 
    2089                 :           0 :   if (!gi_repository_dump (args[0], args[1], &tmp_error))
    2090                 :             :     {
    2091                 :           0 :       g_error ("Failed to extract GType data: %s",
    2092                 :             :                tmp_error->message);
    2093                 :             :       exit (1);
    2094                 :             :     }
    2095                 :           0 :   exit (0);
    2096                 :             : }
    2097                 :             : 
    2098                 :             : static const GOptionEntry introspection_args[] = {
    2099                 :             :   { "introspect-dump", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK,
    2100                 :             :     gi_repository_introspect_cb, "Dump introspection information",
    2101                 :             :     "infile.txt,outfile.xml" },
    2102                 :             :   G_OPTION_ENTRY_NULL
    2103                 :             : };
    2104                 :             : 
    2105                 :             : /**
    2106                 :             :  * gi_repository_get_option_group:
    2107                 :             :  *
    2108                 :             :  * Obtain the option group for girepository.
    2109                 :             :  *
    2110                 :             :  * It’s used by the dumper and for programs that want to provide introspection
    2111                 :             :  * information
    2112                 :             :  *
    2113                 :             :  * Returns: (transfer full): the option group
    2114                 :             :  * Since: 2.80
    2115                 :             :  */
    2116                 :             : GOptionGroup *
    2117                 :           0 : gi_repository_get_option_group (void)
    2118                 :             : {
    2119                 :             :   GOptionGroup *group;
    2120                 :           0 :   group = g_option_group_new ("girepository", "Introspection Options", "Show Introspection Options", NULL, NULL);
    2121                 :             : 
    2122                 :           0 :   g_option_group_add_entries (group, introspection_args);
    2123                 :           0 :   return group;
    2124                 :             : }
    2125                 :             : 
    2126                 :             : GQuark
    2127                 :           3 : gi_repository_error_quark (void)
    2128                 :             : {
    2129                 :             :   static GQuark quark = 0;
    2130                 :           3 :   if (quark == 0)
    2131                 :           2 :     quark = g_quark_from_static_string ("g-irepository-error-quark");
    2132                 :           3 :   return quark;
    2133                 :             : }
    2134                 :             : 
    2135                 :             : /**
    2136                 :             :  * gi_type_tag_to_string:
    2137                 :             :  * @type: the type_tag
    2138                 :             :  *
    2139                 :             :  * Obtain a string representation of @type
    2140                 :             :  *
    2141                 :             :  * Returns: the string
    2142                 :             :  * Since: 2.80
    2143                 :             :  */
    2144                 :             : const char *
    2145                 :       72496 : gi_type_tag_to_string (GITypeTag type)
    2146                 :             : {
    2147                 :       72496 :   switch (type)
    2148                 :             :     {
    2149                 :         184 :     case GI_TYPE_TAG_VOID:
    2150                 :         184 :       return "void";
    2151                 :           2 :     case GI_TYPE_TAG_BOOLEAN:
    2152                 :           2 :       return "gboolean";
    2153                 :           5 :     case GI_TYPE_TAG_INT8:
    2154                 :           5 :       return "gint8";
    2155                 :         173 :     case GI_TYPE_TAG_UINT8:
    2156                 :         173 :       return "guint8";
    2157                 :           0 :     case GI_TYPE_TAG_INT16:
    2158                 :           0 :       return "gint16";
    2159                 :           2 :     case GI_TYPE_TAG_UINT16:
    2160                 :           2 :       return "guint16";
    2161                 :          24 :     case GI_TYPE_TAG_INT32:
    2162                 :          24 :       return "gint32";
    2163                 :          11 :     case GI_TYPE_TAG_UINT32:
    2164                 :          11 :       return "guint32";
    2165                 :           0 :     case GI_TYPE_TAG_INT64:
    2166                 :           0 :       return "gint64";
    2167                 :           2 :     case GI_TYPE_TAG_UINT64:
    2168                 :           2 :       return "guint64";
    2169                 :           0 :     case GI_TYPE_TAG_FLOAT:
    2170                 :           0 :       return "gfloat";
    2171                 :           2 :     case GI_TYPE_TAG_DOUBLE:
    2172                 :           2 :       return "gdouble";
    2173                 :           3 :     case GI_TYPE_TAG_UNICHAR:
    2174                 :           3 :       return "gunichar";
    2175                 :          12 :     case GI_TYPE_TAG_GTYPE:
    2176                 :          12 :       return "GType";
    2177                 :         275 :     case GI_TYPE_TAG_UTF8:
    2178                 :         275 :       return "utf8";
    2179                 :          44 :     case GI_TYPE_TAG_FILENAME:
    2180                 :          44 :       return "filename";
    2181                 :        4654 :     case GI_TYPE_TAG_ARRAY:
    2182                 :        4654 :       return "array";
    2183                 :       65398 :     case GI_TYPE_TAG_INTERFACE:
    2184                 :       65398 :       return "interface";
    2185                 :        1049 :     case GI_TYPE_TAG_GLIST:
    2186                 :        1049 :       return "glist";
    2187                 :          60 :     case GI_TYPE_TAG_GSLIST:
    2188                 :          60 :       return "gslist";
    2189                 :         315 :     case GI_TYPE_TAG_GHASH:
    2190                 :         315 :       return "ghash";
    2191                 :         281 :     case GI_TYPE_TAG_ERROR:
    2192                 :         281 :       return "error";
    2193                 :           0 :     default:
    2194                 :           0 :       return "unknown";
    2195                 :             :     }
    2196                 :             : }
    2197                 :             : 
    2198                 :             : /**
    2199                 :             :  * gi_info_type_to_string:
    2200                 :             :  * @type: the info type
    2201                 :             :  *
    2202                 :             :  * Obtain a string representation of @type
    2203                 :             :  *
    2204                 :             :  * Returns: the string
    2205                 :             :  * Since: 2.80
    2206                 :             :  */
    2207                 :             : const char *
    2208                 :           0 : gi_info_type_to_string (GIInfoType type)
    2209                 :             : {
    2210                 :           0 :   switch (type)
    2211                 :             :     {
    2212                 :           0 :     case GI_INFO_TYPE_INVALID:
    2213                 :           0 :       return "invalid";
    2214                 :           0 :     case GI_INFO_TYPE_FUNCTION:
    2215                 :           0 :       return "function";
    2216                 :           0 :     case GI_INFO_TYPE_CALLBACK:
    2217                 :           0 :       return "callback";
    2218                 :           0 :     case GI_INFO_TYPE_STRUCT:
    2219                 :           0 :       return "struct";
    2220                 :           0 :     case GI_INFO_TYPE_ENUM:
    2221                 :           0 :       return "enum";
    2222                 :           0 :     case GI_INFO_TYPE_FLAGS:
    2223                 :           0 :       return "flags";
    2224                 :           0 :     case GI_INFO_TYPE_OBJECT:
    2225                 :           0 :       return "object";
    2226                 :           0 :     case GI_INFO_TYPE_INTERFACE:
    2227                 :           0 :       return "interface";
    2228                 :           0 :     case GI_INFO_TYPE_CONSTANT:
    2229                 :           0 :       return "constant";
    2230                 :           0 :     case GI_INFO_TYPE_UNION:
    2231                 :           0 :       return "union";
    2232                 :           0 :     case GI_INFO_TYPE_VALUE:
    2233                 :           0 :       return "value";
    2234                 :           0 :     case GI_INFO_TYPE_SIGNAL:
    2235                 :           0 :       return "signal";
    2236                 :           0 :     case GI_INFO_TYPE_VFUNC:
    2237                 :           0 :       return "vfunc";
    2238                 :           0 :     case GI_INFO_TYPE_PROPERTY:
    2239                 :           0 :       return "property";
    2240                 :           0 :     case GI_INFO_TYPE_FIELD:
    2241                 :           0 :       return "field";
    2242                 :           0 :     case GI_INFO_TYPE_ARG:
    2243                 :           0 :       return "arg";
    2244                 :           0 :     case GI_INFO_TYPE_TYPE:
    2245                 :           0 :       return "type";
    2246                 :           0 :     case GI_INFO_TYPE_UNRESOLVED:
    2247                 :           0 :       return "unresolved";
    2248                 :           0 :     default:
    2249                 :           0 :       return "unknown";
    2250                 :             :   }
    2251                 :             : }
    2252                 :             : 
    2253                 :             : GIInfoType
    2254                 :        2023 : gi_typelib_blob_type_to_info_type (GITypelibBlobType blob_type)
    2255                 :             : {
    2256                 :        2023 :   switch (blob_type)
    2257                 :             :     {
    2258                 :           2 :     case BLOB_TYPE_BOXED:
    2259                 :             :       /* `BLOB_TYPE_BOXED` now always refers to a `StructBlob`, and
    2260                 :             :        * `GIRegisteredTypeInfo` (the parent type of `GIStructInfo`) has a method
    2261                 :             :        * for distinguishing whether the struct is a boxed type. So presenting
    2262                 :             :        * `BLOB_TYPE_BOXED` as its own `GIBaseInfo` subclass is not helpful.
    2263                 :             :        * See commit e28078c70cbf4a57c7dbd39626f43f9bd2674145 and
    2264                 :             :        * https://gitlab.gnome.org/GNOME/glib/-/issues/3245. */
    2265                 :           2 :       return GI_INFO_TYPE_STRUCT;
    2266                 :        2021 :     default:
    2267                 :        2021 :       return (GIInfoType) blob_type;
    2268                 :             :     }
    2269                 :             : }
        

Generated by: LCOV version 2.0-1