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