addition of silc.css
[crypto.git] / apps / irssi / src / core / modules.c
1 /*
2  modules.c : irssi
3
4     Copyright (C) 1999-2000 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 "signals.h"
24
25 #include "commands.h"
26 #include "settings.h"
27
28 GSList *modules;
29
30 static GHashTable *uniqids, *uniqstrids;
31 static GHashTable *idlookup, *stridlookup;
32 static int next_uniq_id;
33
34 void *module_check_cast(void *object, int type_pos, const char *id)
35 {
36         return object == NULL || module_find_id(id,
37                 G_STRUCT_MEMBER(int, object, type_pos)) == -1 ? NULL : object;
38 }
39
40 void *module_check_cast_module(void *object, int type_pos,
41                                const char *module, const char *id)
42 {
43         const char *str;
44
45         if (object == NULL)
46                 return NULL;
47
48         str = module_find_id_str(module,
49                                  G_STRUCT_MEMBER(int, object, type_pos));
50         return str == NULL || strcmp(str, id) != 0 ? NULL : object;
51 }
52
53 /* return unique number across all modules for `id' */
54 int module_get_uniq_id(const char *module, int id)
55 {
56         GHashTable *ids;
57         gpointer origkey, uniqid, idp;
58         int ret;
59
60         g_return_val_if_fail(module != NULL, -1);
61
62         ids = g_hash_table_lookup(idlookup, module);
63         if (ids == NULL) {
64                 /* new module */
65                 ids = g_hash_table_new((GHashFunc) g_direct_hash,
66                                        (GCompareFunc) g_direct_equal);
67                 g_hash_table_insert(idlookup, g_strdup(module), ids);
68         }
69
70         idp = GINT_TO_POINTER(id);
71         if (!g_hash_table_lookup_extended(ids, idp, &origkey, &uniqid)) {
72                 /* not found */
73                 ret = next_uniq_id++;
74                 g_hash_table_insert(ids, idp, GINT_TO_POINTER(ret));
75                 g_hash_table_insert(uniqids, GINT_TO_POINTER(ret), idp);
76         } else {
77                 ret = GPOINTER_TO_INT(uniqid);
78         }
79
80         return ret;
81 }
82
83 /* return unique number across all modules for `id' */
84 int module_get_uniq_id_str(const char *module, const char *id)
85 {
86         GHashTable *ids;
87         gpointer origkey, uniqid;
88         int ret;
89
90         g_return_val_if_fail(module != NULL, -1);
91
92         ids = g_hash_table_lookup(stridlookup, module);
93         if (ids == NULL) {
94                 /* new module */
95                 ids = g_hash_table_new((GHashFunc) g_str_hash,
96                                        (GCompareFunc) g_str_equal);
97                 g_hash_table_insert(stridlookup, g_strdup(module), ids);
98         }
99
100         if (!g_hash_table_lookup_extended(ids, id, &origkey, &uniqid)) {
101                 /* not found */
102                 char *saveid;
103
104                 saveid = g_strdup(id);
105                 ret = next_uniq_id++;
106                 g_hash_table_insert(ids, saveid, GINT_TO_POINTER(ret));
107                 g_hash_table_insert(uniqstrids, GINT_TO_POINTER(ret), saveid);
108         } else {
109                 ret = GPOINTER_TO_INT(uniqid);
110         }
111
112         return ret;
113 }
114
115 /* returns the original module specific id, -1 = not found */
116 int module_find_id(const char *module, int uniqid)
117 {
118         GHashTable *idlist;
119         gpointer origkey, id;
120         int ret;
121
122         g_return_val_if_fail(module != NULL, -1);
123
124         if (!g_hash_table_lookup_extended(uniqids, GINT_TO_POINTER(uniqid),
125                                           &origkey, &id))
126                 return -1;
127
128         /* check that module matches */
129         idlist = g_hash_table_lookup(idlookup, module);
130         if (idlist == NULL)
131                 return -1;
132
133         ret = GPOINTER_TO_INT(id);
134         if (!g_hash_table_lookup_extended(idlist, id, &origkey, &id) ||
135             GPOINTER_TO_INT(id) != uniqid)
136                 ret = -1;
137
138         return ret;
139 }
140
141 /* returns the original module specific id, NULL = not found */
142 const char *module_find_id_str(const char *module, int uniqid)
143 {
144         GHashTable *idlist;
145         gpointer origkey, id;
146         const char *ret;
147
148         g_return_val_if_fail(module != NULL, NULL);
149
150         if (!g_hash_table_lookup_extended(uniqstrids, GINT_TO_POINTER(uniqid),
151                                           &origkey, &id))
152                 return NULL;
153
154         /* check that module matches */
155         idlist = g_hash_table_lookup(stridlookup, module);
156         if (idlist == NULL)
157                 return NULL;
158
159         ret = id;
160         if (!g_hash_table_lookup_extended(idlist, id, &origkey, &id) ||
161             GPOINTER_TO_INT(id) != uniqid)
162                 ret = NULL;
163
164         return ret;
165 }
166
167 static void uniq_destroy(gpointer key, gpointer value)
168 {
169         g_hash_table_remove(uniqids, value);
170 }
171
172 static void uniq_destroy_str(gpointer key, gpointer value)
173 {
174         g_hash_table_remove(uniqstrids, value);
175         g_free(key);
176 }
177
178 /* Destroy unique IDs from `module'. This function is automatically called
179    when module is destroyed with module's name as the parameter. */
180 void module_uniq_destroy(const char *module)
181 {
182         GHashTable *idlist;
183         gpointer key;
184
185         if (g_hash_table_lookup_extended(idlookup, module, &key,
186                                          (gpointer *) &idlist)) {
187                 g_hash_table_remove(idlookup, key);
188                 g_free(key);
189
190                 g_hash_table_foreach(idlist, (GHFunc) uniq_destroy, NULL);
191                 g_hash_table_destroy(idlist);
192         }
193
194         if (g_hash_table_lookup_extended(stridlookup, module, &key,
195                                          (gpointer *) &idlist)) {
196                 g_hash_table_remove(stridlookup, key);
197                 g_free(key);
198
199                 g_hash_table_foreach(idlist, (GHFunc) uniq_destroy_str, NULL);
200                 g_hash_table_destroy(idlist);
201         }
202 }
203
204 MODULE_REC *module_find(const char *name)
205 {
206         GSList *tmp;
207
208         for (tmp = modules; tmp != NULL; tmp = tmp->next) {
209                 MODULE_REC *rec = tmp->data;
210
211                 if (g_strcasecmp(rec->name, name) == 0)
212                         return rec;
213         }
214
215         return NULL;
216 }
217
218 #ifdef HAVE_GMODULE
219 static char *module_get_name(const char *path, int *start, int *end)
220 {
221         const char *name;
222         char *module_name, *ptr;
223
224         name = NULL;
225         if (g_path_is_absolute(path)) {
226                 name = strrchr(path, G_DIR_SEPARATOR);
227                 if (name != NULL) name++;
228         }
229
230         if (name == NULL)
231                 name = path;
232
233         if (strncmp(name, "lib", 3) == 0)
234                 name += 3;
235
236         module_name = g_strdup(name);
237         ptr = strchr(module_name, '.');
238         if (ptr != NULL) *ptr = '\0';
239
240         *start = (int) (name-path);
241         *end = *start + (ptr == NULL ? strlen(name) :
242                          (int) (module_name-ptr));
243
244         return module_name;
245 }
246
247 static GModule *module_open(const char *name)
248 {
249         struct stat statbuf;
250         GModule *module;
251         char *path, *str;
252
253         if (g_path_is_absolute(name) ||
254             (*name == '.' && name[1] == G_DIR_SEPARATOR))
255                 path = g_strdup(name);
256         else {
257                 /* first try from home dir */
258                 str = g_strdup_printf("%s/.silc/modules", g_get_home_dir());
259                 path = g_module_build_path(str, name);
260                 g_free(str);
261
262                 if (stat(path, &statbuf) == 0) {
263                         module = g_module_open(path, (GModuleFlags) 0);
264                         g_free(path);
265                         return module;
266                 }
267
268                 /* module not found from home dir, try global module dir */
269                 g_free(path);
270                 path = g_module_build_path(MODULEDIR, name);
271         }
272
273         module = g_module_open(path, (GModuleFlags) 0);
274         g_free(path);
275         return module;
276 }
277
278 #define module_error(error, module, text) \
279         signal_emit("module error", 3, GINT_TO_POINTER(error), module, text)
280
281 static int module_load_name(const char *path, const char *name, int silent)
282 {
283         void (*module_init) (void);
284         GModule *gmodule;
285         MODULE_REC *rec;
286         char *initfunc;
287
288         gmodule = module_open(path);
289         if (gmodule == NULL) {
290                 if (!silent) {
291                         module_error(MODULE_ERROR_LOAD, name,
292                                      g_module_error());
293                 }
294                 return FALSE;
295         }
296
297         /* get the module's init() function */
298         initfunc = g_strconcat(name, "_init", NULL);
299         if (!g_module_symbol(gmodule, initfunc, (gpointer *) &module_init)) {
300                 if (!silent)
301                         module_error(MODULE_ERROR_INVALID, name, NULL);
302                 g_module_close(gmodule);
303                 g_free(initfunc);
304                 return FALSE;
305         }
306         g_free(initfunc);
307
308         rec = g_new0(MODULE_REC, 1);
309         rec->name = g_strdup(name);
310         rec->gmodule = gmodule;
311         modules = g_slist_append(modules, rec);
312
313         module_init();
314         settings_check_module(name);
315
316         signal_emit("module loaded", 1, rec);
317         return TRUE;
318 }
319 #endif
320
321 /* Load module - automatically tries to load also the related non-core
322    modules given in `prefixes' (like irc, fe, fe_text, ..) */
323 int module_load(const char *path, char **prefixes)
324 {
325 #ifdef HAVE_GMODULE
326         GString *realpath;
327         char *name, *pname;
328         int ret, start, end;
329
330         g_return_val_if_fail(path != NULL, FALSE);
331
332         if (!g_module_supported())
333                 return FALSE;
334
335         name = module_get_name(path, &start, &end);
336         if (module_find(name)) {
337                 module_error(MODULE_ERROR_ALREADY_LOADED, name, NULL);
338                 g_free(name);
339                 return FALSE;
340         }
341
342         /* load "module_core" instead of "module" if it exists */
343         realpath = g_string_new(path);
344         g_string_insert(realpath, end, "_core");
345
346         pname = g_strconcat(name, "_core", NULL);
347         ret = module_load_name(realpath->str, pname, TRUE);
348         g_free(pname);
349
350         if (!ret) {
351                 /* load "module" - complain if it's not found */
352                 ret = module_load_name(path, name, FALSE);
353         } else if (prefixes != NULL) {
354                 /* load all the "prefix modules", like the fe-common, irc,
355                    etc. part of the module */
356                 while (*prefixes != NULL) {
357                         g_string_assign(realpath, path);
358                         g_string_insert(realpath, start, "_");
359                         g_string_insert(realpath, start, *prefixes);
360
361                         pname = g_strconcat(*prefixes, "_", name, NULL);
362                         module_load_name(realpath->str, pname, TRUE);
363                         g_free(pname);
364
365                         prefixes++;
366                 }
367         }
368
369         g_string_free(realpath, TRUE);
370         g_free(name);
371         return ret;
372 #else
373         return FALSE;
374 #endif
375 }
376
377 void module_unload(MODULE_REC *module)
378 {
379 #ifdef HAVE_GMODULE
380         void (*module_deinit) (void);
381         char *deinitfunc;
382
383         g_return_if_fail(module != NULL);
384
385         modules = g_slist_remove(modules, module);
386
387         signal_emit("module unloaded", 1, module);
388
389         /* call the module's deinit() function */
390         deinitfunc = g_strconcat(module->name, "_deinit", NULL);
391         if (g_module_symbol(module->gmodule, deinitfunc,
392                             (gpointer *) &module_deinit))
393                 module_deinit();
394         g_free(deinitfunc);
395
396         settings_remove_module(module->name);
397         commands_remove_module(module->name);
398         signals_remove_module(module->name);
399
400         g_module_close(module->gmodule);
401         g_free(module->name);
402         g_free(module);
403 #endif
404 }
405
406 static void uniq_get_modules(char *key, void *value, GSList **list)
407 {
408         *list = g_slist_append(*list, g_strdup(key));
409 }
410
411 void modules_init(void)
412 {
413         modules = NULL;
414
415         idlookup = g_hash_table_new((GHashFunc) g_str_hash,
416                                     (GCompareFunc) g_str_equal);
417         uniqids = g_hash_table_new((GHashFunc) g_direct_hash,
418                                    (GCompareFunc) g_direct_equal);
419
420         stridlookup = g_hash_table_new((GHashFunc) g_str_hash,
421                                        (GCompareFunc) g_str_equal);
422         uniqstrids = g_hash_table_new((GHashFunc) g_direct_hash,
423                                       (GCompareFunc) g_direct_equal);
424         next_uniq_id = 0;
425 }
426
427 void modules_deinit(void)
428 {
429         GSList *list;
430
431         list = NULL;
432         g_hash_table_foreach(idlookup, (GHFunc) uniq_get_modules, &list);
433         g_hash_table_foreach(stridlookup, (GHFunc) uniq_get_modules, &list);
434
435         while (list != NULL) {
436                 module_uniq_destroy(list->data);
437                 g_free(list->data);
438                 list = g_slist_remove(list, list->data);
439         }
440
441         g_hash_table_destroy(idlookup);
442         g_hash_table_destroy(stridlookup);
443         g_hash_table_destroy(uniqids);
444         g_hash_table_destroy(uniqstrids);
445 }