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