FIX: windows build process: enable png build
[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 DWORD last_error;
55 static char errormsg[64];
56
57 static void *
58 g_module_open(char *name, int flags)
59 {
60         HINSTANCE handle;
61         int len=MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, 0, 0);
62         wchar_t filename[len];
63         MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, filename, len) ;
64
65         handle = LoadLibraryW (filename);
66         if (!handle)
67                 last_error=GetLastError();
68         return handle;
69 }
70
71 static char *
72 g_module_error(void)
73 {
74         sprintf(errormsg,"dll error %d",(int)last_error);
75         return errormsg;
76 }
77
78 static int
79 g_module_symbol(GModule *handle, char *symbol, gpointer *addr)
80 {
81         *addr=GetProcAddress ((HANDLE)handle, symbol);
82         if (*addr)
83                 return 1;
84         last_error=GetLastError();
85         return 0;
86 }
87
88 static void
89 g_module_close(GModule *handle)
90 {
91         FreeLibrary((HANDLE)handle);
92 }
93
94 #else
95 static void *
96 g_module_open(char *name, int flags)
97 {
98         return dlopen(name,
99                 (flags & G_MODULE_BIND_LAZY ? RTLD_LAZY : RTLD_NOW) |
100                 (flags & G_MODULE_BIND_LOCAL ? RTLD_LOCAL : RTLD_GLOBAL));
101 }
102
103 static char *
104 g_module_error(void)
105 {
106         return dlerror();
107 }
108
109 static int
110 g_module_symbol(GModule *handle, char *symbol, gpointer *addr)
111 {
112         *addr=dlsym(handle, symbol);
113         return (*addr != NULL);
114 }
115
116 static void
117 g_module_close(GModule *handle)
118 {
119         dlclose(handle);
120 }
121 #endif
122 #endif
123 #endif
124
125 struct plugin {
126         int active;
127         int lazy;
128         int ondemand;
129         char *name;
130 #ifdef USE_PLUGINS
131         GModule *mod;
132 #endif
133         void (*init)(void);
134 };
135
136 struct plugins {
137         GHashTable *hash;
138         GList *list;
139 } *pls;
140
141 static struct plugin *
142 plugin_new_from_path(char *plugin)
143 {
144 #ifdef USE_PLUGINS
145         struct plugin *ret;
146         if (! g_module_supported()) {
147                return NULL;
148         }
149         ret=g_new0(struct plugin, 1);
150         ret->name=g_strdup(plugin);
151         return ret;
152 #else
153         return NULL;
154 #endif
155 }
156
157 int
158 plugin_load(struct plugin *pl)
159 {
160 #ifdef USE_PLUGINS
161         gpointer init;
162
163         GModule *mod;
164
165         if (pl->mod) {
166                 dbg(0,"can't load '%s', already loaded\n", pl->name);
167                 return 0;
168         }
169         mod=g_module_open(pl->name, G_MODULE_BIND_LOCAL | (pl->lazy ? G_MODULE_BIND_LAZY : 0));
170         if (! mod) {
171                 dbg(0,"can't load '%s', Error '%s'\n", pl->name, g_module_error());
172                 return 0;
173         }
174         if (!g_module_symbol(mod, "plugin_init", &init)) {
175                 dbg(0,"can't load '%s', plugin_init not found\n", pl->name);
176                 g_module_close(mod);
177                 return 0;
178         } else {
179                 pl->mod=mod;
180                 pl->init=init;
181         }
182         return 1;
183 #else
184         return 0;
185 #endif
186 }
187
188 char *
189 plugin_get_name(struct plugin *pl)
190 {
191         return pl->name;
192 }
193
194 int
195 plugin_get_active(struct plugin *pl)
196 {
197         return pl->active;
198 }
199
200 void
201 plugin_set_active(struct plugin *pl, int active)
202 {
203         pl->active=active;
204 }
205
206 void
207 plugin_set_lazy(struct plugin *pl, int lazy)
208 {
209         pl->lazy=lazy;
210 }
211
212 #ifdef USE_PLUGINS
213 static int
214 plugin_get_ondemand(struct plugin *pl)
215 {
216         return pl->ondemand;
217 }
218 #endif
219
220 static void
221 plugin_set_ondemand(struct plugin *pl, int ondemand)
222 {
223         pl->ondemand=ondemand;
224 }
225
226 void
227 plugin_call_init(struct plugin *pl)
228 {
229         pl->init();
230 }
231
232 void
233 plugin_unload(struct plugin *pl)
234 {
235 #ifdef USE_PLUGINS
236         g_module_close(pl->mod);
237         pl->mod=NULL;
238 #endif
239 }
240
241 void
242 plugin_destroy(struct plugin *pl)
243 {
244         g_free(pl);
245 }
246
247 struct plugins *
248 plugins_new(void)
249 {
250         struct plugins *ret=g_new0(struct plugins, 1);
251         ret->hash=g_hash_table_new(g_str_hash, g_str_equal);
252         pls=ret;
253         return ret;
254 }
255
256 struct plugin *
257 plugin_new(struct attr *parent, struct attr **attrs) {
258 #ifdef USE_PLUGINS
259         struct attr *path_attr, *attr;
260         struct file_wordexp *we;
261         int active=1; // default active
262         int lazy=0, ondemand=0;
263         int i, count;
264         char **array;
265         char *name;
266         struct plugin *pl=NULL;
267         struct plugins *pls=NULL;
268
269         if (parent)
270                 pls=parent->u.plugins;
271
272         if (! (path_attr=attr_search(attrs, NULL, attr_path))) {
273                 dbg(0,"missing path\n");
274                 return NULL;
275         }
276         if ( (attr=attr_search(attrs, NULL, attr_active))) {
277                 active=attr->u.num;
278         }
279         if ( (attr=attr_search(attrs, NULL, attr_lazy))) {
280                 lazy=attr->u.num;
281         }
282         if ( (attr=attr_search(attrs, NULL, attr_ondemand))) {
283                 ondemand=attr->u.num;
284         }
285         dbg(1, "path=\"%s\", active=%d, lazy=%d, ondemand=%d\n",path_attr->u.str, active, lazy, ondemand);
286
287         we=file_wordexp_new(path_attr->u.str);
288         count=file_wordexp_get_count(we);
289         array=file_wordexp_get_array(we);       
290         dbg(2,"expanded to %d words\n",count);
291         if (count != 1 || file_exists(array[0])) {
292                 for (i = 0 ; i < count ; i++) {
293                         name=array[i];
294                         dbg(2,"name[%d]='%s'\n", i, name);
295                         if (! (pls && (pl=g_hash_table_lookup(pls->hash, name)))) {
296                                 pl=plugin_new_from_path(name);
297                                 if (! pl) {
298                                         dbg(0,"failed to create plugin '%s'\n", name);
299                                         continue;
300                                 }
301                                 if (pls) {
302                                         g_hash_table_insert(pls->hash, plugin_get_name(pl), pl);
303                                         pls->list=g_list_append(pls->list, pl);
304                                 }
305                         } else {
306                                 if (pls) {
307                                         pls->list=g_list_remove(pls->list, pl);
308                                         pls->list=g_list_append(pls->list, pl);
309                                 }
310                         }
311                         plugin_set_active(pl, active);
312                         plugin_set_lazy(pl, lazy);
313                         plugin_set_ondemand(pl, ondemand);
314                         if (!pls && active) {
315                                 if (!plugin_load(pl)) 
316                                         plugin_set_active(pl, 0);
317                                 else
318                                         plugin_call_init(pl);
319                         }
320                 }
321                 file_wordexp_destroy(we);
322         }
323         return pl;
324 #endif
325 }
326
327 void
328 plugins_init(struct plugins *pls)
329 {
330 #ifdef USE_PLUGINS
331         struct plugin *pl;
332         GList *l;
333
334         l=pls->list;
335         while (l) {
336                 pl=l->data;
337                 if (! plugin_get_ondemand(pl)) {
338                         if (plugin_get_active(pl)) 
339                                 if (!plugin_load(pl)) 
340                                         plugin_set_active(pl, 0);
341                         if (plugin_get_active(pl)) 
342                                 plugin_call_init(pl);
343                 }
344                 l=g_list_next(l);
345         }
346 #endif
347 }
348
349 void
350 plugins_destroy(struct plugins *pls)
351 {
352         GList *l;
353         struct plugin *pl;
354
355         l=pls->list;
356         while (l) {
357                 pl=l->data;
358                 plugin_unload(pl);
359                 plugin_destroy(pl);
360         }
361         g_list_free(pls->list);
362         g_hash_table_destroy(pls->hash);
363         g_free(pls);
364 }
365
366         void *
367 plugin_get_type(enum plugin_type type, const char *type_name, const char *name)
368 {
369         dbg(1, "type=\"%s\", name=\"%s\"\n", type_name, name);
370         GList *l,*lpls;
371         struct name_val *nv;
372         struct plugin *pl;
373         char *mod_name, *filename=NULL, *corename=NULL;
374         l=plugin_types[type];
375         while (l) {
376                 nv=l->data;
377                 if (!g_ascii_strcasecmp(nv->name, name))
378                         return nv->val;
379                 l=g_list_next(l);
380         }
381         if (!pls)
382                 return NULL;
383         lpls=pls->list;
384         filename=g_strjoin("", "lib", type_name, "_", name, NULL);
385         corename=g_strjoin("", "lib", type_name, "_", "core", NULL);
386         while (lpls) {
387                 pl=lpls->data;
388                 if ((mod_name=g_strrstr(pl->name, "/")))
389                         mod_name++;
390                 else
391                         mod_name=pl->name;
392                 dbg(2,"compare '%s' with '%s'\n", mod_name, filename);
393                 if (!g_ascii_strncasecmp(mod_name, filename, strlen(filename)) || !g_ascii_strncasecmp(mod_name, corename, strlen(corename))) {
394                         dbg(1, "Loading module \"%s\"\n",pl->name) ;
395                         if (plugin_get_active(pl)) 
396                                 if (!plugin_load(pl)) 
397                                         plugin_set_active(pl, 0);
398                         if (plugin_get_active(pl)) 
399                                 plugin_call_init(pl);
400                         l=plugin_types[type];
401                         while (l) {
402                                 nv=l->data;
403                                 if (!g_ascii_strcasecmp(nv->name, name)) {
404                                         g_free(filename);
405                                         g_free(corename);
406                                         return nv->val;
407                                 }
408                                 l=g_list_next(l);
409                         }
410                 }
411                 lpls=g_list_next(lpls);
412         }
413         g_free(filename);
414         g_free(corename);
415         return NULL;
416 }