Rename metadata to typelib in variable names, comments and apis.
[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 GIRepository *default_repository = NULL;
32 static GHashTable *default_typelib = NULL;
33 static GSList *search_path = NULL;
34
35 struct _GIRepositoryPrivate 
36 {
37   GHashTable *typelib; /* (string) namespace -> GTypelib */
38 };
39
40 G_DEFINE_TYPE (GIRepository, g_irepository, G_TYPE_OBJECT);
41
42 static void 
43 g_irepository_init (GIRepository *repository)
44 {
45   repository->priv = G_TYPE_INSTANCE_GET_PRIVATE (repository, G_TYPE_IREPOSITORY,
46                                                   GIRepositoryPrivate);
47 }
48
49 static void
50 g_irepository_finalize (GObject *object)
51 {
52   GIRepository *repository = G_IREPOSITORY (object);
53
54   g_hash_table_destroy (repository->priv->typelib);
55   
56   (* G_OBJECT_CLASS (g_irepository_parent_class)->finalize) (G_OBJECT (repository));
57 }
58
59 static void
60 g_irepository_class_init (GIRepositoryClass *class)
61 {
62   GObjectClass *gobject_class;
63
64   gobject_class = G_OBJECT_CLASS (class);
65
66   gobject_class->finalize = g_irepository_finalize;
67
68   g_type_class_add_private (class, sizeof (GIRepositoryPrivate)); 
69 }
70
71 const gchar *
72 g_irepository_register (GIRepository *repository,
73                         GTypelib    *typelib)
74 {
75   Header *header;
76   const gchar *name;
77   GHashTable *table;
78   GError *error = NULL;
79   
80   g_return_val_if_fail (typelib != NULL, NULL);
81   
82   header = (Header *)typelib->data;
83
84   g_return_val_if_fail (header != NULL, NULL);
85
86   if (repository != NULL)
87     {
88       if (repository->priv->typelib == NULL)
89         repository->priv->typelib = g_hash_table_new_full (g_str_hash, g_str_equal,
90                                                             (GDestroyNotify) NULL,
91                                                             (GDestroyNotify) g_typelib_free);
92       table = repository->priv->typelib;
93     }
94   else 
95     {
96       if (default_typelib == NULL)
97         default_typelib = g_hash_table_new_full (g_str_hash, g_str_equal,
98                                                   (GDestroyNotify) NULL,
99                                                   (GDestroyNotify) g_typelib_free);
100       table = default_typelib;
101     }
102
103   name = g_typelib_get_string (typelib, header->namespace);
104
105   if (g_hash_table_lookup (table, name))
106     {
107       g_printerr ("typelib (%p) for '%s' already registered\n",
108                  typelib, name);
109
110       return NULL;
111     }
112   g_hash_table_insert (table, g_strdup(name), (void *)typelib);
113
114   if (typelib->module == NULL)
115       typelib->module = g_module_open (NULL, 0); 
116
117   if (g_getenv ("G_IREPOSITORY_VERBOSE"))
118     {
119       g_printerr ("Loaded typelib %s\n", name);
120     }
121
122   return name;
123 }
124
125
126 void
127 g_irepository_unregister (GIRepository *repository,
128                           const gchar  *namespace)
129 {
130   GHashTable *table;
131
132   if (repository != NULL)
133     table = repository->priv->typelib;
134   else
135     table = default_typelib;
136
137   if (!g_hash_table_remove (table, namespace))
138     {
139       g_printerr ("namespace '%s' not registered\n", namespace);
140     }
141 }
142
143 gboolean
144 g_irepository_is_registered (GIRepository *repository, 
145                              const gchar *namespace)
146 {
147   GHashTable *table;
148
149   if (repository != NULL)
150     table = repository->priv->typelib;
151   else
152     table = default_typelib;
153
154   return g_hash_table_lookup (table, namespace) != NULL;
155 }
156
157 GIRepository * 
158 g_irepository_get_default (void)
159 {
160   if (default_repository == NULL) 
161     { 
162       default_repository = g_object_new (G_TYPE_IREPOSITORY, NULL);
163       if (default_typelib == NULL)
164         default_typelib = g_hash_table_new_full (g_str_hash, g_str_equal,
165                                                   (GDestroyNotify) NULL,
166                                                   (GDestroyNotify) g_typelib_free);
167       default_repository->priv->typelib = default_typelib;
168     }
169
170   return default_repository; 
171 }
172
173 static void 
174 count_interfaces (gpointer key,
175                   gpointer value,
176                   gpointer data)
177 {
178   guchar *typelib = ((GTypelib *) value)->data;
179   gint *n_interfaces = (gint *)data;
180   
181   *n_interfaces += ((Header *)typelib)->n_local_entries;
182 }
183
184 gint                   
185 g_irepository_get_n_infos (GIRepository *repository,
186                            const gchar  *namespace)
187 {
188   gint n_interfaces = 0;
189   
190   if (namespace)
191     {
192       GTypelib *typelib;
193
194       typelib = g_hash_table_lookup (repository->priv->typelib, namespace);
195
196       if (typelib)
197         n_interfaces = ((Header *)typelib->data)->n_local_entries;
198     }
199   else
200     {
201       g_hash_table_foreach (repository->priv->typelib, 
202                             count_interfaces, &n_interfaces);
203     }
204
205   return n_interfaces;
206 }
207
208 typedef struct
209 {
210   gint index;
211   const gchar *name;
212   const gchar *type;
213   GIBaseInfo *iface;
214 } IfaceData;
215
216 static void
217 find_interface (gpointer key,
218                 gpointer value,
219                 gpointer data)
220 {
221   gint i;
222   GTypelib *typelib = (GTypelib *)value;
223   IfaceData *iface_data = (IfaceData *)data;
224   gint index;
225   gint n_entries;
226   guint32 offset;
227   const gchar *name;
228   const gchar *type;
229   DirEntry *entry;    
230
231   index = 0;
232   n_entries = ((Header *)typelib->data)->n_local_entries;
233
234   if (iface_data->name)
235     {
236       for (i = 1; i <= n_entries; i++)
237         {
238           entry = g_typelib_get_dir_entry (typelib, i);
239           name = g_typelib_get_string (typelib, entry->name);
240           if (strcmp (name, iface_data->name) == 0)
241             {
242               index = i;
243               break;
244             }
245         }
246     }
247   else if (iface_data->type)
248     {
249       for (i = 1; i <= n_entries; i++)
250         {
251           entry = g_typelib_get_dir_entry (typelib, i);
252           if (entry->blob_type < 4)
253             continue;
254           
255           offset = *(guint32*)&typelib->data[entry->offset + 8];
256           type = g_typelib_get_string (typelib, offset);
257           if (strcmp (type, iface_data->type) == 0)
258             {
259               index = i;
260               break;
261             }
262         }
263     }
264   else if (iface_data->index > n_entries)
265     iface_data->index -= n_entries;
266   else if (iface_data->index > 0)
267     {
268       index = iface_data->index;
269       iface_data->index = 0;
270     }
271
272   if (index != 0)
273     {
274       entry = g_typelib_get_dir_entry (typelib, index);
275       iface_data->iface = g_info_new (entry->blob_type, NULL,
276                                       typelib, entry->offset);
277     }
278 }
279
280 GIBaseInfo * 
281 g_irepository_get_info (GIRepository *repository,
282                         const gchar  *namespace,
283                         gint          index)
284 {
285   IfaceData data;
286
287   data.name = NULL;
288   data.type = NULL;
289   data.index = index + 1;
290   data.iface = NULL;
291
292   if (namespace)
293     {
294       GTypelib *typelib;
295       
296       typelib = g_hash_table_lookup (repository->priv->typelib, namespace);
297       
298       if (typelib)
299         find_interface ((void *)namespace, typelib, &data);
300     }
301   else
302     g_hash_table_foreach (repository->priv->typelib, find_interface, &data);
303
304   return data.iface;  
305 }
306
307 GIBaseInfo * 
308 g_irepository_find_by_gtype (GIRepository *repository,
309                              GType         type)
310 {
311   IfaceData data;
312
313   data.name = NULL;
314   data.type = g_type_name (type);
315   data.index = -1;
316   data.iface = NULL;
317
318   g_hash_table_foreach (repository->priv->typelib, find_interface, &data);
319
320   return data.iface;
321 }
322
323 GIBaseInfo * 
324 g_irepository_find_by_name (GIRepository *repository,
325                             const gchar  *namespace,
326                             const gchar  *name)
327 {
328   IfaceData data;
329
330   data.name = name;
331   data.type = NULL;
332   data.index = -1;
333   data.iface = NULL;
334
335   if (namespace)
336     {
337       GTypelib *typelib;
338       
339       typelib = g_hash_table_lookup (repository->priv->typelib, namespace);
340       
341       if (typelib)
342         find_interface ((void *)namespace, typelib, &data);
343     }
344   else
345     g_hash_table_foreach (repository->priv->typelib, find_interface, &data);
346
347   return data.iface;
348 }
349
350 static void
351 collect_namespaces (gpointer key,
352                     gpointer value,
353                     gpointer data)
354 {
355   GList **list = data;
356
357   *list = g_list_append (*list, key);
358 }
359
360 gchar ** 
361 g_irepository_get_namespaces (GIRepository *repository)
362 {
363   GList *l, *list = NULL;
364   gchar **names;
365   gint i;
366
367   g_hash_table_foreach (repository->priv->typelib, collect_namespaces, &list);
368
369   names = g_malloc0 (sizeof (gchar *) * (g_list_length (list) + 1));
370   i = 0;
371   for (l = list; l; l = l->next)
372     names[i++] = g_strdup (l->data); 
373   g_list_free (list);
374
375   return names;
376 }
377
378 const gchar *
379 g_irepository_get_shared_library (GIRepository *repository,
380                                    const gchar  *namespace)
381 {
382   GTypelib *typelib;
383   Header *header;
384
385   typelib = g_hash_table_lookup (repository->priv->typelib, namespace);
386   if (!typelib)
387     return NULL;
388   header = (Header *) typelib->data;
389   if (header->shared_library)
390     return g_typelib_get_string (typelib, header->shared_library);
391   else
392     return NULL;
393 }
394
395 static inline void
396 g_irepository_build_search_path (void)
397 {
398   gchar **dir;
399   gchar **tokens;
400
401   if (g_getenv ("GIREPOPATH")) {
402     gchar *path;
403     path = g_strconcat (g_getenv ("GIREPOPATH"), ":", GIREPO_DEFAULT_SEARCH_PATH, NULL);
404     tokens = g_strsplit (path, ":", 0);
405     g_free (path);
406   } else
407     tokens = g_strsplit (GIREPO_DEFAULT_SEARCH_PATH, ":", 0);
408
409   search_path = g_slist_prepend (search_path, ".");
410   for (dir = tokens; *dir; ++dir)
411     search_path = g_slist_prepend (search_path, *dir);
412   search_path = g_slist_reverse (search_path);
413   g_free (tokens);
414 }
415
416 const gchar *
417 g_irepository_register_file (GIRepository  *repository,
418                              const gchar   *namespace,
419                              GError       **error)
420 {
421   GSList *ldir;
422   const char *dir;
423   gchar *fname, *full_path;
424   GMappedFile *mfile;
425   GError *error1 = NULL;
426   GTypelib *typelib = NULL;
427   const gchar *typelib_namespace, *shlib_fname;
428   GModule *module;
429   guint32 shlib;
430   GHashTable *table;
431
432   if (repository != NULL)
433     table = repository->priv->typelib;
434   else
435     table = default_typelib;
436
437   /* don't bother loading a namespace if already registered */
438   if (g_hash_table_lookup (table, namespace))
439     return NULL;
440
441   if (search_path == NULL)
442     g_irepository_build_search_path ();
443
444   fname = g_strconcat (namespace, ".repo", NULL);
445
446   for (ldir = search_path; ldir; ldir = ldir->next) {
447     dir = ldir->data;
448     full_path = g_build_filename (dir, fname, NULL);
449     mfile = g_mapped_file_new (full_path, FALSE, &error1);
450     if (error1) {
451       g_debug ("Failed to mmap \"%s\": %s", full_path, error1->message);
452       g_clear_error (&error1);
453       g_free (full_path);
454       continue;
455     }
456     g_free (full_path);
457     typelib = g_typelib_new_from_mapped_file (mfile);
458     typelib_namespace = g_typelib_get_string (typelib, ((Header *) typelib->data)->namespace);
459     if (strcmp (typelib_namespace, namespace) != 0) {
460       g_set_error (error, G_IREPOSITORY_ERROR,
461                    G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH,
462                    "Typelib file %s for namespace '%s' contains namespace '%s'"
463                    " which doesn't match the file name",
464                    full_path, namespace, typelib_namespace);
465       return NULL; 
466     }
467     break;
468   }
469   g_free (fname);
470   if (typelib == NULL) {
471     g_set_error (error, G_IREPOSITORY_ERROR,
472                  G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
473                  "Typelib file for namespace '%s' was not found in search"
474                  " path or could not be openened", namespace);
475     return NULL;
476   }
477   /* optionally load shared library and attach it to the typelib */
478   shlib = ((Header *) typelib->data)->shared_library;
479   if (shlib) {
480     shlib_fname = g_typelib_get_string (typelib, shlib);
481     module = g_module_open (shlib_fname, G_MODULE_BIND_LAZY|G_MODULE_BIND_LOCAL);
482     if (module == NULL) {
483       g_set_error (error, G_IREPOSITORY_ERROR,
484                    G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
485                    "Typelib for namespace '%s' references shared library %s,"
486                    " but it could not be openened (%s)",
487                    namespace, shlib_fname, g_module_error ());
488       return NULL;
489     }
490   }
491
492   g_hash_table_remove (table, namespace);
493   return g_irepository_register (repository, typelib);
494 }
495
496
497 GQuark
498 g_irepository_error_quark (void)
499 {
500   static GQuark quark = 0;
501   if (quark == 0)
502     quark = g_quark_from_static_string ("g-irepository-error-quark");
503   return quark;
504 }