summaryrefslogtreecommitdiff
path: root/src/plugins/exec
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/exec')
-rw-r--r--src/plugins/exec/CMakeLists.txt30
-rw-r--r--src/plugins/exec/Makefile.am40
-rw-r--r--src/plugins/exec/exec-buffer.c146
-rw-r--r--src/plugins/exec/exec-buffer.h27
-rw-r--r--src/plugins/exec/exec-command.c753
-rw-r--r--src/plugins/exec/exec-command.h46
-rw-r--r--src/plugins/exec/exec-completion.c73
-rw-r--r--src/plugins/exec/exec-completion.h25
-rw-r--r--src/plugins/exec/exec-config.c191
-rw-r--r--src/plugins/exec/exec-config.h42
-rw-r--r--src/plugins/exec/exec.c587
-rw-r--r--src/plugins/exec/exec.h79
12 files changed, 2039 insertions, 0 deletions
diff --git a/src/plugins/exec/CMakeLists.txt b/src/plugins/exec/CMakeLists.txt
new file mode 100644
index 000000000..a5286eabd
--- /dev/null
+++ b/src/plugins/exec/CMakeLists.txt
@@ -0,0 +1,30 @@
+#
+# 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(exec MODULE
+exec.c exec.h
+exec-buffer.c exec-buffer.h
+exec-command.c exec-command.h
+exec-completion.c exec-completion.h
+exec-config.c exec-config.h)
+SET_TARGET_PROPERTIES(exec PROPERTIES PREFIX "")
+
+TARGET_LINK_LIBRARIES(exec)
+
+INSTALL(TARGETS exec LIBRARY DESTINATION ${LIBDIR}/plugins)
diff --git a/src/plugins/exec/Makefile.am b/src/plugins/exec/Makefile.am
new file mode 100644
index 000000000..cad543311
--- /dev/null
+++ b/src/plugins/exec/Makefile.am
@@ -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/>.
+#
+
+AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/locale\" $(EXEC_CFLAGS)
+
+libdir = ${weechat_libdir}/plugins
+
+lib_LTLIBRARIES = exec.la
+
+exec_la_SOURCES = exec.c \
+ exec.h \
+ exec-buffer.c \
+ exec-buffer.h \
+ exec-command.c \
+ exec-command.h \
+ exec-completion.c \
+ exec-completion.h \
+ exec-config.c \
+ exec-config.h
+
+exec_la_LDFLAGS = -module -no-undefined
+exec_la_LIBADD = $(EXEC_LFLAGS)
+
+EXTRA_DIST = CMakeLists.txt
diff --git a/src/plugins/exec/exec-buffer.c b/src/plugins/exec/exec-buffer.c
new file mode 100644
index 000000000..1a4019fde
--- /dev/null
+++ b/src/plugins/exec/exec-buffer.c
@@ -0,0 +1,146 @@
+/*
+ * exec-buffer.c - buffers with output of 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 "exec.h"
+#include "exec-buffer.h"
+#include "exec-command.h"
+#include "exec-config.h"
+
+
+/*
+ * Callback for user data in an exec buffer.
+ */
+
+int
+exec_buffer_input_cb (void *data, struct t_gui_buffer *buffer,
+ const char *input_data)
+{
+ char **argv, **argv_eol;
+ int argc;
+
+ /* make C compiler happy */
+ (void) data;
+
+ /* close buffer */
+ if (strcmp (input_data, "q") == 0)
+ {
+ weechat_buffer_close (buffer);
+ return WEECHAT_RC_OK;
+ }
+
+ argv = weechat_string_split (input_data, " ", 0, 0, &argc);
+ argv_eol = weechat_string_split (input_data, " ", 1, 0, NULL);
+
+ if (argv && argv_eol)
+ exec_command_run (buffer, argc, argv, argv_eol, 0);
+
+ if (argv)
+ weechat_string_free_split (argv);
+ if (argv_eol)
+ weechat_string_free_split (argv_eol);
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Callback called when an exec buffer is closed.
+ */
+
+int
+exec_buffer_close_cb (void *data, struct t_gui_buffer *buffer)
+{
+ /* make C compiler happy */
+ (void) data;
+ (void) buffer;
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Restore buffer callbacks (input and close) for buffers created by exec
+ * plugin.
+ */
+
+void
+exec_buffer_set_callbacks ()
+{
+ struct t_infolist *ptr_infolist;
+ struct t_gui_buffer *ptr_buffer;
+ const char *plugin_name;
+
+ ptr_infolist = weechat_infolist_get ("buffer", NULL, "");
+ if (ptr_infolist)
+ {
+ while (weechat_infolist_next (ptr_infolist))
+ {
+ ptr_buffer = weechat_infolist_pointer (ptr_infolist, "pointer");
+ plugin_name = weechat_infolist_string (ptr_infolist, "plugin_name");
+ if (ptr_buffer && plugin_name
+ && (strcmp (plugin_name, EXEC_PLUGIN_NAME) == 0))
+ {
+ weechat_buffer_set_pointer (ptr_buffer, "close_callback",
+ &exec_buffer_close_cb);
+ weechat_buffer_set_pointer (ptr_buffer, "input_callback",
+ &exec_buffer_input_cb);
+ }
+ }
+ weechat_infolist_free (ptr_infolist);
+ }
+}
+
+/*
+ * Creates a new exec buffer for a command.
+ */
+
+struct t_gui_buffer *
+exec_buffer_new (const char *name, int switch_to_buffer)
+{
+ struct t_gui_buffer *new_buffer;
+
+ new_buffer = weechat_buffer_search (EXEC_PLUGIN_NAME, name);
+ if (new_buffer)
+ goto end;
+
+ new_buffer = weechat_buffer_new (name,
+ &exec_buffer_input_cb, NULL,
+ &exec_buffer_close_cb, NULL);
+
+ /* failed to create buffer ? then return */
+ if (!new_buffer)
+ return NULL;
+
+ weechat_buffer_set (new_buffer, "title", _("Executed commands"));
+ weechat_buffer_set (new_buffer, "localvar_set_type", "exec");
+ weechat_buffer_set (new_buffer, "localvar_set_no_log", "1");
+ weechat_buffer_set (new_buffer, "time_for_each_line", "0");
+ weechat_buffer_set (new_buffer, "input_get_unknown_commands", "0");
+
+end:
+ if (switch_to_buffer)
+ weechat_buffer_set (new_buffer, "display", "1");
+
+ return new_buffer;
+}
diff --git a/src/plugins/exec/exec-buffer.h b/src/plugins/exec/exec-buffer.h
new file mode 100644
index 000000000..42e519888
--- /dev/null
+++ b/src/plugins/exec/exec-buffer.h
@@ -0,0 +1,27 @@
+/*
+ * 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_EXEC_BUFFER_H
+#define __WEECHAT_EXEC_BUFFER_H 1
+
+extern void exec_buffer_set_callbacks ();
+extern struct t_gui_buffer *exec_buffer_new (const char *name,
+ int switch_to_buffer);
+
+#endif /* __WEECHAT_EXEC_BUFFER_H */
diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c
new file mode 100644
index 000000000..09230c195
--- /dev/null
+++ b/src/plugins/exec/exec-command.c
@@ -0,0 +1,753 @@
+/*
+ * exec-command.c - exec 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 <string.h>
+#include <time.h>
+
+#include "../weechat-plugin.h"
+#include "exec.h"
+#include "exec-buffer.h"
+#include "exec-command.h"
+#include "exec-config.h"
+
+
+/*
+ * Displays a list of executed commands.
+ */
+
+void
+exec_command_list ()
+{
+ struct t_exec_cmd *ptr_exec_cmd;
+ char str_elapsed[32], str_time1[256], str_time2[256];
+ time_t elapsed_time;
+ struct tm *local_time;
+
+ weechat_printf (NULL, "");
+
+ if (!exec_cmds)
+ {
+ weechat_printf (NULL, _("No command is running"));
+ return;
+ }
+
+ weechat_printf (NULL, _("Commands:"));
+
+ for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd;
+ ptr_exec_cmd = ptr_exec_cmd->next_cmd)
+ {
+ elapsed_time = (ptr_exec_cmd->end_time == 0) ?
+ time (NULL) - ptr_exec_cmd->start_time :
+ ptr_exec_cmd->end_time - ptr_exec_cmd->start_time;
+ if (elapsed_time >= 3600)
+ {
+ snprintf (str_elapsed, sizeof (str_elapsed),
+ /* TRANSLATORS: format: hours + minutes, for example: 3h59 */
+ _("%dh%02d"),
+ elapsed_time / 3600,
+ elapsed_time % 3600);
+ }
+ else if (elapsed_time >= 60)
+ {
+ snprintf (str_elapsed, sizeof (str_elapsed),
+ /* TRANSLATORS: format: minutes + seconds, for example: 3m59 */
+ _("%dm%02d"),
+ elapsed_time / 60,
+ elapsed_time % 60);
+ }
+ else
+ {
+ snprintf (str_elapsed, sizeof (str_elapsed),
+ /* TRANSLATORS: format: seconds, for example: 59s */
+ _("%ds"),
+ elapsed_time);
+ }
+ if (ptr_exec_cmd->end_time == 0)
+ {
+ /* running command */
+ weechat_printf (NULL,
+ /* TRANSLATORS: %s before "ago" is elapsed time, for example: "3m59" */
+ _(" %s%s%s %d%s%s%s: %s\"%s%s%s\"%s (pid: %d, "
+ "started %s ago)"),
+ weechat_color (weechat_config_string (exec_config_color_flag_running)),
+ ">>",
+ weechat_color ("reset"),
+ ptr_exec_cmd->number,
+ (ptr_exec_cmd->name) ? " (" : "",
+ (ptr_exec_cmd->name) ? ptr_exec_cmd->name : "",
+ (ptr_exec_cmd->name) ? ")" : "",
+ weechat_color ("chat_delimiters"),
+ weechat_color ("reset"),
+ ptr_exec_cmd->command,
+ weechat_color ("chat_delimiters"),
+ weechat_color ("reset"),
+ ptr_exec_cmd->pid,
+ str_elapsed);
+ }
+ else
+ {
+ /* process has ended */
+ local_time = localtime (&ptr_exec_cmd->start_time);
+ strftime (str_time1, sizeof (str_time1),
+ "%Y-%m-%d %H:%M:%S", local_time);
+ local_time = localtime (&ptr_exec_cmd->end_time);
+ strftime (str_time2, sizeof (str_time2),
+ "%Y-%m-%d %H:%M:%S", local_time);
+ weechat_printf (NULL,
+ " %s%s%s %d%s%s%s: %s\"%s%s%s\"%s (%s -> %s, %s)",
+ weechat_color (weechat_config_string (exec_config_color_flag_finished)),
+ "[]",
+ weechat_color ("reset"),
+ ptr_exec_cmd->number,
+ (ptr_exec_cmd->name) ? " (" : "",
+ (ptr_exec_cmd->name) ? ptr_exec_cmd->name : "",
+ (ptr_exec_cmd->name) ? ")" : "",
+ weechat_color ("chat_delimiters"),
+ weechat_color ("reset"),
+ ptr_exec_cmd->command,
+ weechat_color ("chat_delimiters"),
+ weechat_color ("reset"),
+ str_time1,
+ str_time2,
+ str_elapsed);
+ }
+ }
+}
+
+/*
+ * Searches a running command by id, and displays an error if command is not
+ * found or not running any more.
+ *
+ * Returns the command found, or NULL if not found or not running.
+ */
+
+struct t_exec_cmd *
+exec_command_search_running_id (const char *id)
+{
+ struct t_exec_cmd *ptr_exec_cmd;
+
+ ptr_exec_cmd = exec_search_by_id (id);
+ if (!ptr_exec_cmd)
+ {
+ weechat_printf (NULL, _("%s%s: command id \"%s\" not found"),
+ weechat_prefix ("error"), EXEC_PLUGIN_NAME, id);
+ return NULL;
+ }
+
+ if (!ptr_exec_cmd->hook)
+ {
+ weechat_printf (NULL,
+ _("%s%s: command with id \"%s\" is not running any "
+ "more"),
+ weechat_prefix ("error"), EXEC_PLUGIN_NAME, id);
+ return NULL;
+ }
+
+ return ptr_exec_cmd;
+}
+
+/*
+ * Parse command options.
+ *
+ * Returns:
+ * 1: options parsed successfully
+ * 0: error parsing options
+ */
+
+int
+exec_command_parse_options (struct t_exec_cmd_options *cmd_options,
+ int argc, char **argv, int start_arg,
+ int set_command_index)
+{
+ int i;
+ char *error;
+
+ for (i = start_arg; i < argc; i++)
+ {
+ if (weechat_strcasecmp (argv[i], "-sh") == 0)
+ {
+ cmd_options->use_shell = 1;
+ }
+ else if (weechat_strcasecmp (argv[i], "-nosh") == 0)
+ {
+ cmd_options->use_shell = 0;
+ }
+ else if (weechat_strcasecmp (argv[i], "-bg") == 0)
+ {
+ cmd_options->detached = 1;
+ }
+ else if (weechat_strcasecmp (argv[i], "-nobg") == 0)
+ {
+ cmd_options->detached = 0;
+ }
+ else if (weechat_strcasecmp (argv[i], "-stdin") == 0)
+ {
+ cmd_options->pipe_stdin = 1;
+ }
+ else if (weechat_strcasecmp (argv[i], "-nostdin") == 0)
+ {
+ cmd_options->pipe_stdin = 0;
+ }
+ else if (weechat_strcasecmp (argv[i], "-buffer") == 0)
+ {
+ if (i + 1 >= argc)
+ return 0;
+ i++;
+ cmd_options->ptr_buffer_name = argv[i];
+ cmd_options->ptr_buffer = weechat_buffer_search ("==", argv[i]);
+ if (cmd_options->ptr_buffer
+ && (weechat_buffer_get_integer (cmd_options->ptr_buffer, "type") != 0))
+ {
+ /* only a buffer with formatted content is allowed */
+ return 0;
+ }
+ if (!cmd_options->ptr_buffer)
+ cmd_options->new_buffer = 1;
+ }
+ else if (weechat_strcasecmp (argv[i], "-l") == 0)
+ {
+ cmd_options->output_to_buffer = 0;
+ cmd_options->new_buffer = 0;
+ }
+ else if (weechat_strcasecmp (argv[i], "-o") == 0)
+ {
+ cmd_options->output_to_buffer = 1;
+ cmd_options->new_buffer = 0;
+ }
+ else if (weechat_strcasecmp (argv[i], "-n") == 0)
+ {
+ cmd_options->output_to_buffer = 0;
+ cmd_options->new_buffer = 1;
+ }
+ else if (weechat_strcasecmp (argv[i], "-sw") == 0)
+ {
+ cmd_options->switch_to_buffer = 1;
+ }
+ else if (weechat_strcasecmp (argv[i], "-nosw") == 0)
+ {
+ cmd_options->switch_to_buffer = 0;
+ }
+ else if (weechat_strcasecmp (argv[i], "-ln") == 0)
+ {
+ cmd_options->line_numbers = 1;
+ }
+ else if (weechat_strcasecmp (argv[i], "-noln") == 0)
+ {
+ cmd_options->line_numbers = 0;
+ }
+ else if (weechat_strcasecmp (argv[i], "-color") == 0)
+ {
+ if (i + 1 >= argc)
+ return 0;
+ i++;
+ cmd_options->color = exec_search_color (argv[i]);
+ if (cmd_options->color < 0)
+ return 0;
+ }
+ else if (weechat_strcasecmp (argv[i], "-rc") == 0)
+ {
+ cmd_options->display_rc = 1;
+ }
+ else if (weechat_strcasecmp (argv[i], "-norc") == 0)
+ {
+ cmd_options->display_rc = 0;
+ }
+ else if (weechat_strcasecmp (argv[i], "-timeout") == 0)
+ {
+ if (i + 1 >= argc)
+ return 0;
+ i++;
+ error = NULL;
+ cmd_options->timeout = strtol (argv[i], &error, 10);
+ if (!error || error[0])
+ return 0;
+ }
+ else if (weechat_strcasecmp (argv[i], "-name") == 0)
+ {
+ if (i + 1 >= argc)
+ return 0;
+ i++;
+ cmd_options->ptr_command_name = argv[i];
+ }
+ else
+ {
+ if (set_command_index)
+ {
+ cmd_options->command_index = i;
+ break;
+ }
+ else
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Runs a command.
+ *
+ * Returns:
+ * WEECHAT_RC_OK: command run successfully
+ * WEECHAT_RC_ERROR: error running command
+ */
+
+int
+exec_command_run (struct t_gui_buffer *buffer,
+ int argc, char **argv, char **argv_eol, int start_arg)
+{
+ char str_buffer[512];
+ struct t_exec_cmd *new_exec_cmd;
+ struct t_exec_cmd_options cmd_options;
+ struct t_hashtable *process_options;
+ struct t_infolist *ptr_infolist;
+ struct t_gui_buffer *new_buffer;
+
+ /* parse command options */
+ cmd_options.command_index = -1;
+ cmd_options.use_shell = 1;
+ cmd_options.detached = 0;
+ cmd_options.pipe_stdin = 0;
+ cmd_options.timeout = 0;
+ cmd_options.ptr_buffer_name = NULL;
+ cmd_options.ptr_buffer = buffer;
+ cmd_options.output_to_buffer = 0;
+ cmd_options.new_buffer = 0;
+ cmd_options.switch_to_buffer = 1;
+ cmd_options.line_numbers = -1;
+ cmd_options.color = EXEC_COLOR_DECODE;
+ cmd_options.display_rc = 1;
+ cmd_options.ptr_command_name = NULL;
+
+ /* parse default options */
+ if (!exec_command_parse_options (&cmd_options,
+ exec_config_cmd_num_options,
+ exec_config_cmd_options,
+ 0, 0))
+ {
+ weechat_printf (NULL,
+ _("%s%s: invalid options in option "
+ "exec.command.default_options"),
+ weechat_prefix ("error"), EXEC_PLUGIN_NAME);
+ return WEECHAT_RC_ERROR;
+ }
+ if (!exec_command_parse_options (&cmd_options, argc, argv, start_arg, 1))
+ return WEECHAT_RC_ERROR;
+
+ /* options "-bg" and "-o"/"-n" are incompatible */
+ if (cmd_options.detached
+ && (cmd_options.output_to_buffer || cmd_options.new_buffer))
+ return WEECHAT_RC_ERROR;
+
+ /* command not found? */
+ if (cmd_options.command_index < 0)
+ return WEECHAT_RC_ERROR;
+
+ new_exec_cmd = exec_add ();
+ if (!new_exec_cmd)
+ return WEECHAT_RC_ERROR;
+
+ /* create hashtable for weechat_hook_process_hashtable() */
+ process_options = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL,
+ NULL);
+ if (!process_options)
+ {
+ exec_free (new_exec_cmd);
+ return WEECHAT_RC_ERROR;
+ }
+ /* automatically disable shell if we are downloading an URL */
+ if (strncmp (argv_eol[cmd_options.command_index], "url:", 4) == 0)
+ cmd_options.use_shell = 0;
+ if (cmd_options.use_shell)
+ {
+ /* command will be: sh -c "command arguments..." */
+ weechat_hashtable_set (process_options, "arg1", "-c");
+ weechat_hashtable_set (process_options, "arg2",
+ argv_eol[cmd_options.command_index]);
+ }
+ if (cmd_options.pipe_stdin)
+ weechat_hashtable_set (process_options, "stdin", "1");
+ if (cmd_options.detached)
+ weechat_hashtable_set (process_options, "detached", "1");
+
+ /* set variables in new command (before running the command) */
+ new_exec_cmd->name = (cmd_options.ptr_command_name) ?
+ strdup (cmd_options.ptr_command_name) : NULL;
+ new_exec_cmd->command = strdup (argv_eol[cmd_options.command_index]);
+ new_exec_cmd->detached = cmd_options.detached;
+ if (cmd_options.ptr_buffer_name && !cmd_options.ptr_buffer)
+ {
+ /* output in a new buffer using given name */
+ new_exec_cmd->output_to_buffer = 0;
+ snprintf (str_buffer, sizeof (str_buffer),
+ "exec.%s", cmd_options.ptr_buffer_name);
+ new_buffer = exec_buffer_new (str_buffer, cmd_options.switch_to_buffer);
+ if (new_buffer)
+ {
+ new_exec_cmd->buffer_full_name =
+ strdup (weechat_buffer_get_string (new_buffer, "full_name"));
+ }
+ }
+ else if (cmd_options.new_buffer)
+ {
+ /* output in a new buffer using automatic name */
+ if (new_exec_cmd->name)
+ {
+ snprintf (str_buffer, sizeof (str_buffer),
+ "exec.%s", new_exec_cmd->name);
+ }
+ else
+ {
+ snprintf (str_buffer, sizeof (str_buffer),
+ "exec.%d", new_exec_cmd->number);
+ }
+ new_buffer = exec_buffer_new (str_buffer, cmd_options.switch_to_buffer);
+ if (new_buffer)
+ {
+ new_exec_cmd->buffer_full_name =
+ strdup (weechat_buffer_get_string (new_buffer, "full_name"));
+ }
+ }
+ else if (cmd_options.ptr_buffer)
+ {
+ new_exec_cmd->buffer_full_name =
+ strdup (weechat_buffer_get_string (cmd_options.ptr_buffer,
+ "full_name"));
+ if (cmd_options.switch_to_buffer)
+ weechat_buffer_set (cmd_options.ptr_buffer, "display", "1");
+ }
+ if (cmd_options.ptr_buffer
+ && (strcmp (weechat_buffer_get_string (cmd_options.ptr_buffer, "plugin"),
+ EXEC_PLUGIN_NAME) == 0))
+ {
+ cmd_options.output_to_buffer = 0;
+ cmd_options.new_buffer = 1;
+ }
+ new_exec_cmd->output_to_buffer = cmd_options.output_to_buffer;
+ new_exec_cmd->line_numbers = (cmd_options.line_numbers < 0) ?
+ cmd_options.new_buffer : cmd_options.line_numbers;
+ new_exec_cmd->color = cmd_options.color;
+ new_exec_cmd->display_rc = cmd_options.display_rc;
+
+ /* execute the command */
+ if (weechat_exec_plugin->debug >= 1)
+ {
+ weechat_printf (NULL, "%s: executing command: \"%s%s%s\"",
+ EXEC_PLUGIN_NAME,
+ (cmd_options.use_shell) ? "" : "sh -c '",
+ argv_eol[cmd_options.command_index],
+ (cmd_options.use_shell) ? "" : "'");
+ }
+ new_exec_cmd->hook = weechat_hook_process_hashtable (
+ (cmd_options.use_shell) ? "sh" : argv_eol[cmd_options.command_index],
+ process_options,
+ cmd_options.timeout * 1000,
+ &exec_process_cb,
+ new_exec_cmd);
+
+ if (new_exec_cmd->hook)
+ {
+ /* get PID of command */
+ ptr_infolist = weechat_infolist_get ("hook", new_exec_cmd->hook, NULL);
+ if (ptr_infolist)
+ {
+ if (weechat_infolist_next (ptr_infolist))
+ {
+ new_exec_cmd->pid = weechat_infolist_integer (ptr_infolist,
+ "child_pid");
+ }
+ weechat_infolist_free (ptr_infolist);
+ }
+ }
+ else
+ {
+ exec_free (new_exec_cmd);
+ weechat_printf (NULL,
+ _("%s%s: failed to run command \"%s\""),
+ weechat_prefix ("error"), EXEC_PLUGIN_NAME,
+ argv_eol[cmd_options.command_index]);
+ }
+
+ weechat_hashtable_free (process_options);
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Callback for command "/exec": manage executed commands.
+ */
+
+int
+exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc,
+ char **argv, char **argv_eol)
+{
+ int i, length, count;
+ char *text;
+ struct t_exec_cmd *ptr_exec_cmd, *ptr_next_exec_cmd;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) buffer;
+
+ /* list running commands */
+ if ((argc == 1)
+ || ((argc == 2) && (weechat_strcasecmp (argv[1], "-list") == 0)))
+ {
+ exec_command_list ();
+ return WEECHAT_RC_OK;
+ }
+
+ /* send text to a running process */
+ if (weechat_strcasecmp (argv[1], "-in") == 0)
+ {
+ if (argc < 4)
+ return WEECHAT_RC_ERROR;
+ ptr_exec_cmd = exec_command_search_running_id (argv[2]);
+ if (ptr_exec_cmd && ptr_exec_cmd->hook)
+ {
+ length = strlen (argv_eol[3]) + 1 + 1;
+ text = malloc (length);
+ if (text)
+ {
+ snprintf (text, length, "%s\n", argv_eol[3]);
+ weechat_hook_set (ptr_exec_cmd->hook, "stdin", text);
+ free (text);
+ }
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ /* send text to a running process (if given), then close stdin */
+ if (weechat_strcasecmp (argv[1], "-inclose") == 0)
+ {
+ if (argc < 3)
+ return WEECHAT_RC_ERROR;
+ ptr_exec_cmd = exec_command_search_running_id (argv[2]);
+ if (ptr_exec_cmd && ptr_exec_cmd->hook)
+ {
+ if (argc > 3)
+ {
+ length = strlen (argv_eol[3]) + 1 + 1;
+ text = malloc (length);
+ if (text)
+ {
+ snprintf (text, length, "%s\n", argv_eol[3]);
+ weechat_hook_set (ptr_exec_cmd->hook, "stdin", text);
+ free (text);
+ }
+ }
+ weechat_hook_set (ptr_exec_cmd->hook, "stdin_close", "1");
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ /* send a signal to a running process */
+ if (weechat_strcasecmp (argv[1], "-signal") == 0)
+ {
+ if (argc < 4)
+ return WEECHAT_RC_ERROR;
+ ptr_exec_cmd = exec_command_search_running_id (argv[2]);
+ if (ptr_exec_cmd)
+ weechat_hook_set (ptr_exec_cmd->hook, "signal", argv[3]);
+ return WEECHAT_RC_OK;
+ }
+
+ /* send a KILL signal to a running process */
+ if (weechat_strcasecmp (argv[1], "-kill") == 0)
+ {
+ if (argc < 3)
+ return WEECHAT_RC_ERROR;
+ ptr_exec_cmd = exec_command_search_running_id (argv[2]);
+ if (ptr_exec_cmd)
+ weechat_hook_set (ptr_exec_cmd->hook, "signal", "kill");
+ return WEECHAT_RC_OK;
+ }
+
+ /* send a KILL signal to all running processes */
+ if (weechat_strcasecmp (argv[1], "-killall") == 0)
+ {
+ for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd;
+ ptr_exec_cmd = ptr_exec_cmd->next_cmd)
+ {
+ if (ptr_exec_cmd->hook)
+ {
+ weechat_hook_set (ptr_exec_cmd->hook, "signal", "kill");
+ }
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ /* set a hook property */
+ if (weechat_strcasecmp (argv[1], "-set") == 0)
+ {
+ if (argc < 5)
+ return WEECHAT_RC_ERROR;
+ ptr_exec_cmd = exec_command_search_running_id (argv[2]);
+ if (ptr_exec_cmd)
+ weechat_hook_set (ptr_exec_cmd->hook, argv[3], argv_eol[4]);
+ return WEECHAT_RC_OK;
+ }
+
+ /* delete terminated command(s) */
+ if (weechat_strcasecmp (argv[1], "-del") == 0)
+ {
+ if (argc < 3)
+ return WEECHAT_RC_ERROR;
+ if (weechat_strcasecmp (argv[2], "-all") == 0)
+ {
+ count = 0;
+ ptr_exec_cmd = exec_cmds;
+ while (ptr_exec_cmd)
+ {
+ ptr_next_exec_cmd = ptr_exec_cmd->next_cmd;
+ if (!ptr_exec_cmd->hook)
+ {
+ exec_free (ptr_exec_cmd);
+ count++;
+ }
+ ptr_exec_cmd = ptr_next_exec_cmd;
+ }
+ weechat_printf (NULL, _("%d commands removed"), count);
+ }
+ else
+ {
+ for (i = 2; i < argc; i++)
+ {
+ ptr_exec_cmd = exec_search_by_id (argv[i]);
+ if (ptr_exec_cmd)
+ {
+ if (ptr_exec_cmd->hook)
+ {
+ weechat_printf (NULL,
+ _("%s%s: command with id \"%s\" is still "
+ "running"),
+ weechat_prefix ("error"), EXEC_PLUGIN_NAME,
+ argv[i]);
+ }
+ else
+ {
+ exec_free (ptr_exec_cmd);
+ weechat_printf (NULL,
+ _("Command \"%s\" removed"), argv[i]);
+ }
+ }
+ else
+ {
+ weechat_printf (NULL,
+ _("%s%s: command id \"%s\" not found"),
+ weechat_prefix ("error"), EXEC_PLUGIN_NAME,
+ argv[i]);
+ }
+ }
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ return exec_command_run (buffer, argc, argv, argv_eol, 1);
+}
+
+/*
+ * Hooks exec commands.
+ */
+
+void
+exec_command_init ()
+{
+ weechat_hook_command (
+ "exec",
+ N_("execute external commands"),
+ N_("-list"
+ " || [-sh|-nosh] [-bg|-nobg] [-stdin|-nostdin] [-buffer <name>] "
+ "[-l|-o|-n] |-sw|-nosw] [-ln|-noln] [-color off|decode|strip] "
+ "[-rc|-norc] [-timeout <timeout>] [-name <name>] <command>"
+ " || -in <id> <text>"
+ " || -inclose <id> [<text>]"
+ " || -signal <id> <signal>"
+ " || -kill <id>"
+ " || -killall"
+ " || -set <id> <property> <value>"
+ " || -del <id>|-all [<id>...]"),
+ N_(" -list: list commands\n"
+ " -sh: use the shell to execute the command (default)\n"
+ " -nosh: do not use the shell to execute the command (required if "
+ "the command has some unsafe data, for example the content of a "
+ "message from another user)\n"
+ " -bg: run process in background: do not display process output "
+ "neither return code (not compatible with option -o/-n)\n"
+ " -nobg: catch process output and display return code (default)\n"
+ " -stdin: create a pipe for sending data to the process (with "
+ "/exec -in)\n"
+ "-nostdin: do not create a pipe for stdin (default)\n"
+ " -buffer: display/send output of command on this buffer (if the "
+ "buffer is not found, a new buffer with name \"exec.exec.xxx\" is "
+ "created)\n"
+ " -l: display locally output of command on buffer (default)\n"
+ " -o: send output of command to the buffer "
+ "(not compatible with option -bg)\n"
+ " -n: display output of command in a new buffer (not compatible "
+ "with -bg)\n"
+ " -sw: switch to the output buffer (default)\n"
+ " -nosw: don't switch to the output buffer\n"
+ " -ln: display line numbers (default in new buffer only)\n"
+ " -noln: don't display line numbers\n"
+ " -color: action on ANSI colors in output:\n"
+ " off: keep ANSI codes as-is\n"
+ " decode: convert ANSI colors to WeeChat/IRC (default)\n"
+ " strip: remove ANSI colors\n"
+ " -rc: display return code (default)\n"
+ " -norc: don't display return code\n"
+ "-timeout: set a timeout for the command (in seconds)\n"
+ " -name: set a name for the command (to name it later with /exec)\n"
+ " command: the command to execute; if beginning with \"url:\", the "
+ "shell is disabled and the content of URL is downloaded and sent as "
+ "output\n"
+ " id: command identifier: either its number or name (if set "
+ "with \"-name xxx\")\n"
+ " -in: send text on standard input of process\n"
+ "-inclose: same a -in, but stdin is closed after (and text is "
+ "optional: without text, the stdin is just closed)\n"
+ " -signal: send a signal to the process; the signal can be an integer "
+ "or one of these names: hup, int, quit, kill, term, usr1, usr2\n"
+ " -kill: alias of \"-signal <id> kill\"\n"
+ "-killall: kill all running processes\n"
+ " -set: set a hook property (see function hook_set in plugin API "
+ "reference)\n"
+ "property: hook property\n"
+ " value: new value for hook property\n"
+ " -del: delete a terminated command\n"
+ " -all: delete all terminated commands\n"
+ "\n"
+ "Default options can be set in the option "
+ "exec.command.default_options."),
+ "-list"
+ " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-buffer|-l|-o|-n|-sw|-nosw|"
+ "-ln|-noln|-color|-timeout|-name|%*"
+ " || -in|-inclose|-signal|-kill %(exec_commands_ids)"
+ " || -killall"
+ " || -set %(exec_commands_ids) stdin|stdin_close|signal"
+ " || -del %(exec_commands_ids)|-all %(exec_commands_ids)|%*",
+ &exec_command_exec, NULL);
+}
diff --git a/src/plugins/exec/exec-command.h b/src/plugins/exec/exec-command.h
new file mode 100644
index 000000000..252350ea1
--- /dev/null
+++ b/src/plugins/exec/exec-command.h
@@ -0,0 +1,46 @@
+/*
+ * 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_EXEC_COMMAND_H
+#define __WEECHAT_EXEC_COMMAND_H 1
+
+struct t_exec_cmd_options
+{
+ int command_index; /* index of command in arguments */
+ int use_shell; /* 1 to use shell (sh -c "command") */
+ int detached; /* 1 if detached (no output) */
+ int pipe_stdin; /* 1 to create a pipe for stdin */
+ int timeout; /* timeout (in seconds) */
+ const char *ptr_buffer_name; /* name of buffer */
+ struct t_gui_buffer *ptr_buffer; /* pointer to buffer */
+ int output_to_buffer; /* 1 if output is sent to buffer */
+ int new_buffer; /* output in a new buffer */
+ int switch_to_buffer; /* switch to the output buffer */
+ int line_numbers; /* 1 to display line numbers */
+ int color; /* what to do with ANSI colors */
+ int display_rc; /* 1 to display return code */
+ const char *ptr_command_name; /* name of command */
+};
+
+extern int exec_command_run (struct t_gui_buffer *buffer,
+ int argc, char **argv, char **argv_eol,
+ int start_arg);
+extern void exec_command_init ();
+
+#endif /* __WEECHAT_EXEC_COMMAND_H */
diff --git a/src/plugins/exec/exec-completion.c b/src/plugins/exec/exec-completion.c
new file mode 100644
index 000000000..80333279e
--- /dev/null
+++ b/src/plugins/exec/exec-completion.c
@@ -0,0 +1,73 @@
+/*
+ * exec-completion.c - completion for exec 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 "exec.h"
+
+
+/*
+ * Adds executed commands ids to completion list.
+ */
+
+int
+exec_completion_commands_ids_cb (void *data, const char *completion_item,
+ struct t_gui_buffer *buffer,
+ struct t_gui_completion *completion)
+{
+ struct t_exec_cmd *ptr_exec_cmd;
+ char str_number[32];
+
+ /* make C compiler happy */
+ (void) data;
+ (void) completion_item;
+ (void) buffer;
+
+ for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd;
+ ptr_exec_cmd = ptr_exec_cmd->next_cmd)
+ {
+ snprintf (str_number, sizeof (str_number), "%d", ptr_exec_cmd->number);
+ weechat_hook_completion_list_add (completion, str_number,
+ 0, WEECHAT_LIST_POS_SORT);
+ if (ptr_exec_cmd->name)
+ {
+ weechat_hook_completion_list_add (completion, ptr_exec_cmd->name,
+ 0, WEECHAT_LIST_POS_SORT);
+ }
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Hooks completions.
+ */
+
+void
+exec_completion_init ()
+{
+ weechat_hook_completion ("exec_commands_ids",
+ N_("ids (numbers and names) of executed commands"),
+ &exec_completion_commands_ids_cb, NULL);
+}
diff --git a/src/plugins/exec/exec-completion.h b/src/plugins/exec/exec-completion.h
new file mode 100644
index 000000000..a01bb03cf
--- /dev/null
+++ b/src/plugins/exec/exec-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_EXEC_COMPLETION_H
+#define __WEECHAT_EXEC_COMPLETION_H 1
+
+extern void exec_completion_init ();
+
+#endif /* __WEECHAT_EXEC_COMPLETION_H */
diff --git a/src/plugins/exec/exec-config.c b/src/plugins/exec/exec-config.c
new file mode 100644
index 000000000..05ecc027e
--- /dev/null
+++ b/src/plugins/exec/exec-config.c
@@ -0,0 +1,191 @@
+/*
+ * exec-config.c - exec configuration options (file exec.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 "exec.h"
+#include "exec-config.h"
+
+
+struct t_config_file *exec_config_file = NULL;
+
+/* exec config, command section */
+
+struct t_config_option *exec_config_command_default_options;
+struct t_config_option *exec_config_command_purge_delay;
+
+/* exec config, color section */
+
+struct t_config_option *exec_config_color_flag_running;
+struct t_config_option *exec_config_color_flag_finished;
+
+char **exec_config_cmd_options = NULL;
+int exec_config_cmd_num_options = 0;
+
+
+/*
+ * Callback for changes on option "exec.command.default_options".
+ */
+
+void
+exec_config_change_command_default_options (void *data,
+ struct t_config_option *option)
+{
+ /* make C compiler happy */
+ (void) data;
+ (void) option;
+
+ if (exec_config_cmd_options)
+ weechat_string_free_split (exec_config_cmd_options);
+
+ exec_config_cmd_options = weechat_string_split (
+ weechat_config_string (exec_config_command_default_options),
+ " ", 0, 0, &exec_config_cmd_num_options);
+}
+
+/*
+ * Reloads exec configuration file.
+ */
+
+int
+exec_config_reload_cb (void *data, struct t_config_file *config_file)
+{
+ /* make C compiler happy */
+ (void) data;
+
+ return weechat_config_reload (config_file);
+}
+
+/*
+ * Initializes exec configuration file.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+exec_config_init ()
+{
+ struct t_config_section *ptr_section;
+
+ exec_config_file = weechat_config_new (EXEC_CONFIG_NAME,
+ &exec_config_reload_cb, NULL);
+ if (!exec_config_file)
+ return 0;
+
+ /* command */
+ ptr_section = weechat_config_new_section (exec_config_file, "command",
+ 0, 0,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL);
+ if (!ptr_section)
+ {
+ weechat_config_free (exec_config_file);
+ return 0;
+ }
+
+ exec_config_command_default_options = weechat_config_new_option (
+ exec_config_file, ptr_section,
+ "default_options", "string",
+ N_("default options for command /exec (see /help exec); example: "
+ "\"-nosh -bg\" to run all commands in background (no output), and "
+ "without using the shell"),
+ NULL, 0, 0, "", NULL, 0, NULL, NULL,
+ &exec_config_change_command_default_options, NULL, NULL, NULL);
+ exec_config_command_purge_delay = weechat_config_new_option (
+ exec_config_file, ptr_section,
+ "purge_delay", "integer",
+ N_("delay for purging finished commands (in seconds, 0 = purge "
+ "commands immediately, -1 = never purge)"),
+ NULL, -1, 36000 * 24 * 30, "0", NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+
+ /* color */
+ ptr_section = weechat_config_new_section (exec_config_file, "color",
+ 0, 0,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL);
+ if (!ptr_section)
+ {
+ weechat_config_free (exec_config_file);
+ return 0;
+ }
+
+ exec_config_color_flag_running = weechat_config_new_option (
+ exec_config_file, ptr_section,
+ "flag_running", "color",
+ N_("text color for a running command flag (in exec buffer and "
+ "/exec -list)"),
+ NULL, 0, 0, "lightgreen", NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ exec_config_color_flag_finished = weechat_config_new_option (
+ exec_config_file, ptr_section,
+ "flag_finished", "color",
+ N_("text color for a finished command flag (in exec buffer and "
+ "/exec -list)"),
+ NULL, 0, 0, "lightred", NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+
+ return 1;
+}
+
+/*
+ * Reads exec configuration file.
+ */
+
+int
+exec_config_read ()
+{
+ return weechat_config_read (exec_config_file);
+}
+
+/*
+ * Writes exec configuration file.
+ */
+
+int
+exec_config_write ()
+{
+ return weechat_config_write (exec_config_file);
+}
+
+/*
+ * Frees exec configuration.
+ */
+
+void
+exec_config_free ()
+{
+ weechat_config_free (exec_config_file);
+
+ if (exec_config_cmd_options)
+ {
+ weechat_string_free_split (exec_config_cmd_options);
+ exec_config_cmd_options = NULL;
+ exec_config_cmd_num_options = 0;
+ }
+}
diff --git a/src/plugins/exec/exec-config.h b/src/plugins/exec/exec-config.h
new file mode 100644
index 000000000..9963fd092
--- /dev/null
+++ b/src/plugins/exec/exec-config.h
@@ -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/>.
+ */
+
+#ifndef __WEECHAT_EXEC_CONFIG_H
+#define __WEECHAT_EXEC_CONFIG_H 1
+
+#define EXEC_CONFIG_NAME "exec"
+#define EXEC_CONFIG_SECTION_EXEC "exec"
+
+extern struct t_config_file *exec_config_file;
+
+extern struct t_config_option *exec_config_command_default_options;
+extern struct t_config_option *exec_config_command_purge_delay;
+
+extern struct t_config_option *exec_config_color_flag_running;
+extern struct t_config_option *exec_config_color_flag_finished;
+
+extern char **exec_config_cmd_options;
+extern int exec_config_cmd_num_options;
+
+extern int exec_config_init ();
+extern int exec_config_read ();
+extern int exec_config_write ();
+extern void exec_config_free ();
+
+#endif /* __WEECHAT_EXEC_CONFIG_H */
diff --git a/src/plugins/exec/exec.c b/src/plugins/exec/exec.c
new file mode 100644
index 000000000..a8184d680
--- /dev/null
+++ b/src/plugins/exec/exec.c
@@ -0,0 +1,587 @@
+/*
+ * exec.c - execution of external commands in 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 <time.h>
+
+#include "../weechat-plugin.h"
+#include "exec.h"
+#include "exec-buffer.h"
+#include "exec-command.h"
+#include "exec-completion.h"
+#include "exec-config.h"
+
+
+WEECHAT_PLUGIN_NAME(EXEC_PLUGIN_NAME);
+WEECHAT_PLUGIN_DESCRIPTION(N_("Execution of external commands in WeeChat"));
+WEECHAT_PLUGIN_AUTHOR("Sébastien Helleu <flashcode@flashtux.org>");
+WEECHAT_PLUGIN_VERSION(WEECHAT_VERSION);
+WEECHAT_PLUGIN_LICENSE(WEECHAT_LICENSE);
+
+struct t_weechat_plugin *weechat_exec_plugin = NULL;
+
+struct t_exec_cmd *exec_cmds = NULL; /* first executed command */
+struct t_exec_cmd *last_exec_cmd = NULL; /* last executed command */
+int exec_cmds_count = 0; /* number of executed commands */
+
+char *exec_color_string[EXEC_NUM_COLORS] =
+{ "off", "decode", "strip" };
+
+
+/*
+ * Searches for a color action name.
+ *
+ * Returns index of color in enum t_exec_color, -1 if not found.
+ */
+
+int
+exec_search_color (const char *color)
+{
+ int i;
+
+ if (!color)
+ return -1;
+
+ for (i = 0; i < EXEC_NUM_COLORS; i++)
+ {
+ if (weechat_strcasecmp (exec_color_string[i], color) == 0)
+ return i;
+ }
+
+ /* color not found */
+ return -1;
+}
+
+/*
+ * Searches for an executed command by id, which can be a number or a name.
+ *
+ * Returns pointer to executed command found, NULL if not found.
+ */
+
+struct t_exec_cmd *
+exec_search_by_id (const char *id)
+{
+ struct t_exec_cmd* ptr_exec_cmd;
+ char *error;
+ long number;
+
+ error = NULL;
+ number = strtol (id, &error, 10);
+ if (!error || error[0])
+ number = -1;
+
+ for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd;
+ ptr_exec_cmd = ptr_exec_cmd->next_cmd)
+ {
+ /* check if number is matching */
+ if ((number >= 0) && (ptr_exec_cmd->number == (int)number))
+ return ptr_exec_cmd;
+
+ /* check if name is matching */
+ if (ptr_exec_cmd->name && (strcmp (ptr_exec_cmd->name, id) == 0))
+ return ptr_exec_cmd;
+ }
+
+ /* executed command not found */
+ return NULL;
+}
+
+/*
+ * Adds a command in list of executed commands.
+ */
+
+struct t_exec_cmd *
+exec_add ()
+{
+ struct t_exec_cmd *new_exec_cmd, *ptr_exec_cmd;
+ int number;
+
+ /* find first available number */
+ number = (last_exec_cmd) ? last_exec_cmd->number + 1 : 0;
+ for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd;
+ ptr_exec_cmd = ptr_exec_cmd->next_cmd)
+ {
+ if (ptr_exec_cmd->prev_cmd
+ && (ptr_exec_cmd->number > ptr_exec_cmd->prev_cmd->number + 1))
+ {
+ number = ptr_exec_cmd->prev_cmd->number + 1;
+ break;
+ }
+ }
+
+ new_exec_cmd = malloc (sizeof (*new_exec_cmd));
+ if (!new_exec_cmd)
+ return NULL;
+
+ new_exec_cmd->prev_cmd = last_exec_cmd;
+ new_exec_cmd->next_cmd = NULL;
+ if (!exec_cmds)
+ exec_cmds = new_exec_cmd;
+ else
+ last_exec_cmd->next_cmd = new_exec_cmd;
+ last_exec_cmd = new_exec_cmd;
+
+ new_exec_cmd->number = number;
+ new_exec_cmd->name = NULL;
+ new_exec_cmd->hook = NULL;
+ new_exec_cmd->command = NULL;
+ new_exec_cmd->pid = 0;
+ new_exec_cmd->detached = 0;
+ new_exec_cmd->start_time = time (NULL);
+ new_exec_cmd->end_time = 0;
+ new_exec_cmd->output_to_buffer = 0;
+ new_exec_cmd->buffer_full_name = NULL;
+ new_exec_cmd->line_numbers = 0;
+ new_exec_cmd->display_rc = 0;
+ new_exec_cmd->stdout_size = 0;
+ new_exec_cmd->stdout = NULL;
+ new_exec_cmd->stderr_size = 0;
+ new_exec_cmd->stderr = NULL;
+ new_exec_cmd->return_code = -1;
+
+ exec_cmds_count++;
+
+ return new_exec_cmd;
+}
+
+/*
+ * Timer callback to delete a command.
+ */
+
+int
+exec_timer_delete_cb (void *data, int remaining_calls)
+{
+ struct t_exec_cmd *exec_cmd, *ptr_exec_cmd;
+
+ /* make C compiler happy */
+ (void) remaining_calls;
+
+ exec_cmd = (struct t_exec_cmd *)data;
+ if (!exec_cmd)
+ return WEECHAT_RC_OK;
+
+ for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd;
+ ptr_exec_cmd = ptr_exec_cmd->next_cmd)
+ {
+ if (ptr_exec_cmd == exec_cmd)
+ {
+ exec_free (ptr_exec_cmd);
+ break;
+ }
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Concatenates some text to stdout/stderr of a command.
+ */
+
+void
+exec_command_concat_output (int *size, char **output, const char *text)
+{
+ int length, new_size;
+ char *new_output;
+
+ length = strlen (text);
+ new_size = *size + length;
+ new_output = realloc (*output, new_size + 1);
+ if (!new_output)
+ return;
+
+ *output = new_output;
+ memcpy (*output + *size, text, length + 1);
+ *size = new_size;
+}
+
+/*
+ * Displays output of a command.
+ */
+
+void
+exec_command_display_output (struct t_exec_cmd *exec_cmd,
+ struct t_gui_buffer *buffer, int stdout)
+{
+ char *ptr_output, *ptr_line, *line, *line2, *pos;
+ char str_number[32], str_tags[1024];
+ int line_nb, length;
+
+ ptr_output = (stdout) ? exec_cmd->stdout : exec_cmd->stderr;
+ if (!ptr_output)
+ return;
+
+ /*
+ * if output is sent to the buffer, the buffer must exist
+ * (we don't send output by default to core buffer)
+ */
+ if (exec_cmd->output_to_buffer && !buffer)
+ return;
+
+ ptr_line = ptr_output;
+ line_nb = 1;
+ while (ptr_line)
+ {
+ /* ignore last empty line */
+ if (!ptr_line[0])
+ break;
+
+ /* search end of line */
+ pos = strchr (ptr_line, '\n');
+ line = (pos) ?
+ weechat_strndup (ptr_line, pos - ptr_line) : strdup (ptr_line);
+ if (!line)
+ break;
+
+ if (exec_cmd->color != EXEC_COLOR_OFF)
+ {
+ line2 = weechat_hook_modifier_exec (
+ (exec_cmd->output_to_buffer) ?
+ "irc_color_decode_ansi" : "color_decode_ansi",
+ (exec_cmd->color == EXEC_COLOR_DECODE) ? "1" : "0",
+ line);
+ free (line);
+ if (!line2)
+ break;
+ line = line2;
+ }
+
+ if (exec_cmd->output_to_buffer)
+ {
+ if (exec_cmd->line_numbers)
+ {
+ length = 32 + strlen (line) + 1;
+ line2 = malloc (length);
+ if (line2)
+ {
+ snprintf (line2, length, "%d. %s", line_nb, line);
+ weechat_command (buffer, line2);
+ free (line2);
+ }
+ }
+ else
+ weechat_command (buffer, (line[0]) ? line : " ");
+ }
+ else
+ {
+
+ snprintf (str_number, sizeof (str_number), "%d", exec_cmd->number);
+ snprintf (str_tags, sizeof (str_tags),
+ "exec_%s,exec_cmd_%s",
+ (stdout) ? "stdout" : "stderr",
+ (exec_cmd->name) ? exec_cmd->name : str_number);
+ snprintf (str_number, sizeof (str_number), "%d\t", line_nb);
+ weechat_printf_tags (buffer, str_tags,
+ "%s%s",
+ (exec_cmd->line_numbers) ? str_number : " \t",
+ line);
+ }
+
+ free (line);
+ line_nb++;
+ ptr_line = (pos) ? pos + 1 : NULL;
+ }
+}
+
+/*
+ * Ends a command.
+ */
+
+void
+exec_end_command (struct t_exec_cmd *exec_cmd, int return_code)
+{
+ struct t_gui_buffer *ptr_buffer;
+
+ ptr_buffer = weechat_buffer_search ("==", exec_cmd->buffer_full_name);
+
+ /* display stdout/stderr (if output to buffer, the buffer must exist) */
+ exec_command_display_output (exec_cmd, ptr_buffer, 1);
+ exec_command_display_output (exec_cmd, ptr_buffer, 0);
+
+ /*
+ * display return code (only if command is not detached and if output is
+ * NOT sent to buffer)
+ */
+ if (!exec_cmd->detached && !exec_cmd->output_to_buffer
+ && exec_cmd->display_rc)
+ {
+ if (return_code >= 0)
+ {
+ weechat_printf_tags (ptr_buffer, "exec_rc",
+ _("%s: end of command %d (\"%s\"), "
+ "return code: %d"),
+ EXEC_PLUGIN_NAME, exec_cmd->number,
+ exec_cmd->command, return_code);
+ }
+ else
+ {
+ weechat_printf_tags (ptr_buffer, "exec_rc",
+ _("%s: unexpected end of command %d "
+ "(\"%s\")"),
+ EXEC_PLUGIN_NAME, exec_cmd->number,
+ exec_cmd->command);
+ }
+ }
+
+ /* (re)set some variables after the end of command */
+ exec_cmd->hook = NULL;
+ exec_cmd->pid = 0;
+ exec_cmd->end_time = time (NULL);
+ exec_cmd->return_code = return_code;
+
+ /* schedule a timer to remove the executed command */
+ if (weechat_config_integer (exec_config_command_purge_delay) >= 0)
+ {
+ weechat_hook_timer (1 + (1000 * weechat_config_integer (exec_config_command_purge_delay)),
+ 0, 1,
+ &exec_timer_delete_cb, exec_cmd);
+ }
+}
+
+/*
+ * Callback for hook process.
+ */
+
+int
+exec_process_cb (void *data, const char *command, int return_code,
+ const char *out, const char *err)
+{
+ struct t_exec_cmd *ptr_exec_cmd;
+
+ /* make C compiler happy */
+ (void) command;
+
+ ptr_exec_cmd = (struct t_exec_cmd *)data;
+ if (!ptr_exec_cmd)
+ return WEECHAT_RC_ERROR;
+
+ if (weechat_exec_plugin->debug >= 2)
+ {
+ weechat_printf (NULL,
+ "%s: process_cb: command=\"%s\", rc=%d, "
+ "out: %d bytes, err: %d bytes",
+ EXEC_PLUGIN_NAME,
+ ptr_exec_cmd->command,
+ return_code,
+ (out) ? strlen (out) : 0,
+ (err) ? strlen (err) : 0);
+ }
+
+ if (return_code == WEECHAT_HOOK_PROCESS_ERROR)
+ {
+ exec_end_command (ptr_exec_cmd, -1);
+ return WEECHAT_RC_OK;
+ }
+
+ if (out)
+ {
+ exec_command_concat_output (&ptr_exec_cmd->stdout_size,
+ &ptr_exec_cmd->stdout,
+ out);
+ }
+ if (err)
+ {
+ exec_command_concat_output (&ptr_exec_cmd->stderr_size,
+ &ptr_exec_cmd->stderr,
+ err);
+ }
+
+ if (return_code >= 0)
+ exec_end_command (ptr_exec_cmd, return_code);
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Deletes a command.
+ */
+
+void
+exec_free (struct t_exec_cmd *exec_cmd)
+{
+ if (!exec_cmd)
+ return;
+
+ /* remove command from commands list */
+ if (exec_cmd->prev_cmd)
+ (exec_cmd->prev_cmd)->next_cmd = exec_cmd->next_cmd;
+ if (exec_cmd->next_cmd)
+ (exec_cmd->next_cmd)->prev_cmd = exec_cmd->prev_cmd;
+ if (exec_cmds == exec_cmd)
+ exec_cmds = exec_cmd->next_cmd;
+ if (last_exec_cmd == exec_cmd)
+ last_exec_cmd = exec_cmd->prev_cmd;
+
+ /* free data */
+ if (exec_cmd->hook)
+ weechat_unhook (exec_cmd->hook);
+ if (exec_cmd->name)
+ free (exec_cmd->name);
+ if (exec_cmd->command)
+ free (exec_cmd->command);
+ if (exec_cmd->buffer_full_name)
+ free (exec_cmd->buffer_full_name);
+ if (exec_cmd->stdout)
+ free (exec_cmd->stdout);
+ if (exec_cmd->stderr)
+ free (exec_cmd->stderr);
+
+ free (exec_cmd);
+
+ exec_cmds_count--;
+}
+
+/*
+ * Deletes all commands.
+ */
+
+void
+exec_free_all ()
+{
+ while (exec_cmds)
+ {
+ exec_free (exec_cmds);
+ }
+}
+
+/*
+ * Prints exec infos in WeeChat log file (usually for crash dump).
+ */
+
+void
+exec_print_log ()
+{
+ struct t_exec_cmd *ptr_exec_cmd;
+
+ for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd;
+ ptr_exec_cmd = ptr_exec_cmd->next_cmd)
+ {
+ weechat_log_printf ("");
+ weechat_log_printf ("[exec command (addr:0x%lx)]", ptr_exec_cmd);
+ weechat_log_printf (" number. . . . . . . . . : %d", ptr_exec_cmd->number);
+ weechat_log_printf (" name. . . . . . . . . . : '%s'", ptr_exec_cmd->name);
+ weechat_log_printf (" hook. . . . . . . . . . : 0x%lx", ptr_exec_cmd->hook);
+ weechat_log_printf (" command . . . . . . . . : '%s'", ptr_exec_cmd->command);
+ weechat_log_printf (" pid . . . . . . . . . . : %d", ptr_exec_cmd->pid);
+ weechat_log_printf (" detached. . . . . . . . : %d", ptr_exec_cmd->detached);
+ weechat_log_printf (" start_time. . . . . . . : %ld", ptr_exec_cmd->start_time);
+ weechat_log_printf (" end_time. . . . . . . . : %ld", ptr_exec_cmd->end_time);
+ weechat_log_printf (" output_to_buffer. . . . : %d", ptr_exec_cmd->output_to_buffer);
+ weechat_log_printf (" buffer_full_name. . . . : '%s'", ptr_exec_cmd->buffer_full_name);
+ weechat_log_printf (" line_numbers. . . . . . : %d", ptr_exec_cmd->line_numbers);
+ weechat_log_printf (" display_rc. . . . . . . : %d", ptr_exec_cmd->display_rc);
+ weechat_log_printf (" stdout_size . . . . . . : %d", ptr_exec_cmd->stdout_size);
+ weechat_log_printf (" stdout. . . . . . . . . : '%s'", ptr_exec_cmd->stdout);
+ weechat_log_printf (" stderr_size . . . . . . : %d", ptr_exec_cmd->stderr_size);
+ weechat_log_printf (" stderr. . . . . . . . . : '%s'", ptr_exec_cmd->stderr);
+ weechat_log_printf (" return_code . . . . . . : %d", ptr_exec_cmd->return_code);
+ weechat_log_printf (" prev_cmd. . . . . . . . : 0x%lx", ptr_exec_cmd->prev_cmd);
+ weechat_log_printf (" next_cmd. . . . . . . . : 0x%lx", ptr_exec_cmd->next_cmd);
+ }
+}
+
+/*
+ * Callback for signal "debug_dump".
+ */
+
+int
+exec_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, EXEC_PLUGIN_NAME) == 0))
+ {
+ weechat_log_printf ("");
+ weechat_log_printf ("***** \"%s\" plugin dump *****",
+ weechat_plugin->name);
+
+ exec_print_log ();
+
+ weechat_log_printf ("");
+ weechat_log_printf ("***** End of \"%s\" plugin dump *****",
+ weechat_plugin->name);
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Initializes exec plugin.
+ */
+
+int
+weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[])
+{
+ int i, upgrading;
+
+ weechat_plugin = plugin;
+
+ exec_command_init ();
+
+ if (!exec_config_init ())
+ return WEECHAT_RC_ERROR;
+
+ exec_config_read ();
+
+ /* hook some signals */
+ weechat_hook_signal ("debug_dump", &exec_debug_dump_cb, NULL);
+
+ /* hook completions */
+ exec_completion_init ();
+
+ /* look at arguments */
+ upgrading = 0;
+ for (i = 0; i < argc; i++)
+ {
+ if (weechat_strcasecmp (argv[i], "--upgrade") == 0)
+ {
+ upgrading = 1;
+ }
+ }
+
+ if (upgrading)
+ exec_buffer_set_callbacks ();
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Ends exec plugin.
+ */
+
+int
+weechat_plugin_end (struct t_weechat_plugin *plugin)
+{
+ /* make C compiler happy */
+ (void) plugin;
+
+ exec_config_write ();
+ exec_free_all ();
+ exec_config_free ();
+
+ return WEECHAT_RC_OK;
+}
diff --git a/src/plugins/exec/exec.h b/src/plugins/exec/exec.h
new file mode 100644
index 000000000..9958ed7a3
--- /dev/null
+++ b/src/plugins/exec/exec.h
@@ -0,0 +1,79 @@
+/*
+ * 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_EXEC_H
+#define __WEECHAT_EXEC_H 1
+
+#include <time.h>
+
+#define weechat_plugin weechat_exec_plugin
+#define EXEC_PLUGIN_NAME "exec"
+
+enum t_exec_color {
+ EXEC_COLOR_OFF = 0,
+ EXEC_COLOR_DECODE,
+ EXEC_COLOR_STRIP,
+ /* number of color actions */
+ EXEC_NUM_COLORS,
+};
+
+struct t_exec_cmd
+{
+ /* command/process */
+ int number; /* command number */
+ char *name; /* name of command */
+ struct t_hook *hook; /* pointer to process hook */
+ char *command; /* command (with arguments) */
+ pid_t pid; /* process id */
+ int detached; /* 1 if command is detached */
+ time_t start_time; /* start time */
+ time_t end_time; /* end time */
+
+ /* display */
+ int output_to_buffer; /* 1 if output is sent to buffer */
+ char *buffer_full_name; /* buffer where output is displayed */
+ int line_numbers; /* 1 if lines numbers are displayed */
+ int color; /* what to do with ANSI colors */
+ int display_rc; /* 1 if return code is displayed */
+
+ /* command output */
+ int stdout_size; /* number of bytes in stdout */
+ char *stdout; /* stdout of command */
+ int stderr_size; /* number of bytes in stderr */
+ char *stderr; /* stderr of command */
+ int return_code; /* command return code */
+
+ struct t_exec_cmd *prev_cmd; /* link to previous command */
+ struct t_exec_cmd *next_cmd; /* link to next command */
+};
+
+extern struct t_weechat_plugin *weechat_exec_plugin;
+extern struct t_exec_cmd *exec_cmds;
+extern struct t_exec_cmd *last_exec_cmd;
+extern int exec_cmds_count;
+
+extern int exec_search_color (const char *color);
+extern struct t_exec_cmd *exec_search_by_id (const char *id);
+extern struct t_exec_cmd *exec_add ();
+extern int exec_process_cb (void *data, const char *command, int return_code,
+ const char *out, const char *err);
+extern void exec_free (struct t_exec_cmd *exec_cmd);
+extern void exec_free_all ();
+
+#endif /* __WEECHAT_EXEC_H */