summaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/CMakeLists.txt12
-rw-r--r--src/plugins/Makefile.am16
-rw-r--r--src/plugins/plugin.c1
-rw-r--r--src/plugins/trigger/CMakeLists.txt31
-rw-r--r--src/plugins/trigger/Makefile.am42
-rw-r--r--src/plugins/trigger/trigger-buffer.c180
-rw-r--r--src/plugins/trigger/trigger-buffer.h32
-rw-r--r--src/plugins/trigger/trigger-callback.c597
-rw-r--r--src/plugins/trigger/trigger-callback.h38
-rw-r--r--src/plugins/trigger/trigger-command.c352
-rw-r--r--src/plugins/trigger/trigger-command.h25
-rw-r--r--src/plugins/trigger/trigger-completion.c185
-rw-r--r--src/plugins/trigger/trigger-completion.h25
-rw-r--r--src/plugins/trigger/trigger-config.c484
-rw-r--r--src/plugins/trigger/trigger-config.h40
-rw-r--r--src/plugins/trigger/trigger.c894
-rw-r--r--src/plugins/trigger/trigger.h129
-rw-r--r--src/plugins/weechat-plugin.h9
18 files changed, 3080 insertions, 12 deletions
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 3e6ac0e26..41a03412a 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -96,10 +96,6 @@ IF(ENABLE_SCRIPT)
ADD_SUBDIRECTORY( script )
ENDIF(ENABLE_SCRIPT)
-IF(ENABLE_XFER)
- ADD_SUBDIRECTORY( xfer )
-ENDIF(ENABLE_XFER)
-
IF(ENABLE_SCRIPTS AND ENABLE_PERL)
FIND_PACKAGE(Perl)
IF(PERL_FOUND)
@@ -142,4 +138,12 @@ IF(ENABLE_SCRIPTS AND ENABLE_GUILE)
ENDIF(GUILE_FOUND)
ENDIF(ENABLE_SCRIPTS AND ENABLE_GUILE)
+IF(ENABLE_TRIGGER)
+ ADD_SUBDIRECTORY( trigger )
+ENDIF(ENABLE_TRIGGER)
+
+IF(ENABLE_XFER)
+ ADD_SUBDIRECTORY( xfer )
+ENDIF(ENABLE_XFER)
+
INSTALL(FILES weechat-plugin.h DESTINATION ${INCLUDEDIR})
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index 16c0e35ac..bfddd6274 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -75,10 +75,6 @@ if PLUGIN_SCRIPT
script_dir = script
endif
-if PLUGIN_XFER
-xfer_dir = xfer
-endif
-
if PLUGIN_PERL
perl_dir = perl
endif
@@ -103,10 +99,18 @@ if PLUGIN_GUILE
guile_dir = guile
endif
+if PLUGIN_TRIGGER
+trigger_dir = trigger
+endif
+
+if PLUGIN_XFER
+xfer_dir = xfer
+endif
+
SUBDIRS = . $(alias_dir) $(aspell_dir) $(charset_dir) $(fifo_dir) $(irc_dir) \
- $(logger_dir) $(relay_dir) $(rmodifier_dir) $(script_dir) $(xfer_dir) \
+ $(logger_dir) $(relay_dir) $(rmodifier_dir) $(script_dir) \
$(perl_dir) $(python_dir) $(ruby_dir) $(lua_dir) $(tcl_dir) \
- $(guile_dir)
+ $(guile_dir) $(trigger_dir) $(xfer_dir)
EXTRA_DIST = CMakeLists.txt
diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c
index a10c7874b..424735560 100644
--- a/src/plugins/plugin.c
+++ b/src/plugins/plugin.c
@@ -521,6 +521,7 @@ plugin_load (const char *filename, int argc, char **argv)
new_plugin->string_regcomp = &string_regcomp;
new_plugin->string_has_highlight = &string_has_highlight;
new_plugin->string_has_highlight_regex = &string_has_highlight_regex;
+ new_plugin->string_replace_regex = &string_replace_regex;
new_plugin->string_split = &string_split;
new_plugin->string_free_split = &string_free_split;
new_plugin->string_build_with_split_string = &string_build_with_split_string;
diff --git a/src/plugins/trigger/CMakeLists.txt b/src/plugins/trigger/CMakeLists.txt
new file mode 100644
index 000000000..59d8e1c00
--- /dev/null
+++ b/src/plugins/trigger/CMakeLists.txt
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+#
+
+ADD_LIBRARY(trigger MODULE
+trigger.c trigger.h
+trigger-buffer.c trigger-buffer.h
+trigger-callback.c trigger-callback.h
+trigger-command.c trigger-command.h
+trigger-completion.c trigger-completion.h
+trigger-config.c trigger-config.h)
+SET_TARGET_PROPERTIES(trigger PROPERTIES PREFIX "")
+
+TARGET_LINK_LIBRARIES(trigger)
+
+INSTALL(TARGETS trigger LIBRARY DESTINATION ${LIBDIR}/plugins)
diff --git a/src/plugins/trigger/Makefile.am b/src/plugins/trigger/Makefile.am
new file mode 100644
index 000000000..2486c0359
--- /dev/null
+++ b/src/plugins/trigger/Makefile.am
@@ -0,0 +1,42 @@
+#
+# Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+#
+
+AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/locale\" $(TRIGGER_CFLAGS)
+
+libdir = ${weechat_libdir}/plugins
+
+lib_LTLIBRARIES = trigger.la
+
+trigger_la_SOURCES = trigger.c \
+ trigger.h \
+ trigger-buffer.c \
+ trigger-buffer.h \
+ trigger-callback.c \
+ trigger-callback.h \
+ trigger-command.c \
+ trigger-command.h \
+ trigger-completion.c \
+ trigger-completion.h \
+ trigger-config.c \
+ trigger-config.h
+
+trigger_la_LDFLAGS = -module -no-undefined
+trigger_la_LIBADD = $(TRIGGER_LFLAGS)
+
+EXTRA_DIST = CMakeLists.txt
diff --git a/src/plugins/trigger/trigger-buffer.c b/src/plugins/trigger/trigger-buffer.c
new file mode 100644
index 000000000..186c97f0c
--- /dev/null
+++ b/src/plugins/trigger/trigger-buffer.c
@@ -0,0 +1,180 @@
+/*
+ * trigger-buffer.c - debug buffer for triggers
+ *
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../weechat-plugin.h"
+#include "trigger.h"
+#include "trigger-buffer.h"
+
+
+struct t_gui_buffer *trigger_buffer = NULL;
+
+
+/*
+ * Callback called for each entry in hashtable.
+ */
+
+void
+trigger_buffer_hashtable_map_cb (void *data,
+ struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ const char *value_type;
+ char *value_no_color;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) hashtable;
+
+ value_type = weechat_hashtable_get_string (hashtable, "type_values");
+ if (!value_type)
+ return;
+
+ if (strcmp (value_type, "string") == 0)
+ {
+ value_no_color = weechat_string_remove_color ((const char *)value, NULL);
+ weechat_printf_tags (trigger_buffer, "no_trigger",
+ "\t %s: \"%s\"",
+ (char *)key,
+ (value_no_color) ? value_no_color : (const char *)value);
+ if (value_no_color)
+ free (value_no_color);
+ }
+ else if (strcmp (value_type, "pointer") == 0)
+ {
+ weechat_printf_tags (trigger_buffer, "no_trigger",
+ "\t %s: 0x%lx",
+ (char *)key,
+ value);
+ }
+}
+
+/*
+ * Displays a hashtable on trigger buffer.
+ */
+
+void
+trigger_buffer_display_hashtable (const char *name,
+ struct t_hashtable *hashtable)
+{
+ if (!trigger_buffer)
+ return;
+
+ weechat_printf_tags (trigger_buffer, "no_trigger", " %s:", name);
+
+ weechat_hashtable_map (hashtable, &trigger_buffer_hashtable_map_cb, NULL);
+}
+
+/*
+ * Callback for user data in trigger buffer.
+ */
+
+int
+trigger_buffer_input_cb (void *data, struct t_gui_buffer *buffer,
+ const char *input_data)
+{
+ /* make C compiler happy */
+ (void) data;
+
+ /* close buffer */
+ if (strcmp (input_data, "q") == 0)
+ {
+ weechat_buffer_close (buffer);
+ return WEECHAT_RC_OK;
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Callback called when trigger buffer is closed.
+ */
+
+int
+trigger_buffer_close_cb (void *data, struct t_gui_buffer *buffer)
+{
+ /* make C compiler happy */
+ (void) data;
+ (void) buffer;
+
+ trigger_buffer = NULL;
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Restore buffer callbacks (input and close) for buffer created by trigger
+ * plugin.
+ */
+
+void
+trigger_buffer_set_callbacks ()
+{
+ struct t_gui_buffer *ptr_buffer;
+
+ ptr_buffer = weechat_buffer_search (TRIGGER_PLUGIN_NAME,
+ TRIGGER_BUFFER_NAME);
+ if (ptr_buffer)
+ {
+ trigger_buffer = ptr_buffer;
+ weechat_buffer_set_pointer (trigger_buffer, "close_callback",
+ &trigger_buffer_close_cb);
+ weechat_buffer_set_pointer (trigger_buffer, "input_callback",
+ &trigger_buffer_input_cb);
+ }
+}
+
+/*
+ * Opens script buffer.
+ */
+
+void
+trigger_buffer_open (int switch_to_buffer)
+{
+ if (!trigger_buffer)
+ {
+ trigger_buffer = weechat_buffer_new (TRIGGER_BUFFER_NAME,
+ &trigger_buffer_input_cb, NULL,
+ &trigger_buffer_close_cb, NULL);
+
+ /* failed to create buffer ? then return */
+ if (!trigger_buffer)
+ return;
+
+ weechat_buffer_set (trigger_buffer, "title", _("Trigger monitor"));
+
+ if (!weechat_buffer_get_integer (trigger_buffer, "short_name_is_set"))
+ weechat_buffer_set (trigger_buffer, "short_name", TRIGGER_BUFFER_NAME);
+ weechat_buffer_set (trigger_buffer, "localvar_set_type", "debug");
+ weechat_buffer_set (trigger_buffer, "localvar_set_server", TRIGGER_BUFFER_NAME);
+ weechat_buffer_set (trigger_buffer, "localvar_set_channel", TRIGGER_BUFFER_NAME);
+ weechat_buffer_set (trigger_buffer, "localvar_set_no_log", "1");
+
+ /* disable all highlights on this buffer */
+ weechat_buffer_set (trigger_buffer, "highlight_words", "-");
+ }
+
+ if (switch_to_buffer)
+ weechat_buffer_set (trigger_buffer, "display", "1");
+}
diff --git a/src/plugins/trigger/trigger-buffer.h b/src/plugins/trigger/trigger-buffer.h
new file mode 100644
index 000000000..a4bc02fd4
--- /dev/null
+++ b/src/plugins/trigger/trigger-buffer.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __WEECHAT_TRIGGER_BUFFER_H
+#define __WEECHAT_TRIGGER_BUFFER_H 1
+
+#define TRIGGER_BUFFER_NAME "monitor"
+
+struct t_gui_buffer *trigger_buffer;
+
+extern void trigger_buffer_display_hashtable (const char *name,
+ struct t_hashtable *hashtable);
+extern void trigger_buffer_set_callbacks ();
+extern void trigger_buffer_open (int switch_to_buffer);
+
+#endif /* __WEECHAT_TRIGGER_BUFFER_H */
diff --git a/src/plugins/trigger/trigger-callback.c b/src/plugins/trigger/trigger-callback.c
new file mode 100644
index 000000000..e14ee6e2e
--- /dev/null
+++ b/src/plugins/trigger/trigger-callback.c
@@ -0,0 +1,597 @@
+/*
+ * trigger-callback.c - callbacks for triggers
+ *
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "../weechat-plugin.h"
+#include "trigger.h"
+#include "trigger-buffer.h"
+
+
+/* one hashtable by hook, used in callback to evaluate "conditions" */
+struct t_hashtable *trigger_callback_hashtable_options = NULL;
+
+
+/*
+ * Checks conditions for a trigger.
+ *
+ * Returns:
+ * 1: conditions are true (or no condition set in trigger)
+ * 0: conditions are false
+ */
+
+int
+trigger_callback_check_conditions (struct t_trigger *trigger,
+ struct t_hashtable *pointers,
+ struct t_hashtable *extra_vars)
+{
+ const char *conditions;
+ char *value;
+ int rc;
+
+ conditions = weechat_config_string (trigger->options[TRIGGER_OPTION_CONDITIONS]);
+ if (!conditions || !conditions[0])
+ return 1;
+
+ value = weechat_string_eval_expression (conditions,
+ pointers,
+ extra_vars,
+ trigger_callback_hashtable_options);
+ rc = (value && (strcmp (value, "1") == 0));
+ if (value)
+ free (value);
+
+ return rc;
+}
+
+/*
+ * Replaces text using one or more regex in the trigger.
+ *
+ * Note: result must be freed after use.
+ */
+
+char *
+trigger_callback_replace_regex (struct t_trigger *trigger, const char *string)
+{
+ char *temp, *res;
+ int i;
+
+ if (trigger->regex_count == 0)
+ return strdup (string);
+
+ res = NULL;
+
+ for (i = 0; i < trigger->regex_count; i++)
+ {
+ temp = weechat_string_replace_regex ((res) ? res : string,
+ trigger->regex[i].regex,
+ trigger->regex[i].replace_eval);
+ if (!temp)
+ return res;
+ res = temp;
+ }
+
+ return res;
+}
+
+/*
+ * Executes the trigger command(s).
+ */
+
+void
+trigger_callback_run_command (struct t_trigger *trigger,
+ struct t_gui_buffer *buffer,
+ struct t_hashtable *pointers,
+ struct t_hashtable *extra_vars)
+{
+ const char *command;
+ char *command_eval, **commands, **ptr_command;
+
+ command = weechat_config_string (trigger->options[TRIGGER_OPTION_COMMAND]);
+ if (!command || !command[0])
+ return;
+
+ command_eval = weechat_string_eval_expression (command, pointers,
+ extra_vars, NULL);
+ if (command_eval)
+ {
+ commands = weechat_string_split_command (command_eval, ';');
+ if (commands)
+ {
+ for (ptr_command = commands; *ptr_command; ptr_command++)
+ {
+ /* display debug info on trigger buffer */
+ if (trigger_buffer)
+ {
+ weechat_printf_tags (trigger_buffer, "no_trigger",
+ "\t running command \"%s\"",
+ *ptr_command);
+ }
+ weechat_command (buffer, *ptr_command);
+ }
+ weechat_string_free_split_command (commands);
+ trigger->hook_count_cmd++;
+ }
+ free (command_eval);
+ }
+}
+
+/*
+ * Callback for a signal hooked.
+ */
+
+int
+trigger_callback_signal_cb (void *data, const char *signal,
+ const char *type_data, void *signal_data)
+{
+ struct t_trigger *trigger;
+ struct t_hashtable *extra_vars;
+ const char *command, *ptr_signal_data;
+ char str_data[128], *signal_data2;
+ int rc;
+
+ /* get trigger pointer, return immediately if not found or trigger running */
+ trigger = (struct t_trigger *)data;
+ if (!trigger || trigger->hook_running)
+ return WEECHAT_RC_OK;
+
+ trigger->hook_count_cb++;
+ trigger->hook_running = 1;
+
+ extra_vars = NULL;
+
+ rc = trigger_return_code[weechat_config_integer (trigger->options[TRIGGER_OPTION_RETURN_CODE])];
+
+ /*
+ * in a signal, the only possible action is to execute a command;
+ * so if the command is empty, just exit without checking conditions
+ */
+ command = weechat_config_string (trigger->options[TRIGGER_OPTION_COMMAND]);
+ if (!command || !command[0])
+ goto end;
+
+ /* create hashtable */
+ extra_vars = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL,
+ NULL);
+ if (!extra_vars)
+ goto end;
+
+ /* add data in hashtable used for conditions/replace/command */
+ weechat_hashtable_set (extra_vars, "tg_signal", signal);
+ if (strcmp (type_data, WEECHAT_HOOK_SIGNAL_STRING) == 0)
+ {
+ ptr_signal_data = (const char *)signal_data;
+ }
+ else if (strcmp (type_data, WEECHAT_HOOK_SIGNAL_INT) == 0)
+ {
+ snprintf (str_data, sizeof (str_data),
+ "%d", *((int *)signal_data));
+ ptr_signal_data = str_data;
+ }
+ else if (strcmp (type_data, WEECHAT_HOOK_SIGNAL_POINTER) == 0)
+ {
+ str_data[0] = '\0';
+ if (signal_data)
+ {
+ snprintf (str_data, sizeof (str_data),
+ "0x%lx", (long unsigned int)signal_data);
+ }
+ ptr_signal_data = str_data;
+ }
+ weechat_hashtable_set (extra_vars, "tg_signal_data", ptr_signal_data);
+
+ /* display debug info on trigger buffer */
+ if (!trigger_buffer && (weechat_trigger_plugin->debug >= 1))
+ trigger_buffer_open (0);
+ if (trigger_buffer)
+ {
+ weechat_printf_tags (trigger_buffer, "no_trigger",
+ "signal\t%s%s",
+ weechat_color ("chat_channel"),
+ trigger->name);
+ weechat_printf_tags (trigger_buffer, "no_trigger",
+ "\t signal_data: \"%s%s\"",
+ ptr_signal_data,
+ weechat_color ("reset"));
+ trigger_buffer_display_hashtable ("extra_vars", extra_vars);
+ }
+
+ /* check conditions */
+ if (!trigger_callback_check_conditions (trigger, NULL, extra_vars))
+ goto end;
+
+ /* replace text with regex */
+ if (trigger->regex_count > 0)
+ {
+ signal_data2 = trigger_callback_replace_regex (trigger, ptr_signal_data);
+ if (signal_data2)
+ {
+ weechat_hashtable_set (extra_vars, "tg_signal_data", signal_data2);
+ free (signal_data2);
+ }
+ }
+
+ /* execute command */
+ trigger_callback_run_command (trigger, NULL, NULL, extra_vars);
+
+end:
+ if (extra_vars)
+ weechat_hashtable_free (extra_vars);
+ trigger->hook_running = 0;
+ return rc;
+}
+
+/*
+ * Callback for a hsignal hooked.
+ */
+
+int
+trigger_callback_hsignal_cb (void *data, const char *signal,
+ struct t_hashtable *hashtable)
+{
+ (void) data;
+ (void) signal;
+ (void) hashtable;
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Callback for a modifier hooked.
+ */
+
+char *
+trigger_callback_modifier_cb (void *data, const char *modifier,
+ const char *modifier_data, const char *string)
+{
+ struct t_trigger *trigger;
+ struct t_hashtable *extra_vars;
+ const char *command;
+ char *string_modified, *pos, *pos2, *plugin_name, *buffer_name;
+ char *buffer_full_name, *tags;
+ int no_trigger, length;
+
+ no_trigger = 0;
+
+ /* get trigger pointer, return immediately if not found or trigger running */
+ trigger = (struct t_trigger *)data;
+ if (!trigger || trigger->hook_running)
+ return NULL;
+
+ trigger->hook_count_cb++;
+ trigger->hook_running = 1;
+
+ extra_vars = NULL;
+ string_modified = NULL;
+
+ /*
+ * in a modifier, the only possible actions are regex or command;
+ * so if both are empty, just exit without checking conditions
+ */
+ command = weechat_config_string (trigger->options[TRIGGER_OPTION_COMMAND]);
+ if ((!command || !command[0]) && !trigger->regex)
+ goto end;
+
+ /* create hashtable */
+ extra_vars = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL,
+ NULL);
+ if (!extra_vars)
+ goto end;
+
+ /* add data in hashtable used for conditions/replace/command */
+ weechat_hashtable_set (extra_vars, "tg_modifier", modifier);
+ weechat_hashtable_set (extra_vars, "tg_modifier_data", modifier_data);
+ weechat_hashtable_set (extra_vars, "tg_string", string);
+
+ /* add special variables for a WeeChat message */
+ if (strcmp (modifier, "weechat_print") == 0)
+ {
+ pos = strchr (modifier_data, ';');
+ if (pos)
+ {
+ plugin_name = weechat_strndup (modifier_data, pos - modifier_data);
+ if (plugin_name)
+ {
+ weechat_hashtable_set (extra_vars, "tg_plugin", plugin_name);
+ pos++;
+ pos2 = strchr (pos, ';');
+ if (pos2)
+ {
+ buffer_name = weechat_strndup (pos, pos2 - pos);
+ if (buffer_name)
+ {
+ length = strlen (plugin_name) + 1 + strlen (buffer_name) + 1;
+ buffer_full_name = malloc (length);
+ if (buffer_full_name)
+ {
+ snprintf (buffer_full_name, length,
+ "%s.%s", plugin_name, buffer_name);
+ weechat_hashtable_set (extra_vars, "tg_buffer",
+ buffer_full_name);
+ free (buffer_full_name);
+ }
+ free (buffer_name);
+ }
+ pos2++;
+ if (pos2[0])
+ {
+ length = 1 + strlen (pos2) + 1 + 1;
+ tags = malloc (length);
+ if (tags)
+ {
+ snprintf (tags, length, ",%s,", pos2);
+ weechat_hashtable_set (extra_vars, "tg_tags", tags);
+ if (strstr (tags, ",no_trigger,"))
+ no_trigger = 1;
+ free (tags);
+ }
+ }
+ }
+ free (plugin_name);
+ }
+ }
+ }
+
+ /*
+ * ignore this modifier if "no_trigger" was in tags
+ * (for modifier "weechat_print")
+ */
+ if (no_trigger)
+ goto end;
+
+ /* display debug info on trigger buffer */
+ if (!trigger_buffer && (weechat_trigger_plugin->debug >= 1))
+ trigger_buffer_open (0);
+ if (trigger_buffer)
+ {
+ weechat_printf_tags (trigger_buffer, "no_trigger",
+ "modifier\t%s%s",
+ weechat_color ("chat_channel"),
+ trigger->name);
+ weechat_printf_tags (trigger_buffer, "no_trigger",
+ "\t modifier: %s", modifier);
+ weechat_printf_tags (trigger_buffer, "no_trigger",
+ "\t modifier_data: \"%s%s\"",
+ modifier_data,
+ weechat_color ("reset"));
+ trigger_buffer_display_hashtable ("extra_vars", extra_vars);
+ }
+
+ /* check conditions */
+ if (!trigger_callback_check_conditions (trigger, NULL, extra_vars))
+ goto end;
+
+ /* replace text with regex */
+ if (trigger->regex_count > 0)
+ {
+ string_modified = trigger_callback_replace_regex (trigger, string);
+ if (string_modified)
+ {
+ weechat_hashtable_set (extra_vars, "tg_string", string_modified);
+ if (strcmp (string, string_modified) == 0)
+ {
+ /* regex did not change the string, ignore it */
+ free (string_modified);
+ string_modified = NULL;
+ }
+ }
+ }
+
+ /* execute command */
+ trigger_callback_run_command (trigger, NULL, NULL, extra_vars);
+
+end:
+ if (extra_vars)
+ weechat_hashtable_free (extra_vars);
+ trigger->hook_running = 0;
+ return string_modified;
+}
+
+/*
+ * Callback for a print hooked.
+ */
+
+int
+trigger_callback_print_cb (void *data, struct t_gui_buffer *buffer,
+ time_t date, int tags_count, const char **tags,
+ int displayed, int highlight, const char *prefix,
+ const char *message)
+{
+ struct t_trigger *trigger;
+ struct t_hashtable *pointers, *extra_vars;
+ const char *command, *localvar_type;
+ char *message2, *str_tags, *str_tags2, str_temp[128];
+ int i, rc, length, tag_notify_private;
+ struct tm *date_tmp;
+
+ /* get trigger pointer, return immediately if not found or trigger running */
+ trigger = (struct t_trigger *)data;
+ if (!trigger || trigger->hook_running)
+ return WEECHAT_RC_OK;
+
+ trigger->hook_count_cb++;
+ trigger->hook_running = 1;
+
+ pointers = NULL;
+ extra_vars = NULL;
+
+ rc = trigger_return_code[weechat_config_integer (trigger->options[TRIGGER_OPTION_RETURN_CODE])];
+
+ /* return if the buffer does not match buffers defined in the trigger */
+ if (trigger->hook_print_buffers
+ && !weechat_buffer_match_list (buffer, trigger->hook_print_buffers))
+ goto end;
+
+ /*
+ * in a print, the only possible action is to execute a command;
+ * so if the command is empty, just exit without checking conditions
+ */
+ command = weechat_config_string (trigger->options[TRIGGER_OPTION_COMMAND]);
+ if (!command || !command[0])
+ goto end;
+
+ /* create hashtables */
+ pointers = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_POINTER,
+ NULL,
+ NULL);
+ if (!pointers)
+ goto end;
+ extra_vars = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL,
+ NULL);
+ if (!extra_vars)
+ goto end;
+
+ /* add data in hashtable used for conditions/replace/command */
+ weechat_hashtable_set (pointers, "buffer", buffer);
+ date_tmp = localtime (&date);
+ if (date_tmp)
+ {
+ strftime (str_temp, sizeof (str_temp), "%Y-%m-%d %H:%M:%S", date_tmp);
+ weechat_hashtable_set (extra_vars, "tg_date", str_temp);
+ }
+ snprintf (str_temp, sizeof (str_temp), "%d", tags_count);
+ weechat_hashtable_set (extra_vars, "tg_tags_count", str_temp);
+ str_tags = weechat_string_build_with_split_string (tags, ",");
+ if (str_tags)
+ {
+ /* build string with tags and commas around: ",tag1,tag2,tag3," */
+ length = 1 + strlen (str_tags) + 1 + 1;
+ str_tags2 = malloc (length);
+ if (str_tags2)
+ {
+ snprintf (str_tags2, length, ",%s,", str_tags);
+ weechat_hashtable_set (extra_vars, "tg_tags", str_tags2);
+ free (str_tags2);
+ }
+ free (str_tags);
+ }
+ snprintf (str_temp, sizeof (str_temp), "%d", displayed);
+ weechat_hashtable_set (extra_vars, "tg_displayed", str_temp);
+ snprintf (str_temp, sizeof (str_temp), "%d", highlight);
+ weechat_hashtable_set (extra_vars, "tg_highlight", str_temp);
+ weechat_hashtable_set (extra_vars, "tg_prefix", prefix);
+ weechat_hashtable_set (extra_vars, "tg_message", message);
+
+ localvar_type = weechat_buffer_get_string (buffer, "localvar_type");
+ tag_notify_private = 0;
+
+ for (i = 0; i < tags_count; i++)
+ {
+ if (strcmp (tags[i], "no_trigger") == 0)
+ {
+ goto end;
+ }
+ else if (strcmp (tags[i], "notify_private") == 0)
+ {
+ tag_notify_private = 1;
+ }
+ }
+ snprintf (str_temp, sizeof (str_temp), "%d",
+ (tag_notify_private && localvar_type &&
+ (strcmp (localvar_type, "private") == 0)) ? 1 : 0);
+ weechat_hashtable_set (extra_vars, "tg_msg_pv", str_temp);
+
+ /* display debug info on trigger buffer */
+ if (!trigger_buffer && (weechat_trigger_plugin->debug >= 1))
+ trigger_buffer_open (0);
+ if (trigger_buffer)
+ {
+ weechat_printf_tags (trigger_buffer, "no_trigger",
+ "print\t%s%s",
+ weechat_color ("chat_channel"),
+ trigger->name);
+ weechat_printf_tags (trigger_buffer, "no_trigger",
+ "\t buffer: %s",
+ weechat_buffer_get_string (buffer, "full_name"));
+ trigger_buffer_display_hashtable ("pointers", pointers);
+ trigger_buffer_display_hashtable ("extra_vars", extra_vars);
+ }
+
+ /* check conditions */
+ if (!trigger_callback_check_conditions (trigger, pointers, extra_vars))
+ goto end;
+
+ /* replace text with regex */
+ if (trigger->regex_count > 0)
+ {
+ message2 = trigger_callback_replace_regex (trigger, message);
+ if (message2)
+ {
+ weechat_hashtable_set (extra_vars, "tg_message", message);
+ free (message2);
+ }
+ }
+
+ /* execute command */
+ trigger_callback_run_command (trigger, buffer, pointers, extra_vars);
+
+end:
+ if (pointers)
+ weechat_hashtable_free (pointers);
+ if (extra_vars)
+ weechat_hashtable_free (extra_vars);
+ trigger->hook_running = 0;
+ return rc;
+}
+
+/*
+ * Initializes trigger callback.
+ */
+
+void
+trigger_callback_init ()
+{
+ trigger_callback_hashtable_options = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL,
+ NULL);
+ if (trigger_callback_hashtable_options)
+ {
+ weechat_hashtable_set (trigger_callback_hashtable_options,
+ "type", "condition");
+ }
+}
+
+/*
+ * Ends trigger callback.
+ */
+
+void
+trigger_callback_end ()
+{
+ if (trigger_callback_hashtable_options)
+ weechat_hashtable_free (trigger_callback_hashtable_options);
+}
diff --git a/src/plugins/trigger/trigger-callback.h b/src/plugins/trigger/trigger-callback.h
new file mode 100644
index 000000000..b9327709b
--- /dev/null
+++ b/src/plugins/trigger/trigger-callback.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __WEECHAT_TRIGGER_CALLBACK_H
+#define __WEECHAT_TRIGGER_CALLBACK_H 1
+
+extern int trigger_callback_signal_cb (void *data, const char *signal,
+ const char *type_data, void *signal_data);
+extern int trigger_callback_hsignal_cb (void *data, const char *signal,
+ struct t_hashtable *hashtable);
+extern char *trigger_callback_modifier_cb (void *data, const char *modifier,
+ const char *modifier_data,
+ const char *string);
+extern int trigger_callback_print_cb (void *data, struct t_gui_buffer *buffer,
+ time_t date, int tags_count,
+ const char **tags, int displayed,
+ int highlight, const char *prefix,
+ const char *message);
+extern void trigger_callback_init ();
+extern void trigger_callback_end ();
+
+#endif /* __WEECHAT_TRIGGER_CALLBACK_H */
diff --git a/src/plugins/trigger/trigger-command.c b/src/plugins/trigger/trigger-command.c
new file mode 100644
index 000000000..a38ffcc28
--- /dev/null
+++ b/src/plugins/trigger/trigger-command.c
@@ -0,0 +1,352 @@
+/*
+ * trigger-command.c - trigger command
+ *
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "../weechat-plugin.h"
+#include "trigger.h"
+#include "trigger-buffer.h"
+#include "trigger-config.h"
+
+
+/*
+ * Callback for command "/trigger": manage triggers.
+ */
+
+int
+trigger_command_trigger (void *data, struct t_gui_buffer *buffer, int argc,
+ char **argv, char **argv_eol)
+{
+ struct t_trigger *ptr_trigger;
+ const char *option;
+ int i, type, count, index_option, enabled;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) buffer;
+
+ /* list all triggers */
+ if ((argc == 1)
+ || ((argc == 2) && (weechat_strcasecmp (argv[1], "list") == 0)))
+ {
+ if (triggers)
+ {
+ weechat_printf_tags (NULL, "no_trigger", "");
+ weechat_printf_tags (NULL, "no_trigger", _("List of triggers:"));
+ for (ptr_trigger = triggers; ptr_trigger;
+ ptr_trigger = ptr_trigger->next_trigger)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ " %s: %s, \"%s\" (%d hooks, %d/%d) %s",
+ ptr_trigger->name,
+ weechat_config_string (ptr_trigger->options[TRIGGER_OPTION_HOOK]),
+ weechat_config_string (ptr_trigger->options[TRIGGER_OPTION_ARGUMENTS]),
+ ptr_trigger->hooks_count,
+ ptr_trigger->hook_count_cb,
+ ptr_trigger->hook_count_cmd,
+ weechat_config_boolean (ptr_trigger->options[TRIGGER_OPTION_ENABLED]) ?
+ "" : _("(disabled)"));
+ option = weechat_config_string (ptr_trigger->options[TRIGGER_OPTION_CONDITIONS]);
+ if (option && option[0])
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ " conditions: \"%s\"", option);
+ }
+ if (ptr_trigger->regex_count > 0)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ " %d regex:",
+ ptr_trigger->regex_count);
+ for (i = 0; i < ptr_trigger->regex_count; i++)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ " %d: %s%s %s-->%s %s",
+ i + 1,
+ weechat_color (weechat_config_string (trigger_config_color_regex)),
+ ptr_trigger->regex[i].str_regex,
+ weechat_color ("chat_delimiters"),
+ weechat_color (weechat_config_string (trigger_config_color_replace)),
+ ptr_trigger->regex[i].replace);
+ }
+ }
+ option = weechat_config_string (ptr_trigger->options[TRIGGER_OPTION_COMMAND]);
+ if (option && option[0])
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ " command: \"%s\"", option);
+ }
+ }
+ }
+ else
+ {
+ weechat_printf_tags (NULL, "no_trigger", _("No trigger defined"));
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ /* add a trigger */
+ if (weechat_strcasecmp (argv[1], "add") == 0)
+ {
+ if (argc < 4)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%sError: missing arguments for \"%s\" "
+ "command"),
+ weechat_prefix ("error"), "trigger");
+ return WEECHAT_RC_OK;
+ }
+ type = trigger_search_hook_type (argv[3]);
+ if (type < 0)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%s%s: invalid hook type \"%s\""),
+ weechat_prefix ("error"), TRIGGER_PLUGIN_NAME,
+ argv[3]);
+ return WEECHAT_RC_OK;
+ }
+ ptr_trigger = trigger_alloc (argv[2]);
+ if (!ptr_trigger)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%s%s: error creating trigger \"%s\""),
+ weechat_prefix ("error"), TRIGGER_PLUGIN_NAME,
+ argv[2]);
+ return WEECHAT_RC_OK;
+ }
+ if (trigger_new (argv[2], "on", argv[3],
+ (argc > 4) ? argv_eol[4] : "",
+ "", "", "", "ok"))
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("Trigger \"%s\" created"), argv[2]);
+ }
+ else
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%sError: failed to create trigger \"%s\""),
+ weechat_prefix ("error"), argv[2]);
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ /* set option in a trigger */
+ if (weechat_strcasecmp (argv[1], "set") == 0)
+ {
+ if (argc < 5)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%sError: missing arguments for \"%s\" "
+ "command"),
+ weechat_prefix ("error"), "trigger");
+ return WEECHAT_RC_OK;
+ }
+ ptr_trigger = trigger_search (argv[2]);
+ if (!ptr_trigger)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%sTrigger \"%s\" not found"),
+ weechat_prefix ("error"), argv[2]);
+ return WEECHAT_RC_OK;
+ }
+ if (weechat_strcasecmp (argv[3], "name") == 0)
+ {
+ trigger_rename (ptr_trigger, argv_eol[4]);
+ }
+ else
+ {
+ index_option = trigger_search_option (argv[3]);
+ if (index_option < 0)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%sTrigger option \"%s\" not found"),
+ weechat_prefix ("error"), argv[3]);
+ return WEECHAT_RC_OK;
+ }
+ weechat_config_option_set (ptr_trigger->options[index_option],
+ argv_eol[4], 1);
+ }
+ weechat_printf_tags (NULL, "no_trigger",
+ _("Trigger \"%s\" updated"), ptr_trigger->name);
+ return WEECHAT_RC_OK;
+ }
+
+ /* delete a trigger */
+ if (weechat_strcasecmp (argv[1], "del") == 0)
+ {
+ if (argc < 3)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%sError: missing arguments for \"%s\" "
+ "command"),
+ weechat_prefix ("error"), "trigger");
+ return WEECHAT_RC_OK;
+ }
+ if (weechat_strcasecmp (argv[2], "-all") == 0)
+ {
+ count = triggers_count;
+ trigger_free_all ();
+ if (count > 0)
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%d triggers removed"), count);
+ }
+ else
+ {
+ for (i = 2; i < argc; i++)
+ {
+ ptr_trigger = trigger_search (argv[i]);
+ if (ptr_trigger)
+ {
+ trigger_free (ptr_trigger);
+ weechat_printf_tags (NULL, "no_trigger",
+ _("Trigger \"%s\" removed"), argv[i]);
+ }
+ else
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%sTrigger \"%s\" not found"),
+ weechat_prefix ("error"), argv[i]);
+ }
+ }
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ /* enable/disable/toggle a trigger */
+ if ((weechat_strcasecmp (argv[1], "enable") == 0)
+ || (weechat_strcasecmp (argv[1], "disable") == 0)
+ || (weechat_strcasecmp (argv[1], "toggle") == 0))
+ {
+ if (argc < 3)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%sError: missing arguments for \"%s\" "
+ "command"),
+ weechat_prefix ("error"), "trigger");
+ return WEECHAT_RC_OK;
+ }
+ ptr_trigger = trigger_search (argv[2]);
+ if (!ptr_trigger)
+ {
+ weechat_printf_tags (NULL, "no_trigger",
+ _("%sTrigger \"%s\" not found"),
+ weechat_prefix ("error"), argv[2]);
+ return WEECHAT_RC_OK;
+ }
+ if (weechat_strcasecmp (argv[1], "enable") == 0)
+ enabled = 1;
+ else if (weechat_strcasecmp (argv[1], "disable") == 0)
+ enabled = 0;
+ else
+ {
+ enabled = weechat_config_boolean (ptr_trigger->options[TRIGGER_OPTION_ENABLED]) ?
+ 0 : 1;
+ }
+ weechat_config_option_set (ptr_trigger->options[TRIGGER_OPTION_ENABLED],
+ (enabled) ? "on" : "off", 1);
+ weechat_printf_tags (NULL, "no_trigger",
+ (enabled) ?
+ _("Trigger \"%s\" enabled") :
+ _("Trigger \"%s\" disabled"),
+ ptr_trigger->name);
+ return WEECHAT_RC_OK;
+ }
+
+ /* open the trigger monitor buffer */
+ if (weechat_strcasecmp (argv[1], "monitor") == 0)
+ {
+ trigger_buffer_open (1);
+ return WEECHAT_RC_OK;
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Hooks trigger commands.
+ */
+
+void
+trigger_command_init ()
+{
+ weechat_hook_command (
+ "trigger",
+ N_("manage triggers"),
+ N_("list"
+ " || add <name> <hook> [<arguments>]"
+ " || set <name> <option> <value>"
+ " || del <name>|-all [<name>...]"
+ " || enable|disable|toggle <name>"
+ " || monitor"),
+ N_(" add: add a trigger\n"
+ " name: name of trigger\n"
+ " hook: signal, hsignal, modifier, print, timer\n"
+ "arguments: arguments for the hook, depending on hook:\n"
+ " signal: name of signal\n"
+ " hsignal: name of hsignal\n"
+ " modifier: name of modifier\n"
+ " print: buffer, tags, message, strip_colors\n"
+ " timer: interval, align_second, max_calls\n"
+ " set: set an option in a trigger\n"
+ " option: name of option: name, hook, arguments, conditions, regex, "
+ "command, return_code\n"
+ " (for help on option, you can do /help "
+ "trigger.trigger.<name>.<option>)\n"
+ " value: new value for the option\n"
+ " del: delete a trigger\n"
+ " -all: delete all triggers\n"
+ " enable: enable a trigger\n"
+ " disable: disable a trigger\n"
+ " toggle: toggle a trigger\n"
+ " monitor: open the trigger monitor buffer\n"
+ "\n"
+ "When a trigger callback is called, following actions are performed, "
+ "in this order:\n"
+ " 1. if no regex/command is defined, exit\n"
+ " 2. check conditions; if false, exit\n"
+ " 3. replace text using regex (if defined in trigger)\n"
+ " 4. execute command (if defined in trigger)\n"
+ "Note: on steps 1 and 2, the exit is made with the return code "
+ "defined in trigger (or NULL for a modifier).\n"
+ "\n"
+ "Examples:\n"
+ " send alert (BEL) on highlight or private message:\n"
+ " /trigger add beep print\n"
+ " /trigger set beep conditions ${tg_highlight} || ${tg_msg_pv}\n"
+ " /trigger set beep command /print -stderr \\a\n"
+ " replace password with '*' in /oper command (in command line and "
+ "command history):\n"
+ " /trigger add oper modifier input_text_display;history_add\n"
+ " /trigger set oper regex ==^(/oper +\\S+ +)(.*)==\\1\\*2\n"
+ " add text attributes in *bold*, _underline_ and /italic/:\n"
+ " /trigger add effects modifier weechat_print\n"
+ " /trigger set effects regex "
+ "==\\*(\\S+)\\*==*${color:bold}\\1${color:-bold}*"
+ "==_(\\S+)_==_${color:underline}\\1${color:-underline}_"
+ "==/(\\S+)/==/${color:italic}\\1${color:-italic}/"),
+ "list"
+ " || add %(trigger_names) %(trigger_hooks)"
+ " || set %(trigger_names) %(trigger_options)|name %(trigger_option_value)"
+ " || del %(trigger_names)|-all %(trigger_names)|%*"
+ " || enable|disable|toggle %(trigger_names)"
+ " || monitor",
+ &trigger_command_trigger, NULL);
+}
diff --git a/src/plugins/trigger/trigger-command.h b/src/plugins/trigger/trigger-command.h
new file mode 100644
index 000000000..a2ad07f25
--- /dev/null
+++ b/src/plugins/trigger/trigger-command.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __WEECHAT_TRIGGER_COMMAND_H
+#define __WEECHAT_TRIGGER_COMMAND_H 1
+
+extern void trigger_command_init ();
+
+#endif /* __WEECHAT_TRIGGER_COMMAND_H */
diff --git a/src/plugins/trigger/trigger-completion.c b/src/plugins/trigger/trigger-completion.c
new file mode 100644
index 000000000..26dddb0fe
--- /dev/null
+++ b/src/plugins/trigger/trigger-completion.c
@@ -0,0 +1,185 @@
+/*
+ * trigger-completion.c - completion for trigger commands
+ *
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../weechat-plugin.h"
+#include "trigger.h"
+
+
+/*
+ * Adds triggers to completion list.
+ */
+
+int
+trigger_completion_triggers_cb (void *data, const char *completion_item,
+ struct t_gui_buffer *buffer,
+ struct t_gui_completion *completion)
+{
+ struct t_trigger *ptr_trigger;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) completion_item;
+ (void) buffer;
+
+ for (ptr_trigger = triggers; ptr_trigger;
+ ptr_trigger = ptr_trigger->next_trigger)
+ {
+ weechat_hook_completion_list_add (completion, ptr_trigger->name,
+ 0, WEECHAT_LIST_POS_SORT);
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Adds options for triggers to completion list.
+ */
+
+int
+trigger_completion_options_cb (void *data, const char *completion_item,
+ struct t_gui_buffer *buffer,
+ struct t_gui_completion *completion)
+{
+ int i;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) completion_item;
+ (void) buffer;
+
+ for (i = 0; i < TRIGGER_NUM_OPTIONS; i++)
+ {
+ weechat_hook_completion_list_add (completion,
+ trigger_option_string[i],
+ 0, WEECHAT_LIST_POS_SORT);
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Adds value of a trigger option to completion list.
+ */
+
+int
+trigger_completion_option_value_cb (void *data, const char *completion_item,
+ struct t_gui_buffer *buffer,
+ struct t_gui_completion *completion)
+{
+ const char *args;
+ char **argv;
+ int argc, index_option;
+ struct t_trigger *ptr_trigger;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) completion_item;
+ (void) buffer;
+
+ args = weechat_hook_completion_get_string (completion, "args");
+ if (!args)
+ return WEECHAT_RC_OK;
+
+ argv = weechat_string_split (args, " ", 0, 0, &argc);
+ if (!argv)
+ return WEECHAT_RC_OK;
+
+ if (argc >= 3)
+ {
+ ptr_trigger = trigger_search (argv[1]);
+ if (ptr_trigger)
+ {
+ if (weechat_strcasecmp (argv[2], "name") == 0)
+ {
+ weechat_hook_completion_list_add (completion,
+ ptr_trigger->name,
+ 0,
+ WEECHAT_LIST_POS_BEGINNING);
+ }
+ else
+ {
+ index_option = trigger_search_option (argv[2]);
+ if (index_option >= 0)
+ {
+ weechat_hook_completion_list_add (completion,
+ weechat_config_string (ptr_trigger->options[index_option]),
+ 0,
+ WEECHAT_LIST_POS_BEGINNING);
+ }
+ }
+ }
+ }
+
+ weechat_string_free_split (argv);
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Adds hooks for triggers to completion list.
+ */
+
+int
+trigger_completion_hooks_cb (void *data, const char *completion_item,
+ struct t_gui_buffer *buffer,
+ struct t_gui_completion *completion)
+{
+ int i;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) completion_item;
+ (void) buffer;
+
+ for (i = 0; i < TRIGGER_NUM_HOOK_TYPES; i++)
+ {
+ weechat_hook_completion_list_add (completion,
+ trigger_hook_type_string[i],
+ 0, WEECHAT_LIST_POS_SORT);
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Hooks completions.
+ */
+
+void
+trigger_completion_init ()
+{
+ weechat_hook_completion ("trigger_names",
+ N_("triggers"),
+ &trigger_completion_triggers_cb, NULL);
+ weechat_hook_completion ("trigger_options",
+ N_("options for triggers"),
+ &trigger_completion_options_cb, NULL);
+ weechat_hook_completion ("trigger_option_value",
+ N_("value of a trigger option"),
+ &trigger_completion_option_value_cb, NULL);
+ weechat_hook_completion ("trigger_hooks",
+ N_("hooks for triggers"),
+ &trigger_completion_hooks_cb, NULL);
+}
diff --git a/src/plugins/trigger/trigger-completion.h b/src/plugins/trigger/trigger-completion.h
new file mode 100644
index 000000000..17dcccf9c
--- /dev/null
+++ b/src/plugins/trigger/trigger-completion.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __WEECHAT_TRIGGER_COMPLETION_H
+#define __WEECHAT_TRIGGER_COMPLETION_H 1
+
+extern void trigger_completion_init ();
+
+#endif /* __WEECHAT_TRIGGER_COMPLETION_H */
diff --git a/src/plugins/trigger/trigger-config.c b/src/plugins/trigger/trigger-config.c
new file mode 100644
index 000000000..416d0ad1f
--- /dev/null
+++ b/src/plugins/trigger/trigger-config.c
@@ -0,0 +1,484 @@
+/*
+ * trigger-config.c - trigger configuration options (file trigger.conf)
+ *
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../weechat-plugin.h"
+#include "trigger.h"
+#include "trigger-config.h"
+
+
+struct t_config_file *trigger_config_file = NULL;
+struct t_config_section *trigger_config_section_trigger = NULL;
+
+/* trigger config, look section */
+
+struct t_config_option *trigger_config_look_test;
+
+/* trigger config, color section */
+
+struct t_config_option *trigger_config_color_regex;
+struct t_config_option *trigger_config_color_replace;
+
+
+/*
+ * Callback called when trigger option "enabled" is changed.
+ */
+
+void
+trigger_config_change_enabled (void *data, struct t_config_option *option)
+{
+ struct t_trigger *ptr_trigger;
+
+ /* make C compiler happy */
+ (void) data;
+
+ ptr_trigger = trigger_search_with_option (option);
+ if (!ptr_trigger)
+ return;
+
+ if (weechat_config_boolean (option))
+ trigger_hook (ptr_trigger);
+ else
+ trigger_unhook (ptr_trigger);
+}
+
+/*
+ * Callback called when trigger option "arguments" is changed.
+ */
+
+void
+trigger_config_change_arguments (void *data, struct t_config_option *option)
+{
+ struct t_trigger *ptr_trigger;
+
+ /* make C compiler happy */
+ (void) data;
+
+ ptr_trigger = trigger_search_with_option (option);
+ if (!ptr_trigger)
+ return;
+
+ trigger_hook (ptr_trigger);
+}
+
+/*
+ * Callback called when trigger option "regex" is changed.
+ */
+
+void
+trigger_config_change_regex (void *data, struct t_config_option *option)
+{
+ struct t_trigger *ptr_trigger;
+
+ /* make C compiler happy */
+ (void) data;
+
+ ptr_trigger = trigger_search_with_option (option);
+ if (!ptr_trigger)
+ return;
+
+ trigger_set_regex (ptr_trigger);
+}
+
+/*
+ * Creates an option for a trigger.
+ *
+ * Returns pointer to new option, NULL if error.
+ */
+
+struct t_config_option *
+trigger_config_create_option (const char *trigger_name, int index_option,
+ const char *value)
+{
+ struct t_config_option *ptr_option;
+ int length;
+ char *option_name;
+
+ ptr_option = NULL;
+
+ length = strlen (trigger_name) + 1 +
+ strlen (trigger_option_string[index_option]) + 1;
+ option_name = malloc (length);
+ if (!option_name)
+ return NULL;
+
+ snprintf (option_name, length, "%s.%s",
+ trigger_name, trigger_option_string[index_option]);
+
+ switch (index_option)
+ {
+ case TRIGGER_OPTION_ENABLED:
+ ptr_option = weechat_config_new_option (
+ trigger_config_file, trigger_config_section_trigger,
+ option_name, "boolean",
+ N_("true if trigger is enabled, otherwise false"),
+ NULL, 0, 0, value, NULL, 0,
+ NULL, NULL, &trigger_config_change_enabled, NULL, NULL, NULL);
+ break;
+ case TRIGGER_OPTION_HOOK:
+ ptr_option = weechat_config_new_option (
+ trigger_config_file, trigger_config_section_trigger,
+ option_name, "integer",
+ N_("type of hook used"),
+ "signal|hsignal|modifier|print|timer", 0, 0, value, NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ break;
+ case TRIGGER_OPTION_ARGUMENTS:
+ ptr_option = weechat_config_new_option (
+ trigger_config_file, trigger_config_section_trigger,
+ option_name, "string",
+ N_("arguments for the hook (depend on the hook type); for "
+ "signal/hsignal/modifier: name, for print: "
+ "buffer;tags;strip_colors;message, for timer: "
+ "interval;align_second;max_calls"),
+ NULL, 0, 0, value, NULL, 0,
+ NULL, NULL, &trigger_config_change_arguments, NULL, NULL, NULL);
+ break;
+ case TRIGGER_OPTION_CONDITIONS:
+ ptr_option = weechat_config_new_option (
+ trigger_config_file, trigger_config_section_trigger,
+ option_name, "string",
+ N_("condition(s) for running the command (it is checked in "
+ "hook callback)"),
+ NULL, 0, 0, value, NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ break;
+ case TRIGGER_OPTION_REGEX:
+ ptr_option = weechat_config_new_option (
+ trigger_config_file, trigger_config_section_trigger,
+ option_name, "string",
+ N_("replace text with a POSIX extended regular expression"),
+ NULL, 0, 0, value, NULL, 0,
+ NULL, NULL, &trigger_config_change_regex, NULL, NULL, NULL);
+ break;
+ case TRIGGER_OPTION_COMMAND:
+ ptr_option = weechat_config_new_option (
+ trigger_config_file, trigger_config_section_trigger,
+ option_name, "string",
+ N_("command run if conditions are OK"),
+ NULL, 0, 0, value, NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ break;
+ case TRIGGER_OPTION_RETURN_CODE:
+ ptr_option = weechat_config_new_option (
+ trigger_config_file, trigger_config_section_trigger,
+ option_name, "integer",
+ N_("return code for hook callback"),
+ "ok|ok_eat|error", 0, 0, value, NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ break;
+ case TRIGGER_NUM_OPTIONS:
+ break;
+ }
+
+ free (option_name);
+
+ return ptr_option;
+}
+
+/*
+ * Creates option for a temporary trigger (when reading configuration file).
+ */
+
+void
+trigger_config_create_option_temp (struct t_trigger *temp_trigger,
+ int index_option, const char *value)
+{
+ struct t_config_option *new_option;
+
+ new_option = trigger_config_create_option (temp_trigger->name,
+ index_option, value);
+ if (new_option
+ && (index_option >= 0) && (index_option < TRIGGER_NUM_OPTIONS))
+ {
+ temp_trigger->options[index_option] = new_option;
+ }
+}
+
+/*
+ * Uses temporary triggers (created by reading configuration file).
+ */
+
+void
+trigger_config_use_temp_triggers ()
+{
+ struct t_trigger *ptr_temp_trigger, *next_temp_trigger;
+ int i, num_options_ok;
+
+ for (ptr_temp_trigger = triggers_temp; ptr_temp_trigger;
+ ptr_temp_trigger = ptr_temp_trigger->next_trigger)
+ {
+ num_options_ok = 0;
+ for (i = 0; i < TRIGGER_NUM_OPTIONS; i++)
+ {
+ if (!ptr_temp_trigger->options[i])
+ {
+ ptr_temp_trigger->options[i] =
+ trigger_config_create_option (ptr_temp_trigger->name,
+ i,
+ trigger_option_default[i]);
+ }
+ if (ptr_temp_trigger->options[i])
+ num_options_ok++;
+ }
+
+ if (num_options_ok == TRIGGER_NUM_OPTIONS)
+ {
+ trigger_new_with_options (ptr_temp_trigger->name,
+ ptr_temp_trigger->options);
+ }
+ else
+ {
+ for (i = 0; i < TRIGGER_NUM_OPTIONS; i++)
+ {
+ if (ptr_temp_trigger->options[i])
+ {
+ weechat_config_option_free (ptr_temp_trigger->options[i]);
+ ptr_temp_trigger->options[i] = NULL;
+ }
+ }
+ }
+ }
+
+ /* free all temporary triggers */
+ while (triggers_temp)
+ {
+ next_temp_trigger = triggers_temp->next_trigger;
+
+ if (triggers_temp->name)
+ free (triggers_temp->name);
+ free (triggers_temp);
+
+ triggers_temp = next_temp_trigger;
+ }
+ last_trigger_temp = NULL;
+}
+
+/*
+ * Reads a trigger option in trigger configuration file.
+ */
+
+int
+trigger_config_trigger_read_cb (void *data, struct t_config_file *config_file,
+ struct t_config_section *section,
+ const char *option_name, const char *value)
+{
+ char *pos_option, *trigger_name;
+ struct t_trigger *ptr_temp_trigger;
+ int index_option;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) config_file;
+ (void) section;
+
+ if (!option_name)
+ return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
+
+ pos_option = strchr (option_name, '.');
+ if (!pos_option)
+ return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
+
+ trigger_name = weechat_strndup (option_name, pos_option - option_name);
+ if (!trigger_name)
+ return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
+
+ pos_option++;
+
+ /* search temporary trigger */
+ for (ptr_temp_trigger = triggers_temp; ptr_temp_trigger;
+ ptr_temp_trigger = ptr_temp_trigger->next_trigger)
+ {
+ if (strcmp (ptr_temp_trigger->name, trigger_name) == 0)
+ break;
+ }
+ if (!ptr_temp_trigger)
+ {
+ /* create new temporary trigger */
+ ptr_temp_trigger = trigger_alloc (trigger_name);
+ if (ptr_temp_trigger)
+ trigger_add (ptr_temp_trigger, &triggers_temp, &last_trigger_temp);
+ }
+
+ if (ptr_temp_trigger)
+ {
+ index_option = trigger_search_option (pos_option);
+ if (index_option >= 0)
+ {
+ trigger_config_create_option_temp (ptr_temp_trigger, index_option,
+ value);
+ }
+ else
+ {
+ weechat_printf (NULL,
+ _("%sWarning: unknown option for section \"%s\": "
+ "%s (value: \"%s\")"),
+ weechat_prefix ("error"),
+ TRIGGER_CONFIG_SECTION_TRIGGER,
+ option_name, value);
+ }
+ }
+
+ free (trigger_name);
+
+ return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
+}
+
+/*
+ * Reloads trigger configuration file.
+ */
+
+int
+trigger_config_reload_cb (void *data, struct t_config_file *config_file)
+{
+ int rc;
+
+ /* make C compiler happy */
+ (void) data;
+
+ trigger_free_all ();
+
+ rc = weechat_config_reload (config_file);
+
+ trigger_config_use_temp_triggers ();
+
+ return rc;
+}
+
+/*
+ * Initializes trigger configuration file.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+trigger_config_init ()
+{
+ struct t_config_section *ptr_section;
+
+ trigger_config_file = weechat_config_new (TRIGGER_CONFIG_NAME,
+ &trigger_config_reload_cb, NULL);
+ if (!trigger_config_file)
+ return 0;
+
+ /* look */
+ ptr_section = weechat_config_new_section (trigger_config_file, "look",
+ 0, 0,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL);
+ if (!ptr_section)
+ {
+ weechat_config_free (trigger_config_file);
+ return 0;
+ }
+
+ trigger_config_look_test = weechat_config_new_option (
+ trigger_config_file, ptr_section,
+ "test", "boolean",
+ "",
+ NULL, 0, 0, "on", NULL, 0, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+
+ /* color */
+ ptr_section = weechat_config_new_section (trigger_config_file, "color",
+ 0, 0,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL);
+ if (!ptr_section)
+ {
+ weechat_config_free (trigger_config_file);
+ return 0;
+ }
+
+ trigger_config_color_regex = weechat_config_new_option (
+ trigger_config_file, ptr_section,
+ "regex", "color",
+ N_("text color for regular expressions"),
+ NULL, 0, 0, "white", NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ trigger_config_color_replace = weechat_config_new_option (
+ trigger_config_file, ptr_section,
+ "replace", "color",
+ N_("text color for replacement text (for regular expressions)"),
+ NULL, 0, 0, "cyan", NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+
+ /* trigger */
+ ptr_section = weechat_config_new_section (trigger_config_file,
+ TRIGGER_CONFIG_SECTION_TRIGGER,
+ 0, 0,
+ &trigger_config_trigger_read_cb, NULL,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL);
+ if (!ptr_section)
+ {
+ weechat_config_free (trigger_config_file);
+ return 0;
+ }
+
+ trigger_config_section_trigger = ptr_section;
+
+ return 1;
+}
+
+/*
+ * Reads trigger configuration file.
+ */
+
+int
+trigger_config_read ()
+{
+ int rc;
+
+ rc = weechat_config_read (trigger_config_file);
+
+ trigger_config_use_temp_triggers ();
+
+ return rc;
+}
+
+/*
+ * Writes trigger configuration file.
+ */
+
+int
+trigger_config_write ()
+{
+ return weechat_config_write (trigger_config_file);
+}
+
+/*
+ * Frees trigger configuration.
+ */
+
+void
+trigger_config_free ()
+{
+ weechat_config_free (trigger_config_file);
+}
diff --git a/src/plugins/trigger/trigger-config.h b/src/plugins/trigger/trigger-config.h
new file mode 100644
index 000000000..a6517aa2d
--- /dev/null
+++ b/src/plugins/trigger/trigger-config.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __WEECHAT_TRIGGER_CONFIG_H
+#define __WEECHAT_TRIGGER_CONFIG_H 1
+
+#define TRIGGER_CONFIG_NAME "trigger"
+#define TRIGGER_CONFIG_SECTION_TRIGGER "trigger"
+
+extern struct t_config_file *trigger_config_file;
+extern struct t_config_section *trigger_config_section_trigger;
+
+extern struct t_config_option *trigger_config_color_regex;
+extern struct t_config_option *trigger_config_color_replace;
+
+extern struct t_config_option *trigger_config_create_option (const char *trigger_name,
+ int index_option,
+ const char *value);
+extern int trigger_config_init ();
+extern int trigger_config_read ();
+extern int trigger_config_write ();
+extern void trigger_config_free ();
+
+#endif /* __WEECHAT_TRIGGER_CONFIG_H */
diff --git a/src/plugins/trigger/trigger.c b/src/plugins/trigger/trigger.c
new file mode 100644
index 000000000..7efa1ace4
--- /dev/null
+++ b/src/plugins/trigger/trigger.c
@@ -0,0 +1,894 @@
+/*
+ * trigger.c - trigger plugin for WeeChat
+ *
+ * Copyright (C) 2014 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 <http://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_("Run actions 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);
+
+struct t_weechat_plugin *weechat_trigger_plugin = NULL;
+
+char *trigger_option_string[TRIGGER_NUM_OPTIONS] =
+{ "enabled", "hook", "arguments", "conditions", "regex", "command",
+ "return_code" };
+char *trigger_option_default[TRIGGER_NUM_OPTIONS] =
+{ "on", "signal", "", "", "", "", "ok" };
+
+char *trigger_hook_type_string[TRIGGER_NUM_HOOK_TYPES] =
+{ "signal", "hsignal", "modifier", "print", "timer" };
+
+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 };
+
+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 */
+
+
+/*
+ * 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 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 (strcmp (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 (strncmp (ptr_trigger->name, ptr_name, pos_option - ptr_name) == 0)
+ break;
+ }
+
+ return ptr_trigger;
+}
+
+/*
+ * Frees all the regex in a trigger.
+ */
+
+void
+trigger_free_regex (struct t_trigger *trigger)
+{
+ int i;
+
+ if (trigger->regex_count > 0)
+ {
+ for (i = 0; i < trigger->regex_count; i++)
+ {
+ if (trigger->regex[i].str_regex)
+ free (trigger->regex[i].str_regex);
+ if (trigger->regex[i].regex)
+ {
+ regfree (trigger->regex[i].regex);
+ free (trigger->regex[i].regex);
+ }
+ if (trigger->regex[i].replace)
+ free (trigger->regex[i].replace);
+ if (trigger->regex[i].replace_eval)
+ free (trigger->regex[i].replace_eval);
+ }
+ free (trigger->regex);
+ trigger->regex = NULL;
+ trigger->regex_count = 0;
+ }
+}
+
+/*
+ * Sets the regex and replacement text in a trigger.
+ */
+
+void
+trigger_set_regex (struct t_trigger *trigger)
+{
+ const char *option_regex, *pos, *pos2;
+ char *delimiter;
+ int i, length_delimiter, regex_count;
+
+ delimiter = NULL;
+
+ /* remove all regex in the trigger */
+ trigger_free_regex (trigger);
+
+ /* get regex option in trigger */
+ option_regex = weechat_config_string (trigger->options[TRIGGER_OPTION_REGEX]);
+ if (!option_regex || !option_regex[0])
+ goto end;
+
+ /* min 3 chars, for example: "/a/" */
+ if (strlen (option_regex) < 3)
+ goto format_error;
+
+ /* search the delimiter (which can be more than one char) */
+ pos = weechat_utf8_next_char (option_regex);
+ while (pos[0] && weechat_utf8_charcmp (option_regex, pos) == 0)
+ {
+ pos = weechat_utf8_next_char (pos);
+ }
+ if (!pos[0])
+ goto format_error;
+ delimiter = weechat_strndup (option_regex, pos - option_regex);
+ if (!delimiter)
+ goto memory_error;
+ if (strcmp (delimiter, "\\") == 0)
+ goto format_error;
+
+ length_delimiter = strlen (delimiter);
+
+ /* count the number of regex in the option */
+ regex_count = 0;
+ pos = option_regex;
+ while (pos && pos[0])
+ {
+ /*
+ * if option "regex" ends with a delimiter, just ignore it
+ * and exit the loop
+ */
+ pos += length_delimiter;
+ if (!pos[0])
+ break;
+
+ /* search the start of replacement string */
+ pos = strstr (pos + length_delimiter, delimiter);
+ if (!pos)
+ goto format_error;
+
+ regex_count++;
+
+ /* search the start of next regex */
+ pos = strstr (pos + length_delimiter, delimiter);
+ }
+
+ /* at least one regex is needed */
+ if (regex_count == 0)
+ goto format_error;
+
+ /* allocate with array of regex/replacement */
+ trigger->regex = malloc (regex_count * sizeof (trigger->regex[0]));
+ if (!trigger->regex)
+ goto memory_error;
+
+ /* initialize regex */
+ for (i = 0; i < trigger->regex_count; i++)
+ {
+ trigger->regex[i].str_regex = NULL;
+ trigger->regex[i].regex = NULL;
+ trigger->regex[i].replace = NULL;
+ trigger->regex[i].replace_eval = NULL;
+ }
+ trigger->regex_count = regex_count;
+
+ /* allocate regex and replacement */
+ i = 0;
+ pos = option_regex;
+ while (pos && pos[0])
+ {
+ pos += length_delimiter;
+ if (!pos[0])
+ break;
+
+ pos2 = strstr (pos + length_delimiter, delimiter);
+ if (!pos)
+ break;
+
+ trigger->regex[i].str_regex = weechat_strndup (pos, pos2 - pos);
+ if (!trigger->regex[i].str_regex)
+ goto memory_error;
+ trigger->regex[i].regex = malloc (sizeof (*trigger->regex[i].regex));
+ if (!trigger->regex[i].regex)
+ goto memory_error;
+ if (weechat_string_regcomp (trigger->regex[i].regex,
+ trigger->regex[i].str_regex,
+ REG_EXTENDED | REG_ICASE) != 0)
+ {
+ weechat_printf (NULL,
+ _("%s%s: error compiling regular expression \"%s\""),
+ weechat_prefix ("error"), TRIGGER_PLUGIN_NAME,
+ trigger->regex[i].str_regex);
+ free (trigger->regex[i].regex);
+ trigger->regex[i].regex = NULL;
+ goto end;
+ }
+
+ pos = pos2 + length_delimiter;
+
+ pos2 = strstr (pos + length_delimiter, delimiter);
+ trigger->regex[i].replace = (pos2) ?
+ weechat_strndup (pos, pos2 - pos) : strdup (pos);
+ if (!trigger->regex[i].replace)
+ goto memory_error;
+ trigger->regex[i].replace_eval =
+ weechat_string_eval_expression (trigger->regex[i].replace,
+ NULL, NULL, NULL);
+
+ pos = pos2;
+
+ i++;
+ }
+
+ goto end;
+
+format_error:
+ weechat_printf (NULL,
+ _("%s%s: invalid value for option \"replace\", format "
+ "is: \"/regex/replace\" (the char '/' can be "
+ "replaced by one or more identical chars, except '\\' "
+ "which is used for matching groups)"),
+ weechat_prefix ("error"), TRIGGER_PLUGIN_NAME);
+ trigger_free_regex (trigger);
+ goto end;
+
+memory_error:
+ weechat_printf (NULL,
+ _("%s%s: not enough memory"),
+ weechat_prefix ("error"), TRIGGER_PLUGIN_NAME);
+ trigger_free_regex (trigger);
+ goto end;
+
+end:
+ if (delimiter)
+ free (delimiter);
+}
+
+/*
+ * 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++)
+ {
+ 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, *tags, *message;
+ int i, argc, strip_colors;
+
+ 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);
+ }
+ }
+ }
+ 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);
+ }
+ }
+ }
+ 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);
+ }
+ }
+ }
+ 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 (1 * 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);
+ }
+ break;
+ case TRIGGER_HOOK_TIMER:
+ if (argv && (argc >= 1))
+ {
+ }
+ break;
+ }
+
+ if (!trigger->hooks)
+ {
+ weechat_printf (NULL,
+ _("%sError: unable to create hook for trigger \"%s\" "
+ "(bad arguments)"),
+ weechat_prefix ("error"), trigger->name);
+ }
+
+ if (argv)
+ weechat_string_free_split (argv);
+ if (argv_eol)
+ weechat_string_free_split (argv_eol);
+}
+
+/*
+ * 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_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->regex_count = 0;
+ new_trigger->regex = 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->prev_trigger = NULL;
+ new_trigger->next_trigger = NULL;
+
+ return new_trigger;
+}
+
+/*
+ * Adds trigger to the end of a linked list.
+ */
+
+void
+trigger_add (struct t_trigger *trigger,
+ struct t_trigger **triggers, struct t_trigger **last_trigger)
+{
+ trigger->prev_trigger = *last_trigger;
+ trigger->next_trigger = NULL;
+ if (!*triggers)
+ *triggers = trigger;
+ else
+ (*last_trigger)->next_trigger = trigger;
+ *last_trigger = trigger;
+
+ triggers_count++;
+}
+
+/*
+ * 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);
+
+ trigger_set_regex (new_trigger);
+
+ if (weechat_config_boolean (new_trigger->options[TRIGGER_OPTION_ENABLED]))
+ 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)
+{
+ struct t_config_option *option[TRIGGER_NUM_OPTIONS];
+ const char *value[TRIGGER_NUM_OPTIONS];
+ struct t_trigger *new_trigger;
+ int i;
+
+ /* it's not possible to create 2 triggers with same name */
+ if (trigger_search (name))
+ return NULL;
+
+ /* look for type */
+ if (trigger_search_hook_type (hook) < 0)
+ return NULL;
+
+ /* look for return code */
+ if (trigger_search_return_code (return_code) < 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;
+
+ for (i = 0; i < TRIGGER_NUM_OPTIONS; i++)
+ {
+ option[i] = trigger_config_create_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;
+}
+
+/*
+ * Renames a trigger.
+ */
+
+void
+trigger_rename (struct t_trigger *trigger, const char *name)
+{
+ int length, i;
+ char *option_name;
+
+ if (!name || !name[0])
+ return;
+
+ length = strlen (name) + 64;
+ option_name = malloc (length);
+ if (!option_name)
+ return;
+
+ 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);
+}
+
+/*
+ * 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_free_regex (trigger);
+ 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]);
+ }
+
+ 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 (" 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].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 (" 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 . . . . . : %lu", ptr_trigger->hook_count_cb);
+ weechat_log_printf (" hook_count_cmd. . . . . : %lu", 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 (" 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 (void *data, const char *signal, const char *type_data,
+ void *signal_data)
+{
+ /* make C compiler happy */
+ (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;
+
+ /* 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);
+
+ /* 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_config_write ();
+ trigger_config_free ();
+
+ trigger_callback_end ();
+
+ return WEECHAT_RC_OK;
+}
diff --git a/src/plugins/trigger/trigger.h b/src/plugins/trigger/trigger.h
new file mode 100644
index 000000000..5ddc81e8c
--- /dev/null
+++ b/src/plugins/trigger/trigger.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __WEECHAT_TRIGGER_H
+#define __WEECHAT_TRIGGER_H 1
+
+#include <regex.h>
+
+#define weechat_plugin weechat_trigger_plugin
+#define TRIGGER_PLUGIN_NAME "trigger"
+
+enum t_trigger_option
+{
+ TRIGGER_OPTION_ENABLED = 0, /* true if trigger is enabled */
+ TRIGGER_OPTION_HOOK, /* hook (signal, modifier, ...) */
+ TRIGGER_OPTION_ARGUMENTS, /* arguments for hook */
+ TRIGGER_OPTION_CONDITIONS, /* conditions for trigger */
+ TRIGGER_OPTION_REGEX, /* replace text with 1 or more regex */
+ TRIGGER_OPTION_COMMAND, /* command run if conditions are OK */
+ TRIGGER_OPTION_RETURN_CODE, /* return code for hook callback */
+ /* number of trigger options */
+ TRIGGER_NUM_OPTIONS,
+};
+
+enum t_trigger_hook_type
+{
+ TRIGGER_HOOK_SIGNAL = 0,
+ TRIGGER_HOOK_HSIGNAL,
+ TRIGGER_HOOK_MODIFIER,
+ TRIGGER_HOOK_PRINT,
+ TRIGGER_HOOK_TIMER,
+ /* number of hook types */
+ TRIGGER_NUM_HOOK_TYPES,
+};
+
+enum t_trigger_return_code
+{
+ TRIGGER_RC_OK = 0,
+ TRIGGER_RC_OK_EAT,
+ TRIGGER_RC_ERROR,
+ /* number of return codes */
+ TRIGGER_NUM_RETURN_CODES,
+};
+
+struct t_trigger_regex
+{
+ char *str_regex; /* regex to search for replacement */
+ regex_t *regex; /* compiled regex */
+ char *replace; /* replacement text */
+ char *replace_eval; /* evaluatued replacement text */
+};
+
+struct t_trigger
+{
+ /* user choices */
+ char *name; /* trigger name */
+ struct t_config_option *options[TRIGGER_NUM_OPTIONS];
+
+ /* internal vars */
+
+ /* regular expressions with their replacement text */
+ int regex_count; /* number of regex/replacement */
+ struct t_trigger_regex *regex; /* array of regex/replacement */
+
+ /* hooks */
+ int hooks_count; /* number of hooks */
+ struct t_hook **hooks; /* array of hooks (signal, ...) */
+ unsigned long hook_count_cb; /* number of calls made to callback */
+ unsigned long hook_count_cmd; /* number of commands run in callback*/
+ int hook_running; /* 1 if one hook callback is running */
+ char *hook_print_buffers; /* buffers (for hook_print only) */
+
+ /* links to other triggers */
+ struct t_trigger *prev_trigger; /* link to previous trigger */
+ struct t_trigger *next_trigger; /* link to next trigger */
+};
+
+extern struct t_weechat_plugin *weechat_trigger_plugin;
+extern char *trigger_option_string[];
+extern char *trigger_option_default[];
+extern char *trigger_hook_type_string[];
+extern int trigger_return_code[];
+extern struct t_trigger *triggers;
+extern struct t_trigger *last_trigger;
+extern int triggers_count;
+extern struct t_trigger *triggers_temp;
+extern struct t_trigger *last_trigger_temp;
+
+extern int trigger_search_option (const char *option_name);
+extern int trigger_search_hook_type (const char *type);
+extern struct t_trigger *trigger_search (const char *name);
+extern struct t_trigger *trigger_search_with_option (struct t_config_option *option);
+extern void trigger_set_regex (struct t_trigger *trigger);
+extern void trigger_unhook (struct t_trigger *trigger);
+extern void trigger_hook (struct t_trigger *trigger);
+extern struct t_trigger *trigger_alloc (const char *name);
+extern void trigger_add (struct t_trigger *trigger, struct t_trigger **triggers,
+ struct t_trigger **last_trigger);
+extern struct t_trigger *trigger_new_with_options (const char *name,
+ struct t_config_option **options);
+extern struct t_trigger *trigger_new (const char *name,
+ const char *enabled,
+ const char *hook,
+ const char *arguments,
+ const char *conditions,
+ const char *replace,
+ const char *command,
+ const char *return_code);
+extern void trigger_rename (struct t_trigger *trigger, const char *name);
+extern void trigger_free (struct t_trigger *trigger);
+extern void trigger_free_all ();
+
+#endif /* __WEECHAT_TRIGGER_H */
diff --git a/src/plugins/weechat-plugin.h b/src/plugins/weechat-plugin.h
index 87df384aa..9eaccb9ba 100644
--- a/src/plugins/weechat-plugin.h
+++ b/src/plugins/weechat-plugin.h
@@ -28,6 +28,7 @@ extern "C" {
#include <sys/types.h>
#include <sys/socket.h>
+#include <regex.h>
/* some systems like GNU/Hurd do not define PATH_MAX */
#ifndef PATH_MAX
@@ -57,7 +58,7 @@ struct timeval;
* please change the date with current one; for a second change at same
* date, increment the 01, otherwise please keep 01.
*/
-#define WEECHAT_PLUGIN_API_VERSION "20140122-01"
+#define WEECHAT_PLUGIN_API_VERSION "20140125-01"
/* macros for defining plugin infos */
#define WEECHAT_PLUGIN_NAME(__name) \
@@ -243,10 +244,12 @@ struct t_weechat_plugin
char *(*string_mask_to_regex) (const char *mask);
const char *(*string_regex_flags) (const char *regex, int default_flags,
int *flags);
- int (*string_regcomp) (void *preg, const char *regex, int default_flags);
+ int (*string_regcomp) (regex_t *preg, const char *regex, int default_flags);
int (*string_has_highlight) (const char *string,
const char *highlight_words);
int (*string_has_highlight_regex) (const char *string, const char *regex);
+ char *(*string_replace_regex) (const char *string, regex_t *regex,
+ const char *replace);
char **(*string_split) (const char *string, const char *separators,
int keep_eol, int num_items_max, int *num_items);
void (*string_free_split) (char **split_string);
@@ -1008,6 +1011,8 @@ extern int weechat_plugin_end (struct t_weechat_plugin *plugin);
weechat_plugin->string_has_highlight(__string, __highlight_words)
#define weechat_string_has_highlight_regex(__string, __regex) \
weechat_plugin->string_has_highlight_regex(__string, __regex)
+#define weechat_string_replace_regex(__string, __regex, __replace) \
+ weechat_plugin->string_replace_regex(__string, __regex, __replace)
#define weechat_string_split(__string, __separator, __eol, __max, \
__num_items) \
weechat_plugin->string_split(__string, __separator, __eol, \