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_plugin_init() call failed"));
262 if (info
->description
== NULL
) {
263 g_module_close(info
->module
);
265 info
->description
= g_strdup(_("dia_plugin_init() call failed"));
269 info
->is_loaded
= TRUE
;
275 dia_plugin_unload(PluginInfo
*info
)
277 g_return_if_fail(info
!= NULL
);
278 g_return_if_fail(info
->filename
!= NULL
);
280 if (!info
->is_loaded
)
283 if (!dia_plugin_can_unload(info
)) {
284 message(_("%s Plugin could not be unloaded"), info
->name
);
287 /* perform plugin cleanup */
288 if (info
->unload_func
)
289 (* info
->unload_func
)(info
);
290 g_module_close(info
->module
);
292 info
->init_func
= NULL
;
293 info
->can_unload_func
= NULL
;
294 info
->unload_func
= NULL
;
296 info
->is_loaded
= FALSE
;
300 dia_register_plugin(const gchar
*filename
)
305 /* check if plugin has already been registered */
306 for (tmp
= plugins
; tmp
!= NULL
; tmp
= tmp
->next
) {
308 if (!strcmp(info
->filename
, filename
))
312 /* If trying to load libdia, abort */
313 if (strstr(filename
, "libdia.")) {
317 /* set up plugin info structure */
318 info
= g_new0(PluginInfo
, 1);
319 info
->filename
= g_strdup(filename
);
320 info
->is_loaded
= FALSE
;
321 info
->inhibit_load
= FALSE
;
323 /* check whether loading of the plugin has been inhibited */
324 if (plugin_load_inhibited(filename
))
325 info_fill_from_pluginrc(info
);
327 dia_plugin_load(info
);
329 plugins
= g_list_prepend(plugins
, info
);
333 this_is_a_plugin(const gchar
*name
)
336 gchar
*soname
,*basename
;
338 guint len
= strlen(name
);
339 if (0 != strcmp(&name
[len
-PLUG_IN_EXT_LEN
], PLUG_IN_EXT
))
342 basename
= g_strndup(name
,len
-PLUG_IN_EXT_LEN
);
343 soname
= g_strconcat(basename
,".so",NULL
);
344 if (!g_file_test(soname
, G_FILE_TEST_IS_REGULAR
)) {
355 typedef void (*ForEachInDirDoFunc
)(const gchar
*name
);
356 typedef gboolean (*ForEachInDirFilterFunc
)(const gchar
*name
);
359 for_each_in_dir(const gchar
*directory
, ForEachInDirDoFunc dofunc
,
360 ForEachInDirFilterFunc filter
)
365 GError
*error
= NULL
;
367 if (stat(directory
, &statbuf
) < 0)
370 dp
= g_dir_open(directory
, 0, &error
);
372 message_warning(_("Could not open `%s'\n`%s'"), directory
, error
->message
);
373 g_error_free (error
);
377 while ((dentry
= g_dir_read_name(dp
)) != NULL
) {
378 gchar
*name
= g_strconcat(directory
,G_DIR_SEPARATOR_S
,dentry
,NULL
);
380 if (filter(name
)) dofunc(name
);
387 directory_filter(const gchar
*name
)
389 guint len
= strlen(name
);
391 if (0 == strcmp(G_DIR_SEPARATOR_S
".",
392 &name
[len
-strlen(G_DIR_SEPARATOR_S
".")])) return FALSE
;
393 if (0 == strcmp(G_DIR_SEPARATOR_S
"..",
394 &name
[len
-strlen(G_DIR_SEPARATOR_S
"..")])) return FALSE
;
396 if (!g_file_test (name
, G_FILE_TEST_IS_DIR
))
403 dia_plugin_filter(const gchar
*name
)
405 gint len
= strlen(name
);
407 if (!g_file_test (name
, G_FILE_TEST_IS_REGULAR
| G_FILE_TEST_IS_DIR
))
410 if (len
<= PLUG_IN_EXT_LEN
) return FALSE
;
412 return this_is_a_plugin(name
);
416 walk_dirs_for_plugins(const gchar
*dirname
)
418 for_each_in_dir(dirname
,walk_dirs_for_plugins
,directory_filter
);
419 for_each_in_dir(dirname
,dia_register_plugin
,dia_plugin_filter
);
422 #define RECURSE (G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S)
425 dia_register_plugins_in_dir(const gchar
*directory
)
427 guint reclen
= strlen(RECURSE
);
428 guint len
= strlen(directory
);
430 if ((len
>= reclen
) &&
431 (0 == strcmp(&directory
[len
-reclen
],RECURSE
))) {
432 gchar
*dirbase
= g_strndup(directory
,len
-reclen
);
433 for_each_in_dir(dirbase
,walk_dirs_for_plugins
,directory_filter
);
436 /* intentional fallback. */
437 for_each_in_dir(directory
,dia_register_plugin
,dia_plugin_filter
);
441 dia_register_plugins(void)
443 const gchar
*library_path
;
444 const gchar
*lib_dir
;
446 library_path
= g_getenv("DIA_LIB_PATH");
448 lib_dir
= dia_config_filename("objects");
450 if (lib_dir
!= NULL
) {
451 dia_register_plugins_in_dir(lib_dir
);
452 g_free((char *)lib_dir
);
455 if (library_path
!= NULL
) {
456 gchar
**paths
= g_strsplit(library_path
, G_SEARCHPATH_SEPARATOR_S
, 0);
459 for (i
= 0; paths
[i
] != NULL
; i
++) {
460 dia_register_plugins_in_dir(paths
[i
]);
464 library_path
= dia_get_lib_directory("dia");
466 dia_register_plugins_in_dir(library_path
);
467 g_free((char *)library_path
);
469 /* FIXME: this isn't needed anymore */
474 dia_register_builtin_plugin(PluginInitFunc init_func
)
478 info
= g_new0(PluginInfo
, 1);
479 info
->filename
= "<builtin>";
480 info
->is_loaded
= TRUE
;
481 info
->inhibit_load
= FALSE
;
483 info
->init_func
= init_func
;
485 if ((* init_func
)(info
) != DIA_PLUGIN_INIT_OK
) {
489 plugins
= g_list_prepend(plugins
, info
);
494 dia_list_plugins(void)
499 static xmlDocPtr pluginrc
= NULL
;
502 <plugin filename="filename">
504 <description></description>
511 ensure_pluginrc(void)
518 filename
= dia_config_filename("pluginrc");
519 pluginrc
= xmlDiaParseFile(filename
);
523 pluginrc
= xmlNewDoc("1.0");
524 pluginrc
->encoding
= xmlStrdup("UTF-8");
525 xmlDocSetRootElement(pluginrc
,
526 xmlNewDocNode(pluginrc
, NULL
, "plugins", NULL
));
534 xmlFreeDoc(pluginrc
);
539 /* whether we should prevent loading on startup */
541 plugin_load_inhibited(const gchar
*filename
)
546 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
549 xmlChar
*node_filename
;
551 if (xmlIsBlankNode(node
)) continue;
553 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
555 node_filename
= xmlGetProp(node
, "filename");
556 if (node_filename
&& !strcmp(filename
, node_filename
)) {
559 xmlFree(node_filename
);
560 for (node2
= node
->xmlChildrenNode
;
562 node2
= node2
->next
) {
563 if (xmlIsBlankNode(node2
)) continue;
564 if (node2
->type
== XML_ELEMENT_NODE
&&
565 !strcmp(node2
->name
, "inhibit-load"))
570 if (node_filename
) xmlFree(node_filename
);
576 info_fill_from_pluginrc(PluginInfo
*info
)
582 info
->description
= NULL
;
583 info
->is_loaded
= FALSE
;
584 info
->inhibit_load
= TRUE
;
585 info
->init_func
= NULL
;
586 info
->can_unload_func
= NULL
;
587 info
->unload_func
= NULL
;
590 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
593 xmlChar
*node_filename
;
595 if (xmlIsBlankNode(node
)) continue;
597 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
599 node_filename
= xmlGetProp(node
, "filename");
600 if (node_filename
&& !strcmp(info
->filename
, node_filename
)) {
603 xmlFree(node_filename
);
604 for (node2
= node
->xmlChildrenNode
;
606 node2
= node2
->next
) {
609 if (xmlIsBlankNode(node2
)) continue;
611 if (node2
->type
!= XML_ELEMENT_NODE
)
613 content
= xmlNodeGetContent(node2
);
614 if (!strcmp(node2
->name
, "name")) {
616 info
->name
= g_strdup(content
);
617 } else if (!strcmp(node2
->name
, "description")) {
618 g_free(info
->description
);
619 info
->description
= g_strdup(content
);
625 if (node_filename
) xmlFree(node_filename
);
630 dia_pluginrc_write(void)
636 for (tmp
= plugins
; tmp
!= NULL
; tmp
= tmp
->next
) {
637 PluginInfo
*info
= tmp
->data
;
638 xmlNodePtr node
, pluginnode
, datanode
;
644 pluginnode
= xmlNewNode(NULL
, "plugin");
645 datanode
= xmlNewChild(pluginnode
, NULL
, "name", info
->name
);
646 /* FIXME: UNICODE_WORK_IN_PROGRESS why is this reencoding necessary ?*/
648 xmlChar
*enc
= xmlEncodeEntitiesReentrant(pluginnode
->doc
,
650 datanode
= xmlNewChild(pluginnode
, NULL
, "description", enc
);
653 if (info
->inhibit_load
)
654 datanode
= xmlNewChild(pluginnode
, NULL
, "inhibit-load", NULL
);
656 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
659 xmlChar
*node_filename
;
661 if (xmlIsBlankNode(node
)) continue;
663 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
665 node_filename
= xmlGetProp(node
, "filename");
666 if (node_filename
&& !strcmp(info
->filename
, node_filename
)) {
667 xmlFree(node_filename
);
668 xmlReplaceNode(node
, pluginnode
);
672 if (node_filename
) xmlFree(node_filename
);
674 /* node wasn't in document ... */
676 xmlAddChild(pluginrc
->xmlRootNode
, pluginnode
);
677 /* have to call this after adding node to document */
678 xmlSetProp(pluginnode
, "filename", info
->filename
);
681 filename
= dia_config_filename("pluginrc");
683 xmlDiaSaveFile(filename
,pluginrc
);