summaryrefslogtreecommitdiff
path: root/src/core/core-hook.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/core-hook.c')
-rw-r--r--src/core/core-hook.c952
1 files changed, 952 insertions, 0 deletions
diff --git a/src/core/core-hook.c b/src/core/core-hook.c
new file mode 100644
index 000000000..d89c2f988
--- /dev/null
+++ b/src/core/core-hook.c
@@ -0,0 +1,952 @@
+/*
+ * core-hook.c - WeeChat hooks management
+ *
+ * Copyright (C) 2003-2024 Sébastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat 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.
+ *
+ * WeeChat 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 WeeChat. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include "weechat.h"
+#include "core-debug.h"
+#include "core-hook.h"
+#include "core-hashtable.h"
+#include "core-infolist.h"
+#include "core-log.h"
+#include "core-signal.h"
+#include "core-string.h"
+#include "core-util.h"
+#include "../gui/gui-chat.h"
+#include "../plugins/plugin.h"
+
+
+char *hook_type_string[HOOK_NUM_TYPES] =
+{ "command", "command_run", "timer", "fd", "process", "connect", "line",
+ "print", "signal", "hsignal", "config", "completion", "modifier",
+ "info", "info_hashtable", "infolist", "hdata", "focus", "url" };
+struct t_hook *weechat_hooks[HOOK_NUM_TYPES]; /* list of hooks */
+struct t_hook *last_weechat_hook[HOOK_NUM_TYPES]; /* last hook */
+int hooks_count[HOOK_NUM_TYPES]; /* number of hooks */
+int hooks_count_total = 0; /* total number of hooks */
+int hook_exec_recursion = 0; /* 1 when a hook is executed */
+int real_delete_pending = 0; /* 1 if some hooks must be deleted */
+
+int hook_socketpair_ok = 0; /* 1 if socketpair() is OK */
+
+/* hook callbacks */
+t_callback_hook *hook_callback_add[HOOK_NUM_TYPES] =
+{ NULL, NULL, NULL, &hook_fd_add_cb, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+t_callback_hook *hook_callback_remove[HOOK_NUM_TYPES] =
+{ NULL, NULL, NULL, &hook_fd_remove_cb, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+t_callback_hook *hook_callback_free_data[HOOK_NUM_TYPES] =
+{
+ &hook_command_free_data,
+ &hook_command_run_free_data,
+ &hook_timer_free_data,
+ &hook_fd_free_data,
+ &hook_process_free_data,
+ &hook_connect_free_data,
+ &hook_line_free_data,
+ &hook_print_free_data,
+ &hook_signal_free_data,
+ &hook_hsignal_free_data,
+ &hook_config_free_data,
+ &hook_completion_free_data,
+ &hook_modifier_free_data,
+ &hook_info_free_data,
+ &hook_info_hashtable_free_data,
+ &hook_infolist_free_data,
+ &hook_hdata_free_data,
+ &hook_focus_free_data,
+ &hook_url_free_data,
+};
+t_callback_hook_get_desc *hook_callback_get_desc[HOOK_NUM_TYPES] =
+{
+ &hook_command_get_description,
+ &hook_command_run_get_description,
+ &hook_timer_get_description,
+ &hook_fd_get_description,
+ &hook_process_get_description,
+ &hook_connect_get_description,
+ &hook_line_get_description,
+ &hook_print_get_description,
+ &hook_signal_get_description,
+ &hook_hsignal_get_description,
+ &hook_config_get_description,
+ &hook_completion_get_description,
+ &hook_modifier_get_description,
+ &hook_info_get_description,
+ &hook_info_hashtable_get_description,
+ &hook_infolist_get_description,
+ &hook_hdata_get_description,
+ &hook_focus_get_description,
+ &hook_url_get_description,
+};
+t_callback_hook_infolist *hook_callback_add_to_infolist[HOOK_NUM_TYPES] =
+{
+ &hook_command_add_to_infolist,
+ &hook_command_run_add_to_infolist,
+ &hook_timer_add_to_infolist,
+ &hook_fd_add_to_infolist,
+ &hook_process_add_to_infolist,
+ &hook_connect_add_to_infolist,
+ &hook_line_add_to_infolist,
+ &hook_print_add_to_infolist,
+ &hook_signal_add_to_infolist,
+ &hook_hsignal_add_to_infolist,
+ &hook_config_add_to_infolist,
+ &hook_completion_add_to_infolist,
+ &hook_modifier_add_to_infolist,
+ &hook_info_add_to_infolist,
+ &hook_info_hashtable_add_to_infolist,
+ &hook_infolist_add_to_infolist,
+ &hook_hdata_add_to_infolist,
+ &hook_focus_add_to_infolist,
+ &hook_url_add_to_infolist,
+};
+t_callback_hook *hook_callback_print_log[HOOK_NUM_TYPES] =
+{
+ &hook_command_print_log,
+ &hook_command_run_print_log,
+ &hook_timer_print_log,
+ &hook_fd_print_log,
+ &hook_process_print_log,
+ &hook_connect_print_log,
+ &hook_line_print_log,
+ &hook_print_print_log,
+ &hook_signal_print_log,
+ &hook_hsignal_print_log,
+ &hook_config_print_log,
+ &hook_completion_print_log,
+ &hook_modifier_print_log,
+ &hook_info_print_log,
+ &hook_info_hashtable_print_log,
+ &hook_infolist_print_log,
+ &hook_hdata_print_log,
+ &hook_focus_print_log,
+ &hook_url_print_log,
+};
+
+
+/*
+ * Initializes hooks.
+ */
+
+void
+hook_init ()
+{
+ int type, sock[2], rc;
+
+ /* initialize list of hooks and callbacks */
+ for (type = 0; type < HOOK_NUM_TYPES; type++)
+ {
+ weechat_hooks[type] = NULL;
+ last_weechat_hook[type] = NULL;
+ hooks_count[type] = 0;
+ }
+ hooks_count_total = 0;
+ hook_last_system_time = time (NULL);
+
+ /*
+ * Set a flag to 0 if socketpair() function is not available.
+ *
+ * For the connect hook, when this is defined an array of sockets will
+ * be passed from the parent process to the child process instead of using
+ * SCM_RIGHTS to pass a socket back from the child process to parent
+ * process.
+ *
+ * This allows connections to work on Windows but it limits the number of
+ * IPs that can be attempted each time.
+ */
+ hook_socketpair_ok = 1;
+
+#if defined(__CYGWIN__) || defined(__APPLE__) || defined(__MACH__)
+ hook_socketpair_ok = 0;
+ (void) sock;
+ (void) rc;
+#else
+ /*
+ * Test if socketpair() function is working fine: this is NOT the case
+ * on Windows with Ubuntu bash
+ * (errno == 94: ESOCKTNOSUPPORT: socket type not supported)
+ */
+ rc = socketpair (AF_LOCAL, SOCK_DGRAM, 0, sock);
+ if (rc < 0)
+ {
+ /* Windows/Ubuntu */
+ hook_socketpair_ok = 0;
+ }
+ else
+ {
+ close (sock[0]);
+ close (sock[1]);
+ }
+#endif
+}
+
+/*
+ * Searches for a hook type.
+ *
+ * Returns index of type in enum t_hook_type, -1 if type is not found.
+ */
+
+int
+hook_search_type (const char *type)
+{
+ int i;
+
+ if (!type)
+ return -1;
+
+ for (i = 0; i < HOOK_NUM_TYPES; i++)
+ {
+ if (strcmp (hook_type_string[i], type) == 0)
+ return i;
+ }
+
+ /* type not found */
+ return -1;
+}
+
+/*
+ * Searches for position of hook in list (to keep hooks sorted).
+ *
+ * Hooks are sorted by priority, except commands which are sorted by command
+ * name, and then priority.
+ */
+
+struct t_hook *
+hook_find_pos (struct t_hook *hook)
+{
+ struct t_hook *ptr_hook;
+ int rc_cmp;
+
+ if (hook->type == HOOK_TYPE_COMMAND)
+ {
+ /* for command hook, sort on command name + priority */
+ for (ptr_hook = weechat_hooks[hook->type]; ptr_hook;
+ ptr_hook = ptr_hook->next_hook)
+ {
+ if (!ptr_hook->deleted)
+ {
+ rc_cmp = string_strcmp (HOOK_COMMAND(hook, command),
+ HOOK_COMMAND(ptr_hook, command));
+ if (rc_cmp < 0)
+ return ptr_hook;
+ if ((rc_cmp == 0) && (hook->priority > ptr_hook->priority))
+ return ptr_hook;
+ }
+ }
+ }
+ else
+ {
+ /* for other types, sort on priority */
+ for (ptr_hook = weechat_hooks[hook->type]; ptr_hook;
+ ptr_hook = ptr_hook->next_hook)
+ {
+ if (!ptr_hook->deleted && (hook->priority > ptr_hook->priority))
+ return ptr_hook;
+ }
+ }
+
+ /* position not found, add at the end */
+ return NULL;
+}
+
+/*
+ * Adds a hook to list.
+ */
+
+void
+hook_add_to_list (struct t_hook *new_hook)
+{
+ struct t_hook *pos_hook;
+
+ if (weechat_hooks[new_hook->type])
+ {
+ pos_hook = hook_find_pos (new_hook);
+ if (pos_hook)
+ {
+ /* add hook before "pos_hook" */
+ new_hook->prev_hook = pos_hook->prev_hook;
+ new_hook->next_hook = pos_hook;
+ if (pos_hook->prev_hook)
+ (pos_hook->prev_hook)->next_hook = new_hook;
+ else
+ weechat_hooks[new_hook->type] = new_hook;
+ pos_hook->prev_hook = new_hook;
+ }
+ else
+ {
+ /* add hook to end of list */
+ new_hook->prev_hook = last_weechat_hook[new_hook->type];
+ new_hook->next_hook = NULL;
+ last_weechat_hook[new_hook->type]->next_hook = new_hook;
+ last_weechat_hook[new_hook->type] = new_hook;
+ }
+ }
+ else
+ {
+ new_hook->prev_hook = NULL;
+ new_hook->next_hook = NULL;
+ weechat_hooks[new_hook->type] = new_hook;
+ last_weechat_hook[new_hook->type] = new_hook;
+ }
+
+ hooks_count[new_hook->type]++;
+ hooks_count_total++;
+
+ if (hook_callback_add[new_hook->type])
+ (hook_callback_add[new_hook->type]) (new_hook);
+}
+
+/*
+ * Removes a hook from list.
+ */
+
+void
+hook_remove_from_list (struct t_hook *hook)
+{
+ struct t_hook *new_hooks;
+ int type;
+
+ type = hook->type;
+
+ if (last_weechat_hook[hook->type] == hook)
+ last_weechat_hook[hook->type] = hook->prev_hook;
+ if (hook->prev_hook)
+ {
+ (hook->prev_hook)->next_hook = hook->next_hook;
+ new_hooks = weechat_hooks[hook->type];
+ }
+ else
+ new_hooks = hook->next_hook;
+
+ if (hook->next_hook)
+ (hook->next_hook)->prev_hook = hook->prev_hook;
+
+ weechat_hooks[type] = new_hooks;
+
+ hooks_count[type]--;
+ hooks_count_total--;
+
+ if (hook_callback_remove[hook->type])
+ (hook_callback_remove[hook->type]) (hook);
+
+ free (hook);
+}
+
+/*
+ * Removes hooks marked as "deleted" from list.
+ */
+
+void
+hook_remove_deleted ()
+{
+ int type;
+ struct t_hook *ptr_hook, *next_hook;
+
+ if (real_delete_pending)
+ {
+ for (type = 0; type < HOOK_NUM_TYPES; type++)
+ {
+ ptr_hook = weechat_hooks[type];
+ while (ptr_hook)
+ {
+ next_hook = ptr_hook->next_hook;
+
+ if (ptr_hook->deleted)
+ hook_remove_from_list (ptr_hook);
+
+ ptr_hook = next_hook;
+ }
+ }
+ real_delete_pending = 0;
+ }
+}
+
+/*
+ * Initializes a new hook with default values.
+ */
+
+void
+hook_init_data (struct t_hook *hook, struct t_weechat_plugin *plugin,
+ int type, int priority,
+ const void *callback_pointer, void *callback_data)
+{
+ hook->plugin = plugin;
+ hook->subplugin = NULL;
+ hook->type = type;
+ hook->deleted = 0;
+ hook->running = 0;
+ hook->priority = priority;
+ hook->callback_pointer = callback_pointer;
+ hook->callback_data = callback_data;
+ hook->hook_data = NULL;
+
+ if (weechat_debug_core >= 2)
+ {
+ gui_chat_printf (NULL,
+ "debug: adding hook: type=%d (%s), plugin=\"%s\", "
+ "priority=%d",
+ hook->type,
+ hook_type_string[hook->type],
+ plugin_get_name (hook->plugin),
+ hook->priority);
+ }
+}
+
+/*
+ * Checks if a hook pointer is valid.
+ *
+ * Returns:
+ * 1: hook exists
+ * 0: hook does not exist
+ */
+
+int
+hook_valid (struct t_hook *hook)
+{
+ int type;
+ struct t_hook *ptr_hook;
+
+ if (!hook)
+ return 0;
+
+ for (type = 0; type < HOOK_NUM_TYPES; type++)
+ {
+ for (ptr_hook = weechat_hooks[type]; ptr_hook;
+ ptr_hook = ptr_hook->next_hook)
+ {
+ if (!ptr_hook->deleted && (ptr_hook == hook))
+ return 1;
+ }
+ }
+
+ /* hook not found */
+ return 0;
+}
+
+/*
+ * Starts a hook exec.
+ */
+
+void
+hook_exec_start ()
+{
+ hook_exec_recursion++;
+}
+
+/*
+ * Ends a hook_exec.
+ */
+
+void
+hook_exec_end ()
+{
+ if (hook_exec_recursion > 0)
+ hook_exec_recursion--;
+
+ if (hook_exec_recursion == 0)
+ hook_remove_deleted ();
+}
+
+/*
+ * Starts execution of a hook callback.
+ */
+
+void
+hook_callback_start (struct t_hook *hook, struct t_hook_exec_cb *hook_exec_cb)
+{
+ if (hook->type == HOOK_TYPE_COMMAND)
+ hook->running++;
+ else
+ hook->running = 1;
+
+ if (debug_long_callbacks > 0)
+ {
+ gettimeofday (&hook_exec_cb->start_time, NULL);
+ }
+ else
+ {
+ hook_exec_cb->start_time.tv_sec = 0;
+ hook_exec_cb->start_time.tv_usec = 0;
+ }
+}
+
+/*
+ * Ends execution of a hook callback.
+ */
+
+void
+hook_callback_end (struct t_hook *hook, struct t_hook_exec_cb *hook_exec_cb)
+{
+ struct timeval end_time;
+ long long time_diff;
+ char *str_diff;
+
+ if (hook->type == HOOK_TYPE_COMMAND)
+ hook->running--;
+ else
+ hook->running = 0;
+
+ if ((debug_long_callbacks > 0)
+ && (hook_exec_cb->start_time.tv_sec > 0))
+ {
+ gettimeofday (&end_time, NULL);
+ time_diff = util_timeval_diff (&hook_exec_cb->start_time, &end_time);
+ if (time_diff >= debug_long_callbacks)
+ {
+ str_diff = util_get_microseconds_string (time_diff);
+ log_printf (
+ _("debug: long callback: hook %s (%s), plugin: %s, "
+ "subplugin: %s, time elapsed: %s"),
+ hook_type_string[hook->type],
+ hook_get_description (hook),
+ plugin_get_name (hook->plugin),
+ (hook->subplugin) ? hook->subplugin : "-",
+ str_diff);
+ if (str_diff)
+ free (str_diff);
+ }
+ }
+}
+
+/*
+ * Returns description of hook.
+ *
+ * Note: result must be freed after use.
+ */
+
+char *
+hook_get_description (struct t_hook *hook)
+{
+ return (hook_callback_get_desc[hook->type]) (hook);
+}
+
+/*
+ * Sets a hook property (string).
+ */
+
+void
+hook_set (struct t_hook *hook, const char *property, const char *value)
+{
+ ssize_t num_written;
+ char *error;
+ long number;
+ int rc;
+
+ /* invalid hook? */
+ if (!hook_valid (hook))
+ return;
+
+ if (!property)
+ return;
+
+ if (strcmp (property, "subplugin") == 0)
+ {
+ if (hook->subplugin)
+ free (hook->subplugin);
+ hook->subplugin = strdup (value);
+ }
+ else if (strcmp (property, "stdin") == 0)
+ {
+ if (!hook->deleted
+ && (hook->type == HOOK_TYPE_PROCESS)
+ && (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]) >= 0))
+ {
+ /* send data on child's stdin */
+ num_written = write (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]),
+ value, strlen (value));
+ (void) num_written;
+ }
+ }
+ else if (strcmp (property, "stdin_close") == 0)
+ {
+ if (!hook->deleted
+ && (hook->type == HOOK_TYPE_PROCESS)
+ && (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]) >= 0))
+ {
+ /* close stdin pipe */
+ close (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]));
+ HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]) = -1;
+ }
+ }
+ else if (strcmp (property, "signal") == 0)
+ {
+ if (!hook->deleted
+ && (hook->type == HOOK_TYPE_PROCESS)
+ && (HOOK_PROCESS(hook, child_pid) > 0))
+ {
+ error = NULL;
+ number = strtol (value, &error, 10);
+ if (!error || error[0])
+ {
+ /* not a number? look for signal by name */
+ number = signal_search_name (value);
+ }
+ if (number >= 0)
+ {
+ rc = kill (HOOK_PROCESS(hook, child_pid), (int)number);
+ if (rc < 0)
+ {
+ gui_chat_printf (NULL,
+ _("%sError sending signal %d to pid %d: %s"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ (int)number,
+ HOOK_PROCESS(hook, child_pid),
+ strerror (errno));
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Callback used to clean a process (forked processes) by acknowledging its end.
+ */
+
+int
+hook_timer_clean_process_cb (const void *pointer, void *data,
+ int remaining_calls)
+{
+ /* make C compiler happy */
+ (void) pointer;
+ (void) data;
+ (void) remaining_calls;
+
+ waitpid (*((pid_t *)data), NULL, WNOHANG);
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Schedule a cleanup timer to clean children (forked processes).
+ */
+
+void
+hook_schedule_clean_process (pid_t pid)
+{
+ pid_t *temp_pid;
+
+ /* temp_pid will be freed when the timer is removed */
+ temp_pid = malloc (sizeof (*temp_pid));
+ if (temp_pid)
+ {
+ *temp_pid = pid;
+ hook_timer (NULL, 100, 0, 1,
+ &hook_timer_clean_process_cb, NULL, temp_pid);
+ }
+}
+
+/*
+ * Unhooks something.
+ */
+
+void
+unhook (struct t_hook *hook)
+{
+ /* invalid hook? */
+ if (!hook_valid (hook))
+ return;
+
+ /* hook already deleted? */
+ if (hook->deleted)
+ return;
+
+ if (weechat_debug_core >= 2)
+ {
+ gui_chat_printf (NULL,
+ "debug: removing hook: type=%d (%s), plugin=\"%s\"",
+ hook->type,
+ hook_type_string[hook->type],
+ plugin_get_name (hook->plugin));
+ }
+
+ /* free data specific to the hook */
+ (hook_callback_free_data[hook->type]) (hook);
+
+ /* free data common to all hooks */
+ if (hook->subplugin)
+ {
+ free (hook->subplugin);
+ hook->subplugin = NULL;
+ }
+ if (hook->callback_data)
+ {
+ free (hook->callback_data);
+ hook->callback_data = NULL;
+ }
+
+ /* remove hook from list (if there's no hook exec pending) */
+ if (hook_exec_recursion == 0)
+ {
+ hook_remove_from_list (hook);
+ }
+ else
+ {
+ /* there is one or more hook exec, then delete later */
+ hook->deleted = 1;
+ real_delete_pending = 1;
+ }
+}
+
+/*
+ * Unhooks everything for a plugin/subplugin.
+ */
+
+void
+unhook_all_plugin (struct t_weechat_plugin *plugin, const char *subplugin)
+{
+ int type;
+ struct t_hook *ptr_hook, *next_hook;
+
+ for (type = 0; type < HOOK_NUM_TYPES; type++)
+ {
+ ptr_hook = weechat_hooks[type];
+ while (ptr_hook)
+ {
+ next_hook = ptr_hook->next_hook;
+ if (ptr_hook->plugin == plugin)
+ {
+ if (!subplugin
+ || (ptr_hook->subplugin &&
+ strcmp (ptr_hook->subplugin, subplugin) == 0))
+ {
+ unhook (ptr_hook);
+ }
+ }
+ ptr_hook = next_hook;
+ }
+ }
+}
+
+/*
+ * Unhooks everything.
+ */
+
+void
+unhook_all ()
+{
+ int type;
+ struct t_hook *ptr_hook, *next_hook;
+
+ for (type = 0; type < HOOK_NUM_TYPES; type++)
+ {
+ ptr_hook = weechat_hooks[type];
+ while (ptr_hook)
+ {
+ next_hook = ptr_hook->next_hook;
+ unhook (ptr_hook);
+ ptr_hook = next_hook;
+ }
+ }
+}
+
+/*
+ * Adds a hook in an infolist.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+hook_add_to_infolist_pointer (struct t_infolist *infolist, struct t_hook *hook)
+{
+ struct t_infolist_item *ptr_item;
+
+ ptr_item = infolist_new_item (infolist);
+ if (!ptr_item)
+ return 0;
+
+ if (!infolist_new_var_pointer (ptr_item, "pointer", hook))
+ return 0;
+ if (!infolist_new_var_pointer (ptr_item, "plugin", hook->plugin))
+ return 0;
+ if (!infolist_new_var_string (ptr_item, "plugin_name",
+ (hook->plugin) ?
+ hook->plugin->name : NULL))
+ return 0;
+ if (!infolist_new_var_string (ptr_item, "subplugin", hook->subplugin))
+ return 0;
+ if (!infolist_new_var_string (ptr_item, "type", hook_type_string[hook->type]))
+ return 0;
+ if (!infolist_new_var_integer (ptr_item, "deleted", hook->deleted))
+ return 0;
+ if (!infolist_new_var_integer (ptr_item, "running", hook->running))
+ return 0;
+ if (!infolist_new_var_integer (ptr_item, "priority", hook->priority))
+ return 0;
+ if (!infolist_new_var_pointer (ptr_item, "callback_pointer", (void *)hook->callback_pointer))
+ return 0;
+ if (!infolist_new_var_pointer (ptr_item, "callback_data", (void *)hook->callback_data))
+ return 0;
+
+ /* hook deleted? return only hook info above */
+ if (hook->deleted)
+ return 1;
+
+ /* hook not deleted: add extra hook info */
+ if (!(hook_callback_add_to_infolist[hook->type]) (ptr_item, hook))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Adds hooks of a type in an infolist.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+hook_add_to_infolist_type (struct t_infolist *infolist, int type,
+ const char *arguments)
+{
+ struct t_hook *ptr_hook;
+ int match;
+
+ for (ptr_hook = weechat_hooks[type]; ptr_hook;
+ ptr_hook = ptr_hook->next_hook)
+ {
+ match = 1;
+ if (arguments && !ptr_hook->deleted)
+ {
+ switch (ptr_hook->type)
+ {
+ case HOOK_TYPE_COMMAND:
+ match = string_match (HOOK_COMMAND(ptr_hook, command), arguments, 1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!match)
+ continue;
+
+ hook_add_to_infolist_pointer (infolist, ptr_hook);
+ }
+
+ return 1;
+}
+
+/*
+ * Adds hooks in an infolist.
+ *
+ * Argument "arguments" can be a hook type with optional comma + name after.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+hook_add_to_infolist (struct t_infolist *infolist, struct t_hook *pointer,
+ const char *arguments)
+{
+ const char *pos_arguments;
+ char *type;
+ int i, type_int;
+
+ if (!infolist)
+ return 0;
+
+ if (pointer)
+ return hook_add_to_infolist_pointer (infolist, pointer);
+
+ type = NULL;
+ pos_arguments = NULL;
+
+ if (arguments && arguments[0])
+ {
+ pos_arguments = strchr (arguments, ',');
+ if (pos_arguments)
+ {
+ type = string_strndup (arguments, pos_arguments - arguments);
+ pos_arguments++;
+ }
+ else
+ type = strdup (arguments);
+ }
+
+ type_int = (type) ? hook_search_type (type) : -1;
+
+ for (i = 0; i < HOOK_NUM_TYPES; i++)
+ {
+ if ((type_int < 0) || (type_int == i))
+ hook_add_to_infolist_type (infolist, i, pos_arguments);
+ }
+
+ if (type)
+ free (type);
+
+ return 1;
+}
+
+/*
+ * Prints hooks in WeeChat log file (usually for crash dump).
+ */
+
+void
+hook_print_log ()
+{
+ int type;
+ struct t_hook *ptr_hook;
+
+ for (type = 0; type < HOOK_NUM_TYPES; type++)
+ {
+ for (ptr_hook = weechat_hooks[type]; ptr_hook;
+ ptr_hook = ptr_hook->next_hook)
+ {
+ log_printf ("");
+ log_printf ("[hook (addr:0x%lx)]", ptr_hook);
+ log_printf (" plugin. . . . . . . . . : 0x%lx ('%s')",
+ ptr_hook->plugin, plugin_get_name (ptr_hook->plugin));
+ log_printf (" subplugin . . . . . . . : '%s'", ptr_hook->subplugin);
+ log_printf (" type. . . . . . . . . . : %d (%s)",
+ ptr_hook->type, hook_type_string[ptr_hook->type]);
+ log_printf (" deleted . . . . . . . . : %d", ptr_hook->deleted);
+ log_printf (" running . . . . . . . . : %d", ptr_hook->running);
+ log_printf (" priority. . . . . . . . : %d", ptr_hook->priority);
+ log_printf (" callback_pointer. . . . : 0x%lx", ptr_hook->callback_pointer);
+ log_printf (" callback_data . . . . . : 0x%lx", ptr_hook->callback_data);
+ if (ptr_hook->deleted)
+ continue;
+
+ (hook_callback_print_log[ptr_hook->type]) (ptr_hook);
+
+ log_printf (" prev_hook . . . . . . . : 0x%lx", ptr_hook->prev_hook);
+ log_printf (" next_hook . . . . . . . : 0x%lx", ptr_hook->next_hook);
+ }
+ }
+}