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