#ifdef'd xmlerror.h
[dia.git] / lib / plug-ins.c
blob3c97add20263d3b91c81b2834f7c9e55e09f78dd
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
33 #ifdef HAVE_DIRENT_H
34 #include <dirent.h>
35 #endif
37 #include <xmlmemory.h>
38 #include <parser.h>
39 #include <tree.h>
40 #include "dia_xml_libxml.h"
41 #include "dia_xml.h"
43 #include "charconv.h"
44 #include "plug-ins.h"
45 #include "intl.h"
46 #include "message.h"
47 #include "dia_dirs.h"
49 #if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20000
50 #define XML2
51 #endif
53 #if defined(G_OS_WIN32) || defined(__EMX__)
54 # define PLUG_IN_EXT ".dll"
55 # define PLUG_IN_EXT_LEN 4
56 # define USING_LIBTOOL 0
57 #else
58 /* this one should work on any platform where libtool is used to compile dia */
59 # define PLUG_IN_EXT ".la"
60 # define PLUG_IN_EXT_LEN 3
61 # define USING_LIBTOOL 1
62 #endif
64 static GList *plugins = NULL;
66 static void ensure_pluginrc(void);
67 static void free_pluginrc(void);
68 static gboolean plugin_load_inhibited(const gchar *filename);
69 static void info_fill_from_pluginrc(PluginInfo *info);
71 gboolean
72 dia_plugin_info_init(PluginInfo *info, gchar *name, utfchar *description,
73 PluginCanUnloadFunc can_unload_func,
74 PluginUnloadFunc unload_func)
76 utfchar *utf;
78 g_free(info->name);
79 g_free(info->description);
80 #ifdef GTK_DOESNT_TALK_UTF8_WE_DO
81 utf = charconv_local8_to_utf8 (name);
82 info->name = utf;
83 utf = charconv_local8_to_utf8 (description);
84 info->description = utf;
85 #else
86 info->name = g_strdup(name);
87 info->description = g_strdup(description);
88 #endif
89 info->can_unload_func = can_unload_func;
90 info->unload_func = unload_func;
92 return TRUE;
95 gchar *
96 dia_plugin_check_version(gint version)
98 if (version != DIA_PLUGIN_API_VERSION)
99 return "Wrong plugin API version";
100 else
101 return NULL;
105 /* functiosn for use by dia ... */
107 const gchar *
108 dia_plugin_get_filename(PluginInfo *info)
110 return info->filename;
113 const gchar *
114 dia_plugin_get_name(PluginInfo *info)
116 return info->name;
119 const gchar *
120 dia_plugin_get_description(PluginInfo *info)
122 return info->description;
125 gboolean
126 dia_plugin_can_unload(PluginInfo *info)
128 return (info->can_unload_func != NULL) && (* info->can_unload_func)(info);
131 gboolean
132 dia_plugin_is_loaded(PluginInfo *info)
134 return info->is_loaded;
137 gboolean
138 dia_plugin_get_inhibit_load(PluginInfo *info)
140 return info->inhibit_load;
143 void
144 dia_plugin_set_inhibit_load(PluginInfo *info, gboolean inhibit_load)
146 info->inhibit_load = inhibit_load;
149 /* implementation stollen from BEAST/BSE */
150 static gchar *
151 find_real_filename(const gchar *filename)
153 const gint TOKEN_DLNAME = G_TOKEN_LAST + 1;
154 GScanner *scanner;
155 gint len, fd;
156 gchar *str, *ret;
158 g_return_val_if_fail(filename != NULL, NULL);
160 len = strlen(filename);
162 /* is this a libtool .la file? */
163 if (len < 3 || strcmp(&filename[len-3], ".la") != 0)
164 return g_strdup(filename);
166 fd = open(filename, O_RDONLY, 0);
167 if (fd < 0)
168 return NULL;
170 scanner = g_scanner_new(NULL);
171 g_scanner_input_file(scanner, fd);
172 scanner->config->symbol_2_token = TRUE;
173 g_scanner_add_symbol(scanner, "dlname", GUINT_TO_POINTER(TOKEN_DLNAME));
175 /* skip ahead to dlname part */
176 while (!g_scanner_eof(scanner) &&
177 g_scanner_peek_next_token(scanner) != TOKEN_DLNAME)
178 g_scanner_get_next_token(scanner);
179 /* parse dlname */
180 if (g_scanner_get_next_token (scanner) != TOKEN_DLNAME ||
181 g_scanner_get_next_token (scanner) != '=' ||
182 g_scanner_get_next_token (scanner) != G_TOKEN_STRING) {
183 g_scanner_destroy(scanner);
184 close(fd);
185 return NULL;
188 str = g_dirname(filename);
189 ret = g_strconcat(str, G_DIR_SEPARATOR_S, scanner->value.v_string,
190 NULL);
191 g_free(str);
192 g_scanner_destroy(scanner);
193 close(fd);
195 return ret;
198 void
199 dia_plugin_load(PluginInfo *info)
201 g_return_if_fail(info != NULL);
202 g_return_if_fail(info->filename != NULL);
204 if (info->is_loaded)
205 return;
207 g_free(info->real_filename);
208 info->real_filename = find_real_filename(info->filename);
209 if (info->real_filename == NULL) {
210 message_error(_("Could not deduce correct path for `%s'"), info->filename);
211 return;
213 info->module = g_module_open(info->real_filename, G_MODULE_BIND_LAZY);
214 if (!info->module) {
215 message_error(_("Could not load plugin `%s'\n%s"), info->filename,
216 g_module_error());
217 return;
220 info->init_func = NULL;
221 if (!g_module_symbol(info->module, "dia_plugin_init",
222 (gpointer)&info->init_func)) {
223 g_module_close(info->module);
224 info->module = NULL;
226 message_error(_("Could not find plugin init function in `%s'"),
227 info->filename);
228 return;
231 if ((* info->init_func)(info) != DIA_PLUGIN_INIT_OK) {
232 /* plugin displayed an error message */
233 g_module_close(info->module);
234 info->module = NULL;
235 return;
238 info->is_loaded = TRUE;
240 return;
243 void
244 dia_plugin_unload(PluginInfo *info)
246 g_return_if_fail(info != NULL);
247 g_return_if_fail(info->filename != NULL);
249 if (!info->is_loaded)
250 return;
252 if (!dia_plugin_can_unload(info)) {
253 message(_("%s Plugin could not be unloaded"), info->name);
254 return;
256 /* perform plugin cleanup */
257 if (info->unload_func)
258 (* info->unload_func)(info);
259 g_module_close(info->module);
260 info->module = NULL;
261 info->init_func = NULL;
262 info->can_unload_func = NULL;
263 info->unload_func = NULL;
265 info->is_loaded = FALSE;
268 void
269 dia_register_plugin(const gchar *filename)
271 GList *tmp;
272 PluginInfo *info;
274 /* check if plugin has already been registered */
275 for (tmp = plugins; tmp != NULL; tmp = tmp->next) {
276 info = tmp->data;
277 if (!strcmp(info->filename, filename))
278 return;
281 /* set up plugin info structure */
282 info = g_new0(PluginInfo, 1);
283 info->filename = g_strdup(filename);
284 info->is_loaded = FALSE;
285 info->inhibit_load = FALSE;
287 /* check whether loading of the plugin has been inhibited */
288 if (plugin_load_inhibited(filename))
289 info_fill_from_pluginrc(info);
290 else
291 dia_plugin_load(info);
293 plugins = g_list_prepend(plugins, info);
296 static gboolean
297 this_is_a_plugin(const gchar *name)
299 #if USING_LIBTOOL
300 gchar *soname,*basename;
301 struct stat statbuf;
302 #endif
303 guint len = strlen(name);
304 if (0 != strcmp(&name[len-PLUG_IN_EXT_LEN], PLUG_IN_EXT))
305 return FALSE;
306 #if USING_LIBTOOL
307 basename = g_strndup(name,len-PLUG_IN_EXT_LEN);
308 soname = g_strconcat(basename,".so",NULL);
309 if ((stat(soname, &statbuf) < 0)||(!S_ISREG(statbuf.st_mode))) {
310 g_free(basename);
311 g_free(soname);
312 return FALSE;
314 g_free(basename);
315 g_free(soname);
316 #endif
317 return TRUE;
320 typedef void (*ForEachInDirDoFunc)(const gchar *name);
321 typedef gboolean (*ForEachInDirFilterFunc)(const gchar *name);
323 static void
324 for_each_in_dir(const gchar *directory, ForEachInDirDoFunc dofunc,
325 ForEachInDirFilterFunc filter)
327 struct stat statbuf;
328 struct dirent *dirp;
329 DIR *dp;
331 if (stat(directory, &statbuf) < 0)
332 return;
333 if (!S_ISDIR(statbuf.st_mode)) {
334 message_warning(_("`%s' is not a directory"), directory);
335 return;
338 dp = opendir(directory);
339 if (dp == NULL) {
340 message_warning(_("Could not open `%s'"), directory);
341 return;
344 while ((dirp = readdir(dp)) != NULL) {
345 gchar *name = g_strconcat(directory,G_DIR_SEPARATOR_S,dirp->d_name,NULL);
347 if (filter(name)) dofunc(name);
348 g_free(name);
350 closedir(dp);
353 static gboolean
354 directory_filter(const gchar *name)
356 guint len = strlen(name);
357 struct stat statbuf;
359 if (0 == strcmp(G_DIR_SEPARATOR_S ".",
360 &name[len-strlen(G_DIR_SEPARATOR_S ".")])) return FALSE;
361 if (0 == strcmp(G_DIR_SEPARATOR_S "..",
362 &name[len-strlen(G_DIR_SEPARATOR_S "..")])) return FALSE;
364 if (stat(name,&statbuf) < 0) return FALSE;
365 if (!S_ISDIR(statbuf.st_mode)) return FALSE;
367 return TRUE;
370 static gboolean
371 dia_plugin_filter(const gchar *name)
373 gint len = strlen(name);
374 struct stat statbuf;
375 if (stat(name,&statbuf) < 0) return FALSE;
376 if (!S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode)) return FALSE;
378 if (len <= PLUG_IN_EXT_LEN) return FALSE;
380 return this_is_a_plugin(name);
383 static void
384 walk_dirs_for_plugins(const gchar *dirname)
386 for_each_in_dir(dirname,walk_dirs_for_plugins,directory_filter);
387 for_each_in_dir(dirname,dia_register_plugin,dia_plugin_filter);
390 #define RECURSE (G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S)
392 void
393 dia_register_plugins_in_dir(const gchar *directory)
395 guint reclen = strlen(RECURSE);
396 guint len = strlen(directory);
398 if ((len >= reclen) &&
399 (0 == strcmp(&directory[len-reclen],RECURSE))) {
400 gchar *dirbase = g_strndup(directory,len-reclen);
401 for_each_in_dir(dirbase,walk_dirs_for_plugins,directory_filter);
402 g_free(dirbase);
404 /* intentional fallback. */
405 for_each_in_dir(directory,dia_register_plugin,dia_plugin_filter);
408 void
409 dia_register_plugins(void)
411 gchar *library_path;
412 gchar *lib_dir;
414 library_path = g_getenv("DIA_LIB_PATH");
416 lib_dir = dia_config_filename("objects");
418 if (lib_dir != NULL) {
419 dia_register_plugins_in_dir(lib_dir);
420 g_free(lib_dir);
423 if (library_path != NULL) {
424 gchar **paths = g_strsplit(library_path, G_SEARCHPATH_SEPARATOR_S, 0);
425 gint i;
427 for (i = 0; paths[i] != NULL; i++) {
428 dia_register_plugins_in_dir(paths[i]);
430 g_strfreev(paths);
431 } else {
432 library_path = dia_get_lib_directory("dia");
434 dia_register_plugins_in_dir(library_path);
435 g_free(library_path);
437 /* this isn't needed anymore */
438 free_pluginrc();
441 void
442 dia_register_builtin_plugin(PluginInitFunc init_func)
444 PluginInfo *info;
446 info = g_new0(PluginInfo, 1);
447 info->filename = "<builtin>";
448 info->is_loaded = TRUE;
449 info->inhibit_load = FALSE;
451 info->init_func = init_func;
453 if ((* init_func)(info) != DIA_PLUGIN_INIT_OK) {
454 g_free(info);
455 return;
457 plugins = g_list_prepend(plugins, info);
461 GList *
462 dia_list_plugins(void)
464 return plugins;
467 static xmlDocPtr pluginrc = NULL;
468 /* format is:
469 <plugins>
470 <plugin filename="filename">
471 <name></name>
472 <description></description>
473 <inhibit-load/>
474 </plugin>
475 </plugins>
478 static void
479 ensure_pluginrc(void)
481 gchar *filename;
483 if (pluginrc)
484 return;
486 filename = dia_config_filename("pluginrc");
487 pluginrc = xmlDiaParseFile(filename);
488 g_free(filename);
490 if (!pluginrc) {
491 pluginrc = xmlNewDoc("1.0");
492 pluginrc->encoding = xmlStrdup("UTF-8");
493 xmlDocSetRootElement(pluginrc,
494 xmlNewDocNode(pluginrc, NULL, "plugins", NULL));
498 static void
499 free_pluginrc(void)
501 if (pluginrc) {
502 xmlFreeDoc(pluginrc);
503 pluginrc = NULL;
507 /* whether we should prevent loading on startup */
508 static gboolean
509 plugin_load_inhibited(const gchar *filename)
511 xmlNodePtr node;
513 ensure_pluginrc();
514 for (node = pluginrc->xmlRootNode->xmlChildrenNode;
515 node != NULL;
516 node = node->next) {
517 xmlChar *node_filename;
519 if (xmlIsBlankNode(node)) continue;
521 if (node->type != XML_ELEMENT_NODE || strcmp(node->name, "plugin") != 0)
522 continue;
523 node_filename = xmlGetProp(node, "filename");
524 if (node_filename && !strcmp(filename, node_filename)) {
525 xmlNodePtr node2;
527 xmlFree(node_filename);
528 for (node2 = node->xmlChildrenNode;
529 node2 != NULL;
530 node2 = node2->next) {
531 if (xmlIsBlankNode(node2)) continue;
532 if (node2->type == XML_ELEMENT_NODE &&
533 !strcmp(node2->name, "inhibit-load"))
534 return TRUE;
536 return FALSE;
538 if (node_filename) xmlFree(node_filename);
540 return FALSE;
543 static void
544 info_fill_from_pluginrc(PluginInfo *info)
546 xmlNodePtr node;
548 info->module = NULL;
549 info->name = NULL;
550 info->description = NULL;
551 info->is_loaded = FALSE;
552 info->inhibit_load = TRUE;
553 info->init_func = NULL;
554 info->can_unload_func = NULL;
555 info->unload_func = NULL;
557 ensure_pluginrc();
558 for (node = pluginrc->xmlRootNode->xmlChildrenNode;
559 node != NULL;
560 node = node->next) {
561 xmlChar *node_filename;
563 if (xmlIsBlankNode(node)) continue;
565 if (node->type != XML_ELEMENT_NODE || strcmp(node->name, "plugin") != 0)
566 continue;
567 node_filename = xmlGetProp(node, "filename");
568 if (node_filename && !strcmp(info->filename, node_filename)) {
569 xmlNodePtr node2;
571 xmlFree(node_filename);
572 for (node2 = node->xmlChildrenNode;
573 node2 != NULL;
574 node2 = node2->next) {
575 char *content;
577 if (xmlIsBlankNode(node2)) continue;
579 if (node2->type != XML_ELEMENT_NODE)
580 continue;
581 content = xmlNodeGetContent(node2);
582 if (!strcmp(node2->name, "name")) {
583 g_free(info->name);
584 info->name = g_strdup(content);
585 } else if (!strcmp(node2->name, "description")) {
586 g_free(info->description);
587 #ifdef UNICODE_WORK_IN_PROGRESS
588 info->description = g_strdup(content);
589 #else
590 info->description = charconv_utf8_to_local8(content);
591 #endif
593 xmlFree(content);
595 break;
597 if (node_filename) xmlFree(node_filename);
601 void
602 dia_pluginrc_write(void)
604 gchar *filename;
605 GList *tmp;
607 ensure_pluginrc();
608 for (tmp = plugins; tmp != NULL; tmp = tmp->next) {
609 PluginInfo *info = tmp->data;
610 xmlNodePtr node, pluginnode, datanode;
612 if (info == NULL) {
613 continue;
616 pluginnode = xmlNewNode(NULL, "plugin");
617 datanode = xmlNewChild(pluginnode, NULL, "name", info->name);
618 #ifdef UNICODE_WORK_IN_PROGRESS
620 xmlChar *enc = xmlEncodeEntitiesReentrant(pluginnode->doc,
621 info->description);
622 datanode = xmlNewChild(pluginnode, NULL, "description", enc);
623 xmlFree(enc);
625 #else
627 gchar *utf = charconv_local8_to_utf8(info->description);
628 xmlChar *enc = xmlEncodeEntitiesReentrant(pluginnode->doc,utf);
629 g_free(utf);
630 datanode = xmlNewChild(pluginnode, NULL, "description", enc);
631 xmlFree(enc);
633 #endif
634 if (info->inhibit_load)
635 datanode = xmlNewChild(pluginnode, NULL, "inhibit-load", NULL);
637 for (node = pluginrc->xmlRootNode->xmlChildrenNode;
638 node != NULL;
639 node = node->next) {
640 xmlChar *node_filename;
642 if (xmlIsBlankNode(node)) continue;
644 if (node->type != XML_ELEMENT_NODE || strcmp(node->name, "plugin") != 0)
645 continue;
646 node_filename = xmlGetProp(node, "filename");
647 if (node_filename && !strcmp(info->filename, node_filename)) {
648 xmlFree(node_filename);
649 xmlReplaceNode(node, pluginnode);
650 xmlFreeNode(node);
651 break;
653 if (node_filename) xmlFree(node_filename);
655 /* node wasn't in document ... */
656 if (!node)
657 xmlAddChild(pluginrc->xmlRootNode, pluginnode);
658 /* have to call this after adding node to document */
659 xmlSetProp(pluginnode, "filename", info->filename);
662 filename = dia_config_filename("pluginrc");
664 xmlDiaSaveFile(filename,pluginrc);
666 g_free(filename);
667 free_pluginrc();