weechat/src/plugins/trigger/trigger.c

1366 lines
40 KiB
C

/*
* trigger.c - trigger plugin for WeeChat
*
* Copyright (C) 2014-2020 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/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <regex.h>
#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 <flashcode@flashtux.org>");
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", "info", "info_hashtable" };
char *trigger_hook_option_values =
"signal|hsignal|modifier|line|print|command|command_run|timer|config|"
"focus|info|info_hashtable";
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", "xxx", "xxx" };
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", "", "tg_info", "" };
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;
char *eval_desc, *eval_args, *eval_desc_args, *eval_completion;
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]),
";",
NULL,
0,
0,
&argc);
argv_eol = weechat_string_split (
weechat_config_string (trigger->options[TRIGGER_OPTION_ARGUMENTS]),
";",
NULL,
WEECHAT_STRING_SPLIT_KEEP_EOL,
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;
eval_desc = (argc > 1) ? weechat_string_eval_expression (
argv[1], NULL, NULL, NULL) : NULL;
eval_args = (argc > 2) ? weechat_string_eval_expression (
argv[2], NULL, NULL, NULL) : NULL;
eval_desc_args = (argc > 3) ? weechat_string_eval_expression (
argv[3], NULL, NULL, NULL) : NULL;
eval_completion = (argc > 4) ? weechat_string_eval_expression (
argv[4], NULL, NULL, NULL) : NULL;
trigger->hooks[0] = weechat_hook_command (
argv[0], /* command */
(eval_desc) ? eval_desc : "",
(eval_args) ? eval_args : "",
(eval_desc_args) ? eval_desc_args : "",
(eval_completion) ? eval_completion : "",
&trigger_callback_command_cb,
trigger, NULL);
if (eval_desc)
free (eval_desc);
if (eval_args)
free (eval_args);
if (eval_desc_args)
free (eval_desc_args);
if (eval_completion)
free (eval_completion);
}
}
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;
case TRIGGER_HOOK_INFO:
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_info (
argv[i],
NULL,
NULL,
&trigger_callback_info_cb,
trigger, NULL);
}
}
}
break;
case TRIGGER_HOOK_INFO_HASHTABLE:
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_info_hashtable (
argv[i],
NULL,
NULL,
NULL,
&trigger_callback_info_hashtable_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[])
{
/* make C compiler happy */
(void) argc;
(void) argv;
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 ();
if (weechat_trigger_plugin->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;
}