cc7011bd8d3dcf0f5b043cd7d08deeb5775b62eb
[navit-package] / navit / plugin.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2008 Navit Team
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * version 2 as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19
20 #include <string.h>
21 #include <glib.h>
22 #include "config.h"
23 #ifdef USE_PLUGINS
24 #ifdef HAVE_GMODULE
25 #include <gmodule.h>
26 #else
27 #ifdef HAVE_API_WIN32_BASE
28 #include <windows.h>
29 #else
30 #include <dlfcn.h>
31 #endif
32 #endif
33 #endif
34 #include "plugin.h"
35 #include "file.h"
36 #define PLUGIN_C
37 #include "plugin.h"
38 #include "item.h"
39 #include "debug.h"
40
41 #ifdef USE_PLUGINS
42 #ifndef HAVE_GMODULE
43 typedef void * GModule;
44 #define G_MODULE_BIND_LOCAL 1
45 #define G_MODULE_BIND_LAZY 2
46 static int
47 g_module_supported(void)
48 {
49         return 1;
50 }
51
52 #ifdef HAVE_API_WIN32_BASE
53
54 static void *
55 g_module_open(char *name, int flags)
56 {
57         HINSTANCE handle;
58         int len=MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, 0, 0);
59         wchar_t filename[len];
60         MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, filename, len) ;
61
62         dbg(0,"%s\n",name);
63         handle = LoadLibraryW (filename);
64         dbg(0,"handle=%p\n",handle);
65         dbg(0,"%d\n",GetLastError ());
66         return handle;
67 }
68
69 static char *
70 g_module_error(void)
71 {
72         return NULL;
73 }
74
75 static int
76 g_module_symbol(GModule *handle, char *symbol, gpointer *addr)
77 {
78         *addr=GetProcAddress ((HANDLE)handle, symbol);
79         return (*addr != NULL);
80 }
81
82 static void
83 g_module_close(GModule *handle)
84 {
85 }
86
87 #else
88 static void *
89 g_module_open(char *name, int flags)
90 {
91         return dlopen(name,
92                 (flags & G_MODULE_BIND_LAZY ? RTLD_LAZY : RTLD_NOW) |
93                 (flags & G_MODULE_BIND_LOCAL ? RTLD_LOCAL : RTLD_GLOBAL));
94 }
95
96 static char *
97 g_module_error(void)
98 {
99         return dlerror();
100 }
101
102 static int
103 g_module_symbol(GModule *handle, char *symbol, gpointer *addr)
104 {
105         *addr=dlsym(handle, symbol);
106         return (*addr != NULL);
107 }
108
109 static void
110 g_module_close(GModule *handle)
111 {
112         dlclose(handle);
113 }
114 #endif
115 #endif
116 #endif
117
118 struct plugin {
119         int active;
120         int lazy;
121         int ondemand;
122         char *name;
123 #ifdef USE_PLUGINS
124         GModule *mod;
125 #endif
126         void (*init)(void);
127 };
128
129 struct plugins {
130         GHashTable *hash;
131         GList *list;
132 } *pls;
133
134 static struct plugin *
135 plugin_new_from_path(char *plugin)
136 {
137 #ifdef USE_PLUGINS
138         struct plugin *ret;
139         if (! g_module_supported()) {
140                return NULL;
141         }
142         ret=g_new0(struct plugin, 1);
143         ret->name=g_strdup(plugin);
144         return ret;
145 #else
146         return NULL;
147 #endif
148 }
149
150 int
151 plugin_load(struct plugin *pl)
152 {
153 #ifdef USE_PLUGINS
154         gpointer init;
155
156         GModule *mod;
157
158         if (pl->mod) {
159                 dbg(0,"can't load '%s', already loaded\n", pl->name);
160                 return 0;
161         }
162         mod=g_module_open(pl->name, G_MODULE_BIND_LOCAL | (pl->lazy ? G_MODULE_BIND_LAZY : 0));
163         if (! mod) {
164                 dbg(0,"can't load '%s', Error '%s'\n", pl->name, g_module_error());
165                 return 0;
166         }
167         if (!g_module_symbol(mod, "plugin_init", &init)) {
168                 dbg(0,"can't load '%s', plugin_init not found\n", pl->name);
169                 g_module_close(mod);
170                 return 0;
171         } else {
172                 pl->mod=mod;
173                 pl->init=init;
174         }
175         return 1;
176 #else
177         return 0;
178 #endif
179 }
180
181 char *
182 plugin_get_name(struct plugin *pl)
183 {
184         return pl->name;
185 }
186
187 int
188 plugin_get_active(struct plugin *pl)
189 {
190         return pl->active;
191 }
192
193 void
194 plugin_set_active(struct plugin *pl, int active)
195 {
196         pl->active=active;
197 }
198
199 void
200 plugin_set_lazy(struct plugin *pl, int lazy)
201 {
202         pl->lazy=lazy;
203 }
204
205 #ifdef USE_PLUGINS
206 static int
207 plugin_get_ondemand(struct plugin *pl)
208 {
209         return pl->ondemand;
210 }
211 #endif
212
213 static void
214 plugin_set_ondemand(struct plugin *pl, int ondemand)
215 {
216         pl->ondemand=ondemand;
217 }
218
219 void
220 plugin_call_init(struct plugin *pl)
221 {
222         pl->init();
223 }
224
225 void
226 plugin_unload(struct plugin *pl)
227 {
228 #ifdef USE_PLUGINS
229         g_module_close(pl->mod);
230         pl->mod=NULL;
231 #endif
232 }
233
234 void
235 plugin_destroy(struct plugin *pl)
236 {
237         g_free(pl);
238 }
239
240 struct plugins *
241 plugins_new(void)
242 {
243         struct plugins *ret=g_new0(struct plugins, 1);
244         ret->hash=g_hash_table_new(g_str_hash, g_str_equal);
245         pls=ret;
246         return ret;
247 }
248
249 struct plugin *
250 plugin_new(struct attr *parent, struct attr **attrs) {
251 #ifdef USE_PLUGINS
252         struct attr *path_attr, *attr;
253         struct file_wordexp *we;
254         int active=1; // default active
255         int lazy=0, ondemand=0;
256         int i, count;
257         char **array;
258         char *name;
259         struct plugin *pl=NULL;
260         struct plugins *pls=NULL;
261
262         if (parent)
263                 pls=parent->u.plugins;
264
265         if (! (path_attr=attr_search(attrs, NULL, attr_path))) {
266                 dbg(0,"missing path\n");
267                 return NULL;
268         }
269         if ( (attr=attr_search(attrs, NULL, attr_active))) {
270                 active=attr->u.num;
271         }
272         if ( (attr=attr_search(attrs, NULL, attr_lazy))) {
273                 lazy=attr->u.num;
274         }
275         if ( (attr=attr_search(attrs, NULL, attr_ondemand))) {
276                 ondemand=attr->u.num;
277         }
278         dbg(1, "path=\"%s\", active=%d, lazy=%d, ondemand=%d\n",path_attr->u.str, active, lazy, ondemand);
279
280         we=file_wordexp_new(path_attr->u.str);
281         count=file_wordexp_get_count(we);
282         array=file_wordexp_get_array(we);       
283         dbg(2,"expanded to %d words\n",count);
284         if (count != 1 || file_exists(array[0])) {
285                 for (i = 0 ; i < count ; i++) {
286                         name=array[i];
287                         dbg(2,"name[%d]='%s'\n", i, name);
288                         if (! (pls && (pl=g_hash_table_lookup(pls->hash, name)))) {
289                                 pl=plugin_new_from_path(name);
290                                 if (! pl) {
291                                         dbg(0,"failed to create plugin '%s'\n", name);
292                                         continue;
293                                 }
294                                 if (pls) {
295                                         g_hash_table_insert(pls->hash, plugin_get_name(pl), pl);
296                                         pls->list=g_list_append(pls->list, pl);
297                                 }
298                         } else {
299                                 if (pls) {
300                                         pls->list=g_list_remove(pls->list, pl);
301                                         pls->list=g_list_append(pls->list, pl);
302                                 }
303                         }
304                         plugin_set_active(pl, active);
305                         plugin_set_lazy(pl, lazy);
306                         plugin_set_ondemand(pl, ondemand);
307                         if (!pls && active) {
308                                 if (!plugin_load(pl)) 
309                                         plugin_set_active(pl, 0);
310                                 else
311                                         plugin_call_init(pl);
312                         }
313                 }
314                 file_wordexp_destroy(we);
315         }
316         return pl;
317 #endif
318 }
319
320 void
321 plugins_init(struct plugins *pls)
322 {
323 #ifdef USE_PLUGINS
324         struct plugin *pl;
325         GList *l;
326
327         l=pls->list;
328         while (l) {
329                 pl=l->data;
330                 if (! plugin_get_ondemand(pl)) {
331                         if (plugin_get_active(pl)) 
332                                 if (!plugin_load(pl)) 
333                                         plugin_set_active(pl, 0);
334                         if (plugin_get_active(pl)) 
335                                 plugin_call_init(pl);
336                 }
337                 l=g_list_next(l);
338         }
339 #endif
340 }
341
342 void
343 plugins_destroy(struct plugins *pls)
344 {
345         GList *l;
346         struct plugin *pl;
347
348         l=pls->list;
349         while (l) {
350                 pl=l->data;
351                 plugin_unload(pl);
352                 plugin_destroy(pl);
353         }
354         g_list_free(pls->list);
355         g_hash_table_destroy(pls->hash);
356         g_free(pls);
357 }
358
359         void *
360 plugin_get_type(enum plugin_type type, const char *type_name, const char *name)
361 {
362         dbg(1, "type=\"%s\", name=\"%s\"\n", type_name, name);
363         GList *l,*lpls;
364         struct name_val *nv;
365         struct plugin *pl;
366         char *mod_name, *filename=NULL, *corename=NULL;
367         l=plugin_types[type];
368         while (l) {
369                 nv=l->data;
370                 if (!g_ascii_strcasecmp(nv->name, name))
371                         return nv->val;
372                 l=g_list_next(l);
373         }
374         if (!pls)
375                 return NULL;
376         lpls=pls->list;
377         filename=g_strjoin("", "lib", type_name, "_", name, NULL);
378         corename=g_strjoin("", "lib", type_name, "_", "core", NULL);
379         while (lpls) {
380                 pl=lpls->data;
381                 if ((mod_name=g_strrstr(pl->name, "/")))
382                         mod_name++;
383                 else
384                         mod_name=pl->name;
385                 dbg(2,"compare '%s' with '%s'\n", mod_name, filename);
386                 if (!g_ascii_strncasecmp(mod_name, filename, strlen(filename)) || !g_ascii_strncasecmp(mod_name, corename, strlen(corename))) {
387                         dbg(1, "Loading module \"%s\"\n",pl->name) ;
388                         if (plugin_get_active(pl)) 
389                                 if (!plugin_load(pl)) 
390                                         plugin_set_active(pl, 0);
391                         if (plugin_get_active(pl)) 
392                                 plugin_call_init(pl);
393                         l=plugin_types[type];
394                         while (l) {
395                                 nv=l->data;
396                                 if (!g_ascii_strcasecmp(nv->name, name)) {
397                                         g_free(filename);
398                                         g_free(corename);
399                                         return nv->val;
400                                 }
401                                 l=g_list_next(l);
402                         }
403                 }
404                 lpls=g_list_next(lpls);
405         }
406         g_free(filename);
407         g_free(corename);
408         return NULL;
409 }