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