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