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