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>
37 #include <xmlmemory.h>
40 #include "dia_xml_libxml.h"
49 #if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20000
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
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
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
);
72 dia_plugin_info_init(PluginInfo
*info
, gchar
*name
, utfchar
*description
,
73 PluginCanUnloadFunc can_unload_func
,
74 PluginUnloadFunc unload_func
)
79 g_free(info
->description
);
80 #ifdef GTK_DOESNT_TALK_UTF8_WE_DO
81 utf
= charconv_local8_to_utf8 (name
);
83 utf
= charconv_local8_to_utf8 (description
);
84 info
->description
= utf
;
86 info
->name
= g_strdup(name
);
87 info
->description
= g_strdup(description
);
89 info
->can_unload_func
= can_unload_func
;
90 info
->unload_func
= unload_func
;
96 dia_plugin_check_version(gint version
)
98 if (version
!= DIA_PLUGIN_API_VERSION
)
99 return "Wrong plugin API version";
105 /* functiosn for use by dia ... */
108 dia_plugin_get_filename(PluginInfo
*info
)
110 return info
->filename
;
114 dia_plugin_get_name(PluginInfo
*info
)
120 dia_plugin_get_description(PluginInfo
*info
)
122 return info
->description
;
126 dia_plugin_can_unload(PluginInfo
*info
)
128 return (info
->can_unload_func
!= NULL
) && (* info
->can_unload_func
)(info
);
132 dia_plugin_is_loaded(PluginInfo
*info
)
134 return info
->is_loaded
;
138 dia_plugin_get_inhibit_load(PluginInfo
*info
)
140 return info
->inhibit_load
;
144 dia_plugin_set_inhibit_load(PluginInfo
*info
, gboolean inhibit_load
)
146 info
->inhibit_load
= inhibit_load
;
149 /* implementation stollen from BEAST/BSE */
151 find_real_filename(const gchar
*filename
)
153 const gint TOKEN_DLNAME
= G_TOKEN_LAST
+ 1;
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);
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
);
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
);
188 str
= g_dirname(filename
);
189 ret
= g_strconcat(str
, G_DIR_SEPARATOR_S
, scanner
->value
.v_string
,
192 g_scanner_destroy(scanner
);
199 dia_plugin_load(PluginInfo
*info
)
201 g_return_if_fail(info
!= NULL
);
202 g_return_if_fail(info
->filename
!= NULL
);
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
);
213 info
->module
= g_module_open(info
->real_filename
, G_MODULE_BIND_LAZY
);
215 message_error(_("Could not load plugin `%s'\n%s"), info
->filename
,
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
);
226 message_error(_("Could not find plugin init function in `%s'"),
231 if ((* info
->init_func
)(info
) != DIA_PLUGIN_INIT_OK
) {
232 /* plugin displayed an error message */
233 g_module_close(info
->module
);
238 info
->is_loaded
= TRUE
;
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
)
252 if (!dia_plugin_can_unload(info
)) {
253 message(_("%s Plugin could not be unloaded"), info
->name
);
256 /* perform plugin cleanup */
257 if (info
->unload_func
)
258 (* info
->unload_func
)(info
);
259 g_module_close(info
->module
);
261 info
->init_func
= NULL
;
262 info
->can_unload_func
= NULL
;
263 info
->unload_func
= NULL
;
265 info
->is_loaded
= FALSE
;
269 dia_register_plugin(const gchar
*filename
)
274 /* check if plugin has already been registered */
275 for (tmp
= plugins
; tmp
!= NULL
; tmp
= tmp
->next
) {
277 if (!strcmp(info
->filename
, filename
))
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
);
291 dia_plugin_load(info
);
293 plugins
= g_list_prepend(plugins
, info
);
297 this_is_a_plugin(const gchar
*name
)
300 gchar
*soname
,*basename
;
303 guint len
= strlen(name
);
304 if (0 != strcmp(&name
[len
-PLUG_IN_EXT_LEN
], PLUG_IN_EXT
))
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
))) {
320 typedef void (*ForEachInDirDoFunc
)(const gchar
*name
);
321 typedef gboolean (*ForEachInDirFilterFunc
)(const gchar
*name
);
324 for_each_in_dir(const gchar
*directory
, ForEachInDirDoFunc dofunc
,
325 ForEachInDirFilterFunc filter
)
331 if (stat(directory
, &statbuf
) < 0)
333 if (!S_ISDIR(statbuf
.st_mode
)) {
334 message_warning(_("`%s' is not a directory"), directory
);
338 dp
= opendir(directory
);
340 message_warning(_("Could not open `%s'"), directory
);
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
);
354 directory_filter(const gchar
*name
)
356 guint len
= strlen(name
);
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
;
371 dia_plugin_filter(const gchar
*name
)
373 gint len
= strlen(name
);
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
);
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)
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
);
404 /* intentional fallback. */
405 for_each_in_dir(directory
,dia_register_plugin
,dia_plugin_filter
);
409 dia_register_plugins(void)
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
);
423 if (library_path
!= NULL
) {
424 gchar
**paths
= g_strsplit(library_path
, G_SEARCHPATH_SEPARATOR_S
, 0);
427 for (i
= 0; paths
[i
] != NULL
; i
++) {
428 dia_register_plugins_in_dir(paths
[i
]);
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 */
442 dia_register_builtin_plugin(PluginInitFunc init_func
)
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
) {
457 plugins
= g_list_prepend(plugins
, info
);
462 dia_list_plugins(void)
467 static xmlDocPtr pluginrc
= NULL
;
470 <plugin filename="filename">
472 <description></description>
479 ensure_pluginrc(void)
486 filename
= dia_config_filename("pluginrc");
487 pluginrc
= xmlDiaParseFile(filename
);
491 pluginrc
= xmlNewDoc("1.0");
492 pluginrc
->encoding
= xmlStrdup("UTF-8");
493 xmlDocSetRootElement(pluginrc
,
494 xmlNewDocNode(pluginrc
, NULL
, "plugins", NULL
));
502 xmlFreeDoc(pluginrc
);
507 /* whether we should prevent loading on startup */
509 plugin_load_inhibited(const gchar
*filename
)
514 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
517 xmlChar
*node_filename
;
519 if (xmlIsBlankNode(node
)) continue;
521 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
523 node_filename
= xmlGetProp(node
, "filename");
524 if (node_filename
&& !strcmp(filename
, node_filename
)) {
527 xmlFree(node_filename
);
528 for (node2
= node
->xmlChildrenNode
;
530 node2
= node2
->next
) {
531 if (xmlIsBlankNode(node2
)) continue;
532 if (node2
->type
== XML_ELEMENT_NODE
&&
533 !strcmp(node2
->name
, "inhibit-load"))
538 if (node_filename
) xmlFree(node_filename
);
544 info_fill_from_pluginrc(PluginInfo
*info
)
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
;
558 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
561 xmlChar
*node_filename
;
563 if (xmlIsBlankNode(node
)) continue;
565 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
567 node_filename
= xmlGetProp(node
, "filename");
568 if (node_filename
&& !strcmp(info
->filename
, node_filename
)) {
571 xmlFree(node_filename
);
572 for (node2
= node
->xmlChildrenNode
;
574 node2
= node2
->next
) {
577 if (xmlIsBlankNode(node2
)) continue;
579 if (node2
->type
!= XML_ELEMENT_NODE
)
581 content
= xmlNodeGetContent(node2
);
582 if (!strcmp(node2
->name
, "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
);
590 info
->description
= charconv_utf8_to_local8(content
);
597 if (node_filename
) xmlFree(node_filename
);
602 dia_pluginrc_write(void)
608 for (tmp
= plugins
; tmp
!= NULL
; tmp
= tmp
->next
) {
609 PluginInfo
*info
= tmp
->data
;
610 xmlNodePtr node
, pluginnode
, datanode
;
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
,
622 datanode
= xmlNewChild(pluginnode
, NULL
, "description", enc
);
627 gchar
*utf
= charconv_local8_to_utf8(info
->description
);
628 xmlChar
*enc
= xmlEncodeEntitiesReentrant(pluginnode
->doc
,utf
);
630 datanode
= xmlNewChild(pluginnode
, NULL
, "description", enc
);
634 if (info
->inhibit_load
)
635 datanode
= xmlNewChild(pluginnode
, NULL
, "inhibit-load", NULL
);
637 for (node
= pluginrc
->xmlRootNode
->xmlChildrenNode
;
640 xmlChar
*node_filename
;
642 if (xmlIsBlankNode(node
)) continue;
644 if (node
->type
!= XML_ELEMENT_NODE
|| strcmp(node
->name
, "plugin") != 0)
646 node_filename
= xmlGetProp(node
, "filename");
647 if (node_filename
&& !strcmp(info
->filename
, node_filename
)) {
648 xmlFree(node_filename
);
649 xmlReplaceNode(node
, pluginnode
);
653 if (node_filename
) xmlFree(node_filename
);
655 /* node wasn't in document ... */
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
);