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