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 /* functions 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_path_get_dirname(filename
);
207 ret
= g_build_filename(str
, scanner
->value
.v_string
, NULL
);
209 g_scanner_destroy(scanner
);
216 dia_plugin_load(PluginInfo
*info
)
218 g_return_if_fail(info
!= NULL
);
219 g_return_if_fail(info
->filename
!= NULL
);
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
);
230 info
->module
= g_module_open(info
->real_filename
, G_MODULE_BIND_LAZY
);
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
;
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
);
246 message_error(_("Could not find plugin init function in `%s'"),
248 info
->description
= g_strdup(_("Missing symbol 'dia_plugin_init'"));
252 if ((* info
->init_func
)(info
) != DIA_PLUGIN_INIT_OK
) {
253 /* plugin displayed an error message */
254 g_module_close(info
->module
);
256 info
->description
= g_strdup(_("dia_plugin_init() call failed"));
261 if (info
->description
== NULL
) {
262 g_module_close(info
->module
);
264 info
->description
= g_strdup(_("dia_plugin_init() call failed"));
268 info
->is_loaded
= TRUE
;
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
)
282 if (!dia_plugin_can_unload(info
)) {
283 message(_("%s Plugin could not be unloaded"), info
->name
);
286 /* perform plugin cleanup */
287 if (info
->unload_func
)
288 (* info
->unload_func
)(info
);
289 g_module_close(info
->module
);
291 info
->init_func
= NULL
;
292 info
->can_unload_func
= NULL
;
293 info
->unload_func
= NULL
;
295 info
->is_loaded
= FALSE
;
299 dia_register_plugin(const gchar
*filename
)
304 /* check if plugin has already been registered */
305 for (tmp
= plugins
; tmp
!= NULL
; tmp
= tmp
->next
) {
307 if (!strcmp(info
->filename
, filename
))
311 /* If trying to load libdia, abort */
312 if (strstr(filename
, "libdia.")) {
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
);
326 dia_plugin_load(info
);
328 plugins
= g_list_prepend(plugins
, info
);
332 this_is_a_plugin(const gchar
*name
)
335 gchar
*soname
,*basename
;
337 guint len
= strlen(name
);
338 if (0 != strcmp(&name
[len
-PLUG_IN_EXT_LEN
], PLUG_IN_EXT
))
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
)) {
354 typedef void (*ForEachInDirDoFunc
)(const gchar
*name
);
355 typedef gboolean (*ForEachInDirFilterFunc
)(const gchar
*name
);
358 for_each_in_dir(const gchar
*directory
, ForEachInDirDoFunc dofunc
,
359 ForEachInDirFilterFunc filter
)
364 GError
*error
= NULL
;
366 if (stat(directory
, &statbuf
) < 0)
369 dp
= g_dir_open(directory
, 0, &error
);
371 message_warning(_("Could not open `%s'\n`%s'"), directory
, error
->message
);
372 g_error_free (error
);
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
);
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
))
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
))
409 if (len
<= PLUG_IN_EXT_LEN
) return FALSE
;
411 return this_is_a_plugin(name
);
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)
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
);
435 /* intentional fallback. */
436 for_each_in_dir(directory
,dia_register_plugin
,dia_plugin_filter
);
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);
458 for (i
= 0; paths
[i
] != NULL
; i
++) {
459 dia_register_plugins_in_dir(paths
[i
]);
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 */
473 dia_register_builtin_plugin(PluginInitFunc init_func
)
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
) {
488 plugins
= g_list_prepend(plugins
, info
);
493 dia_list_plugins(void)
498 static xmlDocPtr pluginrc
= NULL
;
501 <plugin filename="filename">
503 <description></description>
510 ensure_pluginrc(void)
516 filename
= dia_config_filename("pluginrc");
517 if (g_file_test (filename
, G_FILE_TEST_IS_REGULAR
))
518 pluginrc
= xmlDiaParseFile(filename
);
525 pluginrc
= xmlNewDoc("1.0");
526 pluginrc
->encoding
= xmlStrdup("UTF-8");
527 xmlDocSetRootElement(pluginrc
,
528 xmlNewDocNode(pluginrc
, NULL
, "plugins", NULL
));
536 xmlFreeDoc(pluginrc
);
541 /* whether we should prevent loading on startup */
543 plugin_load_inhibited(const gchar
*filename
)
548 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
551 xmlChar
*node_filename
;
553 if (xmlIsBlankNode(node
)) continue;
555 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
557 node_filename
= xmlGetProp(node
, "filename");
558 if (node_filename
&& !strcmp(filename
, node_filename
)) {
561 xmlFree(node_filename
);
562 for (node2
= node
->xmlChildrenNode
;
564 node2
= node2
->next
) {
565 if (xmlIsBlankNode(node2
)) continue;
566 if (node2
->type
== XML_ELEMENT_NODE
&&
567 !strcmp(node2
->name
, "inhibit-load"))
572 if (node_filename
) xmlFree(node_filename
);
578 info_fill_from_pluginrc(PluginInfo
*info
)
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
;
592 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
595 xmlChar
*node_filename
;
597 if (xmlIsBlankNode(node
)) continue;
599 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
601 node_filename
= xmlGetProp(node
, "filename");
602 if (node_filename
&& !strcmp(info
->filename
, node_filename
)) {
605 xmlFree(node_filename
);
606 for (node2
= node
->xmlChildrenNode
;
608 node2
= node2
->next
) {
611 if (xmlIsBlankNode(node2
)) continue;
613 if (node2
->type
!= XML_ELEMENT_NODE
)
615 content
= xmlNodeGetContent(node2
);
616 if (!strcmp(node2
->name
, "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
);
627 if (node_filename
) xmlFree(node_filename
);
632 dia_pluginrc_write(void)
638 for (tmp
= plugins
; tmp
!= NULL
; tmp
= tmp
->next
) {
639 PluginInfo
*info
= tmp
->data
;
640 xmlNodePtr node
, pluginnode
, datanode
;
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
,
652 datanode
= xmlNewChild(pluginnode
, NULL
, "description", enc
);
655 if (info
->inhibit_load
)
656 datanode
= xmlNewChild(pluginnode
, NULL
, "inhibit-load", NULL
);
658 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
661 xmlChar
*node_filename
;
663 if (xmlIsBlankNode(node
)) continue;
665 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
667 node_filename
= xmlGetProp(node
, "filename");
668 if (node_filename
&& !strcmp(info
->filename
, node_filename
)) {
669 xmlFree(node_filename
);
670 xmlReplaceNode(node
, pluginnode
);
674 if (node_filename
) xmlFree(node_filename
);
676 /* node wasn't in document ... */
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
);