Merged with Irssi CVS.
[silc.git] / apps / irssi / src / core / modules-load.c
1 /*
2  modules-load.c : irssi
3
4     Copyright (C) 1999-2001 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program 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
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "modules.h"
23 #include "modules-load.h"
24 #include "signals.h"
25
26 #include "settings.h"
27 #include "commands.h"
28 #include "misc.h"
29
30 #ifdef HAVE_GMODULE
31
32 /* Returns the module name without path, "lib" prefix or ".so" suffix */
33 static char *module_get_name(const char *path, int *start, int *end)
34 {
35         const char *name;
36         char *module_name, *ptr;
37
38         name = NULL;
39         if (*path == '~' || g_path_is_absolute(path)) {
40                 name = strrchr(path, G_DIR_SEPARATOR);
41                 if (name != NULL) name++;
42         }
43
44         if (name == NULL)
45                 name = path;
46
47         if (strncmp(name, "lib", 3) == 0)
48                 name += 3;
49
50         module_name = g_strdup(name);
51         ptr = strchr(module_name, '.');
52         if (ptr != NULL) *ptr = '\0';
53
54         *start = (int) (name-path);
55         *end = *start + (ptr == NULL ? strlen(name) :
56                          (int) (ptr-module_name));
57
58         return module_name;
59 }
60
61 /* Returns the root module name for given submodule (eg. perl_core -> perl) */
62 static char *module_get_root(const char *name, char **prefixes)
63 {
64         int len;
65
66         /* skip any of the prefixes.. */
67         if (prefixes != NULL) {
68                 while (*prefixes != NULL) {
69                         len = strlen(*prefixes);
70                         if (strncmp(name, *prefixes, len) == 0 &&
71                             name[len] == '_') {
72                                 name += len+1;
73                                 break;
74                         }
75                         prefixes++;
76                 }
77         }
78
79         /* skip the _core part */
80         len = strlen(name);
81         if (len > 5 && strcmp(name+len-5, "_core") == 0)
82                 return g_strndup(name, len-5);
83
84         return g_strdup(name);
85 }
86
87 /* Returns the sub module name for given submodule (eg. perl_core -> core) */
88 static char *module_get_sub(const char *name, const char *root)
89 {
90         int rootlen, namelen;
91
92         namelen = strlen(name);
93         rootlen = strlen(root);
94         g_return_val_if_fail(namelen >= rootlen, g_strdup(name));
95
96         if (strncmp(name, root, rootlen) == 0 &&
97             strcmp(name+rootlen, "_core") == 0)
98                 return g_strdup("core");
99
100         if (namelen+1 > rootlen && name[namelen-rootlen-1] == '_' &&
101             strcmp(name+namelen-rootlen, root) == 0)
102                 return g_strndup(name, namelen-rootlen-1);
103
104         return g_strdup(name);
105 }
106
107 static GModule *module_open(const char *name, int *found)
108 {
109         struct stat statbuf;
110         GModule *module;
111         char *path, *str;
112
113         if (g_path_is_absolute(name) || *name == '~' ||
114             (*name == '.' && name[1] == G_DIR_SEPARATOR))
115                 path = g_strdup(name);
116         else {
117                 /* first try from home dir */
118                 str = g_strdup_printf("%s/modules", get_irssi_dir());
119                 path = g_module_build_path(str, name);
120                 g_free(str);
121
122                 if (stat(path, &statbuf) == 0) {
123                         module = g_module_open(path, (GModuleFlags) 0);
124                         g_free(path);
125                         *found = TRUE;
126                         return module;
127                 }
128
129                 /* module not found from home dir, try global module dir */
130                 g_free(path);
131                 path = g_module_build_path(MODULEDIR, name);
132         }
133
134         *found = stat(path, &statbuf) == 0;
135         module = g_module_open(path, (GModuleFlags) 0);
136         g_free(path);
137         return module;
138 }
139
140 static char *module_get_func(const char *rootmodule, const char *submodule,
141                              const char *function)
142 {
143         if (strcmp(submodule, "core") == 0)
144                 return g_strconcat(rootmodule, "_core_", function, NULL);
145
146         if (strcmp(rootmodule, submodule) == 0)
147                 return g_strconcat(rootmodule, "_", function, NULL);
148
149         return g_strconcat(submodule, "_", rootmodule, "_", function, NULL);
150 }
151
152 #define module_error(error, text, rootmodule, submodule) \
153         signal_emit("module error", 4, GINT_TO_POINTER(error), text, \
154                     rootmodule, submodule)
155
156 /* Returns 1 if ok, 0 if error in module and
157    -1 if module wasn't found */
158 static int module_load_name(const char *path, const char *rootmodule,
159                             const char *submodule, int silent)
160 {
161         void (*module_init) (void);
162         void (*module_deinit) (void);
163         GModule *gmodule;
164         MODULE_REC *module;
165         MODULE_FILE_REC *rec;
166         char *initfunc, *deinitfunc;
167         int found;
168
169         gmodule = module_open(path, &found);
170         if (gmodule == NULL) {
171                 if (!silent || found) {
172                         module_error(MODULE_ERROR_LOAD, g_module_error(),
173                                      rootmodule, submodule);
174                 }
175                 return found ? 0 : -1;
176         }
177
178         /* get the module's init() and deinit() functions */
179         initfunc = module_get_func(rootmodule, submodule, "init");
180         deinitfunc = module_get_func(rootmodule, submodule, "deinit");
181         found = g_module_symbol(gmodule, initfunc, (gpointer *) &module_init) &&
182                 g_module_symbol(gmodule, deinitfunc, (gpointer *) &module_deinit);
183         g_free(initfunc);
184         g_free(deinitfunc);
185
186         if (!found) {
187                 module_error(MODULE_ERROR_INVALID, NULL,
188                              rootmodule, submodule);
189                 g_module_close(gmodule);
190                 return 0;
191         }
192
193         /* Call the module's init() function - it should register itself
194            with module_register() function, abort if it doesn't. */
195         module_init();
196
197         module = module_find(rootmodule);
198         rec = module == NULL ? NULL :
199                 strcmp(rootmodule, submodule) == 0 ?
200                 module_file_find(module, "core") :
201                 module_file_find(module, submodule);
202         if (rec == NULL) {
203                 rec = module_register_full(rootmodule, submodule, NULL);
204                 rec->gmodule = gmodule;
205                 module_file_unload(rec);
206
207                 module_error(MODULE_ERROR_INVALID, NULL,
208                              rootmodule, submodule);
209                 return 0;
210         }
211
212         rec->module_deinit = module_deinit;
213         rec->gmodule = gmodule;
214         rec->initialized = TRUE;
215
216         settings_check_module(rec->defined_module_name);
217
218         signal_emit("module loaded", 2, rec->root, rec);
219         return 1;
220 }
221
222 static int module_load_prefixes(const char *path, const char *module,
223                                 int start, int end, char **prefixes)
224 {
225         GString *realpath;
226         int status, ok;
227
228         /* load module_core */
229         realpath = g_string_new(path);
230         g_string_insert(realpath, end, "_core");
231
232         /* Don't print the error message the first time, since the module
233            may not have the core part at all. */
234         status = module_load_name(realpath->str, module, "core", TRUE);
235         ok = status > 0;
236
237         if (prefixes != NULL) {
238                 /* load all the "prefix modules", like the fe-common, irc,
239                    etc. part of the module */
240                 while (*prefixes != NULL) {
241                         g_string_assign(realpath, path);
242                         g_string_insert_c(realpath, start, '_');
243                         g_string_insert(realpath, start, *prefixes);
244
245                         status = module_load_name(realpath->str, module,
246                                                   *prefixes, TRUE);
247                         if (status > 0)
248                                 ok = TRUE;
249
250                         prefixes++;
251                 }
252         }
253
254         if (!ok) {
255                 /* error loading module, print the error message */
256                 g_string_assign(realpath, path);
257                 g_string_insert(realpath, end, "_core");
258                 module_load_name(realpath->str, module, "core", FALSE);
259         }
260
261         g_string_free(realpath, TRUE);
262         return ok;
263 }
264
265 static int module_load_full(const char *path, const char *rootmodule,
266                             const char *submodule, int start, int end,
267                             char **prefixes)
268 {
269         MODULE_REC *module;
270         int status, try_prefixes;
271
272         if (!g_module_supported())
273                 return FALSE;
274
275         module = module_find(rootmodule);
276         if (module != NULL && (strcmp(submodule, rootmodule) == 0 ||
277                                module_file_find(module, submodule) != NULL)) {
278                 /* module is already loaded */
279                 module_error(MODULE_ERROR_ALREADY_LOADED, NULL,
280                              rootmodule, submodule);
281                 return FALSE;
282         }
283
284         /* check if the given module exists.. */
285         try_prefixes = strcmp(rootmodule, submodule) == 0;
286         status = module_load_name(path, rootmodule, submodule, try_prefixes);
287         if (status == -1 && try_prefixes) {
288                 /* nope, try loading the module_core,
289                    fe_module, etc. */
290                 status = module_load_prefixes(path, rootmodule,
291                                               start, end, prefixes);
292         }
293
294         return status > 0;
295 }
296
297 /* Load module - automatically tries to load also the related non-core
298    modules given in `prefixes' (like irc, fe, fe_text, ..) */
299 int module_load(const char *path, char **prefixes)
300 {
301         char *exppath, *name, *submodule, *rootmodule;
302         int start, end, ret;
303
304         g_return_val_if_fail(path != NULL, FALSE);
305
306         exppath = convert_home(path);
307
308         name = module_get_name(exppath, &start, &end);
309         rootmodule = module_get_root(name, prefixes);
310         submodule = module_get_sub(name, rootmodule);
311         g_free(name);
312
313         ret = module_load_full(exppath, rootmodule, submodule,
314                                start, end, prefixes);
315
316         g_free(rootmodule);
317         g_free(submodule);
318         g_free(exppath);
319         return ret;
320 }
321
322 /* Load a sub module. */
323 int module_load_sub(const char *path, const char *submodule, char **prefixes)
324 {
325         GString *full_path;
326         char *exppath, *name, *rootmodule;
327         int start, end, ret;
328
329         g_return_val_if_fail(path != NULL, FALSE);
330         g_return_val_if_fail(submodule != NULL, FALSE);
331
332         exppath = convert_home(path);
333
334         name = module_get_name(exppath, &start, &end);
335         rootmodule = module_get_root(name, prefixes);
336         g_free(name);
337
338         full_path = g_string_new(exppath);
339         if (strcmp(submodule, "core") == 0)
340                 g_string_insert(full_path, end, "_core");
341         else {
342                 g_string_insert_c(full_path, start, '_');
343                 g_string_insert(full_path, start, submodule);
344         }
345
346         ret = module_load_full(full_path->str, rootmodule, submodule,
347                                start, end, NULL);
348
349         g_string_free(full_path, TRUE);
350         g_free(rootmodule);
351         g_free(exppath);
352         return ret;
353 }
354
355 static void module_file_deinit_gmodule(MODULE_FILE_REC *file)
356 {
357         /* call the module's deinit() function */
358         if (file->module_deinit != NULL)
359                 file->module_deinit();
360
361         if (file->defined_module_name != NULL) {
362                 settings_remove_module(file->defined_module_name);
363                 commands_remove_module(file->defined_module_name);
364                 signals_remove_module(file->defined_module_name);
365         }
366
367         g_module_close(file->gmodule);
368 }
369
370 #else /* !HAVE_GMODULE - modules are not supported */
371
372 int module_load(const char *path, char **prefixes)
373 {
374         return FALSE;
375 }
376
377 #endif
378
379 void module_file_unload(MODULE_FILE_REC *file)
380 {
381         MODULE_REC *root;
382
383         root = file->root;
384         root->files = g_slist_remove(root->files, file);
385
386         if (file->initialized)
387                 signal_emit("module unloaded", 2, file->root, file);
388
389 #ifdef HAVE_GMODULE
390         if (file->gmodule != NULL)
391                 module_file_deinit_gmodule(file);
392 #endif
393
394         g_free(file->name);
395         g_free(file->defined_module_name);
396         g_free(file);
397
398         if (root->files == NULL && g_slist_find(modules, root) != NULL)
399                 module_unload(root);
400 }
401
402 void module_unload(MODULE_REC *module)
403 {
404         g_return_if_fail(module != NULL);
405
406         modules = g_slist_remove(modules, module);
407
408         signal_emit("module unloaded", 1, module);
409
410         while (module->files != NULL)
411                 module_file_unload(module->files->data);
412
413         g_free(module->name);
414         g_free(module);
415 }