#include #include #include #include #include "ext.h" extern struct Preferences *PREFS; const int _hook_recursion_soft_limit = 50; const int _hook_recursion_hard_limit = 99; struct PluginEngine { const gchar* type; PluginEngineInitializer init; PluginEngineTerminator term; PluginEngineFileChecker check_file; PluginMetadataReader read_metadata; PluginLoader load_plugin; PluginUnloader unload_plugin; PluginExecutor execute; PluginHookCaller call_hook; }; static GList* _engine_list = NULL; static GHashTable* _loaded_plugins = NULL; void ext_init(int* argc, char** argv[], char** env[]) { GList *list = g_list_first(_engine_list); while (list) { struct PluginEngine* engine = list->data; engine->init(argc, argv, env); list = g_list_next(list); } if (!_loaded_plugins) { _loaded_plugins = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); } } void ext_term(void) { GList *list = g_list_first(_engine_list); while (list) { struct PluginEngine* engine = list->data; engine->term(); list = g_list_next(list); } g_list_free(_engine_list); _engine_list = NULL; if (_loaded_plugins) { g_hash_table_unref(_loaded_plugins); _loaded_plugins = NULL; } } void ext_register(const gchar* type, PluginEngineInitializer init, PluginEngineTerminator term, PluginEngineFileChecker check_file, PluginMetadataReader read_metadata, PluginLoader load_plugin, PluginUnloader unload_plugin, PluginExecutor execute, PluginHookCaller call_hook) { struct PluginEngine* engine = g_malloc0(sizeof(struct PluginEngine)); engine->type = type; engine->init = init; engine->term = term; engine->check_file = check_file; engine->read_metadata = read_metadata; engine->load_plugin = load_plugin; engine->unload_plugin = unload_plugin; engine->execute = execute; engine->call_hook = call_hook; _engine_list = g_list_append(_engine_list, engine); } static struct PluginEngine* _get_engine_for_plugin(const gchar* plugin_filename) { if (!plugin_filename) { return NULL; } GList *list = g_list_first(_engine_list); while (list) { struct PluginEngine* engine = list->data; if (engine->check_file(plugin_filename)) { return engine; } list = g_list_next(list); } return NULL; } static void _read_directory(const gchar* directory, GHashTable* hash) { GDir* dir = g_dir_open(directory, 0, NULL); if (!dir) return; const gchar* filename; while ((filename = g_dir_read_name(dir))) { gchar* full = g_build_filename(directory, filename, NULL); if (g_file_test(full, G_FILE_TEST_IS_REGULAR) && _get_engine_for_plugin(filename)) { g_hash_table_insert(hash, g_strdup(filename), NULL); } g_free(full); } g_dir_close(dir); } gchar** ext_list_plugins() { GHashTable* hash = g_hash_table_new(g_str_hash, g_str_equal); gchar** it; for (it = PREFS->ext_path; it && *it; ++it) { _read_directory(*it, hash); } GList* list = g_list_sort(g_hash_table_get_keys(hash), (GCompareFunc)g_utf8_collate); g_hash_table_unref(hash); guint len = g_list_length(list); gchar** strv = g_new0(gchar*, len + 1); int i; for (i = 0; i < len; ++i) { strv[i] = g_list_nth_data(list, i); } g_list_free(list); return strv; } gchar* ext_find_plugin(const gchar* plugin_filename) { if (!plugin_filename) return NULL; gchar** it; for (it = PREFS->ext_path; *it; ++it) { if (!g_path_is_absolute(*it)) continue; gchar* full = g_build_filename(*it, plugin_filename, NULL); if (g_file_test(full, G_FILE_TEST_IS_REGULAR)) { return full; } g_free(full); } return NULL; } GHashTable* ext_read_plugin_metadata(const gchar* plugin_filename) { gchar* full = ext_find_plugin(plugin_filename); if (!full) return NULL; GHashTable* ret = NULL; struct PluginEngine* engine = _get_engine_for_plugin(plugin_filename); if (engine && engine->read_metadata) { ret = engine->read_metadata(full); } g_free(full); return ret; } gint ext_load_plugin(const gchar* plugin_filename) { gchar* full = ext_find_plugin(plugin_filename); if (!full) return -1; gint ret = -1; struct PluginEngine* engine = _get_engine_for_plugin(plugin_filename); if (engine && engine->load_plugin && engine->load_plugin(full) == 0) { g_hash_table_insert(_loaded_plugins, g_strdup(plugin_filename), NULL); ret = 0; } g_free(full); return ret; } void ext_unload_plugin(const gchar* plugin_filename) { gchar* full = ext_find_plugin(plugin_filename); if (!full) return; struct PluginEngine* engine = _get_engine_for_plugin(plugin_filename); if (engine && engine->unload_plugin) { engine->unload_plugin(full); } g_free(full); g_hash_table_remove(_loaded_plugins, plugin_filename); } gboolean ext_is_plugin_loaded(const gchar* plugin_filename) { return g_hash_table_contains(_loaded_plugins, plugin_filename); } void ext_execute_action(const gchar* plugin_filename) { gchar* full = ext_find_plugin(plugin_filename); if (!full) return; struct PluginEngine* engine = _get_engine_for_plugin(plugin_filename); if (engine && engine->execute) { engine->execute(full); } g_free(full); } void ext_hook(const gchar* hook_id, ...) { GList *list = NULL; va_list ap; va_start(ap, hook_id); for (;;) { GValue* val = (GValue*)va_arg(ap, GValue*); if (!val) break; list = g_list_append(list, val); } va_end(ap); ext_vhook(hook_id, list); g_list_free(list); } void ext_vhook(const gchar* hook_id, GList* args) { static int recursion_level = 0; if (_hook_recursion_hard_limit <= recursion_level) { return; } else if (_hook_recursion_soft_limit <= recursion_level) { int level = recursion_level; recursion_level = -1; GValue val_level = G_VALUE_INIT; ext_hook("deep_hook_recursion", EXT_INT(&val_level, level), NULL); recursion_level = level; } ++recursion_level; g_print("ext_hook: %s (level %d)\n", hook_id, recursion_level); GList *list = g_list_first(_engine_list); while (list) { struct PluginEngine* engine = list->data; engine->call_hook(hook_id, args); list = g_list_next(list); } --recursion_level; } gboolean ext_has(const gchar* feature) { #ifdef OFX_ENABLE if (0 == g_utf8_collate(feature, "libofx")) { return TRUE; } #endif #ifdef PERL_ENABLE if (0 == g_utf8_collate(feature, "perl")) { return TRUE; } #endif return FALSE; } void* ext_symbol_lookup(const gchar* symbol) { static GModule* module = NULL; if (!module) module = g_module_open(NULL, 0); void* ptr; if (module && g_module_symbol(module, symbol, &ptr)) { return ptr; } return NULL; } void ext_run_modal(const gchar* title, const gchar* text, const gchar* type) { GtkMessageType t = GTK_MESSAGE_INFO; if (0 == g_utf8_collate(type, "error")) { t = GTK_MESSAGE_ERROR; } if (0 == g_utf8_collate(type, "warn")) { t = GTK_MESSAGE_WARNING; } if (0 == g_utf8_collate(type, "question")) { t = GTK_MESSAGE_QUESTION; } GtkWidget* dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, t, GTK_BUTTONS_CLOSE, "%s", text); if (title) { gtk_window_set_title(GTK_WINDOW(dialog), title); } gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); }