Add gtk-doc support
[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   if (typelib->modules == NULL)
367     typelib->modules = g_list_append(typelib->modules, g_module_open (NULL, 0));
368
369   return namespace;
370 }
371
372 /**
373  * g_irepository_get_dependencies:
374  * @repository: A #GIRepository, may be %NULL for the default
375  * @namespace_: Namespace of interest
376  *
377  * Return an array of all (transitive) dependencies for namespace
378  * @namespace_, including version.  The returned strings are of the
379  * form <code>namespace-version</code>.
380  *
381  * Note: The namespace must have already been loaded using a function
382  * such as #g_irepository_require before calling this function.
383  *
384  * Returns: Zero-terminated string array of versioned dependencies
385  */
386 char **
387 g_irepository_get_dependencies (GIRepository *repository,
388                                 const char *namespace)
389 {
390   GTypelib *typelib;
391
392   g_return_val_if_fail (namespace != NULL, NULL);
393
394   repository = get_repository (repository);
395
396   typelib = get_registered (repository, namespace, NULL);
397   g_return_val_if_fail (typelib != NULL, NULL);
398
399   return get_typelib_dependencies (typelib);
400 }
401
402 const char *
403 g_irepository_load_typelib (GIRepository *repository,
404                             GTypelib     *typelib,
405                             GIRepositoryLoadFlags flags,
406                             GError      **error)
407 {
408   Header *header;
409   const char *namespace;
410   const char *nsversion;
411   gboolean allow_lazy = flags & G_IREPOSITORY_LOAD_FLAG_LAZY;
412   gboolean is_lazy;
413   char *version_conflict;
414
415   repository = get_repository (repository);
416
417   header = (Header *) typelib->data;
418   namespace = g_typelib_get_string (typelib, header->namespace);
419   nsversion = g_typelib_get_string (typelib, header->nsversion);
420
421   if (get_registered_status (repository, namespace, nsversion, allow_lazy, 
422                              &is_lazy, &version_conflict))
423     {
424       if (version_conflict != NULL)
425         {
426           g_set_error (error, G_IREPOSITORY_ERROR,
427                        G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
428                        "Attempting to load namespace '%s', version '%s', but '%s' is already loaded",
429                        namespace, nsversion, version_conflict);
430           return NULL;
431         }
432       return namespace;
433     }
434   return register_internal (repository, "<builtin>", 
435                             allow_lazy, typelib, error);
436 }
437
438 /**
439  * g_irepository_is_registered:
440  * @repository: A #GIRepository, may be %NULL for the default
441  * @namespace_: Namespace of interest
442  * @version: (allow-none): Required version, may be %NULL for latest
443  *
444  * Check whether a particular namespace (and optionally, a specific
445  * version thereof) is currently loaded.  This function is likely to
446  * only be useful in unusual circumstances; in order to act upon
447  * metadata in the namespace, you should call #g_irepository_require
448  * instead which will ensure the namespace is loaded, and return as
449  * quickly as this function will if it has already been loaded.
450  * 
451  * Returns: %TRUE if namespace-version is loaded, %FALSE otherwise
452  */
453 gboolean
454 g_irepository_is_registered (GIRepository *repository, 
455                              const gchar *namespace,
456                              const gchar *version)
457 {
458   repository = get_repository (repository);
459   return get_registered (repository, namespace, version) != NULL;
460 }
461
462 /**
463  * g_irepository_get_default:
464  *
465  * Returns the singleton process-global default #GIRepository.  It is
466  * not currently supported to have multiple repositories in a
467  * particular process, but this function is provided in the unlikely
468  * eventuality that it would become possible, and as a convenience for
469  * higher level language bindings to conform to the GObject method
470  * call conventions.
471
472  * All methods on #GIRepository also accept %NULL as an instance
473  * parameter to mean this default repository, which is usually more
474  * convenient for C.
475  * 
476  * Returns: (transfer none): The global singleton #GIRepository 
477  */
478 GIRepository * 
479 g_irepository_get_default (void)
480 {
481   return get_repository (NULL);
482 }
483
484 /**
485  * g_irepository_get_n_infos:
486  * @repository: A #GIRepository, may be %NULL for the default
487  * @namespace_: Namespace to inspect
488  *
489  * This function returns the number of metadata entries in
490  * given namespace @namespace_.  The namespace must have
491  * already been loaded before calling this function.
492  *
493  * Returns: number of metadata entries
494  */
495 gint                   
496 g_irepository_get_n_infos (GIRepository *repository,
497                            const gchar  *namespace)
498 {
499   GTypelib *typelib;
500   gint n_interfaces = 0;
501
502   g_return_val_if_fail (namespace != NULL, -1);
503
504   repository = get_repository (repository);
505   
506   typelib = get_registered (repository, namespace, NULL);
507
508   g_return_val_if_fail (typelib != NULL, -1);
509
510   n_interfaces = ((Header *)typelib->data)->n_local_entries;
511
512   return n_interfaces;
513 }
514
515 typedef struct
516 {
517   GIRepository *repo;
518   gint index;
519   const gchar *name;
520   const gchar *type;
521   GIBaseInfo *iface;
522 } IfaceData;
523
524 static void
525 find_interface (gpointer key,
526                 gpointer value,
527                 gpointer data)
528 {
529   gint i;
530   GTypelib *typelib = (GTypelib *)value;
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       for (i = 1; i <= n_entries; i++)
557         {
558           RegisteredTypeBlob *blob;
559
560           entry = g_typelib_get_dir_entry (typelib, i);
561           if (!BLOB_IS_REGISTERED_TYPE (entry))
562             continue;
563
564           blob = (RegisteredTypeBlob *)(&typelib->data[entry->offset]);
565           if (!blob->gtype_name)
566             continue;
567
568           type = g_typelib_get_string (typelib, blob->gtype_name);
569           if (strcmp (type, iface_data->type) == 0)
570             {
571               index = i;
572               break;
573             }
574         }
575     }
576   else if (iface_data->index > n_entries)
577     iface_data->index -= n_entries;
578   else if (iface_data->index > 0)
579     {
580       index = iface_data->index;
581       iface_data->index = 0;
582     }
583
584   if (index != 0)
585     {
586       entry = g_typelib_get_dir_entry (typelib, index);
587       iface_data->iface = g_info_new_full (entry->blob_type,
588                                            iface_data->repo,
589                                            NULL, typelib, entry->offset);
590     }
591 }
592
593 /**
594  * g_irepository_get_info:
595  * @repository: A #GIRepository, may be %NULL for the default
596  * @namespace_: Namespace to inspect
597  * @index: Offset into namespace metadata for entry
598  *
599  * This function returns a particular metadata entry in the
600  * given namespace @namespace_.  The namespace must have
601  * already been loaded before calling this function.
602  *
603  * Returns: #GIBaseInfo containing metadata
604  */
605 GIBaseInfo * 
606 g_irepository_get_info (GIRepository *repository,
607                         const gchar  *namespace,
608                         gint          index)
609 {
610   IfaceData data;
611   GTypelib *typelib;
612
613   g_return_val_if_fail (namespace != NULL, NULL);
614
615   repository = get_repository (repository);
616
617   data.repo = repository;
618   data.name = NULL;
619   data.type = NULL;
620   data.index = index + 1;
621   data.iface = NULL;
622
623   typelib = get_registered (repository, namespace, NULL);
624   
625   g_return_val_if_fail (typelib != NULL, NULL);
626
627   find_interface ((void *)namespace, typelib, &data);
628
629   return data.iface;  
630 }
631
632 /**
633  * g_irepository_find_by_gtype:
634  * @repository: A #GIRepository, may be %NULL for the default
635  * @gtype: GType to search for
636  *
637  * Searches all loaded namespaces for a particular #GType.  Note that
638  * in order to locate the metadata, the namespace corresponding to
639  * the type must first have been loaded.  There is currently no
640  * mechanism for determining the namespace which corresponds to an
641  * arbitrary GType - thus, this function will function most reliably
642  * when you have expect the GType to be from a known namespace.
643  *
644  * Returns: #GIBaseInfo representing metadata about @type, or %NULL
645  */
646 GIBaseInfo * 
647 g_irepository_find_by_gtype (GIRepository *repository,
648                              GType         gtype)
649 {
650   IfaceData data;
651
652   repository = get_repository (repository);
653
654   data.iface = g_hash_table_lookup (repository->priv->info_by_gtype,
655                                     (gpointer)type);
656
657   if (data.iface)
658     return g_base_info_ref (data.iface);
659
660   data.repo = repository;
661   data.name = NULL;
662   data.type = g_type_name (type);
663   data.index = -1;
664   data.iface = NULL;
665
666   g_hash_table_foreach (repository->priv->typelibs, find_interface, &data);
667   g_hash_table_foreach (repository->priv->lazy_typelibs, find_interface, &data);
668
669   if (data.iface)
670     g_hash_table_insert (repository->priv->info_by_gtype,
671                          (gpointer) type,
672                          g_base_info_ref (data.iface));
673
674
675   return data.iface;
676 }
677
678 /**
679  * g_irepository_find_by_name:
680  * @repository: A #GIRepository, may be %NULL for the default
681  * @namespace_: Namespace which will be searched
682  * @name: Entry name to find
683  *
684  * Searches for a particular entry in a namespace.  Before calling
685  * this function for a particular namespace, you must call
686  * #g_irepository_require once to load the namespace, or otherwise
687  * ensure the namespace has already been loaded.
688  *
689  * Returns: #GIBaseInfo representing metadata about @name, or %NULL
690  */
691 GIBaseInfo * 
692 g_irepository_find_by_name (GIRepository *repository,
693                             const gchar  *namespace,
694                             const gchar  *name)
695 {
696   IfaceData data;
697   GTypelib *typelib;
698
699   g_return_val_if_fail (namespace != NULL, NULL);
700
701   repository = get_repository (repository);
702
703   data.repo = repository;
704   data.name = name;
705   data.type = NULL;
706   data.index = -1;
707   data.iface = NULL;
708
709   typelib = get_registered (repository, namespace, NULL);
710   
711   g_return_val_if_fail (typelib != NULL, NULL);
712
713   find_interface ((void *)namespace, typelib, &data);
714
715   return data.iface;
716 }
717
718 static void
719 collect_namespaces (gpointer key,
720                     gpointer value,
721                     gpointer data)
722 {
723   GList **list = data;
724
725   *list = g_list_append (*list, key);
726 }
727
728 /**
729  * g_irepository_get_namespaces:
730  * @repository: A #GIRepository, may be %NULL for the default
731  *
732  * Return the list of currently loaded namespaces.
733  *
734  * Returns: (utf8) (transfer full): List of namespaces
735  */
736 gchar ** 
737 g_irepository_get_loaded_namespaces (GIRepository *repository)
738 {
739   GList *l, *list = NULL;
740   gchar **names;
741   gint i;
742
743   repository = get_repository (repository);
744
745   g_hash_table_foreach (repository->priv->typelibs, collect_namespaces, &list);
746   g_hash_table_foreach (repository->priv->lazy_typelibs, collect_namespaces, &list);
747
748   names = g_malloc0 (sizeof (gchar *) * (g_list_length (list) + 1));
749   i = 0;
750   for (l = list; l; l = l->next)
751     names[i++] = g_strdup (l->data); 
752   g_list_free (list);
753
754   return names;
755 }
756
757 /**
758  * g_irepository_get_version:
759  * @repository: A #GIRepository, may be %NULL for the default
760  * @namespace_: Namespace to inspect
761  *
762  * This function returns the loaded version associated with the given
763  * namespace @namespace_.
764  *
765  * Note: The namespace must have already been loaded using a function
766  * such as #g_irepository_require before calling this function.
767  *
768  * Returns: Loaded version
769  */
770 const gchar *
771 g_irepository_get_version (GIRepository *repository,
772                            const gchar  *namespace)
773 {
774   GTypelib *typelib;
775   Header *header;
776
777   g_return_val_if_fail (namespace != NULL, NULL);
778
779   repository = get_repository (repository);
780
781   typelib = get_registered (repository, namespace, NULL);
782
783   g_return_val_if_fail (typelib != NULL, NULL);
784
785   header = (Header *) typelib->data;
786   return g_typelib_get_string (typelib, header->nsversion);
787 }
788
789 /**
790  * g_irepository_get_shared_library:
791  * @repository: A #GIRepository, may be %NULL for the default
792  * @namespace_: Namespace to inspect
793  *
794  * This function returns the full path to the shared C library
795  * associated with the given namespace @namespace_. There may be no
796  * shared library path associated, in which case this function will
797  * return %NULL.
798  *
799  * Note: The namespace must have already been loaded using a function
800  * such as #g_irepository_require before calling this function.
801  *
802  * Returns: Full path to shared library, or %NULL if none associated
803  */
804 const gchar *
805 g_irepository_get_shared_library (GIRepository *repository,
806                                   const gchar  *namespace)
807 {
808   GTypelib *typelib;
809   Header *header;
810
811   g_return_val_if_fail (namespace != NULL, NULL);
812
813   repository = get_repository (repository);
814
815   typelib = get_registered (repository, namespace, NULL);
816
817   g_return_val_if_fail (typelib != NULL, NULL);
818
819   header = (Header *) typelib->data;
820   if (header->shared_library)
821     return g_typelib_get_string (typelib, header->shared_library);
822   else
823     return NULL;
824 }
825
826 /**
827  * g_irepository_get_typelib_path:
828  * @repository: Repository, may be %NULL for the default
829  * @namespace_: GI namespace to use, e.g. "Gtk"
830  *
831  * If namespace @namespace_ is loaded, return the full path to the
832  * .typelib file it was loaded from.  If the typelib for 
833  * namespace @namespace_ was included in a shared library, return
834  * the special string "$lt;builtin$gt;".
835  *
836  * Returns: Filesystem path (or $lt;builtin$gt;) if successful, %NULL if namespace is not loaded
837  */
838
839 const gchar * 
840 g_irepository_get_typelib_path (GIRepository *repository,
841                                 const gchar  *namespace)
842 {
843   gpointer orig_key, value;
844
845   repository = get_repository (repository);
846
847   if (!g_hash_table_lookup_extended (repository->priv->typelibs, namespace,
848                                      &orig_key, &value))
849     {
850       if (!g_hash_table_lookup_extended (repository->priv->lazy_typelibs, namespace,
851                                          &orig_key, &value))
852         
853         return NULL;
854     }
855   return ((char*)orig_key) + strlen ((char *) orig_key) + 1;
856 }
857
858 /* This simple search function looks for a specified namespace-version;
859    it's faster than the full directory listing required for latest version. */
860 static GMappedFile *
861 find_namespace_version (const gchar  *namespace,
862                         const gchar  *version,
863                         gchar       **path_ret)
864 {
865   GSList *tmp_path;
866   GSList *ldir;
867   GError *error = NULL;
868   GMappedFile *mfile = NULL;
869   char *fname;
870  
871   fname = g_strdup_printf ("%s-%s.typelib", namespace, version);
872
873   tmp_path = build_search_path_with_overrides ();
874   for (ldir = tmp_path; ldir; ldir = ldir->next)
875     {
876       char *path = g_build_filename (ldir->data, fname, NULL);
877       
878       mfile = g_mapped_file_new (path, FALSE, &error);
879       if (error)
880         {
881           g_free (path);
882           g_clear_error (&error);
883           continue;
884         }
885       *path_ret = path;
886       break;
887     }
888   g_free (fname);
889   g_slist_free (tmp_path);
890   return mfile;
891 }
892
893 static gboolean
894 parse_version (const char *version,
895                int *major,
896                int *minor)
897 {
898   const char *dot;
899   char *end;
900
901   *major = strtol (version, &end, 10);
902   dot = strchr (version, '.');
903   if (dot == NULL)
904     {
905       *minor = 0;
906       return TRUE;
907     }
908   if (dot != end)
909     return FALSE;
910   *minor = strtol (dot+1, &end, 10);
911   if (end != (version + strlen (version)))
912     return FALSE;
913   return TRUE;
914 }
915
916 static int
917 compare_version (const char *v1,
918                  const char *v2)
919 {
920   gboolean success;
921   int v1_major, v1_minor;
922   int v2_major, v2_minor;
923
924   success = parse_version (v1, &v1_major, &v1_minor);
925   g_assert (success);
926
927   success = parse_version (v2, &v2_major, &v2_minor);
928   g_assert (success);
929
930   if (v1_major > v2_major)
931     return 1;
932   else if (v2_major > v1_major)
933     return -1;
934   else if (v1_minor > v2_minor)
935     return 1;
936   else if (v2_minor > v1_minor)
937     return -1;
938   return 0;
939 }
940
941 struct NamespaceVersionCandidadate
942 {
943   GMappedFile *mfile;
944   int path_index;
945   char *path;
946   char *version;
947 };
948
949 static int
950 compare_candidate_reverse (struct NamespaceVersionCandidadate *c1,
951                            struct NamespaceVersionCandidadate *c2)
952 {
953   int result = compare_version (c1->version, c2->version);
954   /* First, check the version */
955   if (result > 0)
956     return -1;
957   else if (result < 0)
958     return 1;
959   else 
960     {
961       /* Now check the path index, which says how early in the search path
962        * we found it.  This ensures that of equal version targets, we
963        * pick the earlier one.
964        */
965       if (c1->path_index == c2->path_index)
966         return 0;
967       else if (c1->path_index > c2->path_index)
968         return 1;
969       else
970         return -1;
971     }
972 }
973
974 static void
975 free_candidate (struct NamespaceVersionCandidadate *candidate)
976 {
977   g_mapped_file_free (candidate->mfile);
978   g_free (candidate->path);
979   g_free (candidate->version);
980   g_free (candidate);
981 }
982
983 static GMappedFile *
984 find_namespace_latest (const gchar  *namespace,
985                        gchar       **version_ret,
986                        gchar       **path_ret)
987 {
988   GSList *tmp_path;
989   GSList *ldir;
990   GError *error = NULL;
991   char *namespace_dash;
992   char *namespace_typelib;
993   GSList *candidates = NULL;
994   GMappedFile *result = NULL;
995   int index;
996
997   *version_ret = NULL;
998   *path_ret = NULL;
999
1000   namespace_dash = g_strdup_printf ("%s-", namespace);
1001   namespace_typelib = g_strdup_printf ("%s.typelib", namespace);
1002
1003   index = 0;
1004   tmp_path = build_search_path_with_overrides ();  
1005   for (ldir = tmp_path; ldir; ldir = ldir->next)
1006     {
1007       GDir *dir;
1008       const char *dirname;
1009       const char *entry;
1010
1011       dirname = (const char*)ldir->data;
1012       dir = g_dir_open (dirname, 0, NULL);
1013       if (dir == NULL)
1014         continue;
1015       while ((entry = g_dir_read_name (dir)) != NULL) 
1016         {
1017           GMappedFile *mfile;
1018           char *path, *version;
1019           struct NamespaceVersionCandidadate *candidate;
1020
1021           if (!g_str_has_suffix (entry, ".typelib"))
1022             continue;
1023           
1024           if (g_str_has_prefix (entry, namespace_dash))
1025             {
1026               const char *last_dash;
1027               const char *name_end;
1028               int major, minor;
1029
1030               name_end = strrchr (entry, '.');
1031               last_dash = strrchr (entry, '-');
1032               version = g_strndup (last_dash+1, name_end-(last_dash+1));
1033               if (!parse_version (version, &major, &minor))
1034                 continue;
1035             }
1036           else
1037             continue;
1038
1039           path = g_build_filename (dirname, entry, NULL);
1040           mfile = g_mapped_file_new (path, FALSE, &error);
1041           if (mfile == NULL)
1042             {
1043               g_free (path);
1044               g_free (version);
1045               g_clear_error (&error);
1046               continue;
1047             }
1048           candidate = g_new0 (struct NamespaceVersionCandidadate, 1);
1049           candidate->mfile = mfile;
1050           candidate->path_index = index;
1051           candidate->path = path;
1052           candidate->version = version;
1053           candidates = g_slist_prepend (candidates, candidate);
1054         }
1055       g_dir_close (dir);
1056       index++;
1057     }
1058
1059   if (candidates != NULL)
1060     {
1061       struct NamespaceVersionCandidadate *elected;
1062       candidates = g_slist_sort (candidates, (GCompareFunc) compare_candidate_reverse);
1063       
1064       elected = (struct NamespaceVersionCandidadate *) candidates->data;
1065       /* Remove the elected one so we don't try to free its contents */
1066       candidates = g_slist_delete_link (candidates, candidates);
1067       
1068       result = elected->mfile;
1069       *path_ret = elected->path;
1070       *version_ret = elected->version;
1071       g_free (elected); /* just free the container */
1072       g_slist_foreach (candidates, (GFunc) free_candidate, NULL);
1073       g_slist_free (candidates);
1074     }  
1075   g_free (namespace_dash);
1076   g_free (namespace_typelib);
1077   g_slist_free (tmp_path);  
1078   return result;
1079 }
1080
1081 /**
1082  * g_irepository_require:
1083  * @repository: (allow-none): Repository, may be %NULL for the default
1084  * @namespace_: GI namespace to use, e.g. "Gtk"
1085  * @version: (allow-none): Version of namespace, may be %NULL for latest
1086  * @flags: Set of %GIRepositoryLoadFlags, may be %0
1087  * @error: a #GError.
1088  *
1089  * Force the namespace @namespace_ to be loaded if it isn't already.
1090  * If @namespace_ is not loaded, this function will search for a
1091  * ".typelib" file using the repository search path.  In addition, a
1092  * version @version of namespace may be specified.  If @version is
1093  * not specified, the latest will be used.
1094  *
1095  * Returns: a pointer to the #GTypelib if successful, %NULL otherwise
1096  */
1097 GTypelib *
1098 g_irepository_require (GIRepository  *repository,
1099                        const gchar   *namespace,
1100                        const gchar   *version,
1101                        GIRepositoryLoadFlags flags,
1102                        GError       **error)
1103 {
1104   GMappedFile *mfile;
1105   GTypelib *ret = NULL;
1106   Header *header;
1107   GTypelib *typelib = NULL;
1108   const gchar *typelib_namespace, *typelib_version;
1109   gboolean allow_lazy = (flags & G_IREPOSITORY_LOAD_FLAG_LAZY) > 0;
1110   gboolean is_lazy;
1111   char *version_conflict = NULL;
1112   char *path = NULL;
1113   char *tmp_version = NULL;
1114
1115   g_return_val_if_fail (namespace != NULL, FALSE);
1116
1117   repository = get_repository (repository);
1118
1119   typelib = get_registered_status (repository, namespace, version, allow_lazy, 
1120                                    &is_lazy, &version_conflict);
1121   if (typelib)
1122     return typelib;
1123
1124   if (version_conflict != NULL)
1125     {
1126       g_set_error (error, G_IREPOSITORY_ERROR,
1127                    G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
1128                    "Requiring namespace '%s' version '%s', but '%s' is already loaded",
1129                    namespace, version, version_conflict);
1130       return NULL;
1131     }
1132
1133   if (version != NULL)
1134     {
1135       mfile = find_namespace_version (namespace, version, &path);
1136       tmp_version = g_strdup (version);
1137     }
1138   else
1139     {
1140       mfile = find_namespace_latest (namespace, &tmp_version, &path);
1141     }
1142   
1143   if (mfile == NULL)
1144     {
1145       if (version != NULL)
1146         g_set_error (error, G_IREPOSITORY_ERROR,
1147                      G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
1148                      "Typelib file for namespace '%s', version '%s' not found",
1149                      namespace, version);
1150       else
1151         g_set_error (error, G_IREPOSITORY_ERROR,
1152                      G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
1153                      "Typelib file for namespace '%s' (any version) not found",
1154                      namespace);
1155       goto out;
1156     }
1157
1158   typelib = g_typelib_new_from_mapped_file (mfile);
1159   header = (Header *) typelib->data;
1160   typelib_namespace = g_typelib_get_string (typelib, header->namespace);
1161   typelib_version = g_typelib_get_string (typelib, header->nsversion);
1162   
1163   if (strcmp (typelib_namespace, namespace) != 0)
1164     {
1165       g_set_error (error, G_IREPOSITORY_ERROR,
1166                    G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH,
1167                    "Typelib file %s for namespace '%s' contains "
1168                    "namespace '%s' which doesn't match the file name",
1169                    path, namespace, typelib_namespace);
1170       goto out;
1171     }
1172   if (version != NULL && strcmp (typelib_version, version) != 0)
1173     {
1174       g_set_error (error, G_IREPOSITORY_ERROR,
1175                    G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH,
1176                    "Typelib file %s for namespace '%s' contains "
1177                    "version '%s' which doesn't match the expected version '%s'",
1178                    path, namespace, typelib_version, version);
1179       goto out;
1180     }
1181
1182   if (!register_internal (repository, path, allow_lazy, 
1183                           typelib, error))
1184     {
1185       g_typelib_free (typelib);
1186       goto out;
1187     }
1188   ret = typelib;
1189  out:
1190   g_free (tmp_version);
1191   g_free (path);
1192   return ret; 
1193 }
1194
1195 static gboolean
1196 g_irepository_introspect_cb (const char *option_name,
1197                              const char *value,
1198                              gpointer data,
1199                              GError **error)
1200 {
1201   gboolean ret = g_irepository_dump (value, error);
1202   exit (ret ? 0 : 1);
1203 }
1204
1205 static const GOptionEntry introspection_args[] = {
1206   { "introspect-dump", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK,
1207     g_irepository_introspect_cb, "Dump introspection information",
1208     "infile.txt,outfile.xml" },
1209   { NULL }
1210 };
1211
1212 GOptionGroup *
1213 g_irepository_get_option_group (void)
1214 {
1215   GOptionGroup *group;
1216   group = g_option_group_new ("girepository", "Introspection Options", "Show Introspection Options", NULL, NULL);
1217
1218   g_option_group_add_entries (group, introspection_args);
1219   return group;
1220 }
1221
1222 GQuark
1223 g_irepository_error_quark (void)
1224 {
1225   static GQuark quark = 0;
1226   if (quark == 0)
1227     quark = g_quark_from_static_string ("g-irepository-error-quark");
1228   return quark;
1229 }
1230
1231 const gchar*
1232 g_type_tag_to_string (GITypeTag type)
1233 {
1234   switch (type)
1235     {
1236     case GI_TYPE_TAG_VOID:
1237       return "void";
1238     case GI_TYPE_TAG_BOOLEAN:
1239       return "boolean";
1240     case GI_TYPE_TAG_INT8:
1241       return "int8";
1242     case GI_TYPE_TAG_UINT8:
1243       return "uint8";
1244     case GI_TYPE_TAG_INT16:
1245       return "int16";
1246     case GI_TYPE_TAG_UINT16:
1247       return "uint16";
1248     case GI_TYPE_TAG_INT32:
1249       return "int32";
1250     case GI_TYPE_TAG_UINT32:
1251       return "uint32";
1252     case GI_TYPE_TAG_INT64:
1253       return "int64";
1254     case GI_TYPE_TAG_UINT64:
1255       return "uint64";
1256     case GI_TYPE_TAG_INT:
1257       return "int";
1258     case GI_TYPE_TAG_UINT:
1259       return "uint";
1260     case GI_TYPE_TAG_LONG:
1261       return "long";
1262     case GI_TYPE_TAG_ULONG:
1263       return "ulong";
1264     case GI_TYPE_TAG_SSIZE:
1265       return "ssize";
1266     case GI_TYPE_TAG_SIZE:
1267       return "size";
1268     case GI_TYPE_TAG_FLOAT:
1269       return "float";
1270     case GI_TYPE_TAG_DOUBLE:
1271       return "double";
1272     case GI_TYPE_TAG_TIME_T:
1273       return "time_t";
1274     case GI_TYPE_TAG_GTYPE:
1275       return "GType";
1276     case GI_TYPE_TAG_UTF8:
1277       return "utf8";
1278     case GI_TYPE_TAG_FILENAME:
1279       return "filename";
1280     case GI_TYPE_TAG_ARRAY:
1281       return "array";
1282     case GI_TYPE_TAG_INTERFACE:
1283       return "interface";
1284     case GI_TYPE_TAG_GLIST:
1285       return "glist";
1286     case GI_TYPE_TAG_GSLIST:
1287       return "gslist";
1288     case GI_TYPE_TAG_GHASH:
1289       return "ghash";
1290     case GI_TYPE_TAG_ERROR:
1291       return "error";
1292     default:
1293       return "unknown";
1294     }
1295 }