/* * trigger.c - trigger plugin for WeeChat * * Copyright (C) 2014-2018 Sébastien Helleu * * 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 . */ #include #include #include #include #include "../weechat-plugin.h" #include "trigger.h" #include "trigger-buffer.h" #include "trigger-callback.h" #include "trigger-command.h" #include "trigger-completion.h" #include "trigger-config.h" WEECHAT_PLUGIN_NAME(TRIGGER_PLUGIN_NAME); WEECHAT_PLUGIN_DESCRIPTION(N_("Text replacement and command execution on events triggered by WeeChat/plugins")); WEECHAT_PLUGIN_AUTHOR("Sébastien Helleu "); WEECHAT_PLUGIN_VERSION(WEECHAT_VERSION); WEECHAT_PLUGIN_LICENSE(WEECHAT_LICENSE); WEECHAT_PLUGIN_PRIORITY(12000); struct t_weechat_plugin *weechat_trigger_plugin = NULL; char *trigger_option_string[TRIGGER_NUM_OPTIONS] = { "enabled", "hook", "arguments", "conditions", "regex", "command", "return_code", "post_action" }; char *trigger_option_default[TRIGGER_NUM_OPTIONS] = { "on", "signal", "", "", "", "", "ok", "none" }; char *trigger_hook_type_string[TRIGGER_NUM_HOOK_TYPES] = { "signal", "hsignal", "modifier", "line", "print", "command", "command_run", "timer", "config", "focus" }; char *trigger_hook_option_values = "signal|hsignal|modifier|line|print|command|command_run|timer|config|" "focus"; char *trigger_hook_default_arguments[TRIGGER_NUM_HOOK_TYPES] = { "xxx", "xxx", "xxx", "", "", "cmd;desc;args;args_desc;%(buffers_names)", "/cmd", "60000;0;0", "xxx", "chat" }; char *trigger_hook_default_rc[TRIGGER_NUM_HOOK_TYPES] = { "ok,ok_eat,error", "ok,ok_eat,error", "", "", "ok,error", "ok,error", "ok,ok_eat,error", "ok", "ok", "" }; char *trigger_hook_regex_default_var[TRIGGER_NUM_HOOK_TYPES] = { "tg_signal_data", "", "tg_string", "message", "tg_message", "tg_argv_eol1", "tg_command", "tg_remaining_calls", "tg_value", "" }; char *trigger_return_code_string[TRIGGER_NUM_RETURN_CODES] = { "ok", "ok_eat", "error" }; int trigger_return_code[TRIGGER_NUM_RETURN_CODES] = { WEECHAT_RC_OK, WEECHAT_RC_OK_EAT, WEECHAT_RC_ERROR }; char *trigger_post_action_string[TRIGGER_NUM_POST_ACTIONS] = { "none", "disable", "delete" }; struct t_trigger *triggers = NULL; /* first trigger */ struct t_trigger *last_trigger = NULL; /* last trigger */ int triggers_count = 0; /* number of triggers */ struct t_trigger *triggers_temp = NULL; /* first temporary trigger */ struct t_trigger *last_trigger_temp = NULL; /* last temporary trigger */ int trigger_enabled = 1; /* 0 if triggers are disabled */ /* * Searches for a trigger option name. * * Returns index of option in enum t_trigger_option, -1 if not found. */ int trigger_search_option (const char *option_name) { int i; if (!option_name) return -1; for (i = 0; i < TRIGGER_NUM_OPTIONS; i++) { if (weechat_strcasecmp (trigger_option_string[i], option_name) == 0) return i; } /* trigger option not found */ return -1; } /* * Searches for trigger hook type. * * Returns index of hook type in enum t_trigger_hook_type, -1 if not found. */ int trigger_search_hook_type (const char *type) { int i; for (i = 0; i < TRIGGER_NUM_HOOK_TYPES; i++) { if (weechat_strcasecmp (trigger_hook_type_string[i], type) == 0) return i; } /* hook type not found */ return -1; } /* * Searches for trigger return code. * * Returns index of return code in enum t_trigger_return_code, -1 if not found. */ int trigger_search_return_code (const char *return_code) { int i; for (i = 0; i < TRIGGER_NUM_RETURN_CODES; i++) { if (weechat_strcasecmp (trigger_return_code_string[i], return_code) == 0) return i; } /* return code not found */ return -1; } /* * Searches for trigger post action. * * Returns index of post action in enum t_trigger_post_action, -1 if not found. */ int trigger_search_post_action (const char *post_action) { int i; for (i = 0; i < TRIGGER_NUM_POST_ACTIONS; i++) { if (weechat_strcasecmp (trigger_post_action_string[i], post_action) == 0) return i; } /* post action not found */ return -1; } /* * Searches for a trigger by name. * * Returns pointer to trigger found, NULL if not found. */ struct t_trigger * trigger_search (const char *name) { struct t_trigger *ptr_trigger; if (!name || !name[0]) return NULL; for (ptr_trigger = triggers; ptr_trigger; ptr_trigger = ptr_trigger->next_trigger) { if (weechat_strcasecmp (ptr_trigger->name, name) == 0) return ptr_trigger; } /* trigger not found */ return NULL; } /* * Searches for a trigger with a pointer to a trigger option. * * Returns pointer to trigger found, NULL if not found. */ struct t_trigger * trigger_search_with_option (struct t_config_option *option) { const char *ptr_name; char *pos_option; struct t_trigger *ptr_trigger; ptr_name = weechat_hdata_string (weechat_hdata_get ("config_option"), option, "name"); if (!ptr_name) return NULL; pos_option = strchr (ptr_name, '.'); if (!pos_option) return NULL; for (ptr_trigger = triggers; ptr_trigger; ptr_trigger = ptr_trigger->next_trigger) { if (weechat_strncasecmp (ptr_trigger->name, ptr_name, pos_option - ptr_name) == 0) break; } return ptr_trigger; } /* * Unhooks things hooked in a trigger. */ void trigger_unhook (struct t_trigger *trigger) { int i; if (trigger->hooks) { for (i = 0; i < trigger->hooks_count; i++) { if (trigger->hooks[i]) weechat_unhook (trigger->hooks[i]); } free (trigger->hooks); trigger->hooks = NULL; trigger->hooks_count = 0; } trigger->hook_count_cb = 0; trigger->hook_count_cmd = 0; if (trigger->hook_print_buffers) { free (trigger->hook_print_buffers); trigger->hook_print_buffers = NULL; } } /* * Creates hook(s) in a trigger. */ void trigger_hook (struct t_trigger *trigger) { char **argv, **argv_eol, *buffer_type, *buffer_name, *tags, *message; char *error1, *error2, *error3; int i, argc, strip_colors; long interval, align_second, max_calls; if (!weechat_config_boolean (trigger->options[TRIGGER_OPTION_ENABLED])) return; trigger_unhook (trigger); argv = weechat_string_split (weechat_config_string (trigger->options[TRIGGER_OPTION_ARGUMENTS]), ";", 0, 0, &argc); argv_eol = weechat_string_split (weechat_config_string (trigger->options[TRIGGER_OPTION_ARGUMENTS]), ";", 1, 0, NULL); switch (weechat_config_integer (trigger->options[TRIGGER_OPTION_HOOK])) { case TRIGGER_HOOK_SIGNAL: if (argv && (argc >= 1)) { trigger->hooks = malloc (argc * sizeof (trigger->hooks[0])); if (trigger->hooks) { trigger->hooks_count = argc; for (i = 0; i < argc; i++) { trigger->hooks[i] = weechat_hook_signal ( argv[i], &trigger_callback_signal_cb, trigger, NULL); } } } break; case TRIGGER_HOOK_HSIGNAL: if (argv && (argc >= 1)) { trigger->hooks = malloc (argc * sizeof (trigger->hooks[0])); if (trigger->hooks) { trigger->hooks_count = argc; for (i = 0; i < argc; i++) { trigger->hooks[i] = weechat_hook_hsignal ( argv[i], &trigger_callback_hsignal_cb, trigger, NULL); } } } break; case TRIGGER_HOOK_MODIFIER: if (argv && (argc >= 1)) { trigger->hooks = malloc (argc * sizeof (trigger->hooks[0])); if (trigger->hooks) { trigger->hooks_count = argc; for (i = 0; i < argc; i++) { trigger->hooks[i] = weechat_hook_modifier ( argv[i], &trigger_callback_modifier_cb, trigger, NULL); } } } break; case TRIGGER_HOOK_LINE: buffer_type = NULL; buffer_name = NULL; tags = NULL; if (argv && (argc >= 1)) { buffer_type = argv[0]; if ((argc >= 2) && (strcmp (argv[1], "*") != 0)) buffer_name = argv[1]; if ((argc >= 3) && (strcmp (argv[2], "*") != 0)) tags = argv[2]; } trigger->hooks = malloc (sizeof (trigger->hooks[0])); if (trigger->hooks) { trigger->hooks_count = 1; trigger->hooks[0] = weechat_hook_line ( buffer_type, buffer_name, tags, &trigger_callback_line_cb, trigger, NULL); } break; case TRIGGER_HOOK_PRINT: tags = NULL; message = NULL; strip_colors = 0; if (argv && (argc >= 1)) { if (strcmp (argv[0], "*") != 0) trigger->hook_print_buffers = strdup (argv[0]); if ((argc >= 2) && (strcmp (argv[1], "*") != 0)) tags = argv[1]; if ((argc >= 3) && (strcmp (argv[2], "*") != 0)) message = argv[2]; if (argc >= 4) strip_colors = (strcmp (argv[3], "0") != 0) ? 1 : 0; } trigger->hooks = malloc (sizeof (trigger->hooks[0])); if (trigger->hooks) { trigger->hooks_count = 1; trigger->hooks[0] = weechat_hook_print ( NULL, tags, message, strip_colors, &trigger_callback_print_cb, trigger, NULL); } break; case TRIGGER_HOOK_COMMAND: if (argv && (argc >= 1)) { trigger->hooks = malloc (sizeof (trigger->hooks[0])); if (trigger->hooks) { trigger->hooks_count = 1; trigger->hooks[0] = weechat_hook_command ( argv[0], /* command */ (argc > 1) ? argv[1] : "", /* description */ (argc > 2) ? argv[2] : "", /* arguments */ (argc > 3) ? argv[3] : "", /* description of args */ (argc > 4) ? argv[4] : "", /* completion */ &trigger_callback_command_cb, trigger, NULL); } } break; case TRIGGER_HOOK_COMMAND_RUN: if (argv && (argc >= 1)) { trigger->hooks = malloc (argc * sizeof (trigger->hooks[0])); if (trigger->hooks) { trigger->hooks_count = argc; for (i = 0; i < argc; i++) { trigger->hooks[i] = weechat_hook_command_run ( argv[i], &trigger_callback_command_run_cb, trigger, NULL); } } } break; case TRIGGER_HOOK_TIMER: if (argv && (argc >= 1)) { error1 = NULL; error2 = NULL; error3 = NULL; interval = strtol (argv[0], &error1, 10); align_second = strtol ((argc >= 2) ? argv[1] : "0", &error2, 10); max_calls = strtol ((argc >= 3) ? argv[2] : "0", &error3, 10); if (error1 && !error1[0] && error2 && !error2[0] && error3 && !error3[0] && (interval > 0) && (align_second >= 0) && (max_calls >= 0)) { trigger->hooks = malloc (sizeof (trigger->hooks[0])); if (trigger->hooks) { trigger->hooks_count = 1; trigger->hooks[0] = weechat_hook_timer ( interval, (int)align_second, (int)max_calls, &trigger_callback_timer_cb, trigger, NULL); } } } break; case TRIGGER_HOOK_CONFIG: if (argv && (argc >= 1)) { trigger->hooks = malloc (argc * sizeof (trigger->hooks[0])); if (trigger->hooks) { trigger->hooks_count = argc; for (i = 0; i < argc; i++) { trigger->hooks[i] = weechat_hook_config ( argv[i], &trigger_callback_config_cb, trigger, NULL); } } } break; case TRIGGER_HOOK_FOCUS: if (argv && (argc >= 1)) { trigger->hooks = malloc (argc * sizeof (trigger->hooks[0])); if (trigger->hooks) { trigger->hooks_count = argc; for (i = 0; i < argc; i++) { trigger->hooks[i] = weechat_hook_focus ( argv[i], &trigger_callback_focus_cb, trigger, NULL); } } } break; } if (!trigger->hooks) { weechat_printf (NULL, _("%s%s: unable to create hook for trigger \"%s\" " "(bad arguments)"), weechat_prefix ("error"), TRIGGER_PLUGIN_NAME, trigger->name); } if (argv) weechat_string_free_split (argv); if (argv_eol) weechat_string_free_split (argv_eol); } /* * Frees all the regex in a trigger. */ void trigger_regex_free (int *regex_count, struct t_trigger_regex **regex) { int i; if (!regex_count || !regex) return; if (*regex_count > 0) { for (i = 0; i < *regex_count; i++) { if ((*regex)[i].variable) free ((*regex)[i].variable); if ((*regex)[i].str_regex) free ((*regex)[i].str_regex); if ((*regex)[i].regex) { regfree ((*regex)[i].regex); free ((*regex)[i].regex); } if ((*regex)[i].replace) free ((*regex)[i].replace); if ((*regex)[i].replace_escaped) free ((*regex)[i].replace_escaped); } free (*regex); *regex = NULL; *regex_count = 0; } } /* * Splits the regex in structures, with regex and replacement text. * * Returns: * 0: OK * -1: format error * -2: regex compilation error * -3: not enough memory */ int trigger_regex_split (const char *str_regex, int *regex_count, struct t_trigger_regex **regex) { const char *ptr_regex, *pos, *pos_replace, *pos_replace_end; const char *pos_next_regex; char *delimiter, *str_regex_escaped; int rc, index, length_delimiter; struct t_trigger_regex *new_regex; rc = 0; delimiter = NULL; str_regex_escaped = NULL; if (!regex_count || !regex) goto end; /* remove any existing regex */ trigger_regex_free (regex_count, regex); if (!str_regex || !str_regex[0]) goto end; /* min 3 chars, for example: "/a/" */ if (strlen (str_regex) < 3) goto format_error; /* parse regular expressions in the option */ ptr_regex = str_regex; while (ptr_regex && ptr_regex[0]) { if (delimiter) { free (delimiter); delimiter = NULL; } /* search the delimiter (which can be more than one char) */ pos = weechat_utf8_next_char (ptr_regex); while (pos[0] && (weechat_utf8_charcmp (ptr_regex, pos) == 0)) { pos = weechat_utf8_next_char (pos); } if (!pos[0]) goto format_error; delimiter = weechat_strndup (ptr_regex, pos - ptr_regex); if (!delimiter) goto memory_error; length_delimiter = strlen (delimiter); ptr_regex = pos; if (!ptr_regex[0]) goto format_error; /* search the start of replacement string */ pos_replace = strstr (ptr_regex, delimiter); if (!pos_replace) goto format_error; /* search the end of replacement string */ pos_replace_end = strstr (pos_replace + length_delimiter, delimiter); new_regex = realloc (*regex, (*regex_count + 1) * sizeof ((*regex)[0])); if (!new_regex) goto memory_error; *regex = new_regex; (*regex_count)++; index = *regex_count - 1; /* initialize new regex */ (*regex)[index].variable = NULL; (*regex)[index].str_regex = NULL; (*regex)[index].regex = NULL; (*regex)[index].replace = NULL; (*regex)[index].replace_escaped = NULL; /* set string with regex */ (*regex)[index].str_regex = weechat_strndup (ptr_regex, pos_replace - ptr_regex); if (!(*regex)[index].str_regex) goto memory_error; if (str_regex_escaped) free (str_regex_escaped); str_regex_escaped = weechat_string_convert_escaped_chars ((*regex)[index].str_regex); if (!str_regex_escaped) goto memory_error; /* set regex */ (*regex)[index].regex = malloc (sizeof (*(*regex)[index].regex)); if (!(*regex)[index].regex) goto memory_error; if (weechat_string_regcomp ((*regex)[index].regex, str_regex_escaped, REG_EXTENDED | REG_ICASE) != 0) { free ((*regex)[index].regex); (*regex)[index].regex = NULL; goto compile_error; } /* set replace and replace_eval */ (*regex)[index].replace = (pos_replace_end) ? weechat_strndup (pos_replace + length_delimiter, pos_replace_end - pos_replace - length_delimiter) : strdup (pos_replace + length_delimiter); if (!(*regex)[index].replace) goto memory_error; (*regex)[index].replace_escaped = weechat_string_convert_escaped_chars ((*regex)[index].replace); if (!(*regex)[index].replace_escaped) goto memory_error; if (!pos_replace_end) break; /* set variable (optional) */ ptr_regex = pos_replace_end + length_delimiter; if (!ptr_regex[0]) break; if (ptr_regex[0] == ' ') pos_next_regex = ptr_regex; else { pos_next_regex = strchr (ptr_regex, ' '); (*regex)[index].variable = (pos_next_regex) ? weechat_strndup (ptr_regex, pos_next_regex - ptr_regex) : strdup (ptr_regex); if (!(*regex)[index].variable) goto memory_error; } if (!pos_next_regex) break; /* skip spaces before next regex */ ptr_regex = pos_next_regex + 1; while (ptr_regex[0] == ' ') { ptr_regex++; } } goto end; format_error: rc = -1; goto end; compile_error: rc = -2; goto end; memory_error: rc = -3; goto end; end: if (delimiter) free (delimiter); if (str_regex_escaped) free (str_regex_escaped); if (rc < 0) trigger_regex_free (regex_count, regex); return rc; } /* * Splits command of a trigger. */ void trigger_split_command (const char *command, int *commands_count, char ***commands) { int i; if (!commands_count || !commands) return; if (*commands) { weechat_string_free_split (*commands); *commands = NULL; } *commands_count = 0; if (command && command[0]) { *commands = weechat_string_split_command (command, ';'); if (*commands) { for (i = 0; (*commands)[i]; i++) { } *commands_count = i; } } } /* * Checks if a trigger name is valid: it must not start with "-" and not have * any spaces. * * Returns: * 1: name is valid * 0: name is invalid */ int trigger_name_valid (const char *name) { if (!name || !name[0] || (name[0] == '-')) return 0; /* no spaces allowed */ if (strchr (name, ' ')) return 0; /* no periods allowed */ if (strchr (name, '.')) return 0; /* name is valid */ return 1; } /* * Allocates and initializes new trigger structure. * * Returns pointer to new trigger, NULL if error. */ struct t_trigger * trigger_alloc (const char *name) { struct t_trigger *new_trigger; int i; if (!trigger_name_valid (name)) return NULL; if (trigger_search (name)) return NULL; new_trigger = malloc (sizeof (*new_trigger)); if (!new_trigger) return NULL; new_trigger->name = strdup (name); for (i = 0; i < TRIGGER_NUM_OPTIONS; i++) { new_trigger->options[i] = NULL; } new_trigger->hooks_count = 0; new_trigger->hooks = NULL; new_trigger->hook_count_cb = 0; new_trigger->hook_count_cmd = 0; new_trigger->hook_running = 0; new_trigger->hook_print_buffers = NULL; new_trigger->regex_count = 0; new_trigger->regex = NULL; new_trigger->commands_count = 0; new_trigger->commands = NULL; new_trigger->prev_trigger = NULL; new_trigger->next_trigger = NULL; return new_trigger; } /* * Searches for position of trigger in list (to keep triggers sorted by name). */ struct t_trigger * trigger_find_pos (struct t_trigger *trigger, struct t_trigger *list_triggers) { struct t_trigger *ptr_trigger; for (ptr_trigger = list_triggers; ptr_trigger; ptr_trigger = ptr_trigger->next_trigger) { if (weechat_strcasecmp (trigger->name, ptr_trigger->name) < 0) return ptr_trigger; } /* position not found */ return NULL; } /* * Adds a trigger in a linked list. */ void trigger_add (struct t_trigger *trigger, struct t_trigger **list_triggers, struct t_trigger **last_list_trigger) { struct t_trigger *pos_trigger; pos_trigger = trigger_find_pos (trigger, *list_triggers); if (pos_trigger) { /* add trigger before "pos_trigger" */ trigger->prev_trigger = pos_trigger->prev_trigger; trigger->next_trigger = pos_trigger; if (pos_trigger->prev_trigger) (pos_trigger->prev_trigger)->next_trigger = trigger; else *list_triggers = trigger; pos_trigger->prev_trigger = trigger; } else { /* add trigger to end of list */ trigger->prev_trigger = *last_list_trigger; trigger->next_trigger = NULL; if (*last_list_trigger) (*last_list_trigger)->next_trigger = trigger; else *list_triggers = trigger; *last_list_trigger = trigger; } } /* * Creates a new trigger with options. * * Returns pointer to new trigger, NULL if error. */ struct t_trigger * trigger_new_with_options (const char *name, struct t_config_option **options) { struct t_trigger *new_trigger; int i; new_trigger = trigger_alloc (name); if (!new_trigger) return NULL; for (i = 0; i < TRIGGER_NUM_OPTIONS; i++) { new_trigger->options[i] = options[i]; } trigger_add (new_trigger, &triggers, &last_trigger); triggers_count++; if (trigger_regex_split (weechat_config_string (new_trigger->options[TRIGGER_OPTION_REGEX]), &new_trigger->regex_count, &new_trigger->regex) < 0) { weechat_printf (NULL, _("%s%s: invalid regular expression in trigger: " "\"%s\""), weechat_prefix ("error"), TRIGGER_PLUGIN_NAME, name); } trigger_split_command (weechat_config_string (new_trigger->options[TRIGGER_OPTION_COMMAND]), &new_trigger->commands_count, &new_trigger->commands); trigger_hook (new_trigger); return new_trigger; } /* * Creates a new trigger. * * Returns pointer to new trigger, NULL if error. */ struct t_trigger * trigger_new (const char *name, const char *enabled, const char *hook, const char *arguments, const char *conditions, const char *regex, const char *command, const char *return_code, const char *post_action) { struct t_config_option *option[TRIGGER_NUM_OPTIONS]; const char *value[TRIGGER_NUM_OPTIONS]; struct t_trigger *new_trigger; int i; /* look for type */ if (trigger_search_hook_type (hook) < 0) return NULL; /* look for return code */ if (return_code && return_code[0] && (trigger_search_return_code (return_code) < 0)) { return NULL; } /* look for post action */ if (post_action && post_action[0] && (trigger_search_post_action (post_action) < 0)) { return NULL; } value[TRIGGER_OPTION_ENABLED] = enabled; value[TRIGGER_OPTION_HOOK] = hook; value[TRIGGER_OPTION_ARGUMENTS] = arguments; value[TRIGGER_OPTION_CONDITIONS] = conditions; value[TRIGGER_OPTION_REGEX] = regex; value[TRIGGER_OPTION_COMMAND] = command; value[TRIGGER_OPTION_RETURN_CODE] = return_code; value[TRIGGER_OPTION_POST_ACTION] = post_action; for (i = 0; i < TRIGGER_NUM_OPTIONS; i++) { option[i] = trigger_config_create_trigger_option (name, i, value[i]); } new_trigger = trigger_new_with_options (name, option); if (!new_trigger) { for (i = 0; i < TRIGGER_NUM_OPTIONS; i++) { weechat_config_option_free (option[i]); } } return new_trigger; } /* * Creates default triggers. */ void trigger_create_default () { int i; for (i = 0; trigger_config_default_list[i][0]; i++) { trigger_new (trigger_config_default_list[i][0], /* name */ trigger_config_default_list[i][1], /* enabled */ trigger_config_default_list[i][2], /* hook */ trigger_config_default_list[i][3], /* arguments */ trigger_config_default_list[i][4], /* conditions */ trigger_config_default_list[i][5], /* regex */ trigger_config_default_list[i][6], /* command */ trigger_config_default_list[i][7], /* return code */ trigger_config_default_list[i][8]); /* post action */ } } /* * Renames a trigger. * * Returns: * 1: OK * 0: error (trigger not renamed) */ int trigger_rename (struct t_trigger *trigger, const char *name) { int length, i; char *option_name; if (!name || !name[0] || !trigger_name_valid (name) || trigger_search (name)) { return 0; } length = strlen (name) + 64; option_name = malloc (length); if (!option_name) return 0; for (i = 0; i < TRIGGER_NUM_OPTIONS; i++) { if (trigger->options[i]) { snprintf (option_name, length, "%s.%s", name, trigger_option_string[i]); weechat_config_option_rename (trigger->options[i], option_name); } } if (trigger->name) free (trigger->name); trigger->name = strdup (name); free (option_name); /* re-insert trigger in list (for sorting triggers by name) */ if (trigger->prev_trigger) (trigger->prev_trigger)->next_trigger = trigger->next_trigger; else triggers = trigger->next_trigger; if (trigger->next_trigger) (trigger->next_trigger)->prev_trigger = trigger->prev_trigger; else last_trigger = trigger->prev_trigger; trigger_add (trigger, &triggers, &last_trigger); return 1; } /* * Copies a trigger. * * Returns a pointer to the new trigger, NULL if error. */ struct t_trigger * trigger_copy (struct t_trigger *trigger, const char *name) { if (!name || !name[0] || !trigger_name_valid (name) || trigger_search (name)) { return NULL; } return trigger_new ( name, weechat_config_string (trigger->options[TRIGGER_OPTION_ENABLED]), weechat_config_string (trigger->options[TRIGGER_OPTION_HOOK]), weechat_config_string (trigger->options[TRIGGER_OPTION_ARGUMENTS]), weechat_config_string (trigger->options[TRIGGER_OPTION_CONDITIONS]), weechat_config_string (trigger->options[TRIGGER_OPTION_REGEX]), weechat_config_string (trigger->options[TRIGGER_OPTION_COMMAND]), weechat_config_string (trigger->options[TRIGGER_OPTION_RETURN_CODE]), weechat_config_string (trigger->options[TRIGGER_OPTION_POST_ACTION])); } /* * Deletes a trigger. */ void trigger_free (struct t_trigger *trigger) { int i; if (!trigger) return; /* remove trigger from triggers list */ if (trigger->prev_trigger) (trigger->prev_trigger)->next_trigger = trigger->next_trigger; if (trigger->next_trigger) (trigger->next_trigger)->prev_trigger = trigger->prev_trigger; if (triggers == trigger) triggers = trigger->next_trigger; if (last_trigger == trigger) last_trigger = trigger->prev_trigger; /* free data */ trigger_unhook (trigger); trigger_regex_free (&trigger->regex_count, &trigger->regex); if (trigger->name) free (trigger->name); for (i = 0; i < TRIGGER_NUM_OPTIONS; i++) { if (trigger->options[i]) weechat_config_option_free (trigger->options[i]); } if (trigger->commands) weechat_string_free_split (trigger->commands); free (trigger); triggers_count--; } /* * Deletes all triggers. */ void trigger_free_all () { while (triggers) { trigger_free (triggers); } } /* * Prints trigger infos in WeeChat log file (usually for crash dump). */ void trigger_print_log () { struct t_trigger *ptr_trigger; int i; for (ptr_trigger = triggers; ptr_trigger; ptr_trigger = ptr_trigger->next_trigger) { weechat_log_printf (""); weechat_log_printf ("[trigger (addr:0x%lx)]", ptr_trigger); weechat_log_printf (" name. . . . . . . . . . : '%s'", ptr_trigger->name); weechat_log_printf (" enabled . . . . . . . . : %d", weechat_config_integer (ptr_trigger->options[TRIGGER_OPTION_ENABLED])); weechat_log_printf (" hook . . . . . . . . . : %d ('%s')", weechat_config_integer (ptr_trigger->options[TRIGGER_OPTION_HOOK]), trigger_hook_type_string[weechat_config_integer (ptr_trigger->options[TRIGGER_OPTION_HOOK])]); weechat_log_printf (" arguments . . . . . . . : '%s'", weechat_config_string (ptr_trigger->options[TRIGGER_OPTION_ARGUMENTS])); weechat_log_printf (" conditions. . . . . . . : '%s'", weechat_config_string (ptr_trigger->options[TRIGGER_OPTION_CONDITIONS])); weechat_log_printf (" regex . . . . . . . . . : '%s'", weechat_config_string (ptr_trigger->options[TRIGGER_OPTION_REGEX])); weechat_log_printf (" command . . . . . . . . : '%s'", weechat_config_string (ptr_trigger->options[TRIGGER_OPTION_COMMAND])); weechat_log_printf (" return_code . . . . . . : %d ('%s')", weechat_config_integer (ptr_trigger->options[TRIGGER_OPTION_RETURN_CODE]), trigger_return_code_string[weechat_config_integer (ptr_trigger->options[TRIGGER_OPTION_RETURN_CODE])]); weechat_log_printf (" post_action . . . . . . : %d ('%s')", weechat_config_integer (ptr_trigger->options[TRIGGER_OPTION_POST_ACTION]), trigger_post_action_string[weechat_config_integer (ptr_trigger->options[TRIGGER_OPTION_POST_ACTION])]); weechat_log_printf (" hooks_count . . . . . . : %d", ptr_trigger->hooks_count); weechat_log_printf (" hooks . . . . . . . . . : 0x%lx", ptr_trigger->hooks); for (i = 0; i < ptr_trigger->hooks_count; i++) { weechat_log_printf (" hooks[%03d]. . . . . . : 0x%lx", i, ptr_trigger->hooks[i]); } weechat_log_printf (" hook_count_cb . . . . . : %llu", ptr_trigger->hook_count_cb); weechat_log_printf (" hook_count_cmd. . . . . : %llu", ptr_trigger->hook_count_cmd); weechat_log_printf (" hook_running. . . . . . : %d", ptr_trigger->hook_running); weechat_log_printf (" hook_print_buffers. . . : '%s'", ptr_trigger->hook_print_buffers); weechat_log_printf (" regex_count . . . . . . : %d", ptr_trigger->regex_count); weechat_log_printf (" regex . . . . . . . . . : 0x%lx", ptr_trigger->regex); for (i = 0; i < ptr_trigger->regex_count; i++) { weechat_log_printf (" regex[%03d].variable . . . : '%s'", i, ptr_trigger->regex[i].variable); weechat_log_printf (" regex[%03d].str_regex. . . : '%s'", i, ptr_trigger->regex[i].str_regex); weechat_log_printf (" regex[%03d].regex. . . . . : 0x%lx", i, ptr_trigger->regex[i].regex); weechat_log_printf (" regex[%03d].replace. . . . : '%s'", i, ptr_trigger->regex[i].replace); weechat_log_printf (" regex[%03d].replace_escaped: '%s'", i, ptr_trigger->regex[i].replace_escaped); } weechat_log_printf (" commands_count. . . . . : %d", ptr_trigger->commands_count); weechat_log_printf (" commands. . . . . . . . : 0x%lx", ptr_trigger->commands); if (ptr_trigger->commands) { for (i = 0; ptr_trigger->commands[i]; i++) { weechat_log_printf (" commands[%03d] . . . . : '%s'", i, ptr_trigger->commands[i]); } } weechat_log_printf (" prev_trigger. . . . . . : 0x%lx", ptr_trigger->prev_trigger); weechat_log_printf (" next_trigger. . . . . . : 0x%lx", ptr_trigger->next_trigger); } } /* * Callback for signal "debug_dump". */ int trigger_debug_dump_cb (const void *pointer, void *data, const char *signal, const char *type_data, void *signal_data) { /* make C compiler happy */ (void) pointer; (void) data; (void) signal; (void) type_data; if (!signal_data || (weechat_strcasecmp ((char *)signal_data, TRIGGER_PLUGIN_NAME) == 0)) { weechat_log_printf (""); weechat_log_printf ("***** \"%s\" plugin dump *****", weechat_plugin->name); trigger_print_log (); weechat_log_printf (""); weechat_log_printf ("***** End of \"%s\" plugin dump *****", weechat_plugin->name); } return WEECHAT_RC_OK; } /* * Initializes trigger plugin. */ int weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[]) { int i, upgrading; weechat_plugin = plugin; trigger_callback_init (); trigger_command_init (); if (!trigger_config_init ()) return WEECHAT_RC_ERROR; trigger_config_read (); /* hook some signals */ weechat_hook_signal ("debug_dump", &trigger_debug_dump_cb, NULL, NULL); /* hook completions */ trigger_completion_init (); /* look at arguments */ upgrading = 0; for (i = 0; i < argc; i++) { if (weechat_strcasecmp (argv[i], "--upgrade") == 0) { upgrading = 1; } } if (upgrading) trigger_buffer_set_callbacks (); return WEECHAT_RC_OK; } /* * Ends trigger plugin. */ int weechat_plugin_end (struct t_weechat_plugin *plugin) { /* make C compiler happy */ (void) plugin; trigger_buffer_end (); trigger_config_write (); trigger_free_all (); trigger_config_free (); trigger_callback_end (); return WEECHAT_RC_OK; }