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.
27 #include <sys/types.h>
34 #include <libxml/xmlmemory.h>
35 #include <libxml/parser.h>
36 #include <libxml/tree.h>
37 #include "dia_xml_libxml.h"
46 #include <io.h> /* open, close */
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
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
62 gchar
*filename
; /* plugin filename */
63 gchar
*real_filename
; /* not a .la file */
66 gboolean inhibit_load
;
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
);
85 dia_plugin_info_init(PluginInfo
*info
, gchar
*name
, gchar
*description
,
86 PluginCanUnloadFunc can_unload_func
,
87 PluginUnloadFunc unload_func
)
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
;
100 dia_plugin_check_version(gint version
)
102 if (version
!= DIA_PLUGIN_API_VERSION
)
103 return "Wrong plugin API version";
109 /* functiosn for use by dia ... */
112 dia_plugin_get_filename(PluginInfo
*info
)
114 return info
->filename
;
118 dia_plugin_get_name(PluginInfo
*info
)
120 return info
->name
? info
->name
: _("???");
124 dia_plugin_get_description(PluginInfo
*info
)
126 return info
->description
;
130 dia_plugin_get_symbol(PluginInfo
*info
, const gchar
* name
)
137 if (g_module_symbol (info
->module
, name
, &symbol
))
144 dia_plugin_can_unload(PluginInfo
*info
)
146 return (info
->can_unload_func
!= NULL
) && (* info
->can_unload_func
)(info
);
150 dia_plugin_is_loaded(PluginInfo
*info
)
152 return info
->is_loaded
;
156 dia_plugin_get_inhibit_load(PluginInfo
*info
)
158 return info
->inhibit_load
;
162 dia_plugin_set_inhibit_load(PluginInfo
*info
, gboolean inhibit_load
)
164 info
->inhibit_load
= inhibit_load
;
167 /* implementation stollen from BEAST/BSE */
169 find_real_filename(const gchar
*filename
)
171 const gint TOKEN_DLNAME
= G_TOKEN_LAST
+ 1;
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);
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
);
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
);
206 str
= g_dirname(filename
);
207 ret
= g_strconcat(str
, G_DIR_SEPARATOR_S
, scanner
->value
.v_string
,
210 g_scanner_destroy(scanner
);
217 dia_plugin_load(PluginInfo
*info
)
219 g_return_if_fail(info
!= NULL
);
220 g_return_if_fail(info
->filename
!= NULL
);
225 g_free(info
->real_filename
);
226 info
->real_filename
= find_real_filename(info
->filename
);
227 if (info
->real_filename
== NULL
) {
228 message_error(_("Could not deduce correct path for `%s'"), info
->filename
);
231 info
->module
= g_module_open(info
->real_filename
, G_MODULE_BIND_LAZY
);
233 gchar
*msg_utf8
= g_locale_to_utf8 (g_module_error(), -1, NULL
, NULL
, NULL
);
234 message_error(_("Could not load plugin '%s'\n%s"),
235 info
->filename
, msg_utf8
);
236 /* this is eating the conversion */
237 info
->description
= msg_utf8
;
241 info
->init_func
= NULL
;
242 if (!g_module_symbol(info
->module
, "dia_plugin_init",
243 (gpointer
)&info
->init_func
)) {
244 g_module_close(info
->module
);
247 message_error(_("Could not find plugin init function in `%s'"),
249 info
->description
= g_strdup(_("Missing symbol 'dia_plugin_init'"));
253 if ((* info
->init_func
)(info
) != DIA_PLUGIN_INIT_OK
) {
254 /* plugin displayed an error message */
255 g_module_close(info
->module
);
257 info
->description
= g_strdup(_("dia_pluin_init() call failed"));
261 info
->is_loaded
= TRUE
;
267 dia_plugin_unload(PluginInfo
*info
)
269 g_return_if_fail(info
!= NULL
);
270 g_return_if_fail(info
->filename
!= NULL
);
272 if (!info
->is_loaded
)
275 if (!dia_plugin_can_unload(info
)) {
276 message(_("%s Plugin could not be unloaded"), info
->name
);
279 /* perform plugin cleanup */
280 if (info
->unload_func
)
281 (* info
->unload_func
)(info
);
282 g_module_close(info
->module
);
284 info
->init_func
= NULL
;
285 info
->can_unload_func
= NULL
;
286 info
->unload_func
= NULL
;
288 info
->is_loaded
= FALSE
;
292 dia_register_plugin(const gchar
*filename
)
297 /* check if plugin has already been registered */
298 for (tmp
= plugins
; tmp
!= NULL
; tmp
= tmp
->next
) {
300 if (!strcmp(info
->filename
, filename
))
304 /* If trying to load libdia, abort */
305 if (strstr(filename
, "libdia.")) {
309 /* set up plugin info structure */
310 info
= g_new0(PluginInfo
, 1);
311 info
->filename
= g_strdup(filename
);
312 info
->is_loaded
= FALSE
;
313 info
->inhibit_load
= FALSE
;
315 /* check whether loading of the plugin has been inhibited */
316 if (plugin_load_inhibited(filename
))
317 info_fill_from_pluginrc(info
);
319 dia_plugin_load(info
);
321 plugins
= g_list_prepend(plugins
, info
);
325 this_is_a_plugin(const gchar
*name
)
328 gchar
*soname
,*basename
;
330 guint len
= strlen(name
);
331 if (0 != strcmp(&name
[len
-PLUG_IN_EXT_LEN
], PLUG_IN_EXT
))
334 basename
= g_strndup(name
,len
-PLUG_IN_EXT_LEN
);
335 soname
= g_strconcat(basename
,".so",NULL
);
336 if (!g_file_test(soname
, G_FILE_TEST_IS_REGULAR
)) {
347 typedef void (*ForEachInDirDoFunc
)(const gchar
*name
);
348 typedef gboolean (*ForEachInDirFilterFunc
)(const gchar
*name
);
351 for_each_in_dir(const gchar
*directory
, ForEachInDirDoFunc dofunc
,
352 ForEachInDirFilterFunc filter
)
357 GError
*error
= NULL
;
359 if (stat(directory
, &statbuf
) < 0)
362 dp
= g_dir_open(directory
, 0, &error
);
364 message_warning(_("Could not open `%s'\n`%s'"), directory
, error
->message
);
365 g_error_free (error
);
369 while ((dentry
= g_dir_read_name(dp
)) != NULL
) {
370 gchar
*name
= g_strconcat(directory
,G_DIR_SEPARATOR_S
,dentry
,NULL
);
372 if (filter(name
)) dofunc(name
);
379 directory_filter(const gchar
*name
)
381 guint len
= strlen(name
);
383 if (0 == strcmp(G_DIR_SEPARATOR_S
".",
384 &name
[len
-strlen(G_DIR_SEPARATOR_S
".")])) return FALSE
;
385 if (0 == strcmp(G_DIR_SEPARATOR_S
"..",
386 &name
[len
-strlen(G_DIR_SEPARATOR_S
"..")])) return FALSE
;
388 if (!g_file_test (name
, G_FILE_TEST_IS_DIR
))
395 dia_plugin_filter(const gchar
*name
)
397 gint len
= strlen(name
);
399 if (!g_file_test (name
, G_FILE_TEST_IS_REGULAR
| G_FILE_TEST_IS_DIR
))
402 if (len
<= PLUG_IN_EXT_LEN
) return FALSE
;
404 return this_is_a_plugin(name
);
408 walk_dirs_for_plugins(const gchar
*dirname
)
410 for_each_in_dir(dirname
,walk_dirs_for_plugins
,directory_filter
);
411 for_each_in_dir(dirname
,dia_register_plugin
,dia_plugin_filter
);
414 #define RECURSE (G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S)
417 dia_register_plugins_in_dir(const gchar
*directory
)
419 guint reclen
= strlen(RECURSE
);
420 guint len
= strlen(directory
);
422 if ((len
>= reclen
) &&
423 (0 == strcmp(&directory
[len
-reclen
],RECURSE
))) {
424 gchar
*dirbase
= g_strndup(directory
,len
-reclen
);
425 for_each_in_dir(dirbase
,walk_dirs_for_plugins
,directory_filter
);
428 /* intentional fallback. */
429 for_each_in_dir(directory
,dia_register_plugin
,dia_plugin_filter
);
433 dia_register_plugins(void)
435 const gchar
*library_path
;
436 const gchar
*lib_dir
;
438 library_path
= g_getenv("DIA_LIB_PATH");
440 lib_dir
= dia_config_filename("objects");
442 if (lib_dir
!= NULL
) {
443 dia_register_plugins_in_dir(lib_dir
);
444 g_free((char *)lib_dir
);
447 if (library_path
!= NULL
) {
448 gchar
**paths
= g_strsplit(library_path
, G_SEARCHPATH_SEPARATOR_S
, 0);
451 for (i
= 0; paths
[i
] != NULL
; i
++) {
452 dia_register_plugins_in_dir(paths
[i
]);
456 library_path
= dia_get_lib_directory("dia");
458 dia_register_plugins_in_dir(library_path
);
459 g_free((char *)library_path
);
461 /* FIXME: this isn't needed anymore */
466 dia_register_builtin_plugin(PluginInitFunc init_func
)
470 info
= g_new0(PluginInfo
, 1);
471 info
->filename
= "<builtin>";
472 info
->is_loaded
= TRUE
;
473 info
->inhibit_load
= FALSE
;
475 info
->init_func
= init_func
;
477 if ((* init_func
)(info
) != DIA_PLUGIN_INIT_OK
) {
481 plugins
= g_list_prepend(plugins
, info
);
486 dia_list_plugins(void)
491 static xmlDocPtr pluginrc
= NULL
;
494 <plugin filename="filename">
496 <description></description>
503 ensure_pluginrc(void)
510 filename
= dia_config_filename("pluginrc");
511 pluginrc
= xmlDiaParseFile(filename
);
515 pluginrc
= xmlNewDoc("1.0");
516 pluginrc
->encoding
= xmlStrdup("UTF-8");
517 xmlDocSetRootElement(pluginrc
,
518 xmlNewDocNode(pluginrc
, NULL
, "plugins", NULL
));
526 xmlFreeDoc(pluginrc
);
531 /* whether we should prevent loading on startup */
533 plugin_load_inhibited(const gchar
*filename
)
538 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
541 xmlChar
*node_filename
;
543 if (xmlIsBlankNode(node
)) continue;
545 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
547 node_filename
= xmlGetProp(node
, "filename");
548 if (node_filename
&& !strcmp(filename
, node_filename
)) {
551 xmlFree(node_filename
);
552 for (node2
= node
->xmlChildrenNode
;
554 node2
= node2
->next
) {
555 if (xmlIsBlankNode(node2
)) continue;
556 if (node2
->type
== XML_ELEMENT_NODE
&&
557 !strcmp(node2
->name
, "inhibit-load"))
562 if (node_filename
) xmlFree(node_filename
);
568 info_fill_from_pluginrc(PluginInfo
*info
)
574 info
->description
= NULL
;
575 info
->is_loaded
= FALSE
;
576 info
->inhibit_load
= TRUE
;
577 info
->init_func
= NULL
;
578 info
->can_unload_func
= NULL
;
579 info
->unload_func
= NULL
;
582 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
585 xmlChar
*node_filename
;
587 if (xmlIsBlankNode(node
)) continue;
589 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
591 node_filename
= xmlGetProp(node
, "filename");
592 if (node_filename
&& !strcmp(info
->filename
, node_filename
)) {
595 xmlFree(node_filename
);
596 for (node2
= node
->xmlChildrenNode
;
598 node2
= node2
->next
) {
601 if (xmlIsBlankNode(node2
)) continue;
603 if (node2
->type
!= XML_ELEMENT_NODE
)
605 content
= xmlNodeGetContent(node2
);
606 if (!strcmp(node2
->name
, "name")) {
608 info
->name
= g_strdup(content
);
609 } else if (!strcmp(node2
->name
, "description")) {
610 g_free(info
->description
);
611 info
->description
= g_strdup(content
);
617 if (node_filename
) xmlFree(node_filename
);
622 dia_pluginrc_write(void)
628 for (tmp
= plugins
; tmp
!= NULL
; tmp
= tmp
->next
) {
629 PluginInfo
*info
= tmp
->data
;
630 xmlNodePtr node
, pluginnode
, datanode
;
636 pluginnode
= xmlNewNode(NULL
, "plugin");
637 datanode
= xmlNewChild(pluginnode
, NULL
, "name", info
->name
);
638 /* FIXME: UNICODE_WORK_IN_PROGRESS why is this reencoding necessary ?*/
640 xmlChar
*enc
= xmlEncodeEntitiesReentrant(pluginnode
->doc
,
642 datanode
= xmlNewChild(pluginnode
, NULL
, "description", enc
);
645 if (info
->inhibit_load
)
646 datanode
= xmlNewChild(pluginnode
, NULL
, "inhibit-load", NULL
);
648 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
651 xmlChar
*node_filename
;
653 if (xmlIsBlankNode(node
)) continue;
655 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
657 node_filename
= xmlGetProp(node
, "filename");
658 if (node_filename
&& !strcmp(info
->filename
, node_filename
)) {
659 xmlFree(node_filename
);
660 xmlReplaceNode(node
, pluginnode
);
664 if (node_filename
) xmlFree(node_filename
);
666 /* node wasn't in document ... */
668 xmlAddChild(pluginrc
->xmlRootNode
, pluginnode
);
669 /* have to call this after adding node to document */
670 xmlSetProp(pluginnode
, "filename", info
->filename
);
673 filename
= dia_config_filename("pluginrc");
675 xmlDiaSaveFile(filename
,pluginrc
);