X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fext.c;fp=src%2Fext.c;h=b366491b231dc2ec9ff3e7901b1753acdcd0da10;hb=5b7b5519d955cd0c99d094ba140514e0a2b73083;hp=0000000000000000000000000000000000000000;hpb=8988b3bef0760b4cab8144715cc3d8f55688861c;p=chaz%2Fhomebank diff --git a/src/ext.c b/src/ext.c new file mode 100644 index 0000000..b366491 --- /dev/null +++ b/src/ext.c @@ -0,0 +1,327 @@ + +#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); +} +