2006-12-03 Dimitris Glezos <dimitris@glezos.com>
[dia.git] / lib / plug-ins.c
blob3fb440aa72a8debb27a3ff908cb4d1ed83bcae5b
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 Alexander Larsson
4 * plug-ins.c: plugin loading handling interfaces for dia
5 * Copyright (C) 2000 James Henstridge
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include <config.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
34 #include <libxml/xmlmemory.h>
35 #include <libxml/parser.h>
36 #include <libxml/tree.h>
37 #include "dia_xml_libxml.h"
38 #include "dia_xml.h"
40 #include "plug-ins.h"
41 #include "intl.h"
42 #include "message.h"
43 #include "dia_dirs.h"
45 #ifdef G_OS_WIN32
46 #include <io.h> /* open, close */
47 #endif
49 #if defined(G_OS_WIN32) || defined(__EMX__)
50 # define PLUG_IN_EXT ".dll"
51 # define PLUG_IN_EXT_LEN 4
52 # define USING_LIBTOOL 0
53 #else
54 /* this one should work on any platform where libtool is used to compile dia */
55 # define PLUG_IN_EXT ".la"
56 # define PLUG_IN_EXT_LEN 3
57 # define USING_LIBTOOL 1
58 #endif
60 struct _PluginInfo {
61 GModule *module;
62 gchar *filename; /* plugin filename */
63 gchar *real_filename; /* not a .la file */
65 gboolean is_loaded;
66 gboolean inhibit_load;
68 gchar *name;
69 gchar *description;
71 PluginInitFunc init_func;
72 PluginCanUnloadFunc can_unload_func;
73 PluginUnloadFunc unload_func;
77 static GList *plugins = NULL;
79 static void ensure_pluginrc(void);
80 static void free_pluginrc(void);
81 static gboolean plugin_load_inhibited(const gchar *filename);
82 static void info_fill_from_pluginrc(PluginInfo *info);
84 gboolean
85 dia_plugin_info_init(PluginInfo *info, gchar *name, gchar *description,
86 PluginCanUnloadFunc can_unload_func,
87 PluginUnloadFunc unload_func)
89 g_free(info->name);
90 info->name = g_strdup(name);
91 g_free(info->description);
92 info->description = g_strdup(description);
93 info->can_unload_func = can_unload_func;
94 info->unload_func = unload_func;
96 return TRUE;
99 gchar *
100 dia_plugin_check_version(gint version)
102 if (version != DIA_PLUGIN_API_VERSION)
103 return "Wrong plugin API version";
104 else
105 return NULL;
109 /* functions for use by dia ... */
111 const gchar *
112 dia_plugin_get_filename(PluginInfo *info)
114 return info->filename;
117 const gchar *
118 dia_plugin_get_name(PluginInfo *info)
120 return info->name ? info->name : _("???");
123 const gchar *
124 dia_plugin_get_description(PluginInfo *info)
126 return info->description;
129 const gpointer *
130 dia_plugin_get_symbol(PluginInfo *info, const gchar* name)
132 gpointer symbol;
134 if (!info->module)
135 return NULL;
137 if (g_module_symbol (info->module, name, &symbol))
138 return symbol;
140 return NULL;
143 gboolean
144 dia_plugin_can_unload(PluginInfo *info)
146 return (info->can_unload_func != NULL) && (* info->can_unload_func)(info);
149 gboolean
150 dia_plugin_is_loaded(PluginInfo *info)
152 return info->is_loaded;
155 gboolean
156 dia_plugin_get_inhibit_load(PluginInfo *info)
158 return info->inhibit_load;
161 void
162 dia_plugin_set_inhibit_load(PluginInfo *info, gboolean inhibit_load)
164 info->inhibit_load = inhibit_load;
167 /* implementation stollen from BEAST/BSE */
168 static gchar *
169 find_real_filename(const gchar *filename)
171 const gint TOKEN_DLNAME = G_TOKEN_LAST + 1;
172 GScanner *scanner;
173 gint len, fd;
174 gchar *str, *ret;
176 g_return_val_if_fail(filename != NULL, NULL);
178 len = strlen(filename);
180 /* is this a libtool .la file? */
181 if (len < 3 || strcmp(&filename[len-3], ".la") != 0)
182 return g_strdup(filename);
184 fd = open(filename, O_RDONLY, 0);
185 if (fd < 0)
186 return NULL;
188 scanner = g_scanner_new(NULL);
189 g_scanner_input_file(scanner, fd);
190 scanner->config->symbol_2_token = TRUE;
191 g_scanner_add_symbol(scanner, "dlname", GUINT_TO_POINTER(TOKEN_DLNAME));
193 /* skip ahead to dlname part */
194 while (!g_scanner_eof(scanner) &&
195 g_scanner_peek_next_token(scanner) != TOKEN_DLNAME)
196 g_scanner_get_next_token(scanner);
197 /* parse dlname */
198 if (g_scanner_get_next_token (scanner) != TOKEN_DLNAME ||
199 g_scanner_get_next_token (scanner) != '=' ||
200 g_scanner_get_next_token (scanner) != G_TOKEN_STRING) {
201 g_scanner_destroy(scanner);
202 close(fd);
203 return NULL;
206 str = g_path_get_dirname(filename);
207 ret = g_build_filename(str, scanner->value.v_string, NULL);
208 g_free(str);
209 g_scanner_destroy(scanner);
210 close(fd);
212 return ret;
215 void
216 dia_plugin_load(PluginInfo *info)
218 g_return_if_fail(info != NULL);
219 g_return_if_fail(info->filename != NULL);
221 if (info->is_loaded)
222 return;
224 g_free(info->real_filename);
225 info->real_filename = find_real_filename(info->filename);
226 if (info->real_filename == NULL) {
227 message_error(_("Could not deduce correct path for `%s'"), info->filename);
228 return;
230 info->module = g_module_open(info->real_filename, G_MODULE_BIND_LAZY);
231 if (!info->module) {
232 gchar *msg_utf8 = g_locale_to_utf8 (g_module_error(), -1, NULL, NULL, NULL);
233 message_error(_("Could not load plugin '%s'\n%s"),
234 info->filename, msg_utf8);
235 /* this is eating the conversion */
236 info->description = msg_utf8;
237 return;
240 info->init_func = NULL;
241 if (!g_module_symbol(info->module, "dia_plugin_init",
242 (gpointer)&info->init_func)) {
243 g_module_close(info->module);
244 info->module = NULL;
246 message_error(_("Could not find plugin init function in `%s'"),
247 info->filename);
248 info->description = g_strdup(_("Missing symbol 'dia_plugin_init'"));
249 return;
252 if ((* info->init_func)(info) != DIA_PLUGIN_INIT_OK) {
253 /* plugin displayed an error message */
254 g_module_close(info->module);
255 info->module = NULL;
256 info->description = g_strdup(_("dia_plugin_init() call failed"));
257 return;
260 /* Corrupt? */
261 if (info->description == NULL) {
262 g_module_close(info->module);
263 info->module = NULL;
264 info->description = g_strdup(_("dia_plugin_init() call failed"));
265 return;
268 info->is_loaded = TRUE;
270 return;
273 void
274 dia_plugin_unload(PluginInfo *info)
276 g_return_if_fail(info != NULL);
277 g_return_if_fail(info->filename != NULL);
279 if (!info->is_loaded)
280 return;
282 if (!dia_plugin_can_unload(info)) {
283 message(_("%s Plugin could not be unloaded"), info->name);
284 return;
286 /* perform plugin cleanup */
287 if (info->unload_func)
288 (* info->unload_func)(info);
289 g_module_close(info->module);
290 info->module = NULL;
291 info->init_func = NULL;
292 info->can_unload_func = NULL;
293 info->unload_func = NULL;
295 info->is_loaded = FALSE;
298 void
299 dia_register_plugin(const gchar *filename)
301 GList *tmp;
302 PluginInfo *info;
304 /* check if plugin has already been registered */
305 for (tmp = plugins; tmp != NULL; tmp = tmp->next) {
306 info = tmp->data;
307 if (!strcmp(info->filename, filename))
308 return;
311 /* If trying to load libdia, abort */
312 if (strstr(filename, "libdia.")) {
313 return;
316 /* set up plugin info structure */
317 info = g_new0(PluginInfo, 1);
318 info->filename = g_strdup(filename);
319 info->is_loaded = FALSE;
320 info->inhibit_load = FALSE;
322 /* check whether loading of the plugin has been inhibited */
323 if (plugin_load_inhibited(filename))
324 info_fill_from_pluginrc(info);
325 else
326 dia_plugin_load(info);
328 plugins = g_list_prepend(plugins, info);
331 static gboolean
332 this_is_a_plugin(const gchar *name)
334 #if USING_LIBTOOL
335 gchar *soname,*basename;
336 #endif
337 guint len = strlen(name);
338 if (0 != strcmp(&name[len-PLUG_IN_EXT_LEN], PLUG_IN_EXT))
339 return FALSE;
340 #if USING_LIBTOOL
341 basename = g_strndup(name,len-PLUG_IN_EXT_LEN);
342 soname = g_strconcat(basename,".so",NULL);
343 if (!g_file_test(soname, G_FILE_TEST_IS_REGULAR)) {
344 g_free(basename);
345 g_free(soname);
346 return FALSE;
348 g_free(basename);
349 g_free(soname);
350 #endif
351 return TRUE;
354 typedef void (*ForEachInDirDoFunc)(const gchar *name);
355 typedef gboolean (*ForEachInDirFilterFunc)(const gchar *name);
357 static void
358 for_each_in_dir(const gchar *directory, ForEachInDirDoFunc dofunc,
359 ForEachInDirFilterFunc filter)
361 struct stat statbuf;
362 const char *dentry;
363 GDir *dp;
364 GError *error = NULL;
366 if (stat(directory, &statbuf) < 0)
367 return;
369 dp = g_dir_open(directory, 0, &error);
370 if (dp == NULL) {
371 message_warning(_("Could not open `%s'\n`%s'"), directory, error->message);
372 g_error_free (error);
373 return;
376 while ((dentry = g_dir_read_name(dp)) != NULL) {
377 gchar *name = g_strconcat(directory,G_DIR_SEPARATOR_S,dentry,NULL);
379 if (filter(name)) dofunc(name);
380 g_free(name);
382 g_dir_close(dp);
385 static gboolean
386 directory_filter(const gchar *name)
388 guint len = strlen(name);
390 if (0 == strcmp(G_DIR_SEPARATOR_S ".",
391 &name[len-strlen(G_DIR_SEPARATOR_S ".")])) return FALSE;
392 if (0 == strcmp(G_DIR_SEPARATOR_S "..",
393 &name[len-strlen(G_DIR_SEPARATOR_S "..")])) return FALSE;
395 if (!g_file_test (name, G_FILE_TEST_IS_DIR))
396 return FALSE;
398 return TRUE;
401 static gboolean
402 dia_plugin_filter(const gchar *name)
404 gint len = strlen(name);
406 if (!g_file_test (name, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_DIR))
407 return FALSE;
409 if (len <= PLUG_IN_EXT_LEN) return FALSE;
411 return this_is_a_plugin(name);
414 static void
415 walk_dirs_for_plugins(const gchar *dirname)
417 for_each_in_dir(dirname,walk_dirs_for_plugins,directory_filter);
418 for_each_in_dir(dirname,dia_register_plugin,dia_plugin_filter);
421 #define RECURSE (G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S)
423 void
424 dia_register_plugins_in_dir(const gchar *directory)
426 guint reclen = strlen(RECURSE);
427 guint len = strlen(directory);
429 if ((len >= reclen) &&
430 (0 == strcmp(&directory[len-reclen],RECURSE))) {
431 gchar *dirbase = g_strndup(directory,len-reclen);
432 for_each_in_dir(dirbase,walk_dirs_for_plugins,directory_filter);
433 g_free(dirbase);
435 /* intentional fallback. */
436 for_each_in_dir(directory,dia_register_plugin,dia_plugin_filter);
439 void
440 dia_register_plugins(void)
442 const gchar *library_path;
443 const gchar *lib_dir;
445 library_path = g_getenv("DIA_LIB_PATH");
447 lib_dir = dia_config_filename("objects");
449 if (lib_dir != NULL) {
450 dia_register_plugins_in_dir(lib_dir);
451 g_free((char *)lib_dir);
454 if (library_path != NULL) {
455 gchar **paths = g_strsplit(library_path, G_SEARCHPATH_SEPARATOR_S, 0);
456 gint i;
458 for (i = 0; paths[i] != NULL; i++) {
459 dia_register_plugins_in_dir(paths[i]);
461 g_strfreev(paths);
462 } else {
463 library_path = dia_get_lib_directory("dia");
465 dia_register_plugins_in_dir(library_path);
466 g_free((char *)library_path);
468 /* FIXME: this isn't needed anymore */
469 free_pluginrc();
472 void
473 dia_register_builtin_plugin(PluginInitFunc init_func)
475 PluginInfo *info;
477 info = g_new0(PluginInfo, 1);
478 info->filename = "<builtin>";
479 info->is_loaded = TRUE;
480 info->inhibit_load = FALSE;
482 info->init_func = init_func;
484 if ((* init_func)(info) != DIA_PLUGIN_INIT_OK) {
485 g_free(info);
486 return;
488 plugins = g_list_prepend(plugins, info);
492 GList *
493 dia_list_plugins(void)
495 return plugins;
498 static xmlDocPtr pluginrc = NULL;
499 /* format is:
500 <plugins>
501 <plugin filename="filename">
502 <name></name>
503 <description></description>
504 <inhibit-load/>
505 </plugin>
506 </plugins>
509 static void
510 ensure_pluginrc(void)
512 gchar *filename;
514 if (pluginrc)
515 return;
516 filename = dia_config_filename("pluginrc");
517 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
518 pluginrc = xmlDiaParseFile(filename);
519 else
520 pluginrc = NULL;
522 g_free(filename);
524 if (!pluginrc) {
525 pluginrc = xmlNewDoc("1.0");
526 pluginrc->encoding = xmlStrdup("UTF-8");
527 xmlDocSetRootElement(pluginrc,
528 xmlNewDocNode(pluginrc, NULL, "plugins", NULL));
532 static void
533 free_pluginrc(void)
535 if (pluginrc) {
536 xmlFreeDoc(pluginrc);
537 pluginrc = NULL;
541 /* whether we should prevent loading on startup */
542 static gboolean
543 plugin_load_inhibited(const gchar *filename)
545 xmlNodePtr node;
547 ensure_pluginrc();
548 for (node = pluginrc->xmlRootNode->xmlChildrenNode;
549 node != NULL;
550 node = node->next) {
551 xmlChar *node_filename;
553 if (xmlIsBlankNode(node)) continue;
555 if (node->type != XML_ELEMENT_NODE || strcmp(node->name, "plugin") != 0)
556 continue;
557 node_filename = xmlGetProp(node, "filename");
558 if (node_filename && !strcmp(filename, node_filename)) {
559 xmlNodePtr node2;
561 xmlFree(node_filename);
562 for (node2 = node->xmlChildrenNode;
563 node2 != NULL;
564 node2 = node2->next) {
565 if (xmlIsBlankNode(node2)) continue;
566 if (node2->type == XML_ELEMENT_NODE &&
567 !strcmp(node2->name, "inhibit-load"))
568 return TRUE;
570 return FALSE;
572 if (node_filename) xmlFree(node_filename);
574 return FALSE;
577 static void
578 info_fill_from_pluginrc(PluginInfo *info)
580 xmlNodePtr node;
582 info->module = NULL;
583 info->name = NULL;
584 info->description = NULL;
585 info->is_loaded = FALSE;
586 info->inhibit_load = TRUE;
587 info->init_func = NULL;
588 info->can_unload_func = NULL;
589 info->unload_func = NULL;
591 ensure_pluginrc();
592 for (node = pluginrc->xmlRootNode->xmlChildrenNode;
593 node != NULL;
594 node = node->next) {
595 xmlChar *node_filename;
597 if (xmlIsBlankNode(node)) continue;
599 if (node->type != XML_ELEMENT_NODE || strcmp(node->name, "plugin") != 0)
600 continue;
601 node_filename = xmlGetProp(node, "filename");
602 if (node_filename && !strcmp(info->filename, node_filename)) {
603 xmlNodePtr node2;
605 xmlFree(node_filename);
606 for (node2 = node->xmlChildrenNode;
607 node2 != NULL;
608 node2 = node2->next) {
609 char *content;
611 if (xmlIsBlankNode(node2)) continue;
613 if (node2->type != XML_ELEMENT_NODE)
614 continue;
615 content = xmlNodeGetContent(node2);
616 if (!strcmp(node2->name, "name")) {
617 g_free(info->name);
618 info->name = g_strdup(content);
619 } else if (!strcmp(node2->name, "description")) {
620 g_free(info->description);
621 info->description = g_strdup(content);
623 xmlFree(content);
625 break;
627 if (node_filename) xmlFree(node_filename);
631 void
632 dia_pluginrc_write(void)
634 gchar *filename;
635 GList *tmp;
637 ensure_pluginrc();
638 for (tmp = plugins; tmp != NULL; tmp = tmp->next) {
639 PluginInfo *info = tmp->data;
640 xmlNodePtr node, pluginnode, datanode;
642 if (info == NULL) {
643 continue;
646 pluginnode = xmlNewNode(NULL, "plugin");
647 datanode = xmlNewChild(pluginnode, NULL, "name", info->name);
648 /* FIXME: UNICODE_WORK_IN_PROGRESS why is this reencoding necessary ?*/
650 xmlChar *enc = xmlEncodeEntitiesReentrant(pluginnode->doc,
651 info->description);
652 datanode = xmlNewChild(pluginnode, NULL, "description", enc);
653 xmlFree(enc);
655 if (info->inhibit_load)
656 datanode = xmlNewChild(pluginnode, NULL, "inhibit-load", NULL);
658 for (node = pluginrc->xmlRootNode->xmlChildrenNode;
659 node != NULL;
660 node = node->next) {
661 xmlChar *node_filename;
663 if (xmlIsBlankNode(node)) continue;
665 if (node->type != XML_ELEMENT_NODE || strcmp(node->name, "plugin") != 0)
666 continue;
667 node_filename = xmlGetProp(node, "filename");
668 if (node_filename && !strcmp(info->filename, node_filename)) {
669 xmlFree(node_filename);
670 xmlReplaceNode(node, pluginnode);
671 xmlFreeNode(node);
672 break;
674 if (node_filename) xmlFree(node_filename);
676 /* node wasn't in document ... */
677 if (!node)
678 xmlAddChild(pluginrc->xmlRootNode, pluginnode);
679 /* have to call this after adding node to document */
680 xmlSetProp(pluginnode, "filename", info->filename);
683 filename = dia_config_filename("pluginrc");
685 xmlDiaSaveFile(filename,pluginrc);
687 g_free(filename);
688 free_pluginrc();