mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-11-04 01:58:54 +01:00 
			
		
		
		
	This helps scan-build not emit a ‘potential null pointer dereference’ error for calls to `require_internal()`: ``` Access to field 'message' results in a dereference of a null pointer (loaded from variable 'local_error') ``` See https://gitlab.gnome.org/GNOME/glib/-/jobs/5482526 for details of the error. I don’t think there is an actual null pointer dereference here. Signed-off-by: Philip Withnall <pwithnall@gnome.org>
		
			
				
	
	
		
			2378 lines
		
	
	
		
			76 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2378 lines
		
	
	
		
			76 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
 | 
						||
 * GObject introspection: Repository implementation
 | 
						||
 *
 | 
						||
 * Copyright (C) 2005 Matthias Clasen
 | 
						||
 * Copyright (C) 2008 Colin Walters <walters@verbum.org>
 | 
						||
 * Copyright (C) 2008 Red Hat, Inc.
 | 
						||
 *
 | 
						||
 * SPDX-License-Identifier: LGPL-2.1-or-later
 | 
						||
 *
 | 
						||
 * This library is free software; you can redistribute it and/or
 | 
						||
 * modify it under the terms of the GNU Lesser General Public
 | 
						||
 * License as published by the Free Software Foundation; either
 | 
						||
 * version 2 of the License, or (at your option) any later version.
 | 
						||
 *
 | 
						||
 * This library is distributed in the hope that it will be useful,
 | 
						||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						||
 * Lesser General Public License for more details.
 | 
						||
 *
 | 
						||
 * You should have received a copy of the GNU Lesser General Public
 | 
						||
 * License along with this library; if not, write to the
 | 
						||
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 | 
						||
 * Boston, MA 02111-1307, USA.
 | 
						||
 */
 | 
						||
 | 
						||
#include "config.h"
 | 
						||
 | 
						||
#include <stdio.h>
 | 
						||
#include <string.h>
 | 
						||
#include <stdlib.h>
 | 
						||
 | 
						||
#include <glib.h>
 | 
						||
#include <glib-private.h>
 | 
						||
#include <glib/gprintf.h>
 | 
						||
#include <gmodule.h>
 | 
						||
#include "gibaseinfo-private.h"
 | 
						||
#include "girepository.h"
 | 
						||
#include "gitypelib-internal.h"
 | 
						||
#include "girepository-private.h"
 | 
						||
 | 
						||
/**
 | 
						||
 * GIRepository:
 | 
						||
 *
 | 
						||
 * `GIRepository` is used to manage repositories of namespaces. Namespaces
 | 
						||
 * are represented on disk by type libraries (`.typelib` files).
 | 
						||
 *
 | 
						||
 * The individual pieces of API within a type library are represented by
 | 
						||
 * subclasses of [class@GIRepository.BaseInfo]. These can be found using
 | 
						||
 * methods like [method@GIRepository.Repository.find_by_name] or
 | 
						||
 * [method@GIRepository.Repository.get_info].
 | 
						||
 *
 | 
						||
 * You are responsible for ensuring that the lifetime of the
 | 
						||
 * [class@GIRepository.Repository] exceeds that of the lifetime of any of its
 | 
						||
 * [class@GIRepository.BaseInfo]s. This cannot be guaranteed by using internal
 | 
						||
 * references within libgirepository as that would affect performance.
 | 
						||
 *
 | 
						||
 * ### Discovery of type libraries
 | 
						||
 *
 | 
						||
 * `GIRepository` will typically look for a `girepository-1.0` directory
 | 
						||
 * under the library directory used when compiling gobject-introspection. On a
 | 
						||
 * standard Linux system this will end up being `/usr/lib/girepository-1.0`.
 | 
						||
 *
 | 
						||
 * It is possible to control the search paths programmatically, using
 | 
						||
 * [method@GIRepository.Repository.prepend_search_path]. It is also possible to
 | 
						||
 * modify the search paths by using the `GI_TYPELIB_PATH` environment variable.
 | 
						||
 * The environment variable takes precedence over the default search path
 | 
						||
 * and the [method@GIRepository.Repository.prepend_search_path] calls.
 | 
						||
 *
 | 
						||
 * ### Namespace ordering
 | 
						||
 *
 | 
						||
 * In situations where namespaces may be searched in order, or returned in a
 | 
						||
 * list, the namespaces will be returned in alphabetical order, with all fully
 | 
						||
 * loaded namespaces being returned before any lazily loaded ones (those loaded
 | 
						||
 * with `GI_REPOSITORY_LOAD_FLAG_LAZY`). This allows for deterministic and
 | 
						||
 * reproducible results.
 | 
						||
 *
 | 
						||
 * Similarly, if a symbol (such as a `GType` or error domain) is being searched
 | 
						||
 * for in the set of loaded namespaces, the namespaces will be searched in that
 | 
						||
 * order. In particular, this means that a symbol which exists in two namespaces
 | 
						||
 * will always be returned from the alphabetically-higher namespace. This should
 | 
						||
 * only happen in the case of `Gio` and `GioUnix`/`GioWin32`, which all refer to
 | 
						||
 * the same `.so` file and expose overlapping sets of symbols. Symbols should
 | 
						||
 * always end up being resolved to `GioUnix` or `GioWin32` if they are platform
 | 
						||
 * dependent, rather than `Gio` itself.
 | 
						||
 *
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
 | 
						||
/* The namespace and version corresponding to libgirepository itself, so
 | 
						||
 * that we can refuse to load typelibs corresponding to the older,
 | 
						||
 * incompatible version of this same library in gobject-introspection. */
 | 
						||
#define GIREPOSITORY_TYPELIB_NAME "GIRepository"
 | 
						||
#define GIREPOSITORY_TYPELIB_VERSION "3.0"
 | 
						||
#define GIREPOSITORY_TYPELIB_FILENAME \
 | 
						||
  GIREPOSITORY_TYPELIB_NAME "-" GIREPOSITORY_TYPELIB_VERSION ".typelib"
 | 
						||
 | 
						||
typedef struct {
 | 
						||
  size_t n_interfaces;
 | 
						||
  GIBaseInfo *interfaces[];
 | 
						||
} GTypeInterfaceCache;
 | 
						||
 | 
						||
static void
 | 
						||
gtype_interface_cache_free (gpointer data)
 | 
						||
{
 | 
						||
  GTypeInterfaceCache *cache = data;
 | 
						||
 | 
						||
  for (size_t i = 0; i < cache->n_interfaces; i++)
 | 
						||
    gi_base_info_unref ((GIBaseInfo*) cache->interfaces[i]);
 | 
						||
  g_free (cache);
 | 
						||
}
 | 
						||
 | 
						||
struct _GIRepository
 | 
						||
{
 | 
						||
  GObject parent;
 | 
						||
 | 
						||
  GPtrArray *typelib_search_path;  /* (element-type filename) (owned) */
 | 
						||
  GPtrArray *library_paths;  /* (element-type filename) (owned) */
 | 
						||
 | 
						||
  /* Certain operations require iterating over the typelibs and the iteration
 | 
						||
   * order may affect the results. So keep an ordered list of the typelibs,
 | 
						||
   * alongside the hash table which keep the canonical strong reference to them. */
 | 
						||
  GHashTable *typelibs; /* (string) namespace -> GITypelib */
 | 
						||
  GPtrArray *ordered_typelibs;  /* (element-type unowned GITypelib) (owned) (not nullable) */
 | 
						||
  GHashTable *lazy_typelibs; /* (string) namespace-version -> GITypelib */
 | 
						||
  GPtrArray *ordered_lazy_typelibs;  /* (element-type unowned GITypelib) (owned) (not nullable) */
 | 
						||
 | 
						||
  GHashTable *info_by_gtype; /* GType -> GIBaseInfo */
 | 
						||
  GHashTable *info_by_error_domain; /* GQuark -> GIBaseInfo */
 | 
						||
  GHashTable *interfaces_for_gtype; /* GType -> GTypeInterfaceCache */
 | 
						||
  GHashTable *unknown_gtypes; /* hashset of GType */
 | 
						||
 | 
						||
  char **cached_shared_libraries;  /* (owned) (nullable) (array zero-terminated=1) */
 | 
						||
  size_t cached_n_shared_libraries;  /* length of @cached_shared_libraries, not including NULL terminator */
 | 
						||
};
 | 
						||
 | 
						||
G_DEFINE_TYPE (GIRepository, gi_repository, G_TYPE_OBJECT);
 | 
						||
 | 
						||
static GITypelib *
 | 
						||
require_internal (GIRepository           *repository,
 | 
						||
                  const char             *namespace,
 | 
						||
                  const char             *version,
 | 
						||
                  GIRepositoryLoadFlags   flags,
 | 
						||
                  const char * const     *search_paths,
 | 
						||
                  size_t                  n_search_paths,
 | 
						||
                  GError                **error);
 | 
						||
 | 
						||
#ifdef G_PLATFORM_WIN32
 | 
						||
#include <windows.h>
 | 
						||
 | 
						||
static HMODULE girepository_dll = NULL;
 | 
						||
 | 
						||
BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
 | 
						||
 | 
						||
BOOL WINAPI
 | 
						||
DllMain (HINSTANCE hinstDLL,
 | 
						||
         DWORD     fdwReason,
 | 
						||
         LPVOID    lpvReserved)
 | 
						||
{
 | 
						||
  if (fdwReason == DLL_PROCESS_ATTACH)
 | 
						||
      girepository_dll = hinstDLL;
 | 
						||
 | 
						||
  return TRUE;
 | 
						||
}
 | 
						||
 | 
						||
#endif /* G_PLATFORM_WIN32 */
 | 
						||
 | 
						||
#ifdef __APPLE__
 | 
						||
#include <mach-o/dyld.h>
 | 
						||
 | 
						||
/* This function returns the file path of the loaded libgirepository1.0.dylib.
 | 
						||
 * It iterates over all the loaded images to find the one with the
 | 
						||
 * gi_repository_init symbol and returns its file path.
 | 
						||
  */
 | 
						||
static const char *
 | 
						||
gi_repository_get_library_path_macos (void)
 | 
						||
{
 | 
						||
  /*
 | 
						||
   * Relevant documentation:
 | 
						||
   * https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/0-Introduction/introduction.html
 | 
						||
   * https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dyld.3.html
 | 
						||
   * https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h
 | 
						||
  */
 | 
						||
  const void *ptr = gi_repository_init;
 | 
						||
  const struct mach_header *header;
 | 
						||
  intptr_t offset;
 | 
						||
  uint32_t i, count;
 | 
						||
 | 
						||
  /* Iterate over all the loaded images */
 | 
						||
  count = _dyld_image_count ();
 | 
						||
  for (i = 0; i < count; i++)
 | 
						||
    {
 | 
						||
      header = _dyld_get_image_header (i);
 | 
						||
      offset = _dyld_get_image_vmaddr_slide (i);
 | 
						||
 | 
						||
      /* Locate the first `load` command */
 | 
						||
      struct load_command *cmd = (struct load_command *) ((char *) header + sizeof (struct mach_header));
 | 
						||
      if (header->magic == MH_MAGIC_64)
 | 
						||
        cmd = (struct load_command *) ((char *) header + sizeof (struct mach_header_64));
 | 
						||
 | 
						||
      /* Find the first `segment` command iterating over all the `load` commands.
 | 
						||
       * Then, check if the gi_repository_init symbol is in this image by checking
 | 
						||
       * if the pointer is in the segment's memory address range.
 | 
						||
       */
 | 
						||
      uint32_t j = 0;
 | 
						||
      while (j < header->ncmds)
 | 
						||
        {
 | 
						||
          if (cmd->cmd == LC_SEGMENT)
 | 
						||
            {
 | 
						||
              struct segment_command *seg = (struct segment_command *) cmd;
 | 
						||
              if (((intptr_t) ptr >= ((intptr_t) seg->vmaddr + offset)) && ((intptr_t) ptr < ((intptr_t) seg->vmaddr + offset + (intptr_t) seg->vmsize)))
 | 
						||
                return _dyld_get_image_name (i);
 | 
						||
           }
 | 
						||
          if (cmd->cmd == LC_SEGMENT_64)
 | 
						||
            {
 | 
						||
              struct segment_command_64 *seg = (struct segment_command_64 *) cmd;
 | 
						||
              if (((intptr_t) ptr >= ((intptr_t) seg->vmaddr + offset)) && ((intptr_t) ptr < ((intptr_t) seg->vmaddr + offset + (intptr_t) seg->vmsize)))
 | 
						||
                return _dyld_get_image_name (i);
 | 
						||
            }
 | 
						||
          /* Jump to the next command */
 | 
						||
          j++;
 | 
						||
          cmd = (struct load_command *) ((char *) cmd + cmd->cmdsize);
 | 
						||
        }
 | 
						||
    }
 | 
						||
  return NULL;
 | 
						||
}
 | 
						||
#endif /* __APPLE__ */
 | 
						||
 | 
						||
/*
 | 
						||
 * gi_repository_get_libdir:
 | 
						||
 *
 | 
						||
 * Returns the directory where the typelib files are installed.
 | 
						||
 *
 | 
						||
 * In platforms without relocation support, this functions returns the
 | 
						||
 * `GOBJECT_INTROSPECTION_LIBDIR` directory defined at build time .
 | 
						||
 *
 | 
						||
 * On Windows and macOS this function returns the directory
 | 
						||
 * relative to the installation directory detected at runtime.
 | 
						||
 *
 | 
						||
 * On macOS, if the library is installed in
 | 
						||
 * `/Applications/MyApp.app/Contents/Home/lib/libgirepository-1.0.dylib`, it returns
 | 
						||
 * `/Applications/MyApp.app/Contents/Home/lib/girepository-1.0`
 | 
						||
 *
 | 
						||
 * On Windows, if the application is installed in
 | 
						||
 * `C:/Program Files/MyApp/bin/MyApp.exe`, it returns
 | 
						||
 * `C:/Program Files/MyApp/lib/girepository-1.0`
 | 
						||
*/
 | 
						||
static const gchar *
 | 
						||
gi_repository_get_libdir (void)
 | 
						||
{
 | 
						||
  static gchar *static_libdir;
 | 
						||
 | 
						||
  if (g_once_init_enter_pointer (&static_libdir))
 | 
						||
    {
 | 
						||
      gchar *libdir;
 | 
						||
#if defined(G_PLATFORM_WIN32)
 | 
						||
      const char *toplevel = g_win32_get_package_installation_directory_of_module (girepository_dll);
 | 
						||
      libdir = g_build_filename (toplevel, GOBJECT_INTROSPECTION_RELATIVE_LIBDIR, NULL);
 | 
						||
      g_ignore_leak (libdir);
 | 
						||
#elif defined(__APPLE__)
 | 
						||
      const char *libpath = gi_repository_get_library_path_macos ();
 | 
						||
      if (libpath != NULL)
 | 
						||
        {
 | 
						||
          libdir = g_path_get_dirname (libpath);
 | 
						||
          g_ignore_leak (libdir);
 | 
						||
        } else {
 | 
						||
          libdir = GOBJECT_INTROSPECTION_LIBDIR;
 | 
						||
        }
 | 
						||
#else /* !G_PLATFORM_WIN32 && !__APPLE__ */
 | 
						||
        libdir = GOBJECT_INTROSPECTION_LIBDIR;
 | 
						||
#endif
 | 
						||
      g_once_init_leave_pointer (&static_libdir, libdir);
 | 
						||
    }
 | 
						||
  return static_libdir;
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
gi_repository_init (GIRepository *repository)
 | 
						||
{
 | 
						||
  /* typelib search path */
 | 
						||
    {
 | 
						||
      const char *libdir;
 | 
						||
      char *typelib_dir;
 | 
						||
      const char *type_lib_path_env;
 | 
						||
 | 
						||
      /* This variable is intended to take precedence over both:
 | 
						||
       *   - the default search path;
 | 
						||
       *   - all gi_repository_prepend_search_path() calls.
 | 
						||
       */
 | 
						||
      type_lib_path_env = g_getenv ("GI_TYPELIB_PATH");
 | 
						||
 | 
						||
      if (type_lib_path_env)
 | 
						||
        {
 | 
						||
          char **custom_dirs;
 | 
						||
 | 
						||
          custom_dirs = g_strsplit (type_lib_path_env, G_SEARCHPATH_SEPARATOR_S, 0);
 | 
						||
          repository->typelib_search_path =
 | 
						||
            g_ptr_array_new_take_null_terminated ((gpointer) g_steal_pointer (&custom_dirs), g_free);
 | 
						||
        }
 | 
						||
      else
 | 
						||
        {
 | 
						||
          repository->typelib_search_path = g_ptr_array_new_null_terminated (1, g_free, TRUE);
 | 
						||
        }
 | 
						||
 | 
						||
      libdir = gi_repository_get_libdir ();
 | 
						||
 | 
						||
      typelib_dir = g_build_filename (libdir, "girepository-1.0", NULL);
 | 
						||
 | 
						||
      g_ptr_array_add (repository->typelib_search_path, g_steal_pointer (&typelib_dir));
 | 
						||
    }
 | 
						||
 | 
						||
  repository->library_paths = g_ptr_array_new_null_terminated (1, g_free, TRUE);
 | 
						||
 | 
						||
  repository->typelibs
 | 
						||
    = g_hash_table_new_full (g_str_hash, g_str_equal,
 | 
						||
                             (GDestroyNotify) g_free,
 | 
						||
                             (GDestroyNotify) gi_typelib_unref);
 | 
						||
  repository->ordered_typelibs = g_ptr_array_new_with_free_func (NULL);
 | 
						||
  repository->lazy_typelibs
 | 
						||
    = g_hash_table_new_full (g_str_hash, g_str_equal,
 | 
						||
                             (GDestroyNotify) g_free,
 | 
						||
                             (GDestroyNotify) gi_typelib_unref);
 | 
						||
  repository->ordered_lazy_typelibs = g_ptr_array_new_with_free_func (NULL);
 | 
						||
 | 
						||
  repository->info_by_gtype
 | 
						||
    = g_hash_table_new_full (g_direct_hash, g_direct_equal,
 | 
						||
                             (GDestroyNotify) NULL,
 | 
						||
                             (GDestroyNotify) gi_base_info_unref);
 | 
						||
  repository->info_by_error_domain
 | 
						||
    = g_hash_table_new_full (g_direct_hash, g_direct_equal,
 | 
						||
                             (GDestroyNotify) NULL,
 | 
						||
                             (GDestroyNotify) gi_base_info_unref);
 | 
						||
  repository->interfaces_for_gtype
 | 
						||
    = g_hash_table_new_full (g_direct_hash, g_direct_equal,
 | 
						||
                             (GDestroyNotify) NULL,
 | 
						||
                             (GDestroyNotify) gtype_interface_cache_free);
 | 
						||
  repository->unknown_gtypes = g_hash_table_new (NULL, NULL);
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
gi_repository_finalize (GObject *object)
 | 
						||
{
 | 
						||
  GIRepository *repository = GI_REPOSITORY (object);
 | 
						||
 | 
						||
  g_hash_table_destroy (repository->typelibs);
 | 
						||
  g_ptr_array_unref (repository->ordered_typelibs);
 | 
						||
  g_hash_table_destroy (repository->lazy_typelibs);
 | 
						||
  g_ptr_array_unref (repository->ordered_lazy_typelibs);
 | 
						||
 | 
						||
  g_hash_table_destroy (repository->info_by_gtype);
 | 
						||
  g_hash_table_destroy (repository->info_by_error_domain);
 | 
						||
  g_hash_table_destroy (repository->interfaces_for_gtype);
 | 
						||
  g_hash_table_destroy (repository->unknown_gtypes);
 | 
						||
 | 
						||
  g_clear_pointer (&repository->cached_shared_libraries, g_strfreev);
 | 
						||
 | 
						||
  g_clear_pointer (&repository->library_paths, g_ptr_array_unref);
 | 
						||
  g_clear_pointer (&repository->typelib_search_path, g_ptr_array_unref);
 | 
						||
 | 
						||
  (* G_OBJECT_CLASS (gi_repository_parent_class)->finalize) (G_OBJECT (repository));
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
gi_repository_class_init (GIRepositoryClass *class)
 | 
						||
{
 | 
						||
  GObjectClass *gobject_class;
 | 
						||
 | 
						||
  gobject_class = G_OBJECT_CLASS (class);
 | 
						||
 | 
						||
  gobject_class->finalize = gi_repository_finalize;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_prepend_search_path:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @directory: (type filename): directory name to prepend to the typelib
 | 
						||
 *   search path
 | 
						||
 *
 | 
						||
 * Prepends @directory to the typelib search path.
 | 
						||
 *
 | 
						||
 * See also: gi_repository_get_search_path().
 | 
						||
 *
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
void
 | 
						||
gi_repository_prepend_search_path (GIRepository *repository,
 | 
						||
                                   const char   *directory)
 | 
						||
{
 | 
						||
  g_return_if_fail (GI_IS_REPOSITORY (repository));
 | 
						||
 | 
						||
  g_ptr_array_insert (repository->typelib_search_path, 0, g_strdup (directory));
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_search_path:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @n_paths_out: (optional) (out): The number of search paths returned.
 | 
						||
 *
 | 
						||
 * Returns the current search path [class@GIRepository.Repository] will use when
 | 
						||
 * loading typelib files.
 | 
						||
 *
 | 
						||
 * The list is internal to [class@GIRepository.Repository] and should not be
 | 
						||
 * freed, nor should its string elements.
 | 
						||
 *
 | 
						||
 * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
 | 
						||
 * counted in @n_paths_out.
 | 
						||
 *
 | 
						||
 * Returns: (element-type filename) (transfer none) (array length=n_paths_out): list of search paths, most
 | 
						||
 *   important first
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
const char * const *
 | 
						||
gi_repository_get_search_path (GIRepository *repository,
 | 
						||
                               size_t       *n_paths_out)
 | 
						||
{
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
 | 
						||
  if G_UNLIKELY (!repository->typelib_search_path ||
 | 
						||
                 !repository->typelib_search_path->pdata)
 | 
						||
    {
 | 
						||
      static const char * const empty_search_path[] = {NULL};
 | 
						||
 | 
						||
      if (n_paths_out)
 | 
						||
        *n_paths_out = 0;
 | 
						||
 | 
						||
      return empty_search_path;
 | 
						||
    }
 | 
						||
 | 
						||
  if (n_paths_out)
 | 
						||
    *n_paths_out = repository->typelib_search_path->len;
 | 
						||
 | 
						||
  return (const char * const *) repository->typelib_search_path->pdata;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_prepend_library_path:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @directory: (type filename): a single directory to scan for shared libraries
 | 
						||
 *
 | 
						||
 * Prepends @directory to the search path that is used to
 | 
						||
 * search shared libraries referenced by imported namespaces.
 | 
						||
 *
 | 
						||
 * Multiple calls to this function all contribute to the final
 | 
						||
 * list of paths.
 | 
						||
 *
 | 
						||
 * The list of paths is unique to @repository. When a typelib is loaded by the
 | 
						||
 * repository, the list of paths from the @repository at that instant is used
 | 
						||
 * by the typelib for loading its modules.
 | 
						||
 *
 | 
						||
 * If the library is not found in the directories configured
 | 
						||
 * in this way, loading will fall back to the system library
 | 
						||
 * path (i.e. `LD_LIBRARY_PATH` and `DT_RPATH` in ELF systems).
 | 
						||
 * See the documentation of your dynamic linker for full details.
 | 
						||
 *
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
void
 | 
						||
gi_repository_prepend_library_path (GIRepository *repository,
 | 
						||
                                    const char   *directory)
 | 
						||
{
 | 
						||
  g_return_if_fail (GI_IS_REPOSITORY (repository));
 | 
						||
 | 
						||
  g_ptr_array_insert (repository->library_paths, 0, g_strdup (directory));
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_library_path:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @n_paths_out: (optional) (out): The number of library paths returned.
 | 
						||
 *
 | 
						||
 * Returns the current search path [class@GIRepository.Repository] will use when
 | 
						||
 * loading shared libraries referenced by imported namespaces.
 | 
						||
 *
 | 
						||
 * The list is internal to [class@GIRepository.Repository] and should not be
 | 
						||
 * freed, nor should its string elements.
 | 
						||
 *
 | 
						||
 * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
 | 
						||
 * counted in @n_paths_out.
 | 
						||
 *
 | 
						||
 * Returns: (element-type filename) (transfer none) (array length=n_paths_out): list of search paths, most
 | 
						||
 *   important first
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
const char * const *
 | 
						||
gi_repository_get_library_path (GIRepository *repository,
 | 
						||
                                size_t       *n_paths_out)
 | 
						||
{
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
 | 
						||
  if G_UNLIKELY (!repository->library_paths || !repository->library_paths->pdata)
 | 
						||
    {
 | 
						||
      static const char * const empty_search_path[] = {NULL};
 | 
						||
 | 
						||
      if (n_paths_out)
 | 
						||
        *n_paths_out = 0;
 | 
						||
 | 
						||
      return empty_search_path;
 | 
						||
    }
 | 
						||
 | 
						||
  if (n_paths_out)
 | 
						||
    *n_paths_out = repository->library_paths->len;
 | 
						||
 | 
						||
  return (const char * const *) repository->library_paths->pdata;
 | 
						||
}
 | 
						||
 | 
						||
static char *
 | 
						||
build_typelib_key (const char *name, const char *source)
 | 
						||
{
 | 
						||
  GString *str = g_string_new (name);
 | 
						||
  g_string_append_c (str, '\0');
 | 
						||
  g_string_append (str, source);
 | 
						||
  return g_string_free (str, FALSE);
 | 
						||
}
 | 
						||
 | 
						||
/* Note: Returns %NULL (not an empty %NULL-terminated array) if there are no
 | 
						||
 * dependencies. */
 | 
						||
static char **
 | 
						||
get_typelib_dependencies (GITypelib *typelib)
 | 
						||
{
 | 
						||
  Header *header;
 | 
						||
  const char *dependencies_glob;
 | 
						||
 | 
						||
  header = (Header *)typelib->data;
 | 
						||
 | 
						||
  if (header->dependencies == 0)
 | 
						||
    return NULL;
 | 
						||
 | 
						||
  dependencies_glob = gi_typelib_get_string (typelib, header->dependencies);
 | 
						||
  return g_strsplit (dependencies_glob, "|", 0);
 | 
						||
}
 | 
						||
 | 
						||
static GITypelib *
 | 
						||
check_version_conflict (GITypelib *typelib,
 | 
						||
                        const char  *namespace,
 | 
						||
                        const char  *expected_version,
 | 
						||
                        char       **version_conflict)
 | 
						||
{
 | 
						||
  Header *header;
 | 
						||
  const char *loaded_version;
 | 
						||
 | 
						||
  if (expected_version == NULL)
 | 
						||
    {
 | 
						||
      if (version_conflict)
 | 
						||
        *version_conflict = NULL;
 | 
						||
      return typelib;
 | 
						||
    }
 | 
						||
 | 
						||
  header = (Header*)typelib->data;
 | 
						||
  loaded_version = gi_typelib_get_string (typelib, header->nsversion);
 | 
						||
  g_assert (loaded_version != NULL);
 | 
						||
 | 
						||
  if (strcmp (expected_version, loaded_version) != 0)
 | 
						||
    {
 | 
						||
      if (version_conflict)
 | 
						||
        *version_conflict = (char*)loaded_version;
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
  if (version_conflict)
 | 
						||
    *version_conflict = NULL;
 | 
						||
  return typelib;
 | 
						||
}
 | 
						||
 | 
						||
static GITypelib *
 | 
						||
get_registered_status (GIRepository *repository,
 | 
						||
                       const char   *namespace,
 | 
						||
                       const char   *version,
 | 
						||
                       gboolean      allow_lazy,
 | 
						||
                       gboolean     *lazy_status,
 | 
						||
                       char        **version_conflict)
 | 
						||
{
 | 
						||
  GITypelib *typelib;
 | 
						||
 | 
						||
  if (lazy_status)
 | 
						||
    *lazy_status = FALSE;
 | 
						||
  typelib = g_hash_table_lookup (repository->typelibs, namespace);
 | 
						||
  if (typelib)
 | 
						||
    return check_version_conflict (typelib, namespace, version, version_conflict);
 | 
						||
  typelib = g_hash_table_lookup (repository->lazy_typelibs, namespace);
 | 
						||
  if (!typelib)
 | 
						||
    return NULL;
 | 
						||
  if (lazy_status)
 | 
						||
    *lazy_status = TRUE;
 | 
						||
  if (!allow_lazy)
 | 
						||
    return NULL;
 | 
						||
  return check_version_conflict (typelib, namespace, version, version_conflict);
 | 
						||
}
 | 
						||
 | 
						||
static GITypelib *
 | 
						||
get_registered (GIRepository *repository,
 | 
						||
                const char   *namespace,
 | 
						||
                const char   *version)
 | 
						||
{
 | 
						||
  return get_registered_status (repository, namespace, version, TRUE, NULL, NULL);
 | 
						||
}
 | 
						||
 | 
						||
static gboolean
 | 
						||
load_dependencies_recurse (GIRepository *repository,
 | 
						||
                           GITypelib     *typelib,
 | 
						||
                           GError      **error)
 | 
						||
{
 | 
						||
  char **dependencies;
 | 
						||
 | 
						||
  dependencies = get_typelib_dependencies (typelib);
 | 
						||
 | 
						||
  if (dependencies != NULL)
 | 
						||
    {
 | 
						||
      int i;
 | 
						||
 | 
						||
      const char * const *search_path =
 | 
						||
        (const char * const *) repository->typelib_search_path->pdata;
 | 
						||
      gsize search_path_len = repository->typelib_search_path->len;
 | 
						||
 | 
						||
      for (i = 0; dependencies[i]; i++)
 | 
						||
        {
 | 
						||
          char *dependency = dependencies[i];
 | 
						||
          const char *last_dash;
 | 
						||
          char *dependency_namespace;
 | 
						||
          const char *dependency_version;
 | 
						||
 | 
						||
          last_dash = strrchr (dependency, '-');
 | 
						||
          g_assert (last_dash != NULL);  /* get_typelib_dependencies() guarantees this */
 | 
						||
          dependency_namespace = g_strndup (dependency, (size_t) (last_dash - dependency));
 | 
						||
          dependency_version = last_dash+1;
 | 
						||
 | 
						||
          if (!require_internal (repository, dependency_namespace, dependency_version,
 | 
						||
                                 0, search_path, search_path_len,
 | 
						||
                                 error))
 | 
						||
            {
 | 
						||
              g_free (dependency_namespace);
 | 
						||
              g_strfreev (dependencies);
 | 
						||
              return FALSE;
 | 
						||
            }
 | 
						||
          g_free (dependency_namespace);
 | 
						||
        }
 | 
						||
      g_strfreev (dependencies);
 | 
						||
    }
 | 
						||
  return TRUE;
 | 
						||
}
 | 
						||
 | 
						||
/* Sort typelibs by namespace. The main requirement here is just to make iteration
 | 
						||
 * deterministic, otherwise results can change as a lot of the code here would
 | 
						||
 * just iterate over a `GHashTable`.
 | 
						||
 *
 | 
						||
 * A sub-requirement of this is that namespaces are sorted such that if a GType
 | 
						||
 * or symbol is found in multiple namespaces where one is a prefix of the other,
 | 
						||
 * the longest namespace wins. In practice, this only happens in
 | 
						||
 * Gio/GioUnix/GioWin32, as all three of those namespaces refer to the same
 | 
						||
 * `.so` file and overlapping sets of the same symbols, but we want the platform
 | 
						||
 * specific namespace to be returned in preference to anything else (even though
 | 
						||
 * either namespace is valid).
 | 
						||
 * See https://gitlab.gnome.org/GNOME/glib/-/issues/3303 */
 | 
						||
static int
 | 
						||
sort_typelibs_cb (const void *a,
 | 
						||
                  const void *b)
 | 
						||
{
 | 
						||
  GITypelib *typelib_a = *(GITypelib **) a;
 | 
						||
  GITypelib *typelib_b = *(GITypelib **) b;
 | 
						||
 | 
						||
  return strcmp (gi_typelib_get_namespace (typelib_a),
 | 
						||
                 gi_typelib_get_namespace (typelib_b));
 | 
						||
}
 | 
						||
 | 
						||
static const char *
 | 
						||
register_internal (GIRepository *repository,
 | 
						||
                   const char   *source,
 | 
						||
                   gboolean      lazy,
 | 
						||
                   GITypelib    *typelib,
 | 
						||
                   GError      **error)
 | 
						||
{
 | 
						||
  Header *header;
 | 
						||
  const char *namespace;
 | 
						||
 | 
						||
  g_return_val_if_fail (typelib != NULL, NULL);
 | 
						||
 | 
						||
  header = (Header *)typelib->data;
 | 
						||
 | 
						||
  g_return_val_if_fail (header != NULL, NULL);
 | 
						||
 | 
						||
  namespace = gi_typelib_get_string (typelib, header->namespace);
 | 
						||
 | 
						||
  if (lazy)
 | 
						||
    {
 | 
						||
      g_assert (!g_hash_table_lookup (repository->lazy_typelibs,
 | 
						||
                                      namespace));
 | 
						||
      g_hash_table_insert (repository->lazy_typelibs,
 | 
						||
                           build_typelib_key (namespace, source), gi_typelib_ref (typelib));
 | 
						||
      g_ptr_array_add (repository->ordered_lazy_typelibs, typelib);
 | 
						||
      g_ptr_array_sort (repository->ordered_lazy_typelibs, sort_typelibs_cb);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      gpointer value;
 | 
						||
      char *key;
 | 
						||
 | 
						||
      /* First, try loading all the dependencies */
 | 
						||
      if (!load_dependencies_recurse (repository, typelib, error))
 | 
						||
        return NULL;
 | 
						||
 | 
						||
      /* Check if we are transitioning from lazily loaded state */
 | 
						||
      if (g_hash_table_lookup_extended (repository->lazy_typelibs,
 | 
						||
                                        namespace,
 | 
						||
                                        (gpointer)&key, &value))
 | 
						||
        {
 | 
						||
          g_hash_table_remove (repository->lazy_typelibs, key);
 | 
						||
          g_ptr_array_remove (repository->ordered_lazy_typelibs, typelib);
 | 
						||
        }
 | 
						||
      else
 | 
						||
        {
 | 
						||
          key = build_typelib_key (namespace, source);
 | 
						||
        }
 | 
						||
 | 
						||
      g_hash_table_insert (repository->typelibs,
 | 
						||
                           g_steal_pointer (&key),
 | 
						||
                           gi_typelib_ref (typelib));
 | 
						||
      g_ptr_array_add (repository->ordered_typelibs, typelib);
 | 
						||
      g_ptr_array_sort (repository->ordered_typelibs, sort_typelibs_cb);
 | 
						||
    }
 | 
						||
 | 
						||
  /* These types might be resolved now, clear the cache */
 | 
						||
  g_hash_table_remove_all (repository->unknown_gtypes);
 | 
						||
 | 
						||
  /* Success */
 | 
						||
  g_assert (namespace != NULL);
 | 
						||
 | 
						||
  return namespace;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_immediate_dependencies:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: Namespace of interest
 | 
						||
 * @n_dependencies_out: (optional) (out): Return location for the number of
 | 
						||
 *   dependencies
 | 
						||
 *
 | 
						||
 * Return an array of the immediate versioned dependencies for @namespace_.
 | 
						||
 * Returned strings are of the form `namespace-version`.
 | 
						||
 *
 | 
						||
 * Note: @namespace_ must have already been loaded using a function
 | 
						||
 * such as [method@GIRepository.Repository.require] before calling this
 | 
						||
 * function.
 | 
						||
 *
 | 
						||
 * To get the transitive closure of dependencies for @namespace_, use
 | 
						||
 * [method@GIRepository.Repository.get_dependencies].
 | 
						||
 *
 | 
						||
 * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
 | 
						||
 * counted in @n_dependencies_out.
 | 
						||
 *
 | 
						||
 * Returns: (transfer full) (array length=n_dependencies_out): String array of
 | 
						||
 *   immediate versioned dependencies
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
char **
 | 
						||
gi_repository_get_immediate_dependencies (GIRepository *repository,
 | 
						||
                                          const char   *namespace,
 | 
						||
                                          size_t       *n_dependencies_out)
 | 
						||
{
 | 
						||
  GITypelib *typelib;
 | 
						||
  char **deps;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
  g_return_val_if_fail (namespace != NULL, NULL);
 | 
						||
 | 
						||
  typelib = get_registered (repository, namespace, NULL);
 | 
						||
  g_return_val_if_fail (typelib != NULL, NULL);
 | 
						||
 | 
						||
  /* Ensure we always return a non-%NULL vector. */
 | 
						||
  deps = get_typelib_dependencies (typelib);
 | 
						||
  if (deps == NULL)
 | 
						||
      deps = g_strsplit ("", "|", 0);
 | 
						||
 | 
						||
  if (n_dependencies_out != NULL)
 | 
						||
    *n_dependencies_out = g_strv_length (deps);
 | 
						||
 | 
						||
  return deps;
 | 
						||
}
 | 
						||
 | 
						||
/* Load the transitive closure of dependency namespace-version strings for the
 | 
						||
 * given @typelib. @repository must be non-%NULL. @transitive_dependencies must
 | 
						||
 * be a pre-existing GHashTable<owned utf8, owned utf8> set for storing the
 | 
						||
 * dependencies. */
 | 
						||
static void
 | 
						||
get_typelib_dependencies_transitive (GIRepository *repository,
 | 
						||
                                     GITypelib    *typelib,
 | 
						||
                                     GHashTable   *transitive_dependencies)
 | 
						||
{
 | 
						||
  char **immediate_dependencies;
 | 
						||
 | 
						||
  immediate_dependencies = get_typelib_dependencies (typelib);
 | 
						||
 | 
						||
  for (size_t i = 0; immediate_dependencies != NULL && immediate_dependencies[i]; i++)
 | 
						||
    {
 | 
						||
      char *dependency;
 | 
						||
      const char *last_dash;
 | 
						||
      char *dependency_namespace;
 | 
						||
 | 
						||
      dependency = immediate_dependencies[i];
 | 
						||
 | 
						||
      /* Steal from the strv. */
 | 
						||
      g_hash_table_add (transitive_dependencies, dependency);
 | 
						||
      immediate_dependencies[i] = NULL;
 | 
						||
 | 
						||
      /* Recurse for this namespace. */
 | 
						||
      last_dash = strrchr (dependency, '-');
 | 
						||
      g_assert (last_dash != NULL);  /* get_typelib_dependencies() guarantees this */
 | 
						||
      dependency_namespace = g_strndup (dependency, (size_t) (last_dash - dependency));
 | 
						||
 | 
						||
      typelib = get_registered (repository, dependency_namespace, NULL);
 | 
						||
      g_return_if_fail (typelib != NULL);
 | 
						||
      get_typelib_dependencies_transitive (repository, typelib,
 | 
						||
                                           transitive_dependencies);
 | 
						||
 | 
						||
      g_free (dependency_namespace);
 | 
						||
    }
 | 
						||
 | 
						||
  g_free (immediate_dependencies);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_dependencies:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: Namespace of interest
 | 
						||
 * @n_dependencies_out: (optional) (out): Return location for the number of
 | 
						||
 *   dependencies
 | 
						||
 *
 | 
						||
 * Retrieves all (transitive) versioned dependencies for
 | 
						||
 * @namespace_.
 | 
						||
 *
 | 
						||
 * The returned strings are of the form `namespace-version`.
 | 
						||
 *
 | 
						||
 * Note: @namespace_ must have already been loaded using a function
 | 
						||
 * such as [method@GIRepository.Repository.require] before calling this
 | 
						||
 * function.
 | 
						||
 *
 | 
						||
 * To get only the immediate dependencies for @namespace_, use
 | 
						||
 * [method@GIRepository.Repository.get_immediate_dependencies].
 | 
						||
 *
 | 
						||
 * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
 | 
						||
 * counted in @n_dependencies_out.
 | 
						||
 *
 | 
						||
 * Returns: (transfer full) (array length=n_dependencies_out): String array of
 | 
						||
 *   all versioned dependencies
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
char **
 | 
						||
gi_repository_get_dependencies (GIRepository *repository,
 | 
						||
                                const char   *namespace,
 | 
						||
                                size_t       *n_dependencies_out)
 | 
						||
{
 | 
						||
  GITypelib *typelib;
 | 
						||
  GHashTable *transitive_dependencies;  /* set of owned utf8 */
 | 
						||
  GHashTableIter iter;
 | 
						||
  char *dependency;
 | 
						||
  GPtrArray *out;  /* owned utf8 elements */
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
  g_return_val_if_fail (namespace != NULL, NULL);
 | 
						||
 | 
						||
  typelib = get_registered (repository, namespace, NULL);
 | 
						||
  g_return_val_if_fail (typelib != NULL, NULL);
 | 
						||
 | 
						||
  /* Load the dependencies. */
 | 
						||
  transitive_dependencies = g_hash_table_new_full (g_str_hash, g_str_equal,
 | 
						||
                                                   g_free, NULL);
 | 
						||
  get_typelib_dependencies_transitive (repository, typelib,
 | 
						||
                                       transitive_dependencies);
 | 
						||
 | 
						||
  /* Convert to a string array. */
 | 
						||
  out = g_ptr_array_new_null_terminated (g_hash_table_size (transitive_dependencies),
 | 
						||
                                         g_free, TRUE);
 | 
						||
  g_hash_table_iter_init (&iter, transitive_dependencies);
 | 
						||
 | 
						||
  while (g_hash_table_iter_next (&iter, (gpointer) &dependency, NULL))
 | 
						||
    {
 | 
						||
      g_ptr_array_add (out, dependency);
 | 
						||
      g_hash_table_iter_steal (&iter);
 | 
						||
    }
 | 
						||
 | 
						||
  g_hash_table_unref (transitive_dependencies);
 | 
						||
 | 
						||
  if (n_dependencies_out != NULL)
 | 
						||
    *n_dependencies_out = out->len;
 | 
						||
 | 
						||
  return (char **) g_ptr_array_free (out, FALSE);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_load_typelib:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @typelib: (transfer none): the typelib to load
 | 
						||
 * @flags: flags affecting the loading operation
 | 
						||
 * @error: return location for a [type@GLib.Error], or `NULL`
 | 
						||
 *
 | 
						||
 * Load the given @typelib into the repository.
 | 
						||
 *
 | 
						||
 * Returns: namespace of the loaded typelib
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
const char *
 | 
						||
gi_repository_load_typelib (GIRepository           *repository,
 | 
						||
                            GITypelib              *typelib,
 | 
						||
                            GIRepositoryLoadFlags   flags,
 | 
						||
                            GError                **error)
 | 
						||
{
 | 
						||
  Header *header;
 | 
						||
  const char *namespace;
 | 
						||
  const char *nsversion;
 | 
						||
  gboolean allow_lazy = flags & GI_REPOSITORY_LOAD_FLAG_LAZY;
 | 
						||
  gboolean is_lazy;
 | 
						||
  char *version_conflict;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
 | 
						||
  header = (Header *) typelib->data;
 | 
						||
  namespace = gi_typelib_get_string (typelib, header->namespace);
 | 
						||
  nsversion = gi_typelib_get_string (typelib, header->nsversion);
 | 
						||
 | 
						||
  if (get_registered_status (repository, namespace, nsversion, allow_lazy,
 | 
						||
                             &is_lazy, &version_conflict))
 | 
						||
    {
 | 
						||
      if (version_conflict != NULL)
 | 
						||
        {
 | 
						||
          g_set_error (error, GI_REPOSITORY_ERROR,
 | 
						||
                       GI_REPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
 | 
						||
                       "Attempting to load namespace '%s', version '%s', but '%s' is already loaded",
 | 
						||
                       namespace, nsversion, version_conflict);
 | 
						||
          return NULL;
 | 
						||
        }
 | 
						||
      return namespace;
 | 
						||
    }
 | 
						||
  return register_internal (repository, "<builtin>",
 | 
						||
                            allow_lazy, typelib, error);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_is_registered:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: Namespace of interest
 | 
						||
 * @version: (nullable): Required version, may be `NULL` for latest
 | 
						||
 *
 | 
						||
 * Check whether a particular namespace (and optionally, a specific
 | 
						||
 * version thereof) is currently loaded.
 | 
						||
 *
 | 
						||
 * This function is likely to only be useful in unusual circumstances; in order
 | 
						||
 * to act upon metadata in the namespace, you should call
 | 
						||
 * [method@GIRepository.Repository.require] instead which will ensure the
 | 
						||
 * namespace is loaded, and return as quickly as this function will if it has
 | 
						||
 * already been loaded.
 | 
						||
 *
 | 
						||
 * Returns: `TRUE` if namespace-version is loaded, `FALSE` otherwise
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
gboolean
 | 
						||
gi_repository_is_registered (GIRepository *repository,
 | 
						||
                             const char   *namespace,
 | 
						||
                             const char   *version)
 | 
						||
{
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), FALSE);
 | 
						||
 | 
						||
  return get_registered (repository, namespace, version) != NULL;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_new:
 | 
						||
 *
 | 
						||
 * Create a new [class@GIRepository.Repository].
 | 
						||
 *
 | 
						||
 * Returns: (transfer full): a new [class@GIRepository.Repository]
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
GIRepository *
 | 
						||
gi_repository_new (void)
 | 
						||
{
 | 
						||
  return g_object_new (GI_TYPE_REPOSITORY, NULL);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_n_infos:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: Namespace to inspect
 | 
						||
 *
 | 
						||
 * This function returns the number of metadata entries in
 | 
						||
 * given namespace @namespace_.
 | 
						||
 *
 | 
						||
 * The namespace must have already been loaded before calling this function.
 | 
						||
 *
 | 
						||
 * Returns: number of metadata entries
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
unsigned int
 | 
						||
gi_repository_get_n_infos (GIRepository *repository,
 | 
						||
                           const char   *namespace)
 | 
						||
{
 | 
						||
  GITypelib *typelib;
 | 
						||
  unsigned int n_interfaces = 0;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), 0);
 | 
						||
  g_return_val_if_fail (namespace != NULL, 0);
 | 
						||
 | 
						||
  typelib = get_registered (repository, namespace, NULL);
 | 
						||
 | 
						||
  g_return_val_if_fail (typelib != NULL, 0);
 | 
						||
 | 
						||
  n_interfaces = ((Header *)typelib->data)->n_local_entries;
 | 
						||
 | 
						||
  return n_interfaces;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_info:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: Namespace to inspect
 | 
						||
 * @idx: 0-based offset into namespace metadata for entry
 | 
						||
 *
 | 
						||
 * This function returns a particular metadata entry in the
 | 
						||
 * given namespace @namespace_.
 | 
						||
 *
 | 
						||
 * The namespace must have already been loaded before calling this function.
 | 
						||
 * See [method@GIRepository.Repository.get_n_infos] to find the maximum number
 | 
						||
 * of entries. It is an error to pass an invalid @idx to this function.
 | 
						||
 *
 | 
						||
 * Returns: (transfer full) (not nullable): [class@GIRepository.BaseInfo]
 | 
						||
 *   containing metadata
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
GIBaseInfo *
 | 
						||
gi_repository_get_info (GIRepository *repository,
 | 
						||
                        const char   *namespace,
 | 
						||
                        unsigned int  idx)
 | 
						||
{
 | 
						||
  GITypelib *typelib;
 | 
						||
  DirEntry *entry;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
  g_return_val_if_fail (namespace != NULL, NULL);
 | 
						||
  g_return_val_if_fail (idx < G_MAXUINT16, NULL);
 | 
						||
 | 
						||
  typelib = get_registered (repository, namespace, NULL);
 | 
						||
 | 
						||
  g_return_val_if_fail (typelib != NULL, NULL);
 | 
						||
 | 
						||
  entry = gi_typelib_get_dir_entry (typelib, idx + 1);
 | 
						||
  g_return_val_if_fail (entry != NULL, NULL);
 | 
						||
 | 
						||
  return gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
 | 
						||
                           repository,
 | 
						||
                           NULL, typelib, entry->offset);
 | 
						||
}
 | 
						||
 | 
						||
static DirEntry *
 | 
						||
find_by_gtype (GPtrArray   *ordered_table,
 | 
						||
               const char  *gtype_name,
 | 
						||
               gboolean     check_prefix,
 | 
						||
               GITypelib  **out_result_typelib)
 | 
						||
{
 | 
						||
  /* Search in reverse order as the longest namespaces will be listed last, and
 | 
						||
   * those are the ones we want to search first. */
 | 
						||
  for (guint i = ordered_table->len; i > 0; i--)
 | 
						||
    {
 | 
						||
      GITypelib *typelib = g_ptr_array_index (ordered_table, i - 1);
 | 
						||
      DirEntry *ret;
 | 
						||
 | 
						||
      if (check_prefix)
 | 
						||
        {
 | 
						||
          if (!gi_typelib_matches_gtype_name_prefix (typelib, gtype_name))
 | 
						||
            continue;
 | 
						||
        }
 | 
						||
 | 
						||
      ret = gi_typelib_get_dir_entry_by_gtype_name (typelib, gtype_name);
 | 
						||
      if (ret)
 | 
						||
        {
 | 
						||
          *out_result_typelib = typelib;
 | 
						||
          return ret;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
  return NULL;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_find_by_gtype:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @gtype: [type@GObject.Type] to search for
 | 
						||
 *
 | 
						||
 * Searches all loaded namespaces for a particular [type@GObject.Type].
 | 
						||
 *
 | 
						||
 * Note that in order to locate the metadata, the namespace corresponding to
 | 
						||
 * the type must first have been loaded.  There is currently no
 | 
						||
 * mechanism for determining the namespace which corresponds to an
 | 
						||
 * arbitrary [type@GObject.Type] — thus, this function will operate most
 | 
						||
 * reliably when you know the [type@GObject.Type] is from a loaded namespace.
 | 
						||
 *
 | 
						||
 * Returns: (transfer full) (nullable): [class@GIRepository.BaseInfo]
 | 
						||
 *   representing metadata about @type, or `NULL` if none found
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
GIBaseInfo *
 | 
						||
gi_repository_find_by_gtype (GIRepository *repository,
 | 
						||
                             GType         gtype)
 | 
						||
{
 | 
						||
  const char *gtype_name;
 | 
						||
  GITypelib *result_typelib = NULL;
 | 
						||
  GIBaseInfo *cached;
 | 
						||
  DirEntry *entry;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
  g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL);
 | 
						||
 | 
						||
  cached = g_hash_table_lookup (repository->info_by_gtype,
 | 
						||
                                (gpointer)gtype);
 | 
						||
 | 
						||
  if (cached != NULL)
 | 
						||
    return gi_base_info_ref (cached);
 | 
						||
 | 
						||
  if (g_hash_table_contains (repository->unknown_gtypes, (gpointer)gtype))
 | 
						||
    return NULL;
 | 
						||
 | 
						||
  gtype_name = g_type_name (gtype);
 | 
						||
 | 
						||
  /* Inside each typelib, we include the "C prefix" which acts as
 | 
						||
   * a namespace mechanism.  For GtkTreeView, the C prefix is Gtk.
 | 
						||
   * Given the assumption that GTypes for a library also use the
 | 
						||
   * C prefix, we know we can skip examining a typelib if our
 | 
						||
   * target type does not have this typelib's C prefix. Use this
 | 
						||
   * assumption as our first attempt at locating the DirEntry.
 | 
						||
   */
 | 
						||
  entry = find_by_gtype (repository->ordered_typelibs, gtype_name, TRUE, &result_typelib);
 | 
						||
  if (entry == NULL)
 | 
						||
    entry = find_by_gtype (repository->ordered_lazy_typelibs, gtype_name, TRUE, &result_typelib);
 | 
						||
 | 
						||
  /* Not every class library necessarily specifies a correct c_prefix,
 | 
						||
   * so take a second pass. This time we will try a global lookup,
 | 
						||
   * ignoring prefixes.
 | 
						||
   * See http://bugzilla.gnome.org/show_bug.cgi?id=564016
 | 
						||
   */
 | 
						||
  if (entry == NULL)
 | 
						||
    entry = find_by_gtype (repository->ordered_typelibs, gtype_name, FALSE, &result_typelib);
 | 
						||
  if (entry == NULL)
 | 
						||
    entry = find_by_gtype (repository->ordered_lazy_typelibs, gtype_name, FALSE, &result_typelib);
 | 
						||
 | 
						||
  if (entry != NULL)
 | 
						||
    {
 | 
						||
      cached = gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
 | 
						||
                                 repository,
 | 
						||
                                 NULL, result_typelib, entry->offset);
 | 
						||
 | 
						||
      g_hash_table_insert (repository->info_by_gtype,
 | 
						||
                           (gpointer) gtype,
 | 
						||
                           gi_base_info_ref (cached));
 | 
						||
      return cached;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      g_hash_table_add (repository->unknown_gtypes, (gpointer) gtype);
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_find_by_name:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: Namespace which will be searched
 | 
						||
 * @name: Entry name to find
 | 
						||
 *
 | 
						||
 * Searches for a particular entry in a namespace.
 | 
						||
 *
 | 
						||
 * Before calling this function for a particular namespace, you must call
 | 
						||
 * [method@GIRepository.Repository.require] to load the namespace, or otherwise
 | 
						||
 * ensure the namespace has already been loaded.
 | 
						||
 *
 | 
						||
 * Returns: (transfer full) (nullable): [class@GIRepository.BaseInfo]
 | 
						||
 *   representing metadata about @name, or `NULL` if none found
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
GIBaseInfo *
 | 
						||
gi_repository_find_by_name (GIRepository *repository,
 | 
						||
                            const char   *namespace,
 | 
						||
                            const char   *name)
 | 
						||
{
 | 
						||
  GITypelib *typelib;
 | 
						||
  DirEntry *entry;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
  g_return_val_if_fail (namespace != NULL, NULL);
 | 
						||
 | 
						||
  typelib = get_registered (repository, namespace, NULL);
 | 
						||
  g_return_val_if_fail (typelib != NULL, NULL);
 | 
						||
 | 
						||
  entry = gi_typelib_get_dir_entry_by_name (typelib, name);
 | 
						||
  if (entry == NULL)
 | 
						||
    return NULL;
 | 
						||
  return gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
 | 
						||
                           repository,
 | 
						||
                           NULL, typelib, entry->offset);
 | 
						||
}
 | 
						||
 | 
						||
static DirEntry *
 | 
						||
find_by_error_domain (GPtrArray  *ordered_typelibs,
 | 
						||
                      GQuark      target_domain,
 | 
						||
                      GITypelib **out_typelib)
 | 
						||
{
 | 
						||
  /* Search in reverse order as the longest namespaces will be listed last, and
 | 
						||
   * those are the ones we want to search first. */
 | 
						||
  for (guint i = ordered_typelibs->len; i > 0; i--)
 | 
						||
    {
 | 
						||
      GITypelib *typelib = g_ptr_array_index (ordered_typelibs, i - 1);
 | 
						||
      DirEntry *entry;
 | 
						||
 | 
						||
      entry = gi_typelib_get_dir_entry_by_error_domain (typelib, target_domain);
 | 
						||
      if (entry != NULL)
 | 
						||
        {
 | 
						||
          *out_typelib = typelib;
 | 
						||
          return entry;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
  return NULL;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_find_by_error_domain:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @domain: a [type@GLib.Error] domain
 | 
						||
 *
 | 
						||
 * Searches for the enum type corresponding to the given [type@GLib.Error]
 | 
						||
 * domain.
 | 
						||
 *
 | 
						||
 * Before calling this function for a particular namespace, you must call
 | 
						||
 * [method@GIRepository.Repository.require] to load the namespace, or otherwise
 | 
						||
 * ensure the namespace has already been loaded.
 | 
						||
 *
 | 
						||
 * Returns: (transfer full) (nullable): [class@GIRepository.EnumInfo]
 | 
						||
 *   representing metadata about @domain’s enum type, or `NULL` if none found
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
GIEnumInfo *
 | 
						||
gi_repository_find_by_error_domain (GIRepository *repository,
 | 
						||
                                    GQuark        domain)
 | 
						||
{
 | 
						||
  GIEnumInfo *cached;
 | 
						||
  DirEntry *result = NULL;
 | 
						||
  GITypelib *result_typelib = NULL;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
 | 
						||
  cached = g_hash_table_lookup (repository->info_by_error_domain,
 | 
						||
                                GUINT_TO_POINTER (domain));
 | 
						||
 | 
						||
  if (cached != NULL)
 | 
						||
    return (GIEnumInfo *) gi_base_info_ref ((GIBaseInfo *)cached);
 | 
						||
 | 
						||
  result = find_by_error_domain (repository->ordered_typelibs, domain, &result_typelib);
 | 
						||
  if (result == NULL)
 | 
						||
    result = find_by_error_domain (repository->ordered_lazy_typelibs, domain, &result_typelib);
 | 
						||
 | 
						||
  if (result != NULL)
 | 
						||
    {
 | 
						||
      cached = (GIEnumInfo *) gi_info_new_full (gi_typelib_blob_type_to_info_type (result->blob_type),
 | 
						||
                                                repository,
 | 
						||
                                                NULL, result_typelib, result->offset);
 | 
						||
 | 
						||
      g_hash_table_insert (repository->info_by_error_domain,
 | 
						||
                           GUINT_TO_POINTER (domain),
 | 
						||
                           gi_base_info_ref ((GIBaseInfo *) cached));
 | 
						||
      return cached;
 | 
						||
    }
 | 
						||
  return NULL;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_object_gtype_interfaces:
 | 
						||
 * @repository: a #GIRepository
 | 
						||
 * @gtype: a [type@GObject.Type] whose fundamental type is `G_TYPE_OBJECT`
 | 
						||
 * @n_interfaces_out: (out): Number of interfaces
 | 
						||
 * @interfaces_out: (out) (transfer none) (array length=n_interfaces_out): Interfaces for @gtype
 | 
						||
 *
 | 
						||
 * Look up the implemented interfaces for @gtype.
 | 
						||
 *
 | 
						||
 * This function cannot fail per se; but for a totally ‘unknown’
 | 
						||
 * [type@GObject.Type], it may return 0 implemented interfaces.
 | 
						||
 *
 | 
						||
 * The semantics of this function are designed for a dynamic binding,
 | 
						||
 * where in certain cases (such as a function which returns an
 | 
						||
 * interface which may have ‘hidden’ implementation classes), not all
 | 
						||
 * data may be statically known, and will have to be determined from
 | 
						||
 * the [type@GObject.Type] of the object.  An example is
 | 
						||
 * [func@Gio.File.new_for_path] returning a concrete class of
 | 
						||
 * `GLocalFile`, which is a [type@GObject.Type] we see at runtime, but
 | 
						||
 * not statically.
 | 
						||
 *
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
void
 | 
						||
gi_repository_get_object_gtype_interfaces (GIRepository      *repository,
 | 
						||
                                           GType              gtype,
 | 
						||
                                           size_t            *n_interfaces_out,
 | 
						||
                                           GIInterfaceInfo ***interfaces_out)
 | 
						||
{
 | 
						||
  GTypeInterfaceCache *cache;
 | 
						||
 | 
						||
  g_return_if_fail (GI_IS_REPOSITORY (repository));
 | 
						||
  g_return_if_fail (g_type_fundamental (gtype) == G_TYPE_OBJECT);
 | 
						||
 | 
						||
  cache = g_hash_table_lookup (repository->interfaces_for_gtype,
 | 
						||
                               (void *) gtype);
 | 
						||
  if (cache == NULL)
 | 
						||
    {
 | 
						||
      GType *interfaces;
 | 
						||
      unsigned int i;
 | 
						||
      unsigned int n_interfaces;
 | 
						||
      GList *interface_infos = NULL, *iter;
 | 
						||
 | 
						||
      interfaces = g_type_interfaces (gtype, &n_interfaces);
 | 
						||
      for (i = 0; i < n_interfaces; i++)
 | 
						||
        {
 | 
						||
          GIBaseInfo *base_info;
 | 
						||
 | 
						||
          base_info = gi_repository_find_by_gtype (repository, interfaces[i]);
 | 
						||
          if (base_info == NULL)
 | 
						||
            continue;
 | 
						||
 | 
						||
          if (gi_base_info_get_info_type (base_info) != GI_INFO_TYPE_INTERFACE)
 | 
						||
            {
 | 
						||
              /* FIXME - could this really happen? */
 | 
						||
              gi_base_info_unref (base_info);
 | 
						||
              continue;
 | 
						||
            }
 | 
						||
 | 
						||
          if (!g_list_find (interface_infos, base_info))
 | 
						||
            interface_infos = g_list_prepend (interface_infos, base_info);
 | 
						||
        }
 | 
						||
 | 
						||
      cache = g_malloc (sizeof (GTypeInterfaceCache)
 | 
						||
                        + sizeof (GIBaseInfo*) * g_list_length (interface_infos));
 | 
						||
      cache->n_interfaces = g_list_length (interface_infos);
 | 
						||
      for (iter = interface_infos, i = 0; iter; iter = iter->next, i++)
 | 
						||
        cache->interfaces[i] = iter->data;
 | 
						||
      g_list_free (interface_infos);
 | 
						||
 | 
						||
      g_hash_table_insert (repository->interfaces_for_gtype, (gpointer) gtype,
 | 
						||
                           cache);
 | 
						||
 | 
						||
      g_free (interfaces);
 | 
						||
    }
 | 
						||
 | 
						||
  *n_interfaces_out = cache->n_interfaces;
 | 
						||
  *interfaces_out = (GIInterfaceInfo**)&cache->interfaces[0];
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
collect_namespaces (GPtrArray  *ordered_typelibs,
 | 
						||
                    char      **names,
 | 
						||
                    size_t     *inout_i)
 | 
						||
{
 | 
						||
  for (guint j = 0; j < ordered_typelibs->len; j++)
 | 
						||
    {
 | 
						||
      GITypelib *typelib = g_ptr_array_index (ordered_typelibs, j);
 | 
						||
      const char *namespace = gi_typelib_get_namespace (typelib);
 | 
						||
      names[(*inout_i)++] = g_strdup (namespace);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_loaded_namespaces:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @n_namespaces_out: (optional) (out): Return location for the number of
 | 
						||
 *   namespaces
 | 
						||
 *
 | 
						||
 * Return the list of currently loaded namespaces.
 | 
						||
 *
 | 
						||
 * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
 | 
						||
 * counted in @n_namespaces_out.
 | 
						||
 *
 | 
						||
 * Returns: (element-type utf8) (transfer full) (array length=n_namespaces_out):
 | 
						||
 *   list of namespaces
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
char **
 | 
						||
gi_repository_get_loaded_namespaces (GIRepository *repository,
 | 
						||
                                     size_t       *n_namespaces_out)
 | 
						||
{
 | 
						||
  char **names;
 | 
						||
  size_t i;
 | 
						||
  size_t n_typelibs;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
 | 
						||
  n_typelibs = repository->ordered_typelibs->len + repository->ordered_lazy_typelibs->len;
 | 
						||
  names = g_malloc0 (sizeof (char *) * (n_typelibs + 1));
 | 
						||
  i = 0;
 | 
						||
 | 
						||
  collect_namespaces (repository->ordered_typelibs, names, &i);
 | 
						||
  collect_namespaces (repository->ordered_lazy_typelibs, names, &i);
 | 
						||
 | 
						||
  if (n_namespaces_out != NULL)
 | 
						||
    *n_namespaces_out = i;
 | 
						||
 | 
						||
  return names;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_version:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: Namespace to inspect
 | 
						||
 *
 | 
						||
 * This function returns the loaded version associated with the given
 | 
						||
 * namespace @namespace_.
 | 
						||
 *
 | 
						||
 * Note: The namespace must have already been loaded using a function
 | 
						||
 * such as [method@GIRepository.Repository.require] before calling this
 | 
						||
 * function.
 | 
						||
 *
 | 
						||
 * Returns: Loaded version
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
const char *
 | 
						||
gi_repository_get_version (GIRepository *repository,
 | 
						||
                           const char   *namespace)
 | 
						||
{
 | 
						||
  GITypelib *typelib;
 | 
						||
  Header *header;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
  g_return_val_if_fail (namespace != NULL, NULL);
 | 
						||
 | 
						||
  typelib = get_registered (repository, namespace, NULL);
 | 
						||
 | 
						||
  g_return_val_if_fail (typelib != NULL, NULL);
 | 
						||
 | 
						||
  header = (Header *) typelib->data;
 | 
						||
  return gi_typelib_get_string (typelib, header->nsversion);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_shared_libraries:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: Namespace to inspect
 | 
						||
 * @out_n_elements: (out) (optional): Return location for the number of elements
 | 
						||
 *   in the returned array
 | 
						||
 *
 | 
						||
 * This function returns an array of paths to the
 | 
						||
 * shared C libraries associated with the given namespace @namespace_.
 | 
						||
 *
 | 
						||
 * There may be no shared library path associated, in which case this
 | 
						||
 * function will return `NULL`.
 | 
						||
 *
 | 
						||
 * Note: The namespace must have already been loaded using a function
 | 
						||
 * such as [method@GIRepository.Repository.require] before calling this
 | 
						||
 * function.
 | 
						||
 *
 | 
						||
 * The list is internal to [class@GIRepository.Repository] and should not be
 | 
						||
 * freed, nor should its string elements.
 | 
						||
 *
 | 
						||
 * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
 | 
						||
 * counted in @out_n_elements.
 | 
						||
 *
 | 
						||
 * Returns: (nullable) (array length=out_n_elements) (transfer none): Array of
 | 
						||
 *   paths to shared libraries, or `NULL` if none are associated
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
const char * const *
 | 
						||
gi_repository_get_shared_libraries (GIRepository *repository,
 | 
						||
                                    const char   *namespace,
 | 
						||
                                    size_t       *out_n_elements)
 | 
						||
{
 | 
						||
  GITypelib *typelib;
 | 
						||
  Header *header;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
  g_return_val_if_fail (namespace != NULL, NULL);
 | 
						||
 | 
						||
  typelib = get_registered (repository, namespace, NULL);
 | 
						||
 | 
						||
  g_return_val_if_fail (typelib != NULL, NULL);
 | 
						||
 | 
						||
  header = (Header *) typelib->data;
 | 
						||
  if (!header->shared_library)
 | 
						||
    {
 | 
						||
      if (out_n_elements != NULL)
 | 
						||
        *out_n_elements = 0;
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Populate the cache. */
 | 
						||
  if (repository->cached_shared_libraries == NULL)
 | 
						||
    {
 | 
						||
      const char *comma_separated = gi_typelib_get_string (typelib, header->shared_library);
 | 
						||
 | 
						||
      if (comma_separated != NULL && *comma_separated != '\0')
 | 
						||
        {
 | 
						||
          repository->cached_shared_libraries = g_strsplit (comma_separated, ",", -1);
 | 
						||
          repository->cached_n_shared_libraries = g_strv_length (repository->cached_shared_libraries);
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
  if (out_n_elements != NULL)
 | 
						||
    *out_n_elements = repository->cached_n_shared_libraries;
 | 
						||
 | 
						||
  return (const char * const *) repository->cached_shared_libraries;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_c_prefix:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: Namespace to inspect
 | 
						||
 *
 | 
						||
 * This function returns the ‘C prefix’, or the C level namespace
 | 
						||
 * associated with the given introspection namespace.
 | 
						||
 *
 | 
						||
 * Each C symbol starts with this prefix, as well each [type@GObject.Type] in
 | 
						||
 * the library.
 | 
						||
 *
 | 
						||
 * Note: The namespace must have already been loaded using a function
 | 
						||
 * such as [method@GIRepository.Repository.require] before calling this
 | 
						||
 * function.
 | 
						||
 *
 | 
						||
 * Returns: (nullable): C namespace prefix, or `NULL` if none associated
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
const char *
 | 
						||
gi_repository_get_c_prefix (GIRepository *repository,
 | 
						||
                            const char   *namespace_)
 | 
						||
{
 | 
						||
  GITypelib *typelib;
 | 
						||
  Header *header;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
  g_return_val_if_fail (namespace_ != NULL, NULL);
 | 
						||
 | 
						||
  typelib = get_registered (repository, namespace_, NULL);
 | 
						||
 | 
						||
  g_return_val_if_fail (typelib != NULL, NULL);
 | 
						||
 | 
						||
  header = (Header *) typelib->data;
 | 
						||
  if (header->c_prefix)
 | 
						||
    return gi_typelib_get_string (typelib, header->c_prefix);
 | 
						||
  else
 | 
						||
    return NULL;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_typelib_path:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: GI namespace to use, e.g. `Gtk`
 | 
						||
 *
 | 
						||
 * If namespace @namespace_ is loaded, return the full path to the
 | 
						||
 * .typelib file it was loaded from.
 | 
						||
 *
 | 
						||
 * If the typelib for namespace @namespace_ was included in a shared library,
 | 
						||
 * return the special string `<builtin>`.
 | 
						||
 *
 | 
						||
 * Returns: (type filename) (nullable): Filesystem path (or `<builtin>`) if
 | 
						||
 *   successful, `NULL` if namespace is not loaded
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
const char *
 | 
						||
gi_repository_get_typelib_path (GIRepository *repository,
 | 
						||
                                const char   *namespace)
 | 
						||
{
 | 
						||
  gpointer orig_key, value;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
 | 
						||
  if (!g_hash_table_lookup_extended (repository->typelibs, namespace,
 | 
						||
                                     &orig_key, &value))
 | 
						||
    {
 | 
						||
      if (!g_hash_table_lookup_extended (repository->lazy_typelibs, namespace,
 | 
						||
                                         &orig_key, &value))
 | 
						||
 | 
						||
        return NULL;
 | 
						||
    }
 | 
						||
  return ((char*)orig_key) + strlen ((char *) orig_key) + 1;
 | 
						||
}
 | 
						||
 | 
						||
/* This simple search function looks for a specified namespace-version;
 | 
						||
   it's faster than the full directory listing required for latest version. */
 | 
						||
static GMappedFile *
 | 
						||
find_namespace_version (const char          *namespace,
 | 
						||
                        const char          *version,
 | 
						||
                        const char * const  *search_paths,
 | 
						||
                        size_t               n_search_paths,
 | 
						||
                        char               **path_ret)
 | 
						||
{
 | 
						||
  GError *error = NULL;
 | 
						||
  GMappedFile *mfile = NULL;
 | 
						||
  char *fname;
 | 
						||
 | 
						||
  if (g_str_equal (namespace, GIREPOSITORY_TYPELIB_NAME) &&
 | 
						||
      !g_str_equal (version, GIREPOSITORY_TYPELIB_VERSION))
 | 
						||
    {
 | 
						||
      g_debug ("Ignoring %s-%s.typelib because this libgirepository "
 | 
						||
               "corresponds to %s-%s",
 | 
						||
               namespace, version,
 | 
						||
               namespace, GIREPOSITORY_TYPELIB_VERSION);
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
 | 
						||
  fname = g_strdup_printf ("%s-%s.typelib", namespace, version);
 | 
						||
 | 
						||
  for (size_t i = 0; i < n_search_paths; ++i)
 | 
						||
    {
 | 
						||
      char *path = g_build_filename (search_paths[i], fname, NULL);
 | 
						||
 | 
						||
      mfile = g_mapped_file_new (path, FALSE, &error);
 | 
						||
      if (error)
 | 
						||
        {
 | 
						||
          g_free (path);
 | 
						||
          g_clear_error (&error);
 | 
						||
          continue;
 | 
						||
        }
 | 
						||
      *path_ret = path;
 | 
						||
      break;
 | 
						||
    }
 | 
						||
  g_free (fname);
 | 
						||
  return mfile;
 | 
						||
}
 | 
						||
 | 
						||
static gboolean
 | 
						||
parse_version (const char *version,
 | 
						||
               int *major,
 | 
						||
               int *minor)
 | 
						||
{
 | 
						||
  const char *dot;
 | 
						||
  char *end;
 | 
						||
 | 
						||
  *major = strtol (version, &end, 10);
 | 
						||
  dot = strchr (version, '.');
 | 
						||
  if (dot == NULL)
 | 
						||
    {
 | 
						||
      *minor = 0;
 | 
						||
      return TRUE;
 | 
						||
    }
 | 
						||
  if (dot != end)
 | 
						||
    return FALSE;
 | 
						||
  *minor = strtol (dot+1, &end, 10);
 | 
						||
  if (end != (version + strlen (version)))
 | 
						||
    return FALSE;
 | 
						||
  return TRUE;
 | 
						||
}
 | 
						||
 | 
						||
static int
 | 
						||
compare_version (const char *v1,
 | 
						||
                 const char *v2)
 | 
						||
{
 | 
						||
  gboolean success;
 | 
						||
  int v1_major, v1_minor;
 | 
						||
  int v2_major, v2_minor;
 | 
						||
 | 
						||
  success = parse_version (v1, &v1_major, &v1_minor);
 | 
						||
  g_assert (success);
 | 
						||
 | 
						||
  success = parse_version (v2, &v2_major, &v2_minor);
 | 
						||
  g_assert (success);
 | 
						||
 | 
						||
  /* Avoid a compiler warning about `success` being unused with G_DISABLE_ASSERT */
 | 
						||
  (void) success;
 | 
						||
 | 
						||
  if (v1_major > v2_major)
 | 
						||
    return 1;
 | 
						||
  else if (v2_major > v1_major)
 | 
						||
    return -1;
 | 
						||
  else if (v1_minor > v2_minor)
 | 
						||
    return 1;
 | 
						||
  else if (v2_minor > v1_minor)
 | 
						||
    return -1;
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
struct NamespaceVersionCandidadate
 | 
						||
{
 | 
						||
  GMappedFile *mfile;
 | 
						||
  int path_index;
 | 
						||
  char *path;
 | 
						||
  char *version;
 | 
						||
};
 | 
						||
 | 
						||
static int
 | 
						||
compare_candidate_reverse (struct NamespaceVersionCandidadate *c1,
 | 
						||
                           struct NamespaceVersionCandidadate *c2)
 | 
						||
{
 | 
						||
  int result = compare_version (c1->version, c2->version);
 | 
						||
  /* First, check the version */
 | 
						||
  if (result > 0)
 | 
						||
    return -1;
 | 
						||
  else if (result < 0)
 | 
						||
    return 1;
 | 
						||
  else
 | 
						||
    {
 | 
						||
      /* Now check the path index, which says how early in the search path
 | 
						||
       * we found it.  This ensures that of equal version targets, we
 | 
						||
       * pick the earlier one.
 | 
						||
       */
 | 
						||
      if (c1->path_index == c2->path_index)
 | 
						||
        return 0;
 | 
						||
      else if (c1->path_index > c2->path_index)
 | 
						||
        return 1;
 | 
						||
      else
 | 
						||
        return -1;
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
free_candidate (struct NamespaceVersionCandidadate *candidate)
 | 
						||
{
 | 
						||
  g_mapped_file_unref (candidate->mfile);
 | 
						||
  g_free (candidate->path);
 | 
						||
  g_free (candidate->version);
 | 
						||
  g_slice_free (struct NamespaceVersionCandidadate, candidate);
 | 
						||
}
 | 
						||
 | 
						||
static GSList *
 | 
						||
enumerate_namespace_versions (const char         *namespace,
 | 
						||
                              const char * const *search_paths,
 | 
						||
                              size_t              n_search_paths)
 | 
						||
{
 | 
						||
  GSList *candidates = NULL;
 | 
						||
  GHashTable *found_versions = g_hash_table_new (g_str_hash, g_str_equal);
 | 
						||
  char *namespace_dash;
 | 
						||
  char *namespace_typelib;
 | 
						||
  GError *error = NULL;
 | 
						||
  int index;
 | 
						||
 | 
						||
  namespace_dash = g_strdup_printf ("%s-", namespace);
 | 
						||
  namespace_typelib = g_strdup_printf ("%s.typelib", namespace);
 | 
						||
 | 
						||
  index = 0;
 | 
						||
  for (size_t i = 0; i < n_search_paths; ++i)
 | 
						||
    {
 | 
						||
      GDir *dir;
 | 
						||
      const char *dirname;
 | 
						||
      const char *entry;
 | 
						||
 | 
						||
      dirname = search_paths[i];
 | 
						||
      dir = g_dir_open (dirname, 0, NULL);
 | 
						||
      if (dir == NULL)
 | 
						||
        continue;
 | 
						||
      while ((entry = g_dir_read_name (dir)) != NULL)
 | 
						||
        {
 | 
						||
          GMappedFile *mfile;
 | 
						||
          char *path, *version;
 | 
						||
          struct NamespaceVersionCandidadate *candidate;
 | 
						||
 | 
						||
          if (!g_str_has_suffix (entry, ".typelib"))
 | 
						||
            continue;
 | 
						||
 | 
						||
          if (g_str_has_prefix (entry, namespace_dash))
 | 
						||
            {
 | 
						||
              const char *last_dash;
 | 
						||
              const char *name_end;
 | 
						||
              int major, minor;
 | 
						||
 | 
						||
              if (g_str_equal (namespace, GIREPOSITORY_TYPELIB_NAME) &&
 | 
						||
                  !g_str_equal (entry, GIREPOSITORY_TYPELIB_FILENAME))
 | 
						||
                {
 | 
						||
                  g_debug ("Ignoring %s because this libgirepository "
 | 
						||
                           "corresponds to %s",
 | 
						||
                           entry, GIREPOSITORY_TYPELIB_FILENAME);
 | 
						||
                  continue;
 | 
						||
                }
 | 
						||
 | 
						||
              name_end = strrchr (entry, '.');
 | 
						||
              last_dash = strrchr (entry, '-');
 | 
						||
 | 
						||
              /* These are guaranteed by the suffix and prefix checks above: */
 | 
						||
              g_assert (name_end != NULL);
 | 
						||
              g_assert (last_dash != NULL);
 | 
						||
 | 
						||
              version = g_strndup (last_dash + 1, (size_t) (name_end - (last_dash + 1u)));
 | 
						||
              if (!parse_version (version, &major, &minor))
 | 
						||
                {
 | 
						||
                  g_free (version);
 | 
						||
                  continue;
 | 
						||
                }
 | 
						||
            }
 | 
						||
          else
 | 
						||
            continue;
 | 
						||
 | 
						||
          if (g_hash_table_lookup (found_versions, version) != NULL)
 | 
						||
            {
 | 
						||
              g_free (version);
 | 
						||
              continue;
 | 
						||
            }
 | 
						||
 | 
						||
          path = g_build_filename (dirname, entry, NULL);
 | 
						||
          mfile = g_mapped_file_new (path, FALSE, &error);
 | 
						||
          if (mfile == NULL)
 | 
						||
            {
 | 
						||
              g_free (path);
 | 
						||
              g_free (version);
 | 
						||
              g_clear_error (&error);
 | 
						||
              continue;
 | 
						||
            }
 | 
						||
          candidate = g_slice_new0 (struct NamespaceVersionCandidadate);
 | 
						||
          candidate->mfile = mfile;
 | 
						||
          candidate->path_index = index;
 | 
						||
          candidate->path = path;
 | 
						||
          candidate->version = version;
 | 
						||
          candidates = g_slist_prepend (candidates, candidate);
 | 
						||
          g_hash_table_add (found_versions, version);
 | 
						||
        }
 | 
						||
      g_dir_close (dir);
 | 
						||
      index++;
 | 
						||
    }
 | 
						||
 | 
						||
  g_free (namespace_dash);
 | 
						||
  g_free (namespace_typelib);
 | 
						||
  g_hash_table_destroy (found_versions);
 | 
						||
 | 
						||
  return candidates;
 | 
						||
}
 | 
						||
 | 
						||
static GMappedFile *
 | 
						||
find_namespace_latest (const char          *namespace,
 | 
						||
                       const char * const  *search_paths,
 | 
						||
                       size_t               n_search_paths,
 | 
						||
                       char               **version_ret,
 | 
						||
                       char               **path_ret)
 | 
						||
{
 | 
						||
  GSList *candidates;
 | 
						||
  GMappedFile *result = NULL;
 | 
						||
 | 
						||
  *version_ret = NULL;
 | 
						||
  *path_ret = NULL;
 | 
						||
 | 
						||
  candidates = enumerate_namespace_versions (namespace, search_paths, n_search_paths);
 | 
						||
 | 
						||
  if (candidates != NULL)
 | 
						||
    {
 | 
						||
      struct NamespaceVersionCandidadate *elected;
 | 
						||
      candidates = g_slist_sort (candidates, (GCompareFunc) compare_candidate_reverse);
 | 
						||
 | 
						||
      elected = (struct NamespaceVersionCandidadate *) candidates->data;
 | 
						||
      /* Remove the elected one so we don't try to free its contents */
 | 
						||
      candidates = g_slist_delete_link (candidates, candidates);
 | 
						||
 | 
						||
      result = elected->mfile;
 | 
						||
      *path_ret = elected->path;
 | 
						||
      *version_ret = elected->version;
 | 
						||
      g_slice_free (struct NamespaceVersionCandidadate, elected); /* just free the container */
 | 
						||
      g_slist_foreach (candidates, (GFunc) (void *) free_candidate, NULL);
 | 
						||
      g_slist_free (candidates);
 | 
						||
    }
 | 
						||
  return result;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_enumerate_versions:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: GI namespace, e.g. `Gtk`
 | 
						||
 * @n_versions_out: (optional) (out): The number of versions returned.
 | 
						||
 *
 | 
						||
 * Obtain an unordered list of versions (either currently loaded or
 | 
						||
 * available) for @namespace_ in this @repository.
 | 
						||
 *
 | 
						||
 * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
 | 
						||
 * counted in @n_versions_out.
 | 
						||
 *
 | 
						||
 * Returns: (element-type utf8) (transfer full) (array length=n_versions_out): the array of versions.
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
char **
 | 
						||
gi_repository_enumerate_versions (GIRepository *repository,
 | 
						||
                                  const char   *namespace_,
 | 
						||
                                  size_t       *n_versions_out)
 | 
						||
{
 | 
						||
  GPtrArray *versions;
 | 
						||
  GSList *candidates, *link;
 | 
						||
  const char *loaded_version;
 | 
						||
  char **ret;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
 | 
						||
  candidates = enumerate_namespace_versions (namespace_,
 | 
						||
                                             (const char * const *) repository->typelib_search_path->pdata,
 | 
						||
                                             repository->typelib_search_path->len);
 | 
						||
 | 
						||
  if (!candidates)
 | 
						||
    {
 | 
						||
      if (n_versions_out)
 | 
						||
        *n_versions_out = 0;
 | 
						||
      return g_strdupv ((char *[]) {NULL});
 | 
						||
    }
 | 
						||
 | 
						||
  versions = g_ptr_array_new_null_terminated (1, g_free, TRUE);
 | 
						||
  for (link = candidates; link; link = link->next)
 | 
						||
    {
 | 
						||
      struct NamespaceVersionCandidadate *candidate = link->data;
 | 
						||
      g_ptr_array_add (versions, g_steal_pointer (&candidate->version));
 | 
						||
      free_candidate (candidate);
 | 
						||
    }
 | 
						||
  g_slist_free (candidates);
 | 
						||
 | 
						||
  /* The currently loaded version of a namespace is also part of the
 | 
						||
   * available versions, as it could have been loaded using
 | 
						||
   * require_private().
 | 
						||
   */
 | 
						||
  if (gi_repository_is_registered (repository, namespace_, NULL))
 | 
						||
    {
 | 
						||
      loaded_version = gi_repository_get_version (repository, namespace_);
 | 
						||
      if (loaded_version &&
 | 
						||
          !g_ptr_array_find_with_equal_func (versions, loaded_version, g_str_equal, NULL))
 | 
						||
        g_ptr_array_add (versions, g_strdup (loaded_version));
 | 
						||
    }
 | 
						||
 | 
						||
  ret = (char **) g_ptr_array_steal (versions, n_versions_out);
 | 
						||
  g_ptr_array_unref (g_steal_pointer (&versions));
 | 
						||
 | 
						||
  return ret;
 | 
						||
}
 | 
						||
 | 
						||
static GITypelib *
 | 
						||
require_internal (GIRepository           *repository,
 | 
						||
                  const char             *namespace,
 | 
						||
                  const char             *version,
 | 
						||
                  GIRepositoryLoadFlags   flags,
 | 
						||
                  const char * const     *search_paths,
 | 
						||
                  size_t                  n_search_paths,
 | 
						||
                  GError                **error)
 | 
						||
{
 | 
						||
  GMappedFile *mfile;
 | 
						||
  GITypelib *ret = NULL;
 | 
						||
  Header *header;
 | 
						||
  GITypelib *typelib = NULL;
 | 
						||
  GITypelib *typelib_owned = NULL;
 | 
						||
  const char *typelib_namespace, *typelib_version;
 | 
						||
  gboolean allow_lazy = (flags & GI_REPOSITORY_LOAD_FLAG_LAZY) > 0;
 | 
						||
  gboolean is_lazy;
 | 
						||
  char *version_conflict = NULL;
 | 
						||
  char *path = NULL;
 | 
						||
  char *tmp_version = NULL;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
  g_return_val_if_fail (namespace != NULL, NULL);
 | 
						||
 | 
						||
  typelib = get_registered_status (repository, namespace, version, allow_lazy,
 | 
						||
                                   &is_lazy, &version_conflict);
 | 
						||
  if (typelib)
 | 
						||
    return typelib;
 | 
						||
 | 
						||
  if (version_conflict != NULL)
 | 
						||
    {
 | 
						||
      g_set_error (error, GI_REPOSITORY_ERROR,
 | 
						||
                   GI_REPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
 | 
						||
                   "Requiring namespace '%s' version '%s', but '%s' is already loaded",
 | 
						||
                   namespace, version, version_conflict);
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
 | 
						||
  if (version != NULL)
 | 
						||
    {
 | 
						||
      mfile = find_namespace_version (namespace, version, search_paths,
 | 
						||
                                      n_search_paths, &path);
 | 
						||
      tmp_version = g_strdup (version);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      mfile = find_namespace_latest (namespace, search_paths, n_search_paths,
 | 
						||
                                     &tmp_version, &path);
 | 
						||
    }
 | 
						||
 | 
						||
  if (mfile == NULL)
 | 
						||
    {
 | 
						||
      if (version != NULL)
 | 
						||
        g_set_error (error, GI_REPOSITORY_ERROR,
 | 
						||
                     GI_REPOSITORY_ERROR_TYPELIB_NOT_FOUND,
 | 
						||
                     "Typelib file for namespace '%s', version '%s' not found",
 | 
						||
                     namespace, version);
 | 
						||
      else
 | 
						||
        g_set_error (error, GI_REPOSITORY_ERROR,
 | 
						||
                     GI_REPOSITORY_ERROR_TYPELIB_NOT_FOUND,
 | 
						||
                     "Typelib file for namespace '%s' (any version) not found",
 | 
						||
                     namespace);
 | 
						||
      goto out;
 | 
						||
    }
 | 
						||
 | 
						||
  {
 | 
						||
    GError *temp_error = NULL;
 | 
						||
    GBytes *bytes = NULL;
 | 
						||
 | 
						||
    bytes = g_mapped_file_get_bytes (mfile);
 | 
						||
    typelib_owned = typelib = gi_typelib_new_from_bytes (bytes, &temp_error);
 | 
						||
    g_bytes_unref (bytes);
 | 
						||
    g_clear_pointer (&mfile, g_mapped_file_unref);
 | 
						||
 | 
						||
    if (!typelib)
 | 
						||
      {
 | 
						||
        g_set_error (error, GI_REPOSITORY_ERROR,
 | 
						||
                     GI_REPOSITORY_ERROR_TYPELIB_NOT_FOUND,
 | 
						||
                     "Failed to load typelib file '%s' for namespace '%s': %s",
 | 
						||
                     path, namespace, temp_error->message);
 | 
						||
        g_clear_error (&temp_error);
 | 
						||
        goto out;
 | 
						||
      }
 | 
						||
 | 
						||
    typelib->library_paths = (repository->library_paths != NULL) ? g_ptr_array_ref (repository->library_paths) : NULL;
 | 
						||
  }
 | 
						||
  header = (Header *) typelib->data;
 | 
						||
  typelib_namespace = gi_typelib_get_string (typelib, header->namespace);
 | 
						||
  typelib_version = gi_typelib_get_string (typelib, header->nsversion);
 | 
						||
 | 
						||
  if (strcmp (typelib_namespace, namespace) != 0)
 | 
						||
    {
 | 
						||
      g_set_error (error, GI_REPOSITORY_ERROR,
 | 
						||
                   GI_REPOSITORY_ERROR_NAMESPACE_MISMATCH,
 | 
						||
                   "Typelib file %s for namespace '%s' contains "
 | 
						||
                   "namespace '%s' which doesn't match the file name",
 | 
						||
                   path, namespace, typelib_namespace);
 | 
						||
      goto out;
 | 
						||
    }
 | 
						||
  if (version != NULL && strcmp (typelib_version, version) != 0)
 | 
						||
    {
 | 
						||
      g_set_error (error, GI_REPOSITORY_ERROR,
 | 
						||
                   GI_REPOSITORY_ERROR_NAMESPACE_MISMATCH,
 | 
						||
                   "Typelib file %s for namespace '%s' contains "
 | 
						||
                   "version '%s' which doesn't match the expected version '%s'",
 | 
						||
                   path, namespace, typelib_version, version);
 | 
						||
      goto out;
 | 
						||
    }
 | 
						||
 | 
						||
  if (!register_internal (repository, path, allow_lazy,
 | 
						||
                          typelib, error))
 | 
						||
    goto out;
 | 
						||
  ret = typelib;
 | 
						||
 out:
 | 
						||
  g_clear_pointer (&typelib_owned, gi_typelib_unref);
 | 
						||
  g_free (tmp_version);
 | 
						||
  g_free (path);
 | 
						||
  return ret;
 | 
						||
}
 | 
						||
 | 
						||
static GITypelib *
 | 
						||
require_internal_with_platform_data (GIRepository           *repository,
 | 
						||
                                     const char             *namespace,
 | 
						||
                                     const char             *version,
 | 
						||
                                     GIRepositoryLoadFlags   flags,
 | 
						||
                                     const char * const     *search_paths,
 | 
						||
                                     size_t                  search_paths_len,
 | 
						||
                                     GError                **error)
 | 
						||
{
 | 
						||
  GITypelib *typelib;
 | 
						||
 | 
						||
  typelib = require_internal (repository, namespace, version, flags,
 | 
						||
                              search_paths, search_paths_len,
 | 
						||
                              error);
 | 
						||
  if (!typelib)
 | 
						||
    return NULL;
 | 
						||
 | 
						||
#if defined (G_OS_UNIX) || defined (G_OS_WIN32)
 | 
						||
  /* Backward compatibility hack: if we're loading Gio-2.0, we automatically
 | 
						||
   * load the platform specific introspection data that used to exist inside
 | 
						||
   * Gio-2.0
 | 
						||
   */
 | 
						||
  if (g_str_equal (namespace, "Gio") &&
 | 
						||
      (!version || g_str_equal (version, "2.0")))
 | 
						||
    {
 | 
						||
      GError *local_error = NULL;
 | 
						||
      const char *platform_namespace;
 | 
						||
 | 
						||
#  if defined (G_OS_UNIX)
 | 
						||
      platform_namespace = "GioUnix";
 | 
						||
#  elif defined (G_OS_WIN32)
 | 
						||
      platform_namespace = "GioWin32";
 | 
						||
#  endif /* defined (G_OS_LINUX) */
 | 
						||
 | 
						||
      if (!require_internal (repository, platform_namespace, version, flags,
 | 
						||
                             search_paths, search_paths_len,
 | 
						||
                             &local_error))
 | 
						||
        {
 | 
						||
          g_critical ("Unable to load platform-specific GIO introspection data: %s",
 | 
						||
                      local_error->message);
 | 
						||
          g_error_free (local_error);
 | 
						||
        }
 | 
						||
    }
 | 
						||
#endif /* defined(G_OS_UNIX) || defined(G_OS_WIN32) */
 | 
						||
 | 
						||
  return typelib;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_require:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @namespace_: GI namespace to use, e.g. `Gtk`
 | 
						||
 * @version: (nullable): Version of namespace, may be `NULL` for latest
 | 
						||
 * @flags: Set of [flags@GIRepository.RepositoryLoadFlags], may be 0
 | 
						||
 * @error: a [type@GLib.Error].
 | 
						||
 *
 | 
						||
 * Force the namespace @namespace_ to be loaded if it isn’t already.
 | 
						||
 *
 | 
						||
 * If @namespace_ is not loaded, this function will search for a
 | 
						||
 * `.typelib` file using the repository search path.  In addition, a
 | 
						||
 * version @version of namespace may be specified.  If @version is
 | 
						||
 * not specified, the latest will be used.
 | 
						||
 *
 | 
						||
 * Returns: (transfer none): a pointer to the [type@GIRepository.Typelib] if
 | 
						||
 *   successful, `NULL` otherwise
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
GITypelib *
 | 
						||
gi_repository_require (GIRepository           *repository,
 | 
						||
                       const char             *namespace,
 | 
						||
                       const char             *version,
 | 
						||
                       GIRepositoryLoadFlags   flags,
 | 
						||
                       GError                **error)
 | 
						||
{
 | 
						||
  const char * const *search_paths;
 | 
						||
  size_t search_paths_len;
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
  g_return_val_if_fail (namespace != NULL, NULL);
 | 
						||
 | 
						||
  search_paths = (const char * const *) repository->typelib_search_path->pdata;
 | 
						||
  search_paths_len = repository->typelib_search_path->len;
 | 
						||
 | 
						||
  return require_internal_with_platform_data (repository, namespace, version, flags,
 | 
						||
                                              search_paths, search_paths_len,
 | 
						||
                                              error);
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_require_private:
 | 
						||
 * @repository: A #GIRepository
 | 
						||
 * @typelib_dir: (type filename): Private directory where to find the requested
 | 
						||
 *   typelib
 | 
						||
 * @namespace_: GI namespace to use, e.g. `Gtk`
 | 
						||
 * @version: (nullable): Version of namespace, may be `NULL` for latest
 | 
						||
 * @flags: Set of [flags@GIRepository.RepositoryLoadFlags], may be 0
 | 
						||
 * @error: a [type@GLib.Error].
 | 
						||
 *
 | 
						||
 * Force the namespace @namespace_ to be loaded if it isn’t already.
 | 
						||
 *
 | 
						||
 * If @namespace_ is not loaded, this function will search for a
 | 
						||
 * `.typelib` file within the private directory only. In addition, a
 | 
						||
 * version @version of namespace should be specified.  If @version is
 | 
						||
 * not specified, the latest will be used.
 | 
						||
 *
 | 
						||
 * Returns: (transfer none): a pointer to the [type@GIRepository.Typelib] if
 | 
						||
 *   successful, `NULL` otherwise
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
GITypelib *
 | 
						||
gi_repository_require_private (GIRepository           *repository,
 | 
						||
                               const char             *typelib_dir,
 | 
						||
                               const char             *namespace,
 | 
						||
                               const char             *version,
 | 
						||
                               GIRepositoryLoadFlags   flags,
 | 
						||
                               GError                **error)
 | 
						||
{
 | 
						||
  const char * const search_path[] = { typelib_dir, NULL };
 | 
						||
 | 
						||
  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 | 
						||
  g_return_val_if_fail (namespace != NULL, NULL);
 | 
						||
 | 
						||
  return require_internal_with_platform_data (repository, namespace, version, flags,
 | 
						||
                                              search_path, 1,
 | 
						||
                                              error);
 | 
						||
}
 | 
						||
 | 
						||
static gboolean
 | 
						||
gi_repository_introspect_cb (const char *option_name,
 | 
						||
                             const char *value,
 | 
						||
                             gpointer data,
 | 
						||
                             GError **error)
 | 
						||
{
 | 
						||
  GError *tmp_error = NULL;
 | 
						||
  char **args;
 | 
						||
 | 
						||
  args = g_strsplit (value, ",", 2);
 | 
						||
 | 
						||
  if (!gi_repository_dump (args[0], args[1], &tmp_error))
 | 
						||
    {
 | 
						||
      g_error ("Failed to extract GType data: %s",
 | 
						||
               tmp_error->message);
 | 
						||
      exit (1);
 | 
						||
    }
 | 
						||
  exit (0);
 | 
						||
}
 | 
						||
 | 
						||
static const GOptionEntry introspection_args[] = {
 | 
						||
  { "introspect-dump", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK,
 | 
						||
    gi_repository_introspect_cb, "Dump introspection information",
 | 
						||
    "infile.txt,outfile.xml" },
 | 
						||
  G_OPTION_ENTRY_NULL
 | 
						||
};
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_get_option_group:
 | 
						||
 *
 | 
						||
 * Obtain the option group for girepository.
 | 
						||
 *
 | 
						||
 * It’s used by the dumper and for programs that want to provide introspection
 | 
						||
 * information
 | 
						||
 *
 | 
						||
 * Returns: (transfer full): the option group
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
GOptionGroup *
 | 
						||
gi_repository_get_option_group (void)
 | 
						||
{
 | 
						||
  GOptionGroup *group;
 | 
						||
  group = g_option_group_new ("girepository", "Introspection Options", "Show Introspection Options", NULL, NULL);
 | 
						||
 | 
						||
  g_option_group_add_entries (group, introspection_args);
 | 
						||
  return group;
 | 
						||
}
 | 
						||
 | 
						||
GQuark
 | 
						||
gi_repository_error_quark (void)
 | 
						||
{
 | 
						||
  static GQuark quark = 0;
 | 
						||
  if (quark == 0)
 | 
						||
    quark = g_quark_from_static_string ("g-irepository-error-quark");
 | 
						||
  return quark;
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_type_tag_to_string:
 | 
						||
 * @type: the type_tag
 | 
						||
 *
 | 
						||
 * Obtain a string representation of @type
 | 
						||
 *
 | 
						||
 * Returns: the string
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
const char *
 | 
						||
gi_type_tag_to_string (GITypeTag type)
 | 
						||
{
 | 
						||
  switch (type)
 | 
						||
    {
 | 
						||
    case GI_TYPE_TAG_VOID:
 | 
						||
      return "void";
 | 
						||
    case GI_TYPE_TAG_BOOLEAN:
 | 
						||
      return "gboolean";
 | 
						||
    case GI_TYPE_TAG_INT8:
 | 
						||
      return "gint8";
 | 
						||
    case GI_TYPE_TAG_UINT8:
 | 
						||
      return "guint8";
 | 
						||
    case GI_TYPE_TAG_INT16:
 | 
						||
      return "gint16";
 | 
						||
    case GI_TYPE_TAG_UINT16:
 | 
						||
      return "guint16";
 | 
						||
    case GI_TYPE_TAG_INT32:
 | 
						||
      return "gint32";
 | 
						||
    case GI_TYPE_TAG_UINT32:
 | 
						||
      return "guint32";
 | 
						||
    case GI_TYPE_TAG_INT64:
 | 
						||
      return "gint64";
 | 
						||
    case GI_TYPE_TAG_UINT64:
 | 
						||
      return "guint64";
 | 
						||
    case GI_TYPE_TAG_FLOAT:
 | 
						||
      return "gfloat";
 | 
						||
    case GI_TYPE_TAG_DOUBLE:
 | 
						||
      return "gdouble";
 | 
						||
    case GI_TYPE_TAG_UNICHAR:
 | 
						||
      return "gunichar";
 | 
						||
    case GI_TYPE_TAG_GTYPE:
 | 
						||
      return "GType";
 | 
						||
    case GI_TYPE_TAG_UTF8:
 | 
						||
      return "utf8";
 | 
						||
    case GI_TYPE_TAG_FILENAME:
 | 
						||
      return "filename";
 | 
						||
    case GI_TYPE_TAG_ARRAY:
 | 
						||
      return "array";
 | 
						||
    case GI_TYPE_TAG_INTERFACE:
 | 
						||
      return "interface";
 | 
						||
    case GI_TYPE_TAG_GLIST:
 | 
						||
      return "glist";
 | 
						||
    case GI_TYPE_TAG_GSLIST:
 | 
						||
      return "gslist";
 | 
						||
    case GI_TYPE_TAG_GHASH:
 | 
						||
      return "ghash";
 | 
						||
    case GI_TYPE_TAG_ERROR:
 | 
						||
      return "error";
 | 
						||
    default:
 | 
						||
      return "unknown";
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_info_type_to_string:
 | 
						||
 * @type: the info type
 | 
						||
 *
 | 
						||
 * Obtain a string representation of @type
 | 
						||
 *
 | 
						||
 * Returns: the string
 | 
						||
 * Since: 2.80
 | 
						||
 */
 | 
						||
const char *
 | 
						||
gi_info_type_to_string (GIInfoType type)
 | 
						||
{
 | 
						||
  switch (type)
 | 
						||
    {
 | 
						||
    case GI_INFO_TYPE_INVALID:
 | 
						||
      return "invalid";
 | 
						||
    case GI_INFO_TYPE_FUNCTION:
 | 
						||
      return "function";
 | 
						||
    case GI_INFO_TYPE_CALLBACK:
 | 
						||
      return "callback";
 | 
						||
    case GI_INFO_TYPE_STRUCT:
 | 
						||
      return "struct";
 | 
						||
    case GI_INFO_TYPE_ENUM:
 | 
						||
      return "enum";
 | 
						||
    case GI_INFO_TYPE_FLAGS:
 | 
						||
      return "flags";
 | 
						||
    case GI_INFO_TYPE_OBJECT:
 | 
						||
      return "object";
 | 
						||
    case GI_INFO_TYPE_INTERFACE:
 | 
						||
      return "interface";
 | 
						||
    case GI_INFO_TYPE_CONSTANT:
 | 
						||
      return "constant";
 | 
						||
    case GI_INFO_TYPE_UNION:
 | 
						||
      return "union";
 | 
						||
    case GI_INFO_TYPE_VALUE:
 | 
						||
      return "value";
 | 
						||
    case GI_INFO_TYPE_SIGNAL:
 | 
						||
      return "signal";
 | 
						||
    case GI_INFO_TYPE_VFUNC:
 | 
						||
      return "vfunc";
 | 
						||
    case GI_INFO_TYPE_PROPERTY:
 | 
						||
      return "property";
 | 
						||
    case GI_INFO_TYPE_FIELD:
 | 
						||
      return "field";
 | 
						||
    case GI_INFO_TYPE_ARG:
 | 
						||
      return "arg";
 | 
						||
    case GI_INFO_TYPE_TYPE:
 | 
						||
      return "type";
 | 
						||
    case GI_INFO_TYPE_UNRESOLVED:
 | 
						||
      return "unresolved";
 | 
						||
    default:
 | 
						||
      return "unknown";
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
GIInfoType
 | 
						||
gi_typelib_blob_type_to_info_type (GITypelibBlobType blob_type)
 | 
						||
{
 | 
						||
  switch (blob_type)
 | 
						||
    {
 | 
						||
    case BLOB_TYPE_BOXED:
 | 
						||
      /* `BLOB_TYPE_BOXED` now always refers to a `StructBlob`, and
 | 
						||
       * `GIRegisteredTypeInfo` (the parent type of `GIStructInfo`) has a method
 | 
						||
       * for distinguishing whether the struct is a boxed type. So presenting
 | 
						||
       * `BLOB_TYPE_BOXED` as its own `GIBaseInfo` subclass is not helpful.
 | 
						||
       * See commit e28078c70cbf4a57c7dbd39626f43f9bd2674145 and
 | 
						||
       * https://gitlab.gnome.org/GNOME/glib/-/issues/3245. */
 | 
						||
      return GI_INFO_TYPE_STRUCT;
 | 
						||
    default:
 | 
						||
      return (GIInfoType) blob_type;
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * gi_repository_dup_default:
 | 
						||
 *
 | 
						||
 * Gets the singleton process-global default `GIRepository`.
 | 
						||
 *
 | 
						||
 * The singleton is needed for situations where you must coordinate between
 | 
						||
 * bindings and libraries which also need to interact with introspection which
 | 
						||
 * could affect the bindings. For example, a Python application using a
 | 
						||
 * GObject-based library through `GIRepository` to load plugins also written in
 | 
						||
 * Python.
 | 
						||
 *
 | 
						||
 * Returns: (transfer full): the global singleton repository
 | 
						||
 *
 | 
						||
 * Since: 2.86
 | 
						||
 */
 | 
						||
GIRepository *
 | 
						||
gi_repository_dup_default (void)
 | 
						||
{
 | 
						||
  static GIRepository *instance;
 | 
						||
 | 
						||
  if (g_once_init_enter (&instance))
 | 
						||
    {
 | 
						||
      GIRepository *repository = gi_repository_new ();
 | 
						||
      g_object_add_weak_pointer (G_OBJECT (repository), (gpointer *)&instance);
 | 
						||
      g_once_init_leave (&instance, repository);
 | 
						||
    }
 | 
						||
 | 
						||
  return g_object_ref (instance);
 | 
						||
}
 |