summaryrefslogtreecommitdiff
path: root/src/plugins/plugin.c
diff options
context:
space:
mode:
authorSebastien Helleu <flashcode@flashtux.org>2007-10-31 18:04:44 +0100
committerSebastien Helleu <flashcode@flashtux.org>2007-10-31 18:04:44 +0100
commitbf40cfbdfd6b196aceb084941589efc364c65944 (patch)
tree5267a97e07091affce65ace90250ddbe3d2b9c00 /src/plugins/plugin.c
parent02c0dec9cbf1d5e211b8c3c3041c0219c2b2b87d (diff)
downloadweechat-bf40cfbdfd6b196aceb084941589efc364c65944.zip
Improved plugin API, most functions rewritten from scratch
Diffstat (limited to 'src/plugins/plugin.c')
-rw-r--r--src/plugins/plugin.c601
1 files changed, 601 insertions, 0 deletions
diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c
new file mode 100644
index 000000000..32bb9d195
--- /dev/null
+++ b/src/plugins/plugin.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org>
+ * See README for License detail, AUTHORS for developers list.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* plugin.c: manages WeeChat plugins (dynamic C libraries) */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <dlfcn.h>
+
+#include "../core/weechat.h"
+#include "../core/wee-command.h"
+#include "../core/wee-config.h"
+#include "../core/wee-hook.h"
+#include "../core/wee-log.h"
+#include "../core/wee-string.h"
+#include "../core/wee-util.h"
+#include "../gui/gui-chat.h"
+#include "plugin.h"
+#include "plugin-api.h"
+#include "plugin-config.h"
+#include "plugin-list.h"
+
+
+struct t_weechat_plugin *weechat_plugins = NULL;
+struct t_weechat_plugin *last_weechat_plugin = NULL;
+
+
+/*
+ * plugin_exec_on_files: find files in a directory and execute a
+ * function on each file
+ */
+
+void
+plugin_exec_on_files (struct t_weechat_plugin *plugin, char *directory,
+ int (*callback)(struct t_weechat_plugin *, char *))
+{
+ char complete_filename[1024];
+ DIR *dir;
+ struct dirent *entry;
+ struct stat statbuf;
+
+ dir = opendir (directory);
+ if (dir)
+ {
+ while ((entry = readdir (dir)))
+ {
+ snprintf (complete_filename, sizeof (complete_filename) - 1,
+ "%s/%s", directory, entry->d_name);
+ lstat (complete_filename, &statbuf);
+ if (!S_ISDIR(statbuf.st_mode))
+ {
+ (int) (*callback) (plugin, complete_filename);
+ }
+ }
+ closedir (dir);
+ }
+}
+
+/*
+ * plugin_search: search a plugin by name
+ */
+
+struct t_weechat_plugin *
+plugin_search (char *name)
+{
+ struct t_weechat_plugin *ptr_plugin;
+
+ for (ptr_plugin = weechat_plugins; ptr_plugin;
+ ptr_plugin = ptr_plugin->next_plugin)
+ {
+ if (string_strcasecmp (ptr_plugin->name, name) == 0)
+ return ptr_plugin;
+ }
+
+ /* plugin not found */
+ return NULL;
+}
+
+/*
+ * plugin_load: load a WeeChat plugin (a dynamic library)
+ * return: pointer to new WeeChat plugin, NULL if error
+ */
+
+struct t_weechat_plugin *
+plugin_load (char *filename)
+{
+ char *full_name;
+ void *handle;
+ char *name, *description, *version, *charset;
+ t_weechat_init_func *init_func;
+ struct t_weechat_plugin *new_plugin;
+
+ if (!filename)
+ return NULL;
+
+ full_name = util_search_full_lib_name (filename, "plugins");
+
+ if (!full_name)
+ return NULL;
+
+ handle = dlopen (full_name, RTLD_GLOBAL | RTLD_NOW);
+ if (!handle)
+ {
+ gui_chat_printf (NULL,
+ _("%s%s unable to load plugin \"%s\": %s\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ WEECHAT_ERROR, full_name, dlerror());
+ free (full_name);
+ return NULL;
+ }
+
+ /* look for plugin name */
+ name = dlsym (handle, "plugin_name");
+ if (!name)
+ {
+ dlclose (handle);
+ gui_chat_printf (NULL,
+ _("%s%s symbol \"%s\" not found in "
+ "plugin \"%s\", failed to load\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ WEECHAT_ERROR, "plugin_name",
+ full_name);
+ free (full_name);
+ return NULL;
+ }
+
+ /* check for plugin with same name */
+ if (plugin_search (name))
+ {
+ dlclose (handle);
+ gui_chat_printf (NULL,
+ _("%s%s unable to load plugin \"%s\": a plugin "
+ "with same name already exists\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ WEECHAT_ERROR, full_name);
+ free (full_name);
+ return NULL;
+ }
+
+ /* look for plugin description */
+ description = dlsym (handle, "plugin_description");
+ if (!description)
+ {
+ dlclose (handle);
+ gui_chat_printf (NULL,
+ _("%s%s symbol \"%s\" not found "
+ "in plugin \"%s\", failed to load\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ WEECHAT_ERROR, "plugin_description",
+ full_name);
+ free (full_name);
+ return NULL;
+ }
+
+ /* look for plugin version */
+ version = dlsym (handle, "plugin_version");
+ if (!version)
+ {
+ dlclose (handle);
+ gui_chat_printf (NULL,
+ _("%s%s symbol \"%s\" not found in "
+ "plugin \"%s\", failed to load\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ WEECHAT_ERROR, "plugin_version",
+ full_name);
+ free (full_name);
+ return NULL;
+ }
+
+ /* look for plugin charset (optional) */
+ charset = dlsym (handle, "plugin_charset");
+
+ /* look for plugin init function */
+ init_func = dlsym (handle, "weechat_plugin_init");
+ if (!init_func)
+ {
+ dlclose (handle);
+ gui_chat_printf (NULL,
+ _("%s%s function \"%s\" not "
+ "found in plugin \"%s\", failed to load\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ WEECHAT_ERROR, "weechat_plugin_init",
+ full_name);
+ free (full_name);
+ return NULL;
+ }
+
+ /* create new plugin */
+ new_plugin = (struct t_weechat_plugin *)malloc (sizeof (struct t_weechat_plugin));
+ if (new_plugin)
+ {
+ /* variables */
+ new_plugin->filename = strdup (full_name);
+ new_plugin->handle = handle;
+ new_plugin->name = strdup (name);
+ new_plugin->description = strdup (description);
+ new_plugin->version = strdup (version);
+ new_plugin->charset = (charset) ? strdup (charset) : NULL;
+
+ /* functions */
+ new_plugin->charset_set = &plugin_api_charset_set;
+ new_plugin->iconv_to_internal = &plugin_api_iconv_to_internal;
+ new_plugin->iconv_from_internal = &plugin_api_iconv_from_internal;
+ new_plugin->gettext = &plugin_api_gettext;
+ new_plugin->ngettext = &plugin_api_ngettext;
+ new_plugin->strcasecmp = &plugin_api_strcasecmp;
+ new_plugin->strncasecmp = &plugin_api_strncasecmp;
+ new_plugin->explode_string = &plugin_api_explode_string;
+ new_plugin->free_exploded_string = &plugin_api_free_exploded_string;
+
+ new_plugin->mkdir_home = &plugin_api_mkdir_home;
+ new_plugin->exec_on_files = &plugin_api_exec_on_files;
+
+ new_plugin->printf = &plugin_api_printf;
+ new_plugin->prefix = &plugin_api_prefix;
+ new_plugin->color = &plugin_api_color;
+ new_plugin->print_infobar = &plugin_api_print_infobar;
+ new_plugin->infobar_remove = &plugin_api_infobar_remove;
+
+ new_plugin->hook_command = &plugin_api_hook_command;
+ new_plugin->hook_message = &plugin_api_hook_message;
+ new_plugin->hook_config = &plugin_api_hook_config;
+ new_plugin->hook_timer = &plugin_api_hook_timer;
+ new_plugin->hook_fd = &plugin_api_hook_fd;
+ new_plugin->unhook = &plugin_api_unhook;
+ new_plugin->unhook_all = &plugin_api_unhook_all;
+
+ new_plugin->buffer_new = &plugin_api_buffer_new;
+ new_plugin->buffer_search = &plugin_api_buffer_search;
+ new_plugin->buffer_close = &plugin_api_buffer_close;
+ new_plugin->buffer_set = &plugin_api_buffer_set;
+ new_plugin->buffer_nick_add = &plugin_api_buffer_nick_add;
+ new_plugin->buffer_nick_remove = &plugin_api_buffer_nick_remove;
+
+ new_plugin->command = &plugin_api_command;
+
+ new_plugin->info_get = &plugin_api_info_get;
+
+ new_plugin->config_get = &plugin_api_config_get;
+ new_plugin->config_set = &plugin_api_config_set;
+ new_plugin->plugin_config_get = &plugin_api_plugin_config_get;
+ new_plugin->plugin_config_set = &plugin_api_plugin_config_set;
+
+ new_plugin->log = &plugin_api_log;
+
+ /* add new plugin to list */
+ new_plugin->prev_plugin = last_weechat_plugin;
+ new_plugin->next_plugin = NULL;
+ if (weechat_plugins)
+ last_weechat_plugin->next_plugin = new_plugin;
+ else
+ weechat_plugins = new_plugin;
+ last_weechat_plugin = new_plugin;
+
+ gui_chat_printf (NULL,
+ _("%sInitializing plugin \"%s\" %s\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_INFO],
+ new_plugin->name, new_plugin->version);
+
+ /* init plugin */
+ if (((t_weechat_init_func *)init_func) (new_plugin) < 0)
+ {
+ gui_chat_printf (NULL,
+ _("%s%s unable to initialize plugin "
+ "\"%s\"\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ WEECHAT_ERROR, full_name);
+ plugin_remove (new_plugin);
+ free (full_name);
+ return NULL;
+ }
+ }
+ else
+ {
+ gui_chat_printf (NULL,
+ _("%s%s unable to load plugin \"%s\" "
+ "(not enough memory)\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ WEECHAT_ERROR, full_name);
+ free (full_name);
+ return NULL;
+ }
+
+ gui_chat_printf (NULL,
+ _("%sPlugin \"%s\" (%s) loaded.\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_INFO],
+ name, full_name);
+
+ free (full_name);
+
+ return new_plugin;
+}
+
+/*
+ * plugin_auto_load_file: load a file found by plugin_auto_load,
+ * but only it this is really a dynamic library
+ */
+
+int
+plugin_auto_load_file (struct t_weechat_plugin *plugin, char *filename)
+{
+ char *pos;
+
+ /* make C compiler happy */
+ (void) plugin;
+
+ if (cfg_plugins_extension && cfg_plugins_extension[0])
+ {
+ pos = strstr (filename, cfg_plugins_extension);
+ if (pos)
+ {
+ if (string_strcasecmp (pos, cfg_plugins_extension) == 0)
+ plugin_load (filename);
+ }
+ }
+ else
+ plugin_load (filename);
+ return 1;
+}
+
+/*
+ * plugin_auto_load: auto-load WeeChat plugins
+ */
+
+void
+plugin_auto_load ()
+{
+ char *ptr_home, *dir_name, *plugins_path, *plugins_path2;
+ char *list_plugins, *pos, *pos2;
+
+ if (cfg_plugins_autoload && cfg_plugins_autoload[0])
+ {
+ if (string_strcasecmp (cfg_plugins_autoload, "*") == 0)
+ {
+ /* auto-load plugins in WeeChat home dir */
+ if (cfg_plugins_path && cfg_plugins_path[0])
+ {
+ ptr_home = getenv ("HOME");
+ plugins_path = string_replace (cfg_plugins_path, "~", ptr_home);
+ plugins_path2 = string_replace ((plugins_path) ?
+ plugins_path : cfg_plugins_path,
+ "%h", weechat_home);
+ plugin_exec_on_files (NULL,
+ (plugins_path2) ?
+ plugins_path2 : ((plugins_path) ?
+ plugins_path : cfg_plugins_path),
+ &plugin_auto_load_file);
+ if (plugins_path)
+ free (plugins_path);
+ if (plugins_path2)
+ free (plugins_path2);
+ }
+
+ /* auto-load plugins in WeeChat global lib dir */
+ dir_name = (char *)malloc (strlen (WEECHAT_LIBDIR) + 16);
+ if (dir_name)
+ {
+ snprintf (dir_name, strlen (WEECHAT_LIBDIR) + 16,
+ "%s/plugins", WEECHAT_LIBDIR);
+ plugin_exec_on_files (NULL, dir_name, &plugin_auto_load_file);
+ free (dir_name);
+ }
+ }
+ else
+ {
+ list_plugins = strdup (cfg_plugins_autoload);
+ if (list_plugins)
+ {
+ pos = list_plugins;
+ while (pos && pos[0])
+ {
+ pos2 = strchr (pos, ',');
+ if (pos2)
+ pos2[0] = '\0';
+ plugin_load (pos);
+ if (pos2)
+ pos = pos2 + 1;
+ else
+ pos = NULL;
+ }
+ free (list_plugins);
+ }
+ }
+ }
+}
+
+/*
+ * plugin_remove: remove a WeeChat plugin
+ */
+
+void
+plugin_remove (struct t_weechat_plugin *plugin)
+{
+ struct t_weechat_plugin *new_weechat_plugins;
+
+ /* remove plugin from list */
+ if (last_weechat_plugin == plugin)
+ last_weechat_plugin = plugin->prev_plugin;
+ if (plugin->prev_plugin)
+ {
+ (plugin->prev_plugin)->next_plugin = plugin->next_plugin;
+ new_weechat_plugins = weechat_plugins;
+ }
+ else
+ new_weechat_plugins = plugin->next_plugin;
+
+ if (plugin->next_plugin)
+ (plugin->next_plugin)->prev_plugin = plugin->prev_plugin;
+
+ /* remove all hooks */
+ unhook_all_plugin (plugin);
+
+ /* free data */
+ if (plugin->filename)
+ free (plugin->filename);
+ dlclose (plugin->handle);
+ if (plugin->name)
+ free (plugin->name);
+ if (plugin->description)
+ free (plugin->description);
+ if (plugin->version)
+ free (plugin->version);
+ if (plugin->charset)
+ free (plugin->charset);
+ free (plugin);
+
+ weechat_plugins = new_weechat_plugins;
+}
+
+/*
+ * plugin_unload: unload a WeeChat plugin
+ */
+
+void
+plugin_unload (struct t_weechat_plugin *plugin)
+{
+ t_weechat_end_func *end_func;
+ char *name;
+
+ name = (plugin->name) ? strdup (plugin->name) : strdup ("???");
+
+ end_func = dlsym (plugin->handle, "weechat_plugin_end");
+ if (end_func)
+ (void) (end_func) (plugin);
+ plugin_remove (plugin);
+
+ gui_chat_printf (NULL,
+ _("%sPlugin \"%s\" unloaded.\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_INFO],
+ (name) ? name : "???");
+ if (name)
+ free (name);
+}
+
+/*
+ * plugin_unload_name: unload a WeeChat plugin by name
+ */
+
+void
+plugin_unload_name (char *name)
+{
+ struct t_weechat_plugin *ptr_plugin;
+
+ ptr_plugin = plugin_search (name);
+ if (ptr_plugin)
+ plugin_unload (ptr_plugin);
+ else
+ {
+ gui_chat_printf (NULL,
+ _("%s%s plugin \"%s\" not found\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ WEECHAT_ERROR, name);
+ }
+}
+
+/*
+ * plugin_unload_all: unload all WeeChat plugins
+ */
+
+void
+plugin_unload_all ()
+{
+ while (weechat_plugins)
+ plugin_unload (last_weechat_plugin);
+}
+
+/*
+ * plugin_reload_name: reload a WeeChat plugin by name
+ */
+
+void
+plugin_reload_name (char *name)
+{
+ struct t_weechat_plugin *ptr_plugin;
+ char *filename;
+
+ ptr_plugin = plugin_search (name);
+ if (ptr_plugin)
+ {
+ filename = strdup (ptr_plugin->filename);
+ if (filename)
+ {
+ plugin_unload (ptr_plugin);
+ gui_chat_printf (NULL,
+ _("%sPlugin \"%s\" unloaded.\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_INFO],
+ name);
+ plugin_load (filename);
+ free (filename);
+ }
+ }
+ else
+ {
+ gui_chat_printf (NULL,
+ _("%s%s plugin \"%s\" not found\n"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ WEECHAT_ERROR, name);
+ }
+}
+
+/*
+ * plugin_init: init plugin support
+ */
+
+void
+plugin_init (int auto_load)
+{
+ /* read plugins options on disk */
+ plugin_config_read ();
+
+ /* auto-load plugins if asked */
+ if (auto_load)
+ plugin_auto_load ();
+}
+
+/*
+ * plugin_end: end plugin support
+ */
+
+void
+plugin_end ()
+{
+ /* write plugins config options */
+ plugin_config_write ();
+
+ /* unload all plugins */
+ plugin_unload_all ();
+}
+
+/*
+ * plugin_print_log: print plugin infos in log (usually for crash dump)
+ */
+
+void
+plugin_print_log ()
+{
+ struct t_weechat_plugin *ptr_plugin;
+
+ for (ptr_plugin = weechat_plugins; ptr_plugin;
+ ptr_plugin = ptr_plugin->next_plugin)
+ {
+ weechat_log_printf ("\n");
+ weechat_log_printf ("[plugin (addr:0x%X)]\n", ptr_plugin);
+ weechat_log_printf (" filename . . . . . . . : '%s'\n", ptr_plugin->filename);
+ weechat_log_printf (" handle . . . . . . . . : 0x%X\n", ptr_plugin->handle);
+ weechat_log_printf (" name . . . . . . . . . : '%s'\n", ptr_plugin->name);
+ weechat_log_printf (" description. . . . . . : '%s'\n", ptr_plugin->description);
+ weechat_log_printf (" version. . . . . . . . : '%s'\n", ptr_plugin->version);
+ weechat_log_printf (" charset. . . . . . . . : '%s'\n", ptr_plugin->charset);
+ weechat_log_printf (" prev_plugin. . . . . . : 0x%X\n", ptr_plugin->prev_plugin);
+ weechat_log_printf (" next_plugin. . . . . . : 0x%X\n", ptr_plugin->next_plugin);
+ }
+
+ plugin_list_print_log ();
+}