Fri, 25 Oct 2024 02:40:41 -0500
Update the supported python versions
See https://devguide.python.org/versions/ for the currently supported python versions.
Testing Done:
Called in the turtles.
Reviewed at https://reviews.imfreedom.org/r/3607/
/* * Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see <https://www.gnu.org/licenses/>. */ #include <stdio.h> #include <string.h> #include <glib.h> #include <glib/gi18n-lib.h> #include <gplugin/gplugin-core.h> #include <gplugin/gplugin-file-source.h> #include <gplugin/gplugin-file-tree.h> #include <gplugin/gplugin-manager.h> #include <gplugin/gplugin-native-loader.h> #include <gplugin/gplugin-private.h> #include <gplugin/gplugin-source.h> /** * GPluginManagerForeachFunc: * @id: The id of the plugin. * @plugins: (transfer none) (element-type GPlugin.Plugin): A * [struct@GLib.SList] of each plugin that has the id @id. * @data: User data passed to [method@GPlugin.Manager.foreach]. * * A callback function for [method@GPlugin.Manager.foreach]. */ /** * GPluginManager: * * The manager is responsible for querying plugins as well as telling loaders * when to load and unload plugins. It also keeps track of paths that should be * searched for plugins. * * Since: 0.32 */ /****************************************************************************** * Enums *****************************************************************************/ enum { SIG_LOADING, SIG_LOADED, SIG_LOAD_FAILED, SIG_UNLOADING, SIG_UNLOADED, SIG_UNLOAD_FAILED, SIG_LOADER_REGISTERED, SIG_LOADER_UNREGISTERED, N_SIGNALS, }; /****************************************************************************** * Structs *****************************************************************************/ struct _GPluginManager { GObject parent; GQueue *paths; GHashTable *plugins; GHashTable *loaders; gboolean refresh_needed; GRegex *dependency_regex; }; G_DEFINE_FINAL_TYPE(GPluginManager, gplugin_manager, G_TYPE_OBJECT) /****************************************************************************** * Globals *****************************************************************************/ GPluginManager *default_manager = NULL; static guint signals[N_SIGNALS] = { 0, }; const char *dependency_pattern = "^(?P<id>.+?)((?P<op>\\<=|\\<|==|=|\\>=|\\>)(?P<version>.+))?$"; /****************************************************************************** * Helpers *****************************************************************************/ static gboolean gplugin_manager_remove_list_value( G_GNUC_UNUSED gpointer k, gpointer v, G_GNUC_UNUSED gpointer d) { GSList *l = NULL; for(l = (GSList *)v; l; l = l->next) { if(l->data && G_IS_OBJECT(l->data)) g_object_unref(l->data); } g_slist_free((GSList *)v); return TRUE; } static void gplugin_manager_change_paths_from_environment( GPluginManager *manager, const char *name, gboolean prepend) { char **paths; int i; const char *from_env; from_env = g_getenv(name); if(from_env == NULL) { return; } paths = g_strsplit(from_env, G_SEARCHPATH_SEPARATOR_S, 0); for(i = 0; paths[i]; i++) { if(prepend) { gplugin_manager_prepend_path(manager, paths[i]); } else { gplugin_manager_append_path(manager, paths[i]); } } g_strfreev(paths); } static void gplugin_manager_foreach_unload_plugin( gpointer key, gpointer value, G_GNUC_UNUSED gpointer data) { GList *l = NULL; char *id = (char *)key; for(l = (GList *)value; l; l = l->next) { GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data); GPluginLoader *loader = NULL; GError *error = NULL; if(gplugin_plugin_get_state(plugin) != GPLUGIN_PLUGIN_STATE_LOADED) { continue; } loader = gplugin_plugin_get_loader(plugin); if(!gplugin_loader_unload_plugin(loader, plugin, TRUE, &error)) { g_warning( "failed to unload plugin with id %s: %s", id, error ? error->message : "unknown"); g_clear_error(&error); } g_object_unref(loader); } } static char * gplugin_manager_normalize_path(const char *path) { if(g_str_has_suffix(path, G_DIR_SEPARATOR_S)) { return g_strdup(path); } return g_strdup_printf("%s%s", path, G_DIR_SEPARATOR_S); } static int gplugin_manager_compare_paths(gconstpointer a, gconstpointer b) { char *keya = NULL, *keyb = NULL; int r = 0; keya = g_utf8_collate_key_for_filename((const char *)a, -1); keyb = g_utf8_collate_key_for_filename((const char *)b, -1); r = strcmp(keya, keyb); g_free(keya); g_free(keyb); return r; } static gboolean gplugin_manager_load_dependencies( GPluginManager *manager, GPluginPlugin *plugin, G_GNUC_UNUSED GPluginPluginInfo *info, GError **error) { GSList *dependencies = NULL, *l = NULL; GError *ourerror = NULL; gboolean all_loaded = TRUE; dependencies = gplugin_manager_get_plugin_dependencies(manager, plugin, &ourerror); if(ourerror != NULL) { g_propagate_error(error, ourerror); return FALSE; } for(l = dependencies; l != NULL; l = l->next) { GPluginPlugin *dependency = GPLUGIN_PLUGIN(l->data); gboolean loaded = FALSE; loaded = gplugin_manager_load_plugin(manager, dependency, &ourerror); if(!loaded || ourerror != NULL) { if(ourerror != NULL) { g_propagate_error(error, ourerror); } all_loaded = FALSE; break; } } g_slist_free_full(dependencies, g_object_unref); return all_loaded; } /****************************************************************************** * Manager implementation *****************************************************************************/ static gboolean gplugin_manager_loading_cb( G_GNUC_UNUSED GPluginManager *manager, G_GNUC_UNUSED GPluginPlugin *plugin, G_GNUC_UNUSED GError **error) { return TRUE; } static gboolean gplugin_manager_unloading_cb( G_GNUC_UNUSED GPluginManager *manager, G_GNUC_UNUSED GPluginPlugin *plugin, G_GNUC_UNUSED GError **error) { return TRUE; } /****************************************************************************** * GObject Implementation *****************************************************************************/ static void gplugin_manager_finalize(GObject *obj) { GPluginManager *manager = GPLUGIN_MANAGER(obj); g_queue_free_full(manager->paths, g_free); manager->paths = NULL; g_clear_pointer(&manager->dependency_regex, g_regex_unref); /* unload all of the loaded plugins */ g_hash_table_foreach( manager->plugins, gplugin_manager_foreach_unload_plugin, NULL); /* free all the data in the plugins hash table and destroy it */ g_hash_table_foreach_remove( manager->plugins, gplugin_manager_remove_list_value, NULL); g_clear_pointer(&manager->plugins, g_hash_table_destroy); /* clean up our list of loaders */ g_clear_pointer(&manager->loaders, g_hash_table_destroy); /* call the base class's destructor */ G_OBJECT_CLASS(gplugin_manager_parent_class)->finalize(obj); } static void gplugin_manager_class_init(GPluginManagerClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->finalize = gplugin_manager_finalize; /** * GPluginManager::loading-plugin: * @manager: The [class@GPlugin.Manager] instance. * @plugin: The [iface@GPlugin.Plugin] that's about to be loaded. * @error: Return address for a [struct@GLib.Error]. * * Emitted before @plugin is loaded. * * Returns: %TRUE to allow the plugin to load or %FALSE to stop it from * being loaded. * * Since: 0.33 */ signals[SIG_LOADING] = g_signal_new_class_handler( "loading-plugin", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, G_CALLBACK(gplugin_manager_loading_cb), gplugin_boolean_accumulator, NULL, NULL, G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT, G_TYPE_POINTER); /** * GPluginManager::loaded-plugin: * @manager: The [class@GPlugin.Manager] instance. * @plugin: The [iface@GPlugin.Plugin] that's about to be loaded. * * Emitted after a plugin is loaded. * * Since: 0.33 */ signals[SIG_LOADED] = g_signal_new_class_handler( "loaded-plugin", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); /** * GPluginManager::load-plugin-failed: * @manager: The [class@GPlugin.Manager] instance. * @plugin: The [iface@GPlugin.Plugin] that failed to load. * @error: The [struct@GLib.Error] of what went wrong. * * Emitted after a plugin fails to load. * * Since: 0.33 */ signals[SIG_LOAD_FAILED] = g_signal_new_class_handler( "load-plugin-failed", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_ERROR); /** * GPluginManager::unloading-plugin * @manager: The [class@GPlugin.Manager] instance. * @plugin: The [iface@GPlugin.Plugin] that's about to be unloaded. * @error: Return address for a [struct@GLib.Error]. * * Emitted before a plugin is unloaded. * * Returns: %TRUE to allow the plugin to be unloaded, or %FALSE to stop * the plugin from being unloaded. * * Since: 0.33 */ signals[SIG_UNLOADING] = g_signal_new_class_handler( "unloading-plugin", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, G_CALLBACK(gplugin_manager_unloading_cb), gplugin_boolean_accumulator, NULL, NULL, G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT, G_TYPE_POINTER); /** * GPluginManager::unloaded-plugin: * @manager: The [class@GPlugin.Manager] instance. * @plugin: The [iface@GPlugin.Plugin] that's about to be loaded. * * emitted after a plugin is successfully unloaded. * * Since: 0.33 */ signals[SIG_UNLOADED] = g_signal_new_class_handler( "unloaded-plugin", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); /** * GPluginManager::unload-plugin-failed: * @manager: The [class@GPlugin.Manager] instance. * @plugin: The [iface@GPlugin.Plugin] instance that failed to unload. * @error: A [struct@GLib.Error] instance. * * Emitted when @manager was asked to unload @plugin, but @plugin returned * %FALSE when its unload function was called. * * Since: 0.33 */ signals[SIG_UNLOAD_FAILED] = g_signal_new_class_handler( "unload-plugin-failed", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_ERROR); /** * GPluginManager::loader-registered: * @manager: The [class@GPlugin.Manager] instance. * @loader: The [class@GPlugin.Loader] instance that was registered. * * Emitted when @loader has been registered with @manager via * [method@GPlugin.Manager.register_loader]. * * Since: 0.39 */ signals[SIG_LOADER_REGISTERED] = g_signal_new_class_handler( "loader-registered", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 1, GPLUGIN_TYPE_LOADER); /** * GPluginManager::loader-unregistered: * @manager: The [class@GPlugin.Manager] instance. * @loader: The [class@GPlugin.Loader] instance that was unregistered. * * Emitted when @loader has been unregistered from @manager via * [method@GPlugin.Manager.unregister_loader]. * * Since: 0.39 */ signals[SIG_LOADER_UNREGISTERED] = g_signal_new_class_handler( "loader-unregistered", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 1, GPLUGIN_TYPE_LOADER); } static void gplugin_manager_init(GPluginManager *manager) { manager->paths = g_queue_new(); /* the plugins hashtable is keyed on a plugin id and holds a GSList of all * plugins that share that id. */ manager->plugins = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); manager->loaders = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref); manager->dependency_regex = g_regex_new(dependency_pattern, 0, 0, NULL); } /****************************************************************************** * Private API *****************************************************************************/ void gplugin_manager_private_init(gboolean register_native_loader) { GError *error = NULL; if(GPLUGIN_IS_MANAGER(default_manager)) { return; } default_manager = g_object_new(GPLUGIN_TYPE_MANAGER, NULL); if(register_native_loader) { GPluginLoader *loader = NULL; loader = gplugin_native_loader_new(); if(!gplugin_manager_register_loader(default_manager, loader, &error)) { if(error != NULL) { g_error("failed to register loader: %s", error->message); g_error_free(error); } else { g_error("failed to register loader: unknown failure"); } } g_clear_object(&loader); } } void gplugin_manager_private_uninit(void) { /* g_clear_pointer (and therefore g_clear_object), clears the pointer * before calling the destroy function. So we have to handle this ourself * and clear the pointer after destruction since plugins are unloaded * during destruction and may need to unregister a loader during their * unload. */ if(default_manager != NULL) { g_object_unref(default_manager); default_manager = NULL; } } /****************************************************************************** * API *****************************************************************************/ /** * gplugin_manager_append_path: * @manager: The manager instance. * @path: A path to add to the end of the plugin search paths. * * Adds @path to the end of the list of paths to search for plugins. */ void gplugin_manager_append_path(GPluginManager *manager, const char *path) { GList *l = NULL; char *normalized = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_return_if_fail(path != NULL); normalized = gplugin_manager_normalize_path(path); l = g_queue_find_custom( manager->paths, normalized, gplugin_manager_compare_paths); if(l == NULL) { g_queue_push_tail(manager->paths, normalized); } else { g_free(normalized); } } /** * gplugin_manager_prepend_path: * @manager: The manager instance. * @path: A path to add to the beginning of the plugin search paths. * * Adds @path to the beginning of the list of paths to search for plugins. */ void gplugin_manager_prepend_path(GPluginManager *manager, const char *path) { GList *l = NULL; char *normalized = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_return_if_fail(path != NULL); normalized = gplugin_manager_normalize_path(path); l = g_queue_find_custom( manager->paths, normalized, gplugin_manager_compare_paths); if(l == NULL) { g_queue_push_head(manager->paths, normalized); } else { g_free(normalized); } } /** * gplugin_manager_remove_path: * @manager: The manager instance. * @path: A path to remove from the plugin search paths. * * Removes @path from the list of paths to search for plugins. */ void gplugin_manager_remove_path(GPluginManager *manager, const char *path) { GList *l = NULL; char *normalized = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_return_if_fail(path != NULL); normalized = gplugin_manager_normalize_path(path); l = g_queue_find_custom( manager->paths, normalized, gplugin_manager_compare_paths); if(l != NULL) { g_free(l->data); g_queue_delete_link(manager->paths, l); } g_free(normalized); } /** * gplugin_manager_remove_paths: * @manager: The manager instance. * * Clears all paths that are set to search for plugins. */ void gplugin_manager_remove_paths(GPluginManager *manager) { g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_queue_clear_full(manager->paths, g_free); } /** * gplugin_manager_add_default_paths: * @manager: The manager instance. * * Adds the path that GPlugin was installed to to the plugin search path, as * well as `${XDG_CONFIG_HOME}/gplugin` so users can install additional loaders * themselves. */ void gplugin_manager_add_default_paths(GPluginManager *manager) { char *path; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); path = g_build_filename(PREFIX, LIBDIR, "gplugin", NULL); gplugin_manager_prepend_path(manager, path); g_free(path); path = g_build_filename(g_get_user_config_dir(), "gplugin", NULL); gplugin_manager_prepend_path(manager, path); g_free(path); } /** * gplugin_manager_add_app_paths: * @manager: The manager instance. * @prefix: The installation prefix for the application. * @appname: The name of the application whose paths to add. * * Adds the application installation path for @appname. * * This will add `{prefix}/{appname}/plugins` to the list as well as * `${XDG_CONFIG_HOME}/{appname}/plugins`. */ void gplugin_manager_add_app_paths( GPluginManager *manager, const char *prefix, const char *appname) { char *path; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_return_if_fail(appname != NULL); path = g_build_filename(prefix, LIBDIR, appname, NULL); gplugin_manager_prepend_path(manager, path); g_free(path); path = g_build_filename(g_get_user_config_dir(), appname, "plugins", NULL); gplugin_manager_prepend_path(manager, path); g_free(path); } /** * gplugin_manager_append_paths_from_environment: * @manager: The manager instance. * @name: The name of the environment variable containing the paths to add. * * Append the paths held in the environment variable @name to the list. * * Since: 0.37 */ void gplugin_manager_append_paths_from_environment( GPluginManager *manager, const char *name) { gplugin_manager_change_paths_from_environment(manager, name, FALSE); } /** * gplugin_manager_prepend_paths_from_environment: * @manager: The manager instance. * @name: The name of the environment variable containing the paths to add. * * Prepends the paths held in the environment variable @name to the list. * * Since: 0.37 */ void gplugin_manager_prepend_paths_from_environment( GPluginManager *manager, const char *name) { gplugin_manager_change_paths_from_environment(manager, name, TRUE); } /** * gplugin_manager_get_paths: * @manager: The manager instance. * * Gets the list of paths which will be searched for plugins. * * Returns: (element-type utf8) (transfer none): The [type@GLib.List] of paths * which will be searched for plugins. */ GList * gplugin_manager_get_paths(GPluginManager *manager) { g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); return manager->paths->head; } /** * gplugin_manager_register_loader: * @manager: The manager instance. * @loader: The loader instance to register. * @error: (out) (nullable): The return address for a [struct@GLib.Error]. * * Registers @loader as an available loader. * * Returns: %TRUE if the loader was successfully register, %FALSE otherwise * with @error set. */ gboolean gplugin_manager_register_loader( GPluginManager *manager, GPluginLoader *loader, GError **error) { GPluginLoader *found = NULL; const char *id = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE); g_return_val_if_fail(GPLUGIN_IS_LOADER(loader), FALSE); id = gplugin_loader_get_id(loader); found = g_hash_table_lookup(manager->loaders, id); if(GPLUGIN_IS_LOADER(found)) { g_set_error( error, GPLUGIN_DOMAIN, 0, _("loader %s was already registered"), id); return FALSE; } g_hash_table_insert(manager->loaders, g_strdup(id), g_object_ref(loader)); /* make a note that we need to refresh */ manager->refresh_needed = TRUE; g_signal_emit(manager, signals[SIG_LOADER_REGISTERED], 0, loader); return TRUE; } /** * gplugin_manager_unregister_loader: * @manager: The manager instance. * @loader: The loader instance to unregister. * @error: (out) (nullable): The return address for a [struct@GLib.Error]. * * Unregisters @loader as an available loader. * * Returns: %TRUE if the loader was successfully unregistered, %FALSE * otherwise with @error set. */ gboolean gplugin_manager_unregister_loader( GPluginManager *manager, GPluginLoader *loader, GError **error) { const char *id = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE); g_return_val_if_fail(GPLUGIN_IS_LOADER(loader), FALSE); id = gplugin_loader_get_id(loader); loader = g_hash_table_lookup(manager->loaders, id); if(!GPLUGIN_IS_LOADER(loader)) { g_set_error( error, GPLUGIN_DOMAIN, 0, _("loader %s is not registered"), id); return FALSE; } /* Temporarily add a reference to loader so we can emit the signal if it * was removed from our table correctly. */ g_object_ref(loader); if(g_hash_table_remove(manager->loaders, id)) { g_signal_emit(manager, signals[SIG_LOADER_UNREGISTERED], 0, loader); } g_clear_object(&loader); return TRUE; } /** * gplugin_manager_get_loaders: * @manager: The manager instance. * * Returns a list of all registered loaders. * * Returns: (element-type GPlugin.Loader) (transfer container): Returns a list * of all registered loaders. */ GList * gplugin_manager_get_loaders(GPluginManager *manager) { g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE); return g_hash_table_get_values(manager->loaders); } /** * gplugin_manager_refresh: * @manager: The manager instance. * * Forces a refresh of all plugins found in the search paths. */ void gplugin_manager_refresh(GPluginManager *manager) { GPluginSource *file_source = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); file_source = gplugin_file_source_new(manager); manager->refresh_needed = TRUE; while(manager->refresh_needed) { manager->refresh_needed = FALSE; if(gplugin_source_scan(file_source)) { manager->refresh_needed = TRUE; } } g_clear_object(&file_source); } /** * gplugin_manager_foreach: * @manager: The manager instance. * @func: (scope call): The function to call with each plugin. * @data: User data to pass to @func. * * Calls @func for each plugin that is known. */ void gplugin_manager_foreach( GPluginManager *manager, GPluginManagerForeachFunc func, gpointer data) { GHashTableIter iter; gpointer id = NULL, plugins = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_return_if_fail(func != NULL); g_hash_table_iter_init(&iter, manager->plugins); while(g_hash_table_iter_next(&iter, &id, &plugins)) { func((char *)id, (GSList *)plugins, data); } } /** * gplugin_manager_find_plugins: * @manager: The manager instance. * @id: The ID of the plugin to find. * * Finds all plugins matching @id. * * Returns: (element-type GPlugin.Plugin) (transfer full): A [struct@GLib.SList] * of plugins matching @id. */ GSList * gplugin_manager_find_plugins(GPluginManager *manager, const char *id) { GSList *plugins_list = NULL, *l; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); g_return_val_if_fail(id != NULL, NULL); l = g_hash_table_lookup(manager->plugins, id); plugins_list = g_slist_copy_deep(l, (GCopyFunc)g_object_ref, NULL); return plugins_list; } /** * gplugin_manager_find_plugins_with_version: * @manager: The manager instance. * @id: The ID of the plugin to find. * @op: one of <, <=, =, ==, >=, >. * @version: The version to compare against. * * Similar to [method@GPlugin.Manager.find_plugins] but only returns plugins * whose versions match @op and @version. * * This is primarily used for dependency loading where a plugin may depend on a * specific range of versions of another plugin. * * Returns: (element-type GPlugin.Plugin) (transfer full): A [struct@GLib.SList] * of plugins matching @id and the version constraint. */ GSList * gplugin_manager_find_plugins_with_version( GPluginManager *manager, const char *id, const char *op, const char *version) { GSList *plugins = NULL, *filtered = NULL, *l = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); plugins = gplugin_manager_find_plugins(manager, id); if((op == NULL || *op == '\0') && (version == NULL || *version == '\0')) { /* we weren't actually passed an operator and a version so just return * the list we have based on the id. */ return plugins; } for(l = plugins; l; l = l->next) { GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data); GPluginPluginInfo *info = NULL; const char *found_version = NULL; int result = 0; gboolean keep = FALSE; /* get the plugin's version from it's info */ info = gplugin_plugin_get_info(plugin); found_version = gplugin_plugin_info_get_version(info); /* now compare the version of the plugin to passed in version. This * should be done in this order so it's easier to track the operators. * IE: we want to keep the inequality the same. */ result = gplugin_version_compare(found_version, version); /* we need to keep info around until we're done using found_version */ g_object_unref(info); if(result < 0) { keep = (g_strcmp0(op, "<") == 0 || g_strcmp0(op, "<=") == 0); } else if(result == 0) { keep = (g_strcmp0(op, "=") == 0 || g_strcmp0(op, "==") == 0 || g_strcmp0(op, "<=") == 0 || g_strcmp0(op, ">=") == 0); } else if(result > 0) { keep = (g_strcmp0(op, ">") == 0 || g_strcmp0(op, ">=") == 0); } if(keep) { filtered = g_slist_prepend(filtered, g_object_ref(plugin)); } } g_slist_free_full(plugins, g_object_unref); return g_slist_reverse(filtered); } /** * gplugin_manager_find_plugins_with_state: * @manager: The manager instance. * @state: The state to look for. * * Finds all plugins that currently have a state of @state. * * Returns: (element-type GPlugin.Plugin) (transfer full): A [struct@GLib.SList] * of plugins whose state is @state. */ GSList * gplugin_manager_find_plugins_with_state( GPluginManager *manager, GPluginPluginState state) { GSList *plugins = NULL; GHashTableIter iter; gpointer value = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); g_hash_table_iter_init(&iter, manager->plugins); while(g_hash_table_iter_next(&iter, NULL, &value)) { GSList *l = NULL; for(l = (GSList *)value; l != NULL; l = l->next) { GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data); if(gplugin_plugin_get_state(plugin) == state) { plugins = g_slist_prepend(plugins, g_object_ref(plugin)); } } } return plugins; } /** * gplugin_manager_find_plugin: * @manager: The manager instance. * @id: The ID of the plugin to find. * * Finds the first plugin matching @id. * * This function uses [method@GPlugin.Manager.find_plugins] and returns the * first plugin in the list. * * Returns: (transfer full): A plugin instance or %NULL if no plugin * matching @id was found. */ GPluginPlugin * gplugin_manager_find_plugin(GPluginManager *manager, const char *id) { GSList *plugins_list = NULL; GPluginPlugin *plugin = NULL; g_return_val_if_fail(id != NULL, NULL); plugins_list = gplugin_manager_find_plugins(manager, id); if(plugins_list == NULL) { return NULL; } plugin = g_object_ref(plugins_list->data); g_slist_free_full(plugins_list, g_object_unref); return plugin; } /** * gplugin_manager_find_plugin_with_filename: * @manager: The instance. * @filename: The filename of the plugin. * * Finds a plugin with the given filename. * * This method should be used sparingly as you should typically be using plugin * id's to identify them, however, sometimes it's necessary to find them by * filename. * * Returns: (transfer full) (nullable): The plugin if found, otherwise %NULL. * * Since: 0.44 */ GPluginPlugin * gplugin_manager_find_plugin_with_filename( GPluginManager *manager, const char *filename) { GHashTableIter iter; gpointer value = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); g_return_val_if_fail(filename != NULL, NULL); g_hash_table_iter_init(&iter, manager->plugins); while(g_hash_table_iter_next(&iter, NULL, &value)) { for(GSList *l = value; l != NULL; l = l->next) { GPluginPlugin *plugin = l->data; char *plugin_filename = NULL; if(!GPLUGIN_IS_PLUGIN(plugin)) { continue; } plugin_filename = gplugin_plugin_get_filename(plugin); if(g_strcmp0(filename, plugin_filename) == 0) { g_free(plugin_filename); return g_object_ref(plugin); } g_free(plugin_filename); } } return NULL; } /** * gplugin_manager_find_plugin_with_newest_version: * @manager: The manager instance. * @id: The ID of the plugin to find. * * Calls [method@GPlugin.Manager.find_plugins] with @id, and then returns the * plugins with the highest version number or %NULL if no plugins with @id are * found. * * Returns: (transfer full): The plugin with an ID of @id that has the highest * version number, or %NULL if no plugins were found with @id. */ GPluginPlugin * gplugin_manager_find_plugin_with_newest_version( GPluginManager *manager, const char *id) { GPluginPlugin *plugin_a = NULL; GPluginPluginInfo *info_a = NULL; const char *version_a = NULL; GSList *l = NULL; g_return_val_if_fail(id != NULL, NULL); l = gplugin_manager_find_plugins(manager, id); for(; l != NULL; l = g_slist_delete_link(l, l)) { GPluginPlugin *plugin_b = NULL; GPluginPluginInfo *info_b = NULL; const char *version_b = NULL; int cmp = 0; if(!GPLUGIN_IS_PLUGIN(l->data)) { continue; } plugin_b = GPLUGIN_PLUGIN(l->data); info_b = gplugin_plugin_get_info(plugin_b); /* If this is the first plugin we've found, set the plugin_a values and * continue. */ if(!GPLUGIN_IS_PLUGIN(plugin_a)) { plugin_a = plugin_b; info_a = info_b; version_a = gplugin_plugin_info_get_version(info_a); continue; } /* At this point, we've seen another plugin, so we need to compare * their versions. */ version_b = gplugin_plugin_info_get_version(info_b); cmp = gplugin_version_compare(version_a, version_b); if(cmp < 0) { /* plugin_b has a newer version, so set the plugin_a pointers to * the plugin_b pointers as well as the version pointers. */ g_set_object(&plugin_a, plugin_b); g_set_object(&info_a, info_b); version_a = version_b; } /* Clean up the plugin_b pointers. */ g_clear_object(&plugin_b); g_clear_object(&info_b); } g_clear_object(&info_a); return plugin_a; } /** * gplugin_manager_get_plugin_dependencies: * @manager: The manager instance. * @plugin: The plugin whose dependencies to get. * @error: (out) (nullable): Return address for a [struct@GLib.Error]. * * Returns a list of all the plugins that @plugin depends on. * * Returns: (element-type GPlugin.Plugin) (transfer full): A [struct@GLib.SList] * of plugins that @plugin depends on, or %NULL on error with @error * set. */ GSList * gplugin_manager_get_plugin_dependencies( GPluginManager *manager, GPluginPlugin *plugin, GError **error) { GPluginPluginInfo *info = NULL; GSList *ret = NULL; const char *const *dependencies = NULL; int i = 0; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), NULL); info = gplugin_plugin_get_info(plugin); dependencies = gplugin_plugin_info_get_dependencies(info); g_object_unref(info); if(dependencies == NULL) { return NULL; } for(i = 0; dependencies[i] != NULL; i++) { gboolean found = FALSE; char **ors = NULL; int o = 0; ors = g_strsplit(dependencies[i], "|", 0); for(o = 0; ors[o]; o++) { GMatchInfo *match = NULL; GSList *matches = NULL; char *oid = NULL, *oop = NULL, *over = NULL; if(!g_regex_match(manager->dependency_regex, ors[o], 0, &match)) { continue; } /* grab the or'd id, op, and version */ oid = g_match_info_fetch_named(match, "id"); oop = g_match_info_fetch_named(match, "op"); over = g_match_info_fetch_named(match, "version"); /* free the match info */ g_match_info_free(match); /* now look for a plugin matching the id */ matches = gplugin_manager_find_plugins_with_version( manager, oid, oop, over); g_free(oid); g_free(oop); g_free(over); if(matches == NULL) { continue; } /* prepend the first found match to our return value */ ret = g_slist_prepend(ret, g_object_ref(matches->data)); g_slist_free_full(matches, g_object_unref); found = TRUE; break; } g_strfreev(ors); if(!found) { g_set_error( error, GPLUGIN_DOMAIN, 0, _("failed to find dependency %s for %s"), dependencies[i], gplugin_plugin_info_get_id(info)); g_slist_free_full(ret, g_object_unref); return NULL; } } return ret; } /** * gplugin_manager_load_plugin: * @manager: The manager instance. * @plugin: The plugin instance. * @error: (out) (nullable): Return location for a [struct@GLib.Error] or %NULL. * * Loads @plugin and all of its dependencies. * * If a dependency can not be loaded, @plugin will not be loaded either. * However, any other plugins that @plugin depends on that were loaded from * this call, will not be unloaded. * * Returns: %TRUE if @plugin was loaded successfully or already loaded, %FALSE * otherwise. */ gboolean gplugin_manager_load_plugin( GPluginManager *manager, GPluginPlugin *plugin, GError **error) { GPluginPluginInfo *info = NULL; GPluginLoader *loader = NULL; GError *real_error = NULL; gboolean ret = TRUE; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE); g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), FALSE); /* if the plugin is already loaded there's nothing for us to do */ if(gplugin_plugin_get_state(plugin) == GPLUGIN_PLUGIN_STATE_LOADED) { return TRUE; } /* now try to get the plugin info from the plugin */ info = gplugin_plugin_get_info(plugin); if(info == NULL) { char *filename = gplugin_plugin_get_filename(plugin); g_set_error( error, GPLUGIN_DOMAIN, 0, _("Plugin %s did not return value plugin info"), filename); g_free(filename); gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED); return FALSE; } if(!gplugin_manager_load_dependencies(manager, plugin, info, &real_error)) { g_object_unref(info); g_propagate_error(error, real_error); return FALSE; } g_object_unref(info); /* now load the actual plugin */ loader = gplugin_plugin_get_loader(plugin); if(!GPLUGIN_IS_LOADER(loader)) { char *filename = gplugin_plugin_get_filename(plugin); g_set_error( error, GPLUGIN_DOMAIN, 0, _("The loader for %s is not a loader. This " "should not happened!"), filename); g_free(filename); gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED); g_object_unref(loader); return FALSE; } g_signal_emit(manager, signals[SIG_LOADING], 0, plugin, &real_error, &ret); if(!ret) { /* Set the plugin's error. */ g_object_set(plugin, "error", real_error, NULL); g_propagate_error(error, real_error); gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED); g_object_unref(loader); return ret; } ret = gplugin_loader_load_plugin(loader, plugin, &real_error); if(ret) { g_clear_error(&real_error); g_signal_emit(manager, signals[SIG_LOADED], 0, plugin); } else { g_signal_emit(manager, signals[SIG_LOAD_FAILED], 0, plugin, real_error); g_propagate_error(error, real_error); } g_object_unref(loader); return ret; } /** * gplugin_manager_unload_plugin: * @manager: The manager instance. * @plugin: The plugin instance. * @error: (out) (nullable): Return location for a [struct@GLib.Error] or %NULL. * * Unloads @plugin. * * If @plugin has dependencies, they are not unloaded. * * Returns: %TRUE if @plugin was unloaded successfully or not loaded, %FALSE * otherwise. */ gboolean gplugin_manager_unload_plugin( GPluginManager *manager, GPluginPlugin *plugin, GError **error) { GPluginLoader *loader = NULL; GError *real_error = NULL; gboolean ret = TRUE; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE); g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), FALSE); if(gplugin_plugin_get_state(plugin) != GPLUGIN_PLUGIN_STATE_LOADED) { return TRUE; } loader = gplugin_plugin_get_loader(plugin); if(!GPLUGIN_IS_LOADER(loader)) { g_set_error_literal( error, GPLUGIN_DOMAIN, 0, _("Plugin loader is not a loader")); g_object_unref(loader); return FALSE; } g_signal_emit( manager, signals[SIG_UNLOADING], 0, plugin, &real_error, &ret); if(!ret) { /* Set the plugin's error. */ g_object_set(plugin, "error", real_error, NULL); g_propagate_error(error, real_error); gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED); g_object_unref(loader); return ret; } ret = gplugin_loader_unload_plugin(loader, plugin, FALSE, &real_error); if(ret) { g_clear_error(&real_error); g_signal_emit(manager, signals[SIG_UNLOADED], 0, plugin); } else { g_signal_emit( manager, signals[SIG_UNLOAD_FAILED], 0, plugin, real_error); g_propagate_error(error, real_error); } g_object_unref(loader); return ret; } /** * gplugin_manager_list_plugins: * @manager: The manager instance. * * Returns a list of all plugin IDs. * * Each id should be queried directly for more information. * * Returns: (element-type utf8) (transfer container): A [struct@GLib.List] of * each unique plugin ID. */ GList * gplugin_manager_list_plugins(GPluginManager *manager) { GQueue *queue = NULL; GList *ret = NULL; GHashTableIter iter; gpointer key = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); queue = g_queue_new(); g_hash_table_iter_init(&iter, manager->plugins); while(g_hash_table_iter_next(&iter, &key, NULL)) { g_queue_push_tail(queue, (char *)key); } ret = g_list_copy(queue->head); g_queue_free(queue); return ret; } /** * gplugin_manager_add_plugin: * @manager: The instance. * @id: The id of the plugin to add. * @plugin: The plugin to add. * * Adds @plugin to @manager with @id. This should only be called by * [iface@GPlugin.Source] implementations. * * Since: 0.39 */ void gplugin_manager_add_plugin( GPluginManager *manager, const char *id, GPluginPlugin *plugin) { GSList *plugins = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_return_if_fail(id != NULL); g_return_if_fail(GPLUGIN_IS_PLUGIN(plugin)); plugins = g_hash_table_lookup(manager->plugins, id); plugins = g_slist_prepend(plugins, g_object_ref(plugin)); g_hash_table_insert(manager->plugins, g_strdup(id), plugins); } /** * gplugin_manager_get_default: * * Gets the default plugin manager in GPlugin. * * Returns: (transfer none): The default GPluginManager instance. * * Since: 0.33 */ GPluginManager * gplugin_manager_get_default(void) { return default_manager; }