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