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