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