1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2
3 /*
4 * GImageView
5 * Copyright (C) 2001 Takuro Ashie
6 *
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.
11 *
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.
16 *
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.
20 *
21 * $Id: gimv_plugin.c,v 1.6 2004/09/21 08:44:32 makeinu Exp $
22 */
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <gmodule.h>
27
28 #include "gimageview.h"
29
30 #include "fileutil.h"
31 #include "gfileutil.h"
32 #include "gimv_plugin.h"
33 #include "prefs.h"
34
35 #define PLUGIN_RC_DIR "pluginrc"
36
37 typedef struct PluginTypeEntry_Tag
38 {
39 const gchar *type;
40 GimvPluginRegistFn regist_fn;
41 GList *plugin_list;
42 const gchar *prefsrc_filename;
43 GHashTable *prefs_table;
44 } PluginTypeEntry;
45
46 #define PLUGIN_REGIST_FUNC(type) \
47 extern gboolean type##_plugin_regist (const gchar *plugin_name, \
48 const gchar *module_name, \
49 gpointer impl, \
50 gint size)
51
52 PLUGIN_REGIST_FUNC(gimv_image_loader);
53 PLUGIN_REGIST_FUNC(gimv_image_saver);
54 PLUGIN_REGIST_FUNC(gimv_io);
55 PLUGIN_REGIST_FUNC(fr_archive);
56 PLUGIN_REGIST_FUNC(gimv_thumb_cache);
57 PLUGIN_REGIST_FUNC(gimv_image_view);
58 PLUGIN_REGIST_FUNC(gimv_thumb_view);
59
60 static PluginTypeEntry plugin_types[] = {
61 {GIMV_PLUGIN_IMAGE_LOADER, gimv_image_loader_plugin_regist, NULL, "image_loader", NULL},
62 {GIMV_PLUGIN_IMAGE_SAVER, gimv_image_saver_plugin_regist, NULL, "image_saver", NULL},
63 {GIMV_PLUGIN_IO_STREAMER, gimv_io_plugin_regist, NULL, "io_stream", NULL},
64 {GIMV_PLUGIN_EXT_ARCHIVER, fr_archive_plugin_regist, NULL, "ext_archiver", NULL},
65 {GIMV_PLUGIN_THUMB_CACHE, gimv_thumb_cache_plugin_regist, NULL, "thumbnail", NULL},
66 {GIMV_PLUGIN_IMAGEVIEW_EMBEDER, gimv_image_view_plugin_regist, NULL, "image_view", NULL},
67 {GIMV_PLUGIN_THUMBVIEW_EMBEDER, gimv_thumb_view_plugin_regist, NULL, "thumbnail_view", NULL},
68 };
69 static guint plugin_types_num = sizeof (plugin_types) / sizeof (PluginTypeEntry);
70
71 static GList *search_dir_list = NULL;
72 static GHashTable *plugin_types_table = NULL;
73 static GList *all_plugin_list = NULL;
74
75
76 /******************************************************************************
77 *
78 * Private functions.
79 *
80 ******************************************************************************/
81 static void
plugin_clear_search_dir_list(void)82 plugin_clear_search_dir_list (void)
83 {
84 g_list_foreach (search_dir_list, (GFunc) g_free, NULL);
85 g_list_free (search_dir_list);
86 search_dir_list = NULL;
87 }
88
89
90 static const gchar *
get_plugin_prefs_filename(const gchar * type)91 get_plugin_prefs_filename (const gchar *type)
92 {
93 PluginTypeEntry *entry = g_hash_table_lookup (plugin_types_table, type);
94
95 g_return_val_if_fail(entry, NULL);
96
97 return entry->prefsrc_filename;
98 }
99
100
101 static gchar *
get_plugin_prefs_path(const gchar * type)102 get_plugin_prefs_path (const gchar *type)
103 {
104 const gchar *filename;
105 gchar *path;
106
107 filename = get_plugin_prefs_filename (type);
108 g_return_val_if_fail (filename, NULL);
109
110 path = g_strconcat (g_getenv("HOME"), "/", GIMV_RC_DIR, "/",
111 PLUGIN_RC_DIR, "/", filename, NULL);
112
113 return path;
114 }
115
116
117 static GHashTable *
get_prefs_table(const gchar * type)118 get_prefs_table (const gchar *type)
119 {
120 PluginTypeEntry *entry = g_hash_table_lookup (plugin_types_table, type);
121
122 g_return_val_if_fail(entry, NULL);
123
124 if (!entry->prefs_table)
125 entry->prefs_table = g_hash_table_new (g_str_hash, g_str_equal);
126 return entry->prefs_table;
127 }
128
129
130 static void
plugin_prefs_read_file(const gchar * type)131 plugin_prefs_read_file (const gchar *type)
132 {
133 FILE *pluginrc;
134 gchar *path;
135 gchar buf[BUF_SIZE], section[256], **pair = NULL;
136 gint len;
137
138 if (!plugin_types_table) {
139 guint i;
140
141 plugin_types_table = g_hash_table_new (g_str_hash, g_str_equal);
142
143 for (i = 0; i < plugin_types_num; i++) {
144 PluginTypeEntry *entry = &plugin_types[i];
145 g_hash_table_insert (plugin_types_table, (gpointer) entry->type, entry);
146 }
147 }
148
149 path = get_plugin_prefs_path (type);
150 if (!path || !*path) return;
151
152 pluginrc = fopen(path, "r");
153 if (!pluginrc) {
154 /* g_warning (_("Can't open plugin preference file for read.")); */
155 goto ERROR;
156 }
157
158 section[0] = '\0';
159 while (fgets (buf, sizeof(buf), pluginrc)) {
160 g_strstrip (buf);
161 if (buf[0] == '\0') continue;
162 if (buf[0] == '\n') continue;
163
164 len = strlen(buf);
165
166 if (len > 2 && len < 256 && buf[0] == '[' && buf[len - 1] == ']') {
167 strncpy (section, buf + 1, len - 2);
168 section[len - 2] = '\0';
169 continue;
170 }
171
172 if (!*section) continue;
173
174 pair = g_strsplit (buf, "=", 2);
175 if (!pair) continue;
176
177 if (pair[0]) g_strstrip(pair[0]);
178 if (pair[1]) g_strstrip(pair[1]);
179
180 if (pair[0] && *pair[0]) {
181 gimv_plugin_prefs_save_value (section, type,
182 pair[0],
183 pair[1] ? pair[1] : NULL);
184 }
185
186 g_strfreev (pair);
187 pair = NULL;
188 }
189
190 fclose (pluginrc);
191 ERROR:
192 g_free (path);
193 }
194
195
196 static void
write_value(gpointer key,gpointer value,gpointer user_data)197 write_value (gpointer key, gpointer value, gpointer user_data)
198 {
199 FILE *pluginrc = user_data;
200 gchar *key_string = key;
201 gchar *value_string = value;
202
203 g_return_if_fail (key_string && *key_string);
204 g_return_if_fail (value_string);
205 g_return_if_fail (pluginrc);
206
207 fprintf (pluginrc, "%s=%s\n", key_string, value_string);
208 }
209
210
211 static void
write_section(gpointer key,gpointer value,gpointer user_data)212 write_section (gpointer key, gpointer value, gpointer user_data)
213 {
214 GHashTable *htable = value;
215 FILE *pluginrc = user_data;
216 gchar *string = key;
217
218 g_return_if_fail (string && *string);
219 g_return_if_fail (pluginrc);
220
221 if (!htable) return;
222 if (g_hash_table_size (htable) < 1) return;
223
224 fprintf (pluginrc, "[%s]\n", string);
225 g_hash_table_foreach (htable, (GHFunc) write_value, pluginrc);
226 fprintf (pluginrc, "\n");
227 }
228
229
230 static void
plugin_prefs_write_file(const gchar * type)231 plugin_prefs_write_file (const gchar *type)
232 {
233 GHashTable *prefs_table;
234 gchar *path;
235 FILE *pluginrc;
236
237 prefs_table = get_prefs_table (type);
238 if (!prefs_table) return;
239 if (g_hash_table_size (prefs_table) < 1) return;
240
241 path = get_plugin_prefs_path (type);
242 if (!path) return;
243
244 mkdirs (path);
245
246 pluginrc = fopen (path, "w");
247 if (!pluginrc) {
248 g_warning (_("Can't open plugin preference file for write."));
249 goto ERROR;
250 }
251
252 g_hash_table_foreach (prefs_table, (GHFunc) write_section, pluginrc);
253
254 fclose (pluginrc);
255 ERROR:
256 g_free (path);
257 }
258
259
260 /******************************************************************************
261 *
262 * Public functions.
263 *
264 ******************************************************************************/
265 const gchar *
gimv_plugin_type_get(guint idx)266 gimv_plugin_type_get (guint idx)
267 {
268 if (idx >= plugin_types_num) return NULL;
269 return plugin_types[idx].type;
270 }
271
272
273 gboolean
gimv_plugin_load_module(const gchar * filename,gint * error)274 gimv_plugin_load_module (const gchar *filename, gint *error)
275 {
276 #ifndef G_MODULE_SUFFIX
277 # define G_MODULE_SUFFIX "so"
278 #endif /* G_MODULE_SUFFIX */
279 #define SO_EXT ("." G_MODULE_SUFFIX)
280 #define SO_EXT_LEN (strlen(SO_EXT))
281 GModule *module;
282 GimvPluginInfo *info;
283 const gchar *type;
284 gpointer impl;
285 GimvPrefsWinPage *prefs_ui;
286 GimvMimeTypeEntry *mime_type;
287 guint size;
288 GHashTable *hash = NULL;
289 gboolean success, regist;
290 gint i;
291
292 success = FALSE;
293 regist = FALSE;
294
295 /* load module */
296 module = g_module_open (filename, G_MODULE_BIND_LAZY);
297 if (!module) {
298 /* FIXME: show message (return GError?) */
299 return FALSE;
300 }
301 success = g_module_symbol (module, "gimv_plugin_info", (gpointer) &info);
302 if (!success) {
303 /* FIXME: show message (return GError?) */
304 return FALSE;
305 }
306
307 if (info->if_version != GIMV_PLUGIN_IF_VERSION) {
308 g_warning ("GimvPlugin: plugin interface version is invalid!: %s",
309 filename);
310 g_module_close (module);
311 return FALSE;
312 }
313
314 if (!info->get_implement) {
315 g_warning ("GimvPlugin: get_implement() method is not found!");
316 g_module_close (module);
317 return FALSE;
318 }
319
320 /* get plugin implement */
321 hash = g_hash_table_new (g_str_hash, g_str_equal);
322
323 for (i = 0; (type = info->get_implement(i, &impl, &size)); i++) {
324 PluginTypeEntry *type_entry;
325
326 if (!impl || size <= 0) continue;
327
328 /* regist each method to memory */
329 type_entry = g_hash_table_lookup (plugin_types_table, type);
330 if (!type_entry || !type_entry->regist_fn) continue;
331
332 success = type_entry->regist_fn (info->name,
333 g_module_name (module),
334 impl, size);
335 regist = regist || success;
336
337 if (success && !g_hash_table_lookup (hash, type)) {
338 type_entry->plugin_list
339 = g_list_append (type_entry->plugin_list, module);
340 g_hash_table_insert (hash, (gpointer) type, (gpointer) type);
341 }
342 }
343
344 g_hash_table_destroy(hash);
345 hash = NULL;
346
347 /* get mime types */
348 for (i = 0;
349 regist
350 && info->get_mime_type
351 && info->get_mime_type (i, &mime_type, &size);
352 i++)
353 {
354 if (!mime_type || size <= 0) continue;
355 gimv_mime_types_regist (mime_type, module);
356 }
357
358 /* get preference UI */
359 for (i = 0;
360 regist
361 && info->get_prefs_ui
362 && info->get_prefs_ui (i, &prefs_ui, &size);
363 i++)
364 {
365 if (!prefs_ui || size <= 0) continue;
366 gimv_prefs_win_add_page_entry (prefs_ui);
367 }
368
369 /* append to list */
370 if (regist) {
371 all_plugin_list = g_list_append (all_plugin_list, module);
372 } else {
373 g_module_close (module);
374 }
375
376 return regist;
377 }
378
379
380 gboolean
gimv_plugin_unload_module(const gchar * filename,gint * error)381 gimv_plugin_unload_module (const gchar *filename, gint *error)
382 {
383 g_warning ("GimvPlugin: not imlemented yet!");
384 return FALSE;
385 }
386
387
388 void
gimv_plugin_search_directory(const gchar * dirname)389 gimv_plugin_search_directory (const gchar *dirname)
390 {
391 GList *filelist = NULL, *node;
392
393 g_return_if_fail (dirname);
394 if (!isdir (dirname)) return;
395
396 get_dir (dirname, GETDIR_FOLLOW_SYMLINK, &filelist, NULL);
397 if (!filelist) return;
398
399 for (node = filelist; node; node = g_list_next (node)) {
400 gchar *filename = node->data;
401 size_t len;
402
403 len = strlen (filename);
404 if (len < SO_EXT_LEN || strcmp (filename + len - SO_EXT_LEN, SO_EXT))
405 continue;
406
407 gimv_plugin_load_module (filename, NULL);
408 }
409
410 g_list_foreach (filelist, (GFunc) g_free, NULL);
411 g_list_free (filelist);
412 }
413
414
415 void
gimv_plugin_create_search_dir_list(void)416 gimv_plugin_create_search_dir_list (void)
417 {
418 const gchar *dirlist = NULL;
419 gchar **dirs;
420 gint i;
421
422 plugin_clear_search_dir_list ();
423
424 if (conf.plugin_use_default_search_dir_list)
425 dirlist = PLUGIN_DEFAULT_SEARCH_DIR_LIST;
426 else
427 dirlist = conf.plugin_search_dir_list;
428
429 if (!dirlist || !*dirlist) return;
430
431 dirs = g_strsplit (dirlist, ",", -1);
432
433 for (i = 0; dirs && dirs[i]; i++) {
434 if (*dirs[i])
435 search_dir_list = g_list_append (search_dir_list, g_strdup (dirs[i]));
436 }
437
438 g_strfreev (dirs);
439 }
440
441
442 GList *
gimv_plugin_get_search_dir_list(void)443 gimv_plugin_get_search_dir_list (void)
444 {
445 return search_dir_list;
446 }
447
448
449 const gchar *
gimv_plugin_get_name(GModule * module)450 gimv_plugin_get_name (GModule *module)
451 {
452 gboolean success;
453 GimvPluginInfo *info;
454
455 g_return_val_if_fail (module, NULL);
456
457 success = g_module_symbol (module, "gimv_plugin_info", (gpointer) &info);
458 g_return_val_if_fail (success, NULL);
459
460 return info->name;
461 }
462
463
464 const gchar *
gimv_plugin_get_version_string(GModule * module)465 gimv_plugin_get_version_string (GModule *module)
466 {
467 gboolean success;
468 GimvPluginInfo *info;
469
470 g_return_val_if_fail (module, NULL);
471
472 success = g_module_symbol (module, "gimv_plugin_info", (gpointer) &info);
473 g_return_val_if_fail (success, NULL);
474
475 return info->version;
476 }
477
478
479 const gchar *
gimv_plugin_get_author(GModule * module)480 gimv_plugin_get_author (GModule *module)
481 {
482 gboolean success;
483 GimvPluginInfo *info;
484
485 g_return_val_if_fail (module, NULL);
486
487 success = g_module_symbol (module, "gimv_plugin_info", (gpointer) &info);
488 g_return_val_if_fail (success, NULL);
489
490 return info->author;
491 }
492
493
494 const gchar *
gimv_plugin_get_module_name(GModule * module)495 gimv_plugin_get_module_name (GModule *module)
496 {
497 gboolean success;
498 GimvPluginInfo *info;
499
500 g_return_val_if_fail (module, NULL);
501
502 success = g_module_symbol (module, "gimv_plugin_info", (gpointer) &info);
503 g_return_val_if_fail (success, NULL);
504
505 return g_module_name (module);
506 }
507
508
509 GList *
gimv_plugin_get_list(const gchar * type)510 gimv_plugin_get_list (const gchar *type)
511 {
512 PluginTypeEntry *entry;
513
514 if (!type || !g_strcasecmp(type, "all")) {
515 return all_plugin_list;
516 } else {
517 entry = g_hash_table_lookup(plugin_types_table, type);
518 g_return_val_if_fail (entry, NULL);
519 return entry->plugin_list;
520 }
521 }
522
523
524 gboolean
gimv_plugin_prefs_save_value(const gchar * pname,const gchar * ptype,const gchar * key,const gchar * value)525 gimv_plugin_prefs_save_value (const gchar *pname,
526 const gchar *ptype,
527 const gchar *key,
528 const gchar *value)
529 {
530 GHashTable *prefs_table, *htable;
531 gchar *old_value, *orig_key;
532 gboolean success;
533 gchar *new_key, *new_value;
534
535 g_return_val_if_fail (pname && *pname, FALSE);
536 g_return_val_if_fail (key && *key, FALSE);
537
538 prefs_table = get_prefs_table (ptype);
539 g_return_val_if_fail (prefs_table, FALSE);
540
541 new_key = g_strdup(key);
542 new_value = value ? g_strdup(value) : g_strdup("");
543
544 htable = g_hash_table_lookup (prefs_table, pname);
545 if (!htable) {
546 htable = g_hash_table_new (g_str_hash, g_str_equal);
547 g_hash_table_insert (prefs_table, g_strdup (pname), htable);
548 }
549
550 success = g_hash_table_lookup_extended (htable, key,
551 (gpointer) &orig_key,
552 (gpointer) &old_value);
553 if (success) {
554 g_hash_table_remove (htable, key);
555 g_free (orig_key);
556 g_free (old_value);
557 }
558
559 g_hash_table_insert (htable, new_key, new_value);
560
561 return TRUE;
562 }
563
564
565 gboolean
gimv_plugin_prefs_load_value(const gchar * pname,const gchar * ptype,const gchar * key,GimvPluginPrefsType type,gpointer data)566 gimv_plugin_prefs_load_value (const gchar *pname,
567 const gchar *ptype,
568 const gchar *key,
569 GimvPluginPrefsType type,
570 gpointer data)
571 {
572 GHashTable *prefs_table, *htable;
573 gchar *value;
574
575 g_return_val_if_fail (pname && *pname, FALSE);
576 g_return_val_if_fail (key && *key, FALSE);
577 g_return_val_if_fail (data, FALSE);
578
579 prefs_table = get_prefs_table (ptype);
580 g_return_val_if_fail (prefs_table, FALSE);
581
582 htable = g_hash_table_lookup (prefs_table, pname);
583 if (!htable) return FALSE;
584
585 value = g_hash_table_lookup (htable, key);
586 if (!value) return FALSE;
587 /* g_return_val_if_fail (!*value, FALSE); */
588
589 switch (type) {
590 case GIMV_PLUGIN_PREFS_STRING:
591 *((gchar **) data) = value;
592 break;
593 case GIMV_PLUGIN_PREFS_INT:
594 *((gint *) data) = atoi (value);
595 break;
596 case GIMV_PLUGIN_PREFS_FLOAT:
597 *((gfloat *) data) = atof (value);
598 break;
599 case GIMV_PLUGIN_PREFS_BOOL:
600 if (!g_strcasecmp (value, "TRUE"))
601 *((gboolean *) data) = TRUE;
602 else
603 *((gboolean *) data) = FALSE;
604 break;
605 default:
606 return FALSE;
607 }
608
609 return TRUE;
610 }
611
612
613 void
gimv_plugin_prefs_read_files(void)614 gimv_plugin_prefs_read_files (void)
615 {
616 guint i;
617 for (i = 0; i < plugin_types_num; i ++)
618 plugin_prefs_read_file (plugin_types[i].type);
619 }
620
621
622 void
gimv_plugin_prefs_write_files(void)623 gimv_plugin_prefs_write_files (void)
624 {
625 guint i;
626 for (i = 0; i < plugin_types_num; i ++)
627 plugin_prefs_write_file (plugin_types[i].type);
628 }
629
630
631 void
gimv_plugin_init(void)632 gimv_plugin_init (void)
633 {
634 GList *list;
635
636 gimv_plugin_create_search_dir_list ();
637
638 /* check whether platform supports dynamic loading or not */
639 if (!g_module_supported ()) return;
640
641 if (!plugin_types_table) {
642 guint i;
643
644 plugin_types_table = g_hash_table_new (g_str_hash, g_str_equal);
645
646 for (i = 0; i < plugin_types_num; i++) {
647 PluginTypeEntry *entry = &plugin_types[i];
648 g_hash_table_insert (plugin_types_table, (gpointer) entry->type, entry);
649 }
650 }
651
652 /* load module from disk */
653 list = g_list_last (search_dir_list);
654 for (; list; list = g_list_previous (list)) {
655 gchar *dir = list->data;
656
657 if (dir && *dir)
658 gimv_plugin_search_directory (dir);
659 }
660 }
661