Remove g_irepository_register_file in favor of g_irepository_require.
[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  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24
25 #include <glib.h>
26 #include <glib/gprintf.h>
27 #include <gmodule.h>
28 #include "girepository.h"
29 #include "gtypelib.h"
30
31 static GStaticMutex globals_lock = G_STATIC_MUTEX_INIT;
32 static GIRepository *default_repository = NULL;
33 static GHashTable *default_typelib = NULL;
34 static GSList *search_path = NULL;
35
36 struct _GIRepositoryPrivate 
37 {
38   GHashTable *typelib; /* (string) namespace -> GTypelib */
39 };
40
41 G_DEFINE_TYPE (GIRepository, g_irepository, G_TYPE_OBJECT);
42
43 static void 
44 g_irepository_init (GIRepository *repository)
45 {
46   repository->priv = G_TYPE_INSTANCE_GET_PRIVATE (repository, G_TYPE_IREPOSITORY,
47                                                   GIRepositoryPrivate);
48 }
49
50 static void
51 g_irepository_finalize (GObject *object)
52 {
53   GIRepository *repository = G_IREPOSITORY (object);
54
55   g_hash_table_destroy (repository->priv->typelib);
56   
57   (* G_OBJECT_CLASS (g_irepository_parent_class)->finalize) (G_OBJECT (repository));
58 }
59
60 static void
61 g_irepository_class_init (GIRepositoryClass *class)
62 {
63   GObjectClass *gobject_class;
64
65   gobject_class = G_OBJECT_CLASS (class);
66
67   gobject_class->finalize = g_irepository_finalize;
68
69   g_type_class_add_private (class, sizeof (GIRepositoryPrivate)); 
70 }
71
72 static void
73 init_globals ()
74 {
75   g_static_mutex_lock (&globals_lock);
76
77   if (default_repository == NULL) 
78     { 
79       default_repository = g_object_new (G_TYPE_IREPOSITORY, NULL);
80       if (default_typelib == NULL)
81         default_typelib = g_hash_table_new_full (g_str_hash, g_str_equal,
82                                                   (GDestroyNotify) NULL,
83                                                   (GDestroyNotify) g_typelib_free);
84       default_repository->priv->typelib = default_typelib;
85     }
86
87   if (search_path == NULL)
88     {
89       const gchar *const *datadirs;
90       const gchar *const *dir;
91       
92       datadirs = g_get_system_data_dirs ();
93       
94       search_path = NULL;
95       for (dir = datadirs; *dir; dir++) {
96         char *path = g_build_filename (*dir, "gitypelibs", NULL);
97         search_path = g_slist_prepend (search_path, path);
98       }
99       search_path = g_slist_reverse (search_path);
100     }
101
102   g_static_mutex_unlock (&globals_lock);
103 }
104
105 const gchar *
106 g_irepository_register (GIRepository *repository,
107                         GTypelib    *typelib)
108 {
109   Header *header;
110   const gchar *name;
111   GHashTable *table;
112   GError *error = NULL;
113   
114   g_return_val_if_fail (typelib != NULL, NULL);
115   
116   header = (Header *)typelib->data;
117
118   g_return_val_if_fail (header != NULL, NULL);
119
120   if (repository != NULL)
121     {
122       if (repository->priv->typelib == NULL)
123         repository->priv->typelib = g_hash_table_new_full (g_str_hash, g_str_equal,
124                                                             (GDestroyNotify) NULL,
125                                                             (GDestroyNotify) g_typelib_free);
126       table = repository->priv->typelib;
127     }
128   else 
129     {
130       init_globals ();
131       table = default_typelib;
132     }
133
134   name = g_typelib_get_string (typelib, header->namespace);
135
136   if (g_hash_table_lookup (table, name))
137     {
138       g_printerr ("typelib (%p) for '%s' already registered\n",
139                  typelib, name);
140
141       return NULL;
142     }
143   g_hash_table_insert (table, g_strdup(name), (void *)typelib);
144
145   if (typelib->module == NULL)
146       typelib->module = g_module_open (NULL, 0); 
147
148   return name;
149 }
150
151
152 void
153 g_irepository_unregister (GIRepository *repository,
154                           const gchar  *namespace)
155 {
156   GHashTable *table;
157
158   if (repository != NULL)
159     table = repository->priv->typelib;
160   else 
161     {
162       init_globals ();
163       table = default_typelib;
164     }
165
166   if (!g_hash_table_remove (table, namespace))
167     {
168       g_printerr ("namespace '%s' not registered\n", namespace);
169     }
170 }
171
172 gboolean
173 g_irepository_is_registered (GIRepository *repository, 
174                              const gchar *namespace)
175 {
176   GHashTable *table;
177
178   if (repository != NULL)
179     table = repository->priv->typelib;
180   else
181     {
182       init_globals ();
183       table = default_typelib;
184     }
185
186   return g_hash_table_lookup (table, namespace) != NULL;
187 }
188
189 GIRepository * 
190 g_irepository_get_default (void)
191 {
192   init_globals ();
193   return default_repository; 
194 }
195
196 static void 
197 count_interfaces (gpointer key,
198                   gpointer value,
199                   gpointer data)
200 {
201   guchar *typelib = ((GTypelib *) value)->data;
202   gint *n_interfaces = (gint *)data;
203   
204   *n_interfaces += ((Header *)typelib)->n_local_entries;
205 }
206
207 gint                   
208 g_irepository_get_n_infos (GIRepository *repository,
209                            const gchar  *namespace)
210 {
211   gint n_interfaces = 0;
212   
213   if (namespace)
214     {
215       GTypelib *typelib;
216
217       typelib = g_hash_table_lookup (repository->priv->typelib, namespace);
218
219       if (typelib)
220         n_interfaces = ((Header *)typelib->data)->n_local_entries;
221     }
222   else
223     {
224       g_hash_table_foreach (repository->priv->typelib, 
225                             count_interfaces, &n_interfaces);
226     }
227
228   return n_interfaces;
229 }
230
231 typedef struct
232 {
233   gint index;
234   const gchar *name;
235   const gchar *type;
236   GIBaseInfo *iface;
237 } IfaceData;
238
239 static void
240 find_interface (gpointer key,
241                 gpointer value,
242                 gpointer data)
243 {
244   gint i;
245   GTypelib *typelib = (GTypelib *)value;
246   IfaceData *iface_data = (IfaceData *)data;
247   gint index;
248   gint n_entries;
249   guint32 offset;
250   const gchar *name;
251   const gchar *type;
252   DirEntry *entry;    
253
254   index = 0;
255   n_entries = ((Header *)typelib->data)->n_local_entries;
256
257   if (iface_data->name)
258     {
259       for (i = 1; i <= n_entries; i++)
260         {
261           entry = g_typelib_get_dir_entry (typelib, i);
262           name = g_typelib_get_string (typelib, entry->name);
263           if (strcmp (name, iface_data->name) == 0)
264             {
265               index = i;
266               break;
267             }
268         }
269     }
270   else if (iface_data->type)
271     {
272       for (i = 1; i <= n_entries; i++)
273         {
274           entry = g_typelib_get_dir_entry (typelib, i);
275           if (entry->blob_type < 4)
276             continue;
277           
278           offset = *(guint32*)&typelib->data[entry->offset + 8];
279           type = g_typelib_get_string (typelib, offset);
280           if (strcmp (type, iface_data->type) == 0)
281             {
282               index = i;
283               break;
284             }
285         }
286     }
287   else if (iface_data->index > n_entries)
288     iface_data->index -= n_entries;
289   else if (iface_data->index > 0)
290     {
291       index = iface_data->index;
292       iface_data->index = 0;
293     }
294
295   if (index != 0)
296     {
297       entry = g_typelib_get_dir_entry (typelib, index);
298       iface_data->iface = g_info_new (entry->blob_type, NULL,
299                                       typelib, entry->offset);
300     }
301 }
302
303 GIBaseInfo * 
304 g_irepository_get_info (GIRepository *repository,
305                         const gchar  *namespace,
306                         gint          index)
307 {
308   IfaceData data;
309
310   data.name = NULL;
311   data.type = NULL;
312   data.index = index + 1;
313   data.iface = NULL;
314
315   if (namespace)
316     {
317       GTypelib *typelib;
318       
319       typelib = g_hash_table_lookup (repository->priv->typelib, namespace);
320       
321       if (typelib)
322         find_interface ((void *)namespace, typelib, &data);
323     }
324   else
325     g_hash_table_foreach (repository->priv->typelib, find_interface, &data);
326
327   return data.iface;  
328 }
329
330 GIBaseInfo * 
331 g_irepository_find_by_gtype (GIRepository *repository,
332                              GType         type)
333 {
334   IfaceData data;
335
336   data.name = NULL;
337   data.type = g_type_name (type);
338   data.index = -1;
339   data.iface = NULL;
340
341   g_hash_table_foreach (repository->priv->typelib, find_interface, &data);
342
343   return data.iface;
344 }
345
346 /**
347  * g_irepository_find_by_name
348  * @repository: A #GIRepository, may be %NULL for the default
349  * @namespace: Namespace to search in, may be %NULL for all
350  * @name: Name to find
351  *
352  * Searches for a particular name in one or all namespaces.
353  * See #g_irepository_require to load metadata for namespaces.
354
355  * Returns: #GIBaseInfo representing metadata about @name, or %NULL
356  */
357 GIBaseInfo * 
358 g_irepository_find_by_name (GIRepository *repository,
359                             const gchar  *namespace,
360                             const gchar  *name)
361 {
362   IfaceData data;
363
364   data.name = name;
365   data.type = NULL;
366   data.index = -1;
367   data.iface = NULL;
368
369   if (namespace)
370     {
371       GTypelib *typelib;
372       
373       typelib = g_hash_table_lookup (repository->priv->typelib, namespace);
374       
375       if (typelib)
376         find_interface ((void *)namespace, typelib, &data);
377     }
378   else
379     g_hash_table_foreach (repository->priv->typelib, find_interface, &data);
380
381   return data.iface;
382 }
383
384 static void
385 collect_namespaces (gpointer key,
386                     gpointer value,
387                     gpointer data)
388 {
389   GList **list = data;
390
391   *list = g_list_append (*list, key);
392 }
393
394 /**
395  * g_irepository_get_namespaces
396  * @repository: A #GIRepository, may be %NULL for the default
397  *
398  * Return the list of currently known namespaces.  Normally
399  * if you want a particular namespace, you should call 
400  * #g_irepository_require to load it in.
401
402  * Returns: List of namespaces
403  */
404 gchar ** 
405 g_irepository_get_namespaces (GIRepository *repository)
406 {
407   GList *l, *list = NULL;
408   gchar **names;
409   gint i;
410
411   g_hash_table_foreach (repository->priv->typelib, collect_namespaces, &list);
412
413   names = g_malloc0 (sizeof (gchar *) * (g_list_length (list) + 1));
414   i = 0;
415   for (l = list; l; l = l->next)
416     names[i++] = g_strdup (l->data); 
417   g_list_free (list);
418
419   return names;
420 }
421
422 const gchar *
423 g_irepository_get_shared_library (GIRepository *repository,
424                                    const gchar  *namespace)
425 {
426   GTypelib *typelib;
427   Header *header;
428
429   typelib = g_hash_table_lookup (repository->priv->typelib, namespace);
430   if (!typelib)
431     return NULL;
432   header = (Header *) typelib->data;
433   if (header->shared_library)
434     return g_typelib_get_string (typelib, header->shared_library);
435   else
436     return NULL;
437 }
438
439 static inline void
440 g_irepository_build_search_path (void)
441 {
442 }
443
444 /**
445  * g_irepository_require
446  * @repository: Repository, may be null for the default
447  * @namespace: GI namespace to use, e.g. "Gtk"
448  * @error: a #GError.
449  *
450  * Force the namespace @namespace to be loaded if it isn't
451  * already.  If @namespace is not loaded, this function will
452  * search for a ".typelib" file using the repository search 
453  * path.
454  *
455  * Returns: Namespace if successful, NULL otherwise
456  */
457 const gchar *
458 g_irepository_require (GIRepository  *repository,
459                        const gchar   *namespace,
460                        GError       **error)
461 {
462   GSList *ldir;
463   const char *dir;
464   gchar *fname, *full_path;
465   GMappedFile *mfile;
466   GError *error1 = NULL;
467   GTypelib *typelib = NULL;
468   const gchar *typelib_namespace, *shlib_fname;
469   GModule *module;
470   guint32 shlib;
471   GHashTable *table;
472
473   if (repository != NULL)
474     table = repository->priv->typelib;
475   else
476     {
477       init_globals ();
478       table = default_typelib;
479     }
480
481   /* don't bother loading a namespace if already registered */
482   if (g_hash_table_lookup (table, namespace))
483     return namespace;
484
485   fname = g_strconcat (namespace, ".typelib", NULL);
486
487   for (ldir = search_path; ldir; ldir = ldir->next) {
488     dir = ldir->data;
489     full_path = g_build_filename (dir, fname, NULL);
490     mfile = g_mapped_file_new (full_path, FALSE, &error1);
491     if (error1) {
492       g_clear_error (&error1);
493       g_free (full_path);
494       continue;
495     }
496     g_free (full_path);
497     typelib = g_typelib_new_from_mapped_file (mfile);
498     typelib_namespace = g_typelib_get_string (typelib, ((Header *) typelib->data)->namespace);
499     if (strcmp (typelib_namespace, namespace) != 0) {
500       g_set_error (error, G_IREPOSITORY_ERROR,
501                    G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH,
502                    "Typelib file %s for namespace '%s' contains namespace '%s'"
503                    " which doesn't match the file name",
504                    full_path, namespace, typelib_namespace);
505       return NULL; 
506     }
507     break;
508   }
509   g_free (fname);
510   if (typelib == NULL) {
511     g_set_error (error, G_IREPOSITORY_ERROR,
512                  G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
513                  "Typelib file for namespace '%s' was not found in search"
514                  " path or could not be openened", namespace);
515     return NULL;
516   }
517   /* optionally load shared library and attach it to the typelib */
518   shlib = ((Header *) typelib->data)->shared_library;
519   if (shlib) {
520     shlib_fname = g_typelib_get_string (typelib, shlib);
521     module = g_module_open (shlib_fname, G_MODULE_BIND_LAZY|G_MODULE_BIND_LOCAL);
522     if (module == NULL) {
523       g_set_error (error, G_IREPOSITORY_ERROR,
524                    G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
525                    "Typelib for namespace '%s' references shared library %s,"
526                    " but it could not be openened (%s)",
527                    namespace, shlib_fname, g_module_error ());
528       return NULL;
529     }
530   }
531
532   g_hash_table_remove (table, namespace);
533   return g_irepository_register (repository, typelib);
534 }
535
536
537 GQuark
538 g_irepository_error_quark (void)
539 {
540   static GQuark quark = 0;
541   if (quark == 0)
542     quark = g_quark_from_static_string ("g-irepository-error-quark");
543   return quark;
544 }
545
546 const gchar*
547 g_type_tag_to_string (GITypeTag type)
548 {
549   switch (type)
550     {
551     case GI_TYPE_TAG_VOID:
552       return "void";
553     case GI_TYPE_TAG_BOOLEAN:
554       return "boolean";
555     case GI_TYPE_TAG_INT8:
556       return "int8";
557     case GI_TYPE_TAG_UINT8:
558       return "uint8";
559     case GI_TYPE_TAG_INT16:
560       return "int16";
561     case GI_TYPE_TAG_UINT16:
562       return "uint16";
563     case GI_TYPE_TAG_INT32:
564       return "int32";
565     case GI_TYPE_TAG_UINT32:
566       return "uint32";
567     case GI_TYPE_TAG_INT64:
568       return "int64";
569     case GI_TYPE_TAG_UINT64:
570       return "uint64";
571     case GI_TYPE_TAG_INT:
572       return "int";
573     case GI_TYPE_TAG_UINT:
574       return "uint";
575     case GI_TYPE_TAG_LONG:
576       return "long";
577     case GI_TYPE_TAG_ULONG:
578       return "ulong";
579     case GI_TYPE_TAG_SSIZE:
580       return "ssize";
581     case GI_TYPE_TAG_SIZE:
582       return "size";
583     case GI_TYPE_TAG_FLOAT:
584       return "float";
585     case GI_TYPE_TAG_DOUBLE:
586       return "double";
587     case GI_TYPE_TAG_UTF8:
588       return "utf8";
589     case GI_TYPE_TAG_FILENAME:
590       return "filename";
591     case GI_TYPE_TAG_ARRAY:
592       return "array";
593     case GI_TYPE_TAG_INTERFACE:
594       return "interface";
595     case GI_TYPE_TAG_GLIST:
596       return "glist";
597     case GI_TYPE_TAG_GSLIST:
598       return "gslist";
599     case GI_TYPE_TAG_GHASH:
600       return "ghash";
601     case GI_TYPE_TAG_ERROR:
602       return "error";
603     default:
604       return "unknown";
605     }
606 }