girepository: Refactor lookup code
[platform/upstream/gobject-introspection.git] / girepository / girepository.c
1 /* -*- Mode: C; c-file-style: "gnu"; -*- */
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 <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27
28 #include <glib.h>
29 #include <glib/gprintf.h>
30 #include <gmodule.h>
31 #include "girepository.h"
32 #include "gitypelib-internal.h"
33 #include "girepository-private.h"
34 #include "glib-compat.h"
35
36 #include "config.h"
37
38 static GStaticMutex globals_lock = G_STATIC_MUTEX_INIT;
39 static GIRepository *default_repository = NULL;
40 static GSList *search_path = NULL;
41 static GSList *override_search_path = NULL;
42
43 struct _GIRepositoryPrivate
44 {
45   GHashTable *typelibs; /* (string) namespace -> GITypelib */
46   GHashTable *lazy_typelibs; /* (string) namespace-version -> GITypelib */
47   GHashTable *info_by_gtype; /* GType -> GIBaseInfo */
48 };
49
50 G_DEFINE_TYPE (GIRepository, g_irepository, G_TYPE_OBJECT);
51
52 static void
53 g_irepository_init (GIRepository *repository)
54 {
55   repository->priv = G_TYPE_INSTANCE_GET_PRIVATE (repository, G_TYPE_IREPOSITORY,
56                                                   GIRepositoryPrivate);
57   repository->priv->typelibs
58     = g_hash_table_new_full (g_str_hash, g_str_equal,
59                              (GDestroyNotify) NULL,
60                              (GDestroyNotify) g_typelib_free);
61   repository->priv->lazy_typelibs
62     = g_hash_table_new (g_str_hash, g_str_equal);
63   repository->priv->info_by_gtype
64     = g_hash_table_new_full (g_direct_hash, g_direct_equal,
65                              (GDestroyNotify) NULL,
66                              (GDestroyNotify) g_base_info_unref);
67 }
68
69 static void
70 g_irepository_finalize (GObject *object)
71 {
72   GIRepository *repository = G_IREPOSITORY (object);
73
74   g_hash_table_destroy (repository->priv->typelibs);
75   g_hash_table_destroy (repository->priv->lazy_typelibs);
76   g_hash_table_destroy (repository->priv->info_by_gtype);
77
78   (* G_OBJECT_CLASS (g_irepository_parent_class)->finalize) (G_OBJECT (repository));
79 }
80
81 static void
82 g_irepository_class_init (GIRepositoryClass *class)
83 {
84   GObjectClass *gobject_class;
85
86   gobject_class = G_OBJECT_CLASS (class);
87
88   gobject_class->finalize = g_irepository_finalize;
89
90   g_type_class_add_private (class, sizeof (GIRepositoryPrivate));
91 }
92
93 static void
94 init_globals (void)
95 {
96   g_static_mutex_lock (&globals_lock);
97
98   if (default_repository == NULL)
99     {
100       default_repository = g_object_new (G_TYPE_IREPOSITORY, NULL);
101     }
102
103   if (search_path == NULL)
104     {
105       const char *libdir;
106       char *typelib_dir;
107       const gchar *type_lib_path_env;
108
109       /* This variable is intended to take precedence over both the default
110        * search path, as well as anything written into code with g_irepository_prepend_search_path.
111        */
112       type_lib_path_env = g_getenv ("GI_TYPELIB_PATH");
113
114       search_path = NULL;
115       override_search_path = NULL;
116       if (type_lib_path_env)
117         {
118           gchar **custom_dirs;
119           gchar **d;
120
121           custom_dirs = g_strsplit (type_lib_path_env, G_SEARCHPATH_SEPARATOR_S, 0);
122
123           d = custom_dirs;
124           while (*d)
125             {
126               override_search_path = g_slist_prepend (override_search_path, *d);
127               d++;
128             }
129
130           /* ownership of the array content was passed to the list */
131           g_free (custom_dirs);
132         }
133
134       if (override_search_path != NULL)
135         override_search_path = g_slist_reverse (override_search_path);
136
137       libdir = GOBJECT_INTROSPECTION_LIBDIR;
138
139       typelib_dir = g_build_filename (libdir, "girepository-1.0", NULL);
140
141       search_path = g_slist_prepend (search_path, typelib_dir);
142
143       search_path = g_slist_reverse (search_path);
144     }
145
146   g_static_mutex_unlock (&globals_lock);
147 }
148
149 void
150 g_irepository_prepend_search_path (const char *directory)
151 {
152   init_globals ();
153   search_path = g_slist_prepend (search_path, g_strdup (directory));
154 }
155
156 /**
157  * g_irepository_get_search_path:
158  *
159  * Returns the search path the GIRepository will use when looking for typelibs.
160  * The string is internal to GIRespository and should not be freed, nor should
161  * the elements.
162  *
163  * Return value: (element-type filename) (transfer none): list of strings
164  */
165 GSList *
166 g_irepository_get_search_path (void)
167 {
168   return search_path;
169 }
170
171 static GSList *
172 build_search_path_with_overrides (void)
173 {
174   GSList *result;
175
176   init_globals ();
177
178   if (override_search_path != NULL)
179     {
180       result = g_slist_copy (override_search_path);
181       g_slist_last (result)->next = g_slist_copy (search_path);
182     }
183   else
184     result = g_slist_copy (search_path);
185   return result;
186 }
187
188 static char *
189 build_typelib_key (const char *name, const char *source)
190 {
191   GString *str = g_string_new (name);
192   g_string_append_c (str, '\0');
193   g_string_append (str, source);
194   return g_string_free (str, FALSE);
195 }
196
197 static char **
198 get_typelib_dependencies (GITypelib *typelib)
199 {
200   Header *header;
201   const char *dependencies_glob;
202
203   header = (Header *)typelib->data;
204
205   if (header->dependencies == 0)
206     return NULL;
207
208   dependencies_glob = g_typelib_get_string (typelib, header->dependencies);
209   return g_strsplit (dependencies_glob, "|", 0);
210 }
211
212 static GIRepository *
213 get_repository (GIRepository *repository)
214 {
215   init_globals ();
216
217   if (repository != NULL)
218     return repository;
219   else
220     return default_repository;
221 }
222
223 static GITypelib *
224 check_version_conflict (GITypelib *typelib,
225                         const gchar *namespace,
226                         const gchar *expected_version,
227                         char       **version_conflict)
228 {
229   Header *header;
230   const char *loaded_version;
231
232   if (expected_version == NULL)
233     {
234       if (version_conflict)
235         *version_conflict = NULL;
236       return typelib;
237     }
238
239   header = (Header*)typelib->data;
240   loaded_version = g_typelib_get_string (typelib, header->nsversion);
241   g_assert (loaded_version != NULL);
242
243   if (strcmp (expected_version, loaded_version) != 0)
244     {
245       if (version_conflict)
246         *version_conflict = (char*)loaded_version;
247       return NULL;
248     }
249   if (version_conflict)
250     *version_conflict = NULL;
251   return typelib;
252 }
253
254 static GITypelib *
255 get_registered_status (GIRepository *repository,
256                        const char   *namespace,
257                        const char   *version,
258                        gboolean      allow_lazy,
259                        gboolean     *lazy_status,
260                        char        **version_conflict)
261 {
262   GITypelib *typelib;
263   repository = get_repository (repository);
264   if (lazy_status)
265     *lazy_status = FALSE;
266   typelib = g_hash_table_lookup (repository->priv->typelibs, namespace);
267   if (typelib)
268     return check_version_conflict (typelib, namespace, version, version_conflict);
269   typelib = g_hash_table_lookup (repository->priv->lazy_typelibs, namespace);
270   if (!typelib)
271     return NULL;
272   if (lazy_status)
273     *lazy_status = TRUE;
274   if (!allow_lazy)
275     return NULL;
276   return check_version_conflict (typelib, namespace, version, version_conflict);
277 }
278
279 static GITypelib *
280 get_registered (GIRepository *repository,
281                 const char   *namespace,
282                 const char   *version)
283 {
284   return get_registered_status (repository, namespace, version, TRUE, NULL, NULL);
285 }
286
287 static gboolean
288 load_dependencies_recurse (GIRepository *repository,
289                            GITypelib     *typelib,
290                            GError      **error)
291 {
292   char **dependencies;
293
294   dependencies = get_typelib_dependencies (typelib);
295
296   if (dependencies != NULL)
297     {
298       int i;
299
300       for (i = 0; dependencies[i]; i++)
301         {
302           char *dependency = dependencies[i];
303           const char *last_dash;
304           char *dependency_namespace;
305           const char *dependency_version;
306
307           last_dash = strrchr (dependency, '-');
308           dependency_namespace = g_strndup (dependency, last_dash - dependency);
309           dependency_version = last_dash+1;
310
311           if (!g_irepository_require (repository, dependency_namespace, dependency_version,
312                                       0, error))
313             {
314               g_free (dependency_namespace);
315               g_strfreev (dependencies);
316               return FALSE;
317             }
318           g_free (dependency_namespace);
319         }
320       g_strfreev (dependencies);
321     }
322   return TRUE;
323 }
324
325 static const char *
326 register_internal (GIRepository *repository,
327                    const char   *source,
328                    gboolean      lazy,
329                    GITypelib     *typelib,
330                    GError      **error)
331 {
332   Header *header;
333   const gchar *namespace;
334   const gchar *version;
335
336   g_return_val_if_fail (typelib != NULL, FALSE);
337
338   header = (Header *)typelib->data;
339
340   g_return_val_if_fail (header != NULL, FALSE);
341
342   namespace = g_typelib_get_string (typelib, header->namespace);
343   version = g_typelib_get_string (typelib, header->nsversion);
344
345   if (lazy)
346     {
347       g_assert (!g_hash_table_lookup (repository->priv->lazy_typelibs,
348                                       namespace));
349       g_hash_table_insert (repository->priv->lazy_typelibs,
350                            build_typelib_key (namespace, source), (void *)typelib);
351     }
352   else
353     {
354       gpointer value;
355       char *key;
356
357       /* First, try loading all the dependencies */
358       if (!load_dependencies_recurse (repository, typelib, error))
359         return NULL;
360
361       /* Check if we are transitioning from lazily loaded state */
362       if (g_hash_table_lookup_extended (repository->priv->lazy_typelibs,
363                                         namespace,
364                                         (gpointer)&key, &value))
365         g_hash_table_remove (repository->priv->lazy_typelibs, key);
366       else
367         key = build_typelib_key (namespace, source);
368
369       g_hash_table_insert (repository->priv->typelibs, key, (void *)typelib);
370     }
371
372   return namespace;
373 }
374
375 /**
376  * g_irepository_get_dependencies:
377  * @repository: (allow-none): A #GIRepository, may be %NULL for the default
378  * @namespace_: Namespace of interest
379  *
380  * Return an array of all (transitive) dependencies for namespace
381  * @namespace_, including version.  The returned strings are of the
382  * form <code>namespace-version</code>.
383  *
384  * Note: The namespace must have already been loaded using a function
385  * such as #g_irepository_require before calling this function.
386  *
387  * Returns: (transfer full): Zero-terminated string array of versioned dependencies
388  */
389 char **
390 g_irepository_get_dependencies (GIRepository *repository,
391                                 const char *namespace)
392 {
393   GITypelib *typelib;
394
395   g_return_val_if_fail (namespace != NULL, NULL);
396
397   repository = get_repository (repository);
398
399   typelib = get_registered (repository, namespace, NULL);
400   g_return_val_if_fail (typelib != NULL, NULL);
401
402   return get_typelib_dependencies (typelib);
403 }
404
405 const char *
406 g_irepository_load_typelib (GIRepository *repository,
407                             GITypelib     *typelib,
408                             GIRepositoryLoadFlags flags,
409                             GError      **error)
410 {
411   Header *header;
412   const char *namespace;
413   const char *nsversion;
414   gboolean allow_lazy = flags & G_IREPOSITORY_LOAD_FLAG_LAZY;
415   gboolean is_lazy;
416   char *version_conflict;
417
418   repository = get_repository (repository);
419
420   header = (Header *) typelib->data;
421   namespace = g_typelib_get_string (typelib, header->namespace);
422   nsversion = g_typelib_get_string (typelib, header->nsversion);
423
424   if (get_registered_status (repository, namespace, nsversion, allow_lazy,
425                              &is_lazy, &version_conflict))
426     {
427       if (version_conflict != NULL)
428         {
429           g_set_error (error, G_IREPOSITORY_ERROR,
430                        G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
431                        "Attempting to load namespace '%s', version '%s', but '%s' is already loaded",
432                        namespace, nsversion, version_conflict);
433           return NULL;
434         }
435       return namespace;
436     }
437   return register_internal (repository, "<builtin>",
438                             allow_lazy, typelib, error);
439 }
440
441 /**
442  * g_irepository_is_registered:
443  * @repository: (allow-none): A #GIRepository, may be %NULL for the default
444  * @namespace_: Namespace of interest
445  * @version: (allow-none): Required version, may be %NULL for latest
446  *
447  * Check whether a particular namespace (and optionally, a specific
448  * version thereof) is currently loaded.  This function is likely to
449  * only be useful in unusual circumstances; in order to act upon
450  * metadata in the namespace, you should call #g_irepository_require
451  * instead which will ensure the namespace is loaded, and return as
452  * quickly as this function will if it has already been loaded.
453  *
454  * Returns: %TRUE if namespace-version is loaded, %FALSE otherwise
455  */
456 gboolean
457 g_irepository_is_registered (GIRepository *repository,
458                              const gchar *namespace,
459                              const gchar *version)
460 {
461   repository = get_repository (repository);
462   return get_registered (repository, namespace, version) != NULL;
463 }
464
465 /**
466  * g_irepository_get_default:
467  *
468  * Returns the singleton process-global default #GIRepository.  It is
469  * not currently supported to have multiple repositories in a
470  * particular process, but this function is provided in the unlikely
471  * eventuality that it would become possible, and as a convenience for
472  * higher level language bindings to conform to the GObject method
473  * call conventions.
474
475  * All methods on #GIRepository also accept %NULL as an instance
476  * parameter to mean this default repository, which is usually more
477  * convenient for C.
478  *
479  * Returns: (transfer none): The global singleton #GIRepository
480  */
481 GIRepository *
482 g_irepository_get_default (void)
483 {
484   return get_repository (NULL);
485 }
486
487 /**
488  * g_irepository_get_n_infos:
489  * @repository: (allow-none): A #GIRepository, may be %NULL for the default
490  * @namespace_: Namespace to inspect
491  *
492  * This function returns the number of metadata entries in
493  * given namespace @namespace_.  The namespace must have
494  * already been loaded before calling this function.
495  *
496  * Returns: number of metadata entries
497  */
498 gint
499 g_irepository_get_n_infos (GIRepository *repository,
500                            const gchar  *namespace)
501 {
502   GITypelib *typelib;
503   gint n_interfaces = 0;
504
505   g_return_val_if_fail (namespace != NULL, -1);
506
507   repository = get_repository (repository);
508
509   typelib = get_registered (repository, namespace, NULL);
510
511   g_return_val_if_fail (typelib != NULL, -1);
512
513   n_interfaces = ((Header *)typelib->data)->n_local_entries;
514
515   return n_interfaces;
516 }
517
518 /**
519  * g_irepository_get_info:
520  * @repository: (allow-none): A #GIRepository, may be %NULL for the default
521  * @namespace_: Namespace to inspect
522  * @index: Offset into namespace metadata for entry
523  *
524  * This function returns a particular metadata entry in the
525  * given namespace @namespace_.  The namespace must have
526  * already been loaded before calling this function.
527  *
528  * Returns: (transfer full): #GIBaseInfo containing metadata
529  */
530 GIBaseInfo *
531 g_irepository_get_info (GIRepository *repository,
532                         const gchar  *namespace,
533                         gint          index)
534 {
535   GITypelib *typelib;
536   DirEntry *entry;
537
538   g_return_val_if_fail (namespace != NULL, NULL);
539
540   repository = get_repository (repository);
541
542   typelib = get_registered (repository, namespace, NULL);
543
544   g_return_val_if_fail (typelib != NULL, NULL);
545
546   entry = g_typelib_get_dir_entry (typelib, index);
547   if (entry == NULL)
548     return NULL;
549   return _g_info_new_full (entry->blob_type,
550                            repository,
551                            NULL, typelib, entry->offset);
552 }
553
554 typedef struct {
555   GIRepository *repository;
556   GType type;
557
558   gboolean fastpass;
559   GITypelib *result_typelib;
560   DirEntry *result;
561 } FindByGTypeData;
562
563 static void
564 find_by_gtype_foreach (gpointer key,
565                        gpointer value,
566                        gpointer datap)
567 {
568   GITypelib *typelib = (GITypelib*)value;
569   FindByGTypeData *data = datap;
570
571   if (data->result != NULL)
572     return;
573
574   data->result = g_typelib_get_dir_entry_by_gtype (typelib, data->fastpass, data->type);
575   if (data->result)
576     data->result_typelib = typelib;
577 }
578
579 /**
580  * g_irepository_find_by_gtype:
581  * @repository: (allow-none): A #GIRepository, may be %NULL for the default
582  * @gtype: GType to search for
583  *
584  * Searches all loaded namespaces for a particular #GType.  Note that
585  * in order to locate the metadata, the namespace corresponding to
586  * the type must first have been loaded.  There is currently no
587  * mechanism for determining the namespace which corresponds to an
588  * arbitrary GType - thus, this function will operate most reliably
589  * when you know the GType to originate from be from a loaded namespace.
590  *
591  * Returns: (transfer full): #GIBaseInfo representing metadata about @type, or %NULL
592  */
593 GIBaseInfo *
594 g_irepository_find_by_gtype (GIRepository *repository,
595                              GType         gtype)
596 {
597   FindByGTypeData data;
598   GIBaseInfo *cached;
599
600   repository = get_repository (repository);
601
602   cached = g_hash_table_lookup (repository->priv->info_by_gtype,
603                                 (gpointer)gtype);
604
605   if (cached != NULL)
606     return g_base_info_ref (cached);
607
608   data.repository = repository;
609   data.fastpass = TRUE;
610   data.type = gtype;
611   data.result_typelib = NULL;
612   data.result = NULL;
613
614   g_hash_table_foreach (repository->priv->typelibs, find_by_gtype_foreach, &data);
615   if (data.result == NULL)
616     g_hash_table_foreach (repository->priv->lazy_typelibs, find_by_gtype_foreach, &data);
617
618   /* We do two passes; see comment in find_interface */
619   if (data.result == NULL)
620     {
621       data.fastpass = FALSE;
622       g_hash_table_foreach (repository->priv->typelibs, find_by_gtype_foreach, &data);
623     }
624   if (data.result == NULL)
625     g_hash_table_foreach (repository->priv->lazy_typelibs, find_by_gtype_foreach, &data);
626
627   if (data.result != NULL)
628     {
629       cached = _g_info_new_full (data.result->blob_type,
630                                  repository,
631                                  NULL, data.result_typelib, data.result->offset);
632
633       g_hash_table_insert (repository->priv->info_by_gtype,
634                            (gpointer) gtype,
635                            g_base_info_ref (cached));
636       return cached;
637     }
638   return NULL;
639 }
640
641 /**
642  * g_irepository_find_by_name:
643  * @repository: (allow-none): A #GIRepository, may be %NULL for the default
644  * @namespace_: Namespace which will be searched
645  * @name: Entry name to find
646  *
647  * Searches for a particular entry in a namespace.  Before calling
648  * this function for a particular namespace, you must call
649  * #g_irepository_require once to load the namespace, or otherwise
650  * ensure the namespace has already been loaded.
651  *
652  * Returns: (transfer full): #GIBaseInfo representing metadata about @name, or %NULL
653  */
654 GIBaseInfo *
655 g_irepository_find_by_name (GIRepository *repository,
656                             const gchar  *namespace,
657                             const gchar  *name)
658 {
659   GITypelib *typelib;
660   DirEntry *entry;
661
662   g_return_val_if_fail (namespace != NULL, NULL);
663
664   repository = get_repository (repository);
665   typelib = get_registered (repository, namespace, NULL);
666   g_return_val_if_fail (typelib != NULL, NULL);
667
668   entry = g_typelib_get_dir_entry_by_name (typelib, name);
669   if (entry == NULL)
670     return NULL;
671   return _g_info_new_full (entry->blob_type,
672                            repository,
673                            NULL, typelib, entry->offset);
674 }
675
676 static void
677 collect_namespaces (gpointer key,
678                     gpointer value,
679                     gpointer data)
680 {
681   GList **list = data;
682
683   *list = g_list_append (*list, key);
684 }
685
686 /**
687  * g_irepository_get_loaded_namespaces:
688  * @repository: (allow-none): A #GIRepository, may be %NULL for the default
689  *
690  * Return the list of currently loaded namespaces.
691  *
692  * Returns: (utf8) (transfer full): List of namespaces
693  */
694 gchar **
695 g_irepository_get_loaded_namespaces (GIRepository *repository)
696 {
697   GList *l, *list = NULL;
698   gchar **names;
699   gint i;
700
701   repository = get_repository (repository);
702
703   g_hash_table_foreach (repository->priv->typelibs, collect_namespaces, &list);
704   g_hash_table_foreach (repository->priv->lazy_typelibs, collect_namespaces, &list);
705
706   names = g_malloc0 (sizeof (gchar *) * (g_list_length (list) + 1));
707   i = 0;
708   for (l = list; l; l = l->next)
709     names[i++] = g_strdup (l->data);
710   g_list_free (list);
711
712   return names;
713 }
714
715 /**
716  * g_irepository_get_version:
717  * @repository: (allow-none): A #GIRepository, may be %NULL for the default
718  * @namespace_: Namespace to inspect
719  *
720  * This function returns the loaded version associated with the given
721  * namespace @namespace_.
722  *
723  * Note: The namespace must have already been loaded using a function
724  * such as #g_irepository_require before calling this function.
725  *
726  * Returns: Loaded version
727  */
728 const gchar *
729 g_irepository_get_version (GIRepository *repository,
730                            const gchar  *namespace)
731 {
732   GITypelib *typelib;
733   Header *header;
734
735   g_return_val_if_fail (namespace != NULL, NULL);
736
737   repository = get_repository (repository);
738
739   typelib = get_registered (repository, namespace, NULL);
740
741   g_return_val_if_fail (typelib != NULL, NULL);
742
743   header = (Header *) typelib->data;
744   return g_typelib_get_string (typelib, header->nsversion);
745 }
746
747 /**
748  * g_irepository_get_shared_library:
749  * @repository: (allow-none): A #GIRepository, may be %NULL for the default
750  * @namespace_: Namespace to inspect
751  *
752  * This function returns the full path to the shared C library
753  * associated with the given namespace @namespace_. There may be no
754  * shared library path associated, in which case this function will
755  * return %NULL.
756  *
757  * Note: The namespace must have already been loaded using a function
758  * such as #g_irepository_require before calling this function.
759  *
760  * Returns: Full path to shared library, or %NULL if none associated
761  */
762 const gchar *
763 g_irepository_get_shared_library (GIRepository *repository,
764                                   const gchar  *namespace)
765 {
766   GITypelib *typelib;
767   Header *header;
768
769   g_return_val_if_fail (namespace != NULL, NULL);
770
771   repository = get_repository (repository);
772
773   typelib = get_registered (repository, namespace, NULL);
774
775   g_return_val_if_fail (typelib != NULL, NULL);
776
777   header = (Header *) typelib->data;
778   if (header->shared_library)
779     return g_typelib_get_string (typelib, header->shared_library);
780   else
781     return NULL;
782 }
783
784 /**
785  * g_irepository_get_c_prefix
786  * @repository: (allow-none): A #GIRepository, may be %NULL for the default
787  * @namespace_: Namespace to inspect
788  *
789  * This function returns the "C prefix", or the C level namespace
790  * associated with the given introspection namespace.  Each C symbol
791  * starts with this prefix, as well each #GType in the library.
792  *
793  * Note: The namespace must have already been loaded using a function
794  * such as #g_irepository_require before calling this function.
795  *
796  * Returns: C namespace prefix, or %NULL if none associated
797  */
798 const gchar *
799 g_irepository_get_c_prefix (GIRepository *repository,
800                             const gchar  *namespace_)
801 {
802   GITypelib *typelib;
803   Header *header;
804
805   g_return_val_if_fail (namespace_ != NULL, NULL);
806
807   repository = get_repository (repository);
808
809   typelib = get_registered (repository, namespace_, NULL);
810
811   g_return_val_if_fail (typelib != NULL, NULL);
812
813   header = (Header *) typelib->data;
814   if (header->shared_library)
815     return g_typelib_get_string (typelib, header->c_prefix);
816   else
817     return NULL;
818 }
819
820 /**
821  * g_irepository_get_typelib_path
822  * @repository: (allow-none): Repository, may be %NULL for the default
823  * @namespace_: GI namespace to use, e.g. "Gtk"
824  *
825  * If namespace @namespace_ is loaded, return the full path to the
826  * .typelib file it was loaded from.  If the typelib for
827  * namespace @namespace_ was included in a shared library, return
828  * the special string "$lt;builtin$gt;".
829  *
830  * Returns: Filesystem path (or $lt;builtin$gt;) if successful, %NULL if namespace is not loaded
831  */
832
833 const gchar *
834 g_irepository_get_typelib_path (GIRepository *repository,
835                                 const gchar  *namespace)
836 {
837   gpointer orig_key, value;
838
839   repository = get_repository (repository);
840
841   if (!g_hash_table_lookup_extended (repository->priv->typelibs, namespace,
842                                      &orig_key, &value))
843     {
844       if (!g_hash_table_lookup_extended (repository->priv->lazy_typelibs, namespace,
845                                          &orig_key, &value))
846
847         return NULL;
848     }
849   return ((char*)orig_key) + strlen ((char *) orig_key) + 1;
850 }
851
852 /* This simple search function looks for a specified namespace-version;
853    it's faster than the full directory listing required for latest version. */
854 static GMappedFile *
855 find_namespace_version (const gchar  *namespace,
856                         const gchar  *version,
857                         GSList       *search_path,
858                         gchar       **path_ret)
859 {
860   GSList *ldir;
861   GError *error = NULL;
862   GMappedFile *mfile = NULL;
863   char *fname;
864
865   fname = g_strdup_printf ("%s-%s.typelib", namespace, version);
866
867   for (ldir = search_path; ldir; ldir = ldir->next)
868     {
869       char *path = g_build_filename (ldir->data, fname, NULL);
870
871       mfile = g_mapped_file_new (path, FALSE, &error);
872       if (error)
873         {
874           g_free (path);
875           g_clear_error (&error);
876           continue;
877         }
878       *path_ret = path;
879       break;
880     }
881   g_free (fname);
882   return mfile;
883 }
884
885 static gboolean
886 parse_version (const char *version,
887                int *major,
888                int *minor)
889 {
890   const char *dot;
891   char *end;
892
893   *major = strtol (version, &end, 10);
894   dot = strchr (version, '.');
895   if (dot == NULL)
896     {
897       *minor = 0;
898       return TRUE;
899     }
900   if (dot != end)
901     return FALSE;
902   *minor = strtol (dot+1, &end, 10);
903   if (end != (version + strlen (version)))
904     return FALSE;
905   return TRUE;
906 }
907
908 static int
909 compare_version (const char *v1,
910                  const char *v2)
911 {
912   gboolean success;
913   int v1_major, v1_minor;
914   int v2_major, v2_minor;
915
916   success = parse_version (v1, &v1_major, &v1_minor);
917   g_assert (success);
918
919   success = parse_version (v2, &v2_major, &v2_minor);
920   g_assert (success);
921
922   if (v1_major > v2_major)
923     return 1;
924   else if (v2_major > v1_major)
925     return -1;
926   else if (v1_minor > v2_minor)
927     return 1;
928   else if (v2_minor > v1_minor)
929     return -1;
930   return 0;
931 }
932
933 struct NamespaceVersionCandidadate
934 {
935   GMappedFile *mfile;
936   int path_index;
937   char *path;
938   char *version;
939 };
940
941 static int
942 compare_candidate_reverse (struct NamespaceVersionCandidadate *c1,
943                            struct NamespaceVersionCandidadate *c2)
944 {
945   int result = compare_version (c1->version, c2->version);
946   /* First, check the version */
947   if (result > 0)
948     return -1;
949   else if (result < 0)
950     return 1;
951   else
952     {
953       /* Now check the path index, which says how early in the search path
954        * we found it.  This ensures that of equal version targets, we
955        * pick the earlier one.
956        */
957       if (c1->path_index == c2->path_index)
958         return 0;
959       else if (c1->path_index > c2->path_index)
960         return 1;
961       else
962         return -1;
963     }
964 }
965
966 static void
967 free_candidate (struct NamespaceVersionCandidadate *candidate)
968 {
969   g_mapped_file_unref (candidate->mfile);
970   g_free (candidate->path);
971   g_free (candidate->version);
972   g_slice_free (struct NamespaceVersionCandidadate, candidate);
973 }
974
975 static GSList *
976 enumerate_namespace_versions (const gchar *namespace,
977                               GSList      *search_path)
978 {
979   GSList *candidates = NULL;
980   GHashTable *found_versions = g_hash_table_new (g_str_hash, g_str_equal);
981   char *namespace_dash;
982   char *namespace_typelib;
983   GSList *ldir;
984   GError *error = NULL;
985   int index;
986
987   namespace_dash = g_strdup_printf ("%s-", namespace);
988   namespace_typelib = g_strdup_printf ("%s.typelib", namespace);
989
990   index = 0;
991   for (ldir = search_path; ldir; ldir = ldir->next)
992     {
993       GDir *dir;
994       const char *dirname;
995       const char *entry;
996
997       dirname = (const char*)ldir->data;
998       dir = g_dir_open (dirname, 0, NULL);
999       if (dir == NULL)
1000         continue;
1001       while ((entry = g_dir_read_name (dir)) != NULL)
1002         {
1003           GMappedFile *mfile;
1004           char *path, *version;
1005           struct NamespaceVersionCandidadate *candidate;
1006
1007           if (!g_str_has_suffix (entry, ".typelib"))
1008             continue;
1009
1010           if (g_str_has_prefix (entry, namespace_dash))
1011             {
1012               const char *last_dash;
1013               const char *name_end;
1014               int major, minor;
1015
1016               name_end = strrchr (entry, '.');
1017               last_dash = strrchr (entry, '-');
1018               version = g_strndup (last_dash+1, name_end-(last_dash+1));
1019               if (!parse_version (version, &major, &minor))
1020                 {
1021                   g_free (version);
1022                   continue;
1023                 }
1024             }
1025           else
1026             continue;
1027
1028           if (g_hash_table_lookup (found_versions, version) != NULL)
1029             {
1030               g_free (version);
1031               continue;
1032             }
1033           g_hash_table_insert (found_versions, version, version);
1034
1035           path = g_build_filename (dirname, entry, NULL);
1036           mfile = g_mapped_file_new (path, FALSE, &error);
1037           if (mfile == NULL)
1038             {
1039               g_free (path);
1040               g_free (version);
1041               g_clear_error (&error);
1042               continue;
1043             }
1044           candidate = g_slice_new0 (struct NamespaceVersionCandidadate);
1045           candidate->mfile = mfile;
1046           candidate->path_index = index;
1047           candidate->path = path;
1048           candidate->version = version;
1049           candidates = g_slist_prepend (candidates, candidate);
1050         }
1051       g_dir_close (dir);
1052       index++;
1053     }
1054
1055   g_free (namespace_dash);
1056   g_free (namespace_typelib);
1057   g_hash_table_destroy (found_versions);
1058
1059   return candidates;
1060 }
1061
1062 static GMappedFile *
1063 find_namespace_latest (const gchar  *namespace,
1064                        GSList       *search_path,
1065                        gchar       **version_ret,
1066                        gchar       **path_ret)
1067 {
1068   GSList *candidates;
1069   GMappedFile *result = NULL;
1070
1071   *version_ret = NULL;
1072   *path_ret = NULL;
1073
1074   candidates = enumerate_namespace_versions (namespace, search_path);
1075
1076   if (candidates != NULL)
1077     {
1078       struct NamespaceVersionCandidadate *elected;
1079       candidates = g_slist_sort (candidates, (GCompareFunc) compare_candidate_reverse);
1080
1081       elected = (struct NamespaceVersionCandidadate *) candidates->data;
1082       /* Remove the elected one so we don't try to free its contents */
1083       candidates = g_slist_delete_link (candidates, candidates);
1084
1085       result = elected->mfile;
1086       *path_ret = elected->path;
1087       *version_ret = elected->version;
1088       g_slice_free (struct NamespaceVersionCandidadate, elected); /* just free the container */
1089       g_slist_foreach (candidates, (GFunc) free_candidate, NULL);
1090       g_slist_free (candidates);
1091     }
1092   return result;
1093 }
1094
1095 /**
1096  * g_irepository_enumerate_versions:
1097  * @repository: (allow-none): the repository
1098  * @namespace_: GI namespace, e.g. "Gtk"
1099  *
1100  * Obtain an unordered list of versions (either currently loaded or
1101  * available) for @namespace_ in this @repository.
1102  *
1103  * Returns: (element-type utf8) (transfer full): the array of versions.
1104  */
1105 GList *
1106 g_irepository_enumerate_versions (GIRepository *repository,
1107                          const gchar  *namespace_)
1108 {
1109   GList *ret = NULL;
1110   GSList *search_path;
1111   GSList *candidates, *link;
1112   const gchar *loaded_version;
1113
1114   search_path = build_search_path_with_overrides ();
1115   candidates = enumerate_namespace_versions (namespace_, search_path);
1116   g_slist_free (search_path);
1117
1118   for (link = candidates; link; link = link->next)
1119     {
1120       struct NamespaceVersionCandidadate *candidate = link->data;
1121       ret = g_list_prepend (ret, g_strdup (candidate->version));
1122       free_candidate (candidate);
1123     }
1124   g_slist_free (candidates);
1125
1126   /* The currently loaded version of a namespace is also part of the
1127    * available versions, as it could have been loaded using
1128    * require_private().
1129    */
1130   if (g_irepository_is_registered (repository, namespace_, NULL))
1131     {
1132       loaded_version = g_irepository_get_version (repository, namespace_);
1133       if (loaded_version && !g_list_find_custom (ret, loaded_version, g_str_equal))
1134         ret = g_list_prepend (ret, g_strdup (loaded_version));
1135     }
1136
1137   return ret;
1138 }
1139
1140 static GITypelib *
1141 require_internal (GIRepository  *repository,
1142                   const gchar   *namespace,
1143                   const gchar   *version,
1144                   GIRepositoryLoadFlags flags,
1145                   GSList        *search_path,
1146                   GError       **error)
1147 {
1148   GMappedFile *mfile;
1149   GITypelib *ret = NULL;
1150   Header *header;
1151   GITypelib *typelib = NULL;
1152   const gchar *typelib_namespace, *typelib_version;
1153   gboolean allow_lazy = (flags & G_IREPOSITORY_LOAD_FLAG_LAZY) > 0;
1154   gboolean is_lazy;
1155   char *version_conflict = NULL;
1156   char *path = NULL;
1157   char *tmp_version = NULL;
1158
1159   g_return_val_if_fail (namespace != NULL, FALSE);
1160
1161   repository = get_repository (repository);
1162
1163   typelib = get_registered_status (repository, namespace, version, allow_lazy,
1164                                    &is_lazy, &version_conflict);
1165   if (typelib)
1166     return typelib;
1167
1168   if (version_conflict != NULL)
1169     {
1170       g_set_error (error, G_IREPOSITORY_ERROR,
1171                    G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
1172                    "Requiring namespace '%s' version '%s', but '%s' is already loaded",
1173                    namespace, version, version_conflict);
1174       return NULL;
1175     }
1176
1177   if (version != NULL)
1178     {
1179       mfile = find_namespace_version (namespace, version,
1180                                       search_path, &path);
1181       tmp_version = g_strdup (version);
1182     }
1183   else
1184     {
1185       mfile = find_namespace_latest (namespace, search_path,
1186                                      &tmp_version, &path);
1187     }
1188
1189   if (mfile == NULL)
1190     {
1191       if (version != NULL)
1192         g_set_error (error, G_IREPOSITORY_ERROR,
1193                      G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
1194                      "Typelib file for namespace '%s', version '%s' not found",
1195                      namespace, version);
1196       else
1197         g_set_error (error, G_IREPOSITORY_ERROR,
1198                      G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
1199                      "Typelib file for namespace '%s' (any version) not found",
1200                      namespace);
1201       goto out;
1202     }
1203
1204   {
1205     GError *temp_error = NULL;
1206     typelib = g_typelib_new_from_mapped_file (mfile, &temp_error);
1207     if (!typelib)
1208       {
1209         g_set_error (error, G_IREPOSITORY_ERROR,
1210                      G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
1211                      "Failed to load typelib file '%s' for namespace '%s': %s",
1212                      path, namespace, temp_error->message);
1213         g_clear_error (&temp_error);
1214         goto out;
1215       }
1216   }
1217   header = (Header *) typelib->data;
1218   typelib_namespace = g_typelib_get_string (typelib, header->namespace);
1219   typelib_version = g_typelib_get_string (typelib, header->nsversion);
1220
1221   if (strcmp (typelib_namespace, namespace) != 0)
1222     {
1223       g_set_error (error, G_IREPOSITORY_ERROR,
1224                    G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH,
1225                    "Typelib file %s for namespace '%s' contains "
1226                    "namespace '%s' which doesn't match the file name",
1227                    path, namespace, typelib_namespace);
1228       g_typelib_free (typelib);
1229       goto out;
1230     }
1231   if (version != NULL && strcmp (typelib_version, version) != 0)
1232     {
1233       g_set_error (error, G_IREPOSITORY_ERROR,
1234                    G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH,
1235                    "Typelib file %s for namespace '%s' contains "
1236                    "version '%s' which doesn't match the expected version '%s'",
1237                    path, namespace, typelib_version, version);
1238       g_typelib_free (typelib);
1239       goto out;
1240     }
1241
1242   if (!register_internal (repository, path, allow_lazy,
1243                           typelib, error))
1244     {
1245       g_typelib_free (typelib);
1246       goto out;
1247     }
1248   ret = typelib;
1249  out:
1250   g_free (tmp_version);
1251   g_free (path);
1252   return ret;
1253 }
1254
1255 /**
1256  * g_irepository_require:
1257  * @repository: (allow-none): Repository, may be %NULL for the default
1258  * @namespace_: GI namespace to use, e.g. "Gtk"
1259  * @version: (allow-none): Version of namespace, may be %NULL for latest
1260  * @flags: Set of %GIRepositoryLoadFlags, may be 0
1261  * @error: a #GError.
1262  *
1263  * Force the namespace @namespace_ to be loaded if it isn't already.
1264  * If @namespace_ is not loaded, this function will search for a
1265  * ".typelib" file using the repository search path.  In addition, a
1266  * version @version of namespace may be specified.  If @version is
1267  * not specified, the latest will be used.
1268  *
1269  * Returns: (transfer none): a pointer to the #GITypelib if successful, %NULL otherwise
1270  */
1271 GITypelib *
1272 g_irepository_require (GIRepository  *repository,
1273                        const gchar   *namespace,
1274                        const gchar   *version,
1275                        GIRepositoryLoadFlags flags,
1276                        GError       **error)
1277 {
1278   GSList *search_path;
1279   GITypelib *typelib;
1280
1281   search_path = build_search_path_with_overrides ();
1282   typelib = require_internal (repository, namespace, version, flags,
1283                               search_path, error);
1284   g_slist_free (search_path);
1285
1286   return typelib;
1287 }
1288
1289 /**
1290  * g_irepository_require_private:
1291  * @repository: (allow-none): Repository, may be %NULL for the default
1292  * @typelib_dir: Private directory where to find the requested typelib
1293  * @namespace_: GI namespace to use, e.g. "Gtk"
1294  * @version: (allow-none): Version of namespace, may be %NULL for latest
1295  * @flags: Set of %GIRepositoryLoadFlags, may be 0
1296  * @error: a #GError.
1297  *
1298  * Force the namespace @namespace_ to be loaded if it isn't already.
1299  * If @namespace_ is not loaded, this function will search for a
1300  * ".typelib" file within the private directory only. In addition, a
1301  * version @version of namespace should be specified.  If @version is
1302  * not specified, the latest will be used.
1303  *
1304  * Returns: (transfer none): a pointer to the #GITypelib if successful, %NULL otherwise
1305  */
1306 GITypelib *
1307 g_irepository_require_private (GIRepository  *repository,
1308                                const gchar   *typelib_dir,
1309                                const gchar   *namespace,
1310                                const gchar   *version,
1311                                GIRepositoryLoadFlags flags,
1312                                GError       **error)
1313 {
1314   GSList search_path = { (gpointer) typelib_dir, NULL };
1315
1316   return require_internal (repository, namespace, version, flags,
1317                            &search_path, error);
1318 }
1319
1320 static gboolean
1321 g_irepository_introspect_cb (const char *option_name,
1322                              const char *value,
1323                              gpointer data,
1324                              GError **error)
1325 {
1326   GError *tmp_error = NULL;
1327   gboolean ret = g_irepository_dump (value, &tmp_error);
1328   if (!ret)
1329     {
1330       g_error ("Failed to extract GType data: %s",
1331                tmp_error->message);
1332       exit (1);
1333     }
1334   exit (0);
1335 }
1336
1337 static const GOptionEntry introspection_args[] = {
1338   { "introspect-dump", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK,
1339     g_irepository_introspect_cb, "Dump introspection information",
1340     "infile.txt,outfile.xml" },
1341   { NULL }
1342 };
1343
1344 /**
1345  * g_irepository_get_option_group: (skip)
1346  *
1347  * Obtain the option group for girepository, it's used
1348  * by the dumper and for programs that wants to provide
1349  * introspection information
1350  *
1351  * Returns: (transfer full): the option group
1352  */
1353 GOptionGroup *
1354 g_irepository_get_option_group (void)
1355 {
1356   GOptionGroup *group;
1357   group = g_option_group_new ("girepository", "Introspection Options", "Show Introspection Options", NULL, NULL);
1358
1359   g_option_group_add_entries (group, introspection_args);
1360   return group;
1361 }
1362
1363 GQuark
1364 g_irepository_error_quark (void)
1365 {
1366   static GQuark quark = 0;
1367   if (quark == 0)
1368     quark = g_quark_from_static_string ("g-irepository-error-quark");
1369   return quark;
1370 }
1371
1372 /**
1373  * g_type_tag_to_string:
1374  * @type: the type_tag
1375  *
1376  * Obtain a string representation of @type
1377  *
1378  * Returns: the string
1379  */
1380 const gchar*
1381 g_type_tag_to_string (GITypeTag type)
1382 {
1383   switch (type)
1384     {
1385     case GI_TYPE_TAG_VOID:
1386       return "void";
1387     case GI_TYPE_TAG_BOOLEAN:
1388       return "gboolean";
1389     case GI_TYPE_TAG_INT8:
1390       return "gint8";
1391     case GI_TYPE_TAG_UINT8:
1392       return "guint8";
1393     case GI_TYPE_TAG_INT16:
1394       return "gint16";
1395     case GI_TYPE_TAG_UINT16:
1396       return "guint16";
1397     case GI_TYPE_TAG_INT32:
1398       return "gint32";
1399     case GI_TYPE_TAG_UINT32:
1400       return "guint32";
1401     case GI_TYPE_TAG_INT64:
1402       return "gint64";
1403     case GI_TYPE_TAG_UINT64:
1404       return "guint64";
1405     case GI_TYPE_TAG_FLOAT:
1406       return "gfloat";
1407     case GI_TYPE_TAG_DOUBLE:
1408       return "gdouble";
1409     case GI_TYPE_TAG_GTYPE:
1410       return "GType";
1411     case GI_TYPE_TAG_UTF8:
1412       return "utf8";
1413     case GI_TYPE_TAG_FILENAME:
1414       return "filename";
1415     case GI_TYPE_TAG_ARRAY:
1416       return "array";
1417     case GI_TYPE_TAG_INTERFACE:
1418       return "interface";
1419     case GI_TYPE_TAG_GLIST:
1420       return "glist";
1421     case GI_TYPE_TAG_GSLIST:
1422       return "gslist";
1423     case GI_TYPE_TAG_GHASH:
1424       return "ghash";
1425     case GI_TYPE_TAG_ERROR:
1426       return "error";
1427     default:
1428       return "unknown";
1429     }
1430 }
1431
1432 /**
1433  * g_info_type_to_string:
1434  * @type: the info type
1435  *
1436  * Obtain a string representation of @type
1437  *
1438  * Returns: the string
1439  */
1440 const gchar*
1441 g_info_type_to_string (GIInfoType type)
1442 {
1443   switch (type)
1444     {
1445     case GI_INFO_TYPE_INVALID:
1446       return "invalid";
1447     case GI_INFO_TYPE_FUNCTION:
1448       return "function";
1449     case GI_INFO_TYPE_CALLBACK:
1450       return "callback";
1451     case GI_INFO_TYPE_STRUCT:
1452       return "struct";
1453     case GI_INFO_TYPE_BOXED:
1454       return "boxed";
1455     case GI_INFO_TYPE_ENUM:
1456       return "enum";
1457     case GI_INFO_TYPE_FLAGS:
1458       return "flags";
1459     case GI_INFO_TYPE_OBJECT:
1460       return "object";
1461     case GI_INFO_TYPE_INTERFACE:
1462       return "interface";
1463     case GI_INFO_TYPE_CONSTANT:
1464       return "constant";
1465     case GI_INFO_TYPE_ERROR_DOMAIN:
1466       return "error domain";
1467     case GI_INFO_TYPE_UNION:
1468       return "union";
1469     case GI_INFO_TYPE_VALUE:
1470       return "value";
1471     case GI_INFO_TYPE_SIGNAL:
1472       return "signal";
1473     case GI_INFO_TYPE_VFUNC:
1474       return "vfunc";
1475     case GI_INFO_TYPE_PROPERTY:
1476       return "property";
1477     case GI_INFO_TYPE_FIELD:
1478       return "field";
1479     case GI_INFO_TYPE_ARG:
1480       return "arg";
1481     case GI_INFO_TYPE_TYPE:
1482       return "type";
1483     case GI_INFO_TYPE_UNRESOLVED:
1484       return "unresolved";
1485     default:
1486       return "unknown";
1487   }
1488 }