summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--configure.ac19
-rw-r--r--debian/weechat-plugins.install1
-rw-r--r--doc/docgen.py1
-rw-r--r--po/POTFILES.in8
-rw-r--r--po/srcfiles.cmake8
-rw-r--r--src/plugins/CMakeLists.txt4
-rw-r--r--src/plugins/Makefile.am12
-rw-r--r--src/plugins/exec/CMakeLists.txt29
-rw-r--r--src/plugins/exec/Makefile.am38
-rw-r--r--src/plugins/exec/exec-command.c409
-rw-r--r--src/plugins/exec/exec-command.h25
-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.c151
-rw-r--r--src/plugins/exec/exec-config.h38
-rw-r--r--src/plugins/exec/exec.c500
-rw-r--r--src/plugins/exec/exec.h67
-rw-r--r--weechat.cygport.in1
19 files changed, 1406 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b1301cc82..da61424a5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -71,6 +71,7 @@ OPTION(ENABLE_ALIAS "Enable Alias plugin" ON)
OPTION(ENABLE_ASPELL "Enable Aspell plugin" ON)
OPTION(ENABLE_ENCHANT "Enable Enchant lib for Aspell plugin" OFF)
OPTION(ENABLE_CHARSET "Enable Charset plugin" ON)
+OPTION(ENABLE_EXEC "Enable Exec plugin" ON)
OPTION(ENABLE_FIFO "Enable FIFO plugin" ON)
OPTION(ENABLE_IRC "Enable IRC plugin" ON)
OPTION(ENABLE_LOGGER "Enable Logger plugin" ON)
diff --git a/configure.ac b/configure.ac
index 2b466082f..8871cc21b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -104,6 +104,7 @@ AH_VERBATIM([HAVE_ASPELL_VERSION_STRING], [#undef HAVE_ASPELL_VERSION_STRING])
AH_VERBATIM([PLUGIN_ALIAS], [#undef PLUGIN_ALIAS])
AH_VERBATIM([PLUGIN_ASPELL], [#undef PLUGIN_ASPELL])
AH_VERBATIM([PLUGIN_CHARSET], [#undef PLUGIN_CHARSET])
+AH_VERBATIM([PLUGIN_EXEC], [#undef PLUGIN_EXEC])
AH_VERBATIM([PLUGIN_FIFO], [#undef PLUGIN_FIFO])
AH_VERBATIM([PLUGIN_IRC], [#undef PLUGIN_IRC])
AH_VERBATIM([PLUGIN_LOGGER], [#undef PLUGIN_LOGGER])
@@ -131,6 +132,7 @@ AC_ARG_ENABLE(alias, [ --disable-alias turn off Alias plugin (de
AC_ARG_ENABLE(aspell, [ --disable-aspell turn off Aspell plugin (default=compiled)],enable_aspell=$enableval,enable_aspell=yes)
AC_ARG_ENABLE(enchant, [ --enable-enchant turn on Enchant lib for Aspell plugin (default=off)],enable_enchant=$enableval,enable_enchant=no)
AC_ARG_ENABLE(charset, [ --disable-charset turn off Charset plugin (default=compiled if found)],enable_charset=$enableval,enable_charset=yes)
+AC_ARG_ENABLE(exec, [ --disable-exec turn off Exec plugin (default=compiled)],enable_exec=$enableval,enable_exec=yes)
AC_ARG_ENABLE(fifo, [ --disable-fifo turn off Fifo plugin (default=compiled)],enable_fifo=$enableval,enable_fifo=yes)
AC_ARG_ENABLE(irc, [ --disable-irc turn off IRC plugin (default=compiled)],enable_irc=$enableval,enable_irc=yes)
AC_ARG_ENABLE(logger, [ --disable-logger turn off Logger plugin (default=compiled)],enable_logger=$enableval,enable_logger=yes)
@@ -358,6 +360,18 @@ else
not_asked="$not_asked charset"
fi
+# ---------------------------------- exec --------------------------------------
+
+if test "x$enable_exec" = "xyes" ; then
+ EXEC_CFLAGS=""
+ EXEC_LFLAGS=""
+ AC_SUBST(EXEC_CFLAGS)
+ AC_SUBST(EXEC_LFLAGS)
+ AC_DEFINE(PLUGIN_EXEC)
+else
+ not_asked="$not_asked exec"
+fi
+
# ---------------------------------- fifo --------------------------------------
if test "x$enable_fifo" = "xyes" ; then
@@ -1117,6 +1131,7 @@ AM_CONDITIONAL(GUI_NCURSES, test "$enable_ncurses" = "yes")
AM_CONDITIONAL(PLUGIN_ALIAS, test "$enable_alias" = "yes")
AM_CONDITIONAL(PLUGIN_ASPELL, test "$enable_aspell" = "yes")
AM_CONDITIONAL(PLUGIN_CHARSET, test "$enable_charset" = "yes")
+AM_CONDITIONAL(PLUGIN_EXEC, test "$enable_exec" = "yes")
AM_CONDITIONAL(PLUGIN_FIFO, test "$enable_fifo" = "yes")
AM_CONDITIONAL(PLUGIN_IRC, test "$enable_irc" = "yes")
AM_CONDITIONAL(PLUGIN_LOGGER, test "$enable_logger" = "yes")
@@ -1149,6 +1164,7 @@ AC_OUTPUT([Makefile
src/plugins/alias/Makefile
src/plugins/aspell/Makefile
src/plugins/charset/Makefile
+ src/plugins/exec/Makefile
src/plugins/fifo/Makefile
src/plugins/irc/Makefile
src/plugins/logger/Makefile
@@ -1192,6 +1208,9 @@ fi
if test "x$enable_charset" = "xyes"; then
listplugins="$listplugins charset"
fi
+if test "x$enable_exec" = "xyes"; then
+ listplugins="$listplugins exec"
+fi
if test "x$enable_fifo" = "xyes"; then
listplugins="$listplugins fifo"
fi
diff --git a/debian/weechat-plugins.install b/debian/weechat-plugins.install
index ef09e1747..ff345a770 100644
--- a/debian/weechat-plugins.install
+++ b/debian/weechat-plugins.install
@@ -1,4 +1,5 @@
usr/lib/weechat/plugins/aspell.so
+usr/lib/weechat/plugins/exec.so
usr/lib/weechat/plugins/fifo.so
usr/lib/weechat/plugins/guile.so
usr/lib/weechat/plugins/perl.so
diff --git a/doc/docgen.py b/doc/docgen.py
index bafa2dbed..2ebfd3b43 100644
--- a/doc/docgen.py
+++ b/doc/docgen.py
@@ -88,6 +88,7 @@ plugin_list = {
'alias': '',
'aspell': 'o',
'charset': 'o',
+ 'exec': 'o',
'fifo': 'o',
'irc': 'co',
'logger': 'o',
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 82aec229f..f3f120ee0 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -113,6 +113,14 @@
./src/plugins/aspell/weechat-aspell-speller.c
./src/plugins/aspell/weechat-aspell-speller.h
./src/plugins/charset/charset.c
+./src/plugins/exec/exec.c
+./src/plugins/exec/exec-command.c
+./src/plugins/exec/exec-command.h
+./src/plugins/exec/exec-completion.c
+./src/plugins/exec/exec-completion.h
+./src/plugins/exec/exec-config.c
+./src/plugins/exec/exec-config.h
+./src/plugins/exec/exec.h
./src/plugins/fifo/fifo.c
./src/plugins/fifo/fifo.h
./src/plugins/fifo/fifo-info.c
diff --git a/po/srcfiles.cmake b/po/srcfiles.cmake
index b51385503..2fce5d3d2 100644
--- a/po/srcfiles.cmake
+++ b/po/srcfiles.cmake
@@ -114,6 +114,14 @@ SET(WEECHAT_SOURCES
./src/plugins/aspell/weechat-aspell-speller.c
./src/plugins/aspell/weechat-aspell-speller.h
./src/plugins/charset/charset.c
+./src/plugins/exec/exec.c
+./src/plugins/exec/exec-command.c
+./src/plugins/exec/exec-command.h
+./src/plugins/exec/exec-completion.c
+./src/plugins/exec/exec-completion.h
+./src/plugins/exec/exec-config.c
+./src/plugins/exec/exec-config.h
+./src/plugins/exec/exec.h
./src/plugins/fifo/fifo.c
./src/plugins/fifo/fifo.h
./src/plugins/fifo/fifo-info.c
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 5bfdb9444..1e5587454 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -72,6 +72,10 @@ IF(ENABLE_CHARSET)
ENDIF(ICONV_FOUND)
ENDIF(ENABLE_CHARSET)
+IF(ENABLE_EXEC)
+ ADD_SUBDIRECTORY( exec )
+ENDIF(ENABLE_EXEC)
+
IF(ENABLE_FIFO)
ADD_SUBDIRECTORY( fifo )
ENDIF(ENABLE_FIFO)
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index d546c026e..d42af9c74 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -51,6 +51,10 @@ if PLUGIN_CHARSET
charset_dir = charset
endif
+if PLUGIN_EXEC
+exec_dir = exec
+endif
+
if PLUGIN_FIFO
fifo_dir = fifo
endif
@@ -103,10 +107,10 @@ if PLUGIN_XFER
xfer_dir = xfer
endif
-SUBDIRS = . $(alias_dir) $(aspell_dir) $(charset_dir) $(fifo_dir) $(irc_dir) \
- $(logger_dir) $(relay_dir) $(script_dir) $(perl_dir) $(python_dir) \
- $(ruby_dir) $(lua_dir) $(tcl_dir) $(guile_dir) $(trigger_dir) \
- $(xfer_dir)
+SUBDIRS = . $(alias_dir) $(aspell_dir) $(charset_dir) $(exec_dir) $(fifo_dir) \
+ $(irc_dir) $(logger_dir) $(relay_dir) $(script_dir) $(perl_dir) \
+ $(python_dir) $(ruby_dir) $(lua_dir) $(tcl_dir) $(guile_dir) \
+ $(trigger_dir) $(xfer_dir)
EXTRA_DIST = CMakeLists.txt
diff --git a/src/plugins/exec/CMakeLists.txt b/src/plugins/exec/CMakeLists.txt
new file mode 100644
index 000000000..bd86d831f
--- /dev/null
+++ b/src/plugins/exec/CMakeLists.txt
@@ -0,0 +1,29 @@
+#
+# 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-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..3fa30374f
--- /dev/null
+++ b/src/plugins/exec/Makefile.am
@@ -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/>.
+#
+
+AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/locale\" $(EXEC_CFLAGS)
+
+libdir = ${weechat_libdir}/plugins
+
+lib_LTLIBRARIES = exec.la
+
+exec_la_SOURCES = exec.c \
+ exec.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-command.c b/src/plugins/exec/exec-command.c
new file mode 100644
index 000000000..0f07e02c6
--- /dev/null
+++ b/src/plugins/exec/exec-command.c
@@ -0,0 +1,409 @@
+/*
+ * 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-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;
+}
+
+/*
+ * 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, command_index, use_shell, pipe_stdin, output_to_buffer, length;
+ long timeout;
+ char *error, *ptr_name, *text;
+ struct t_exec_cmd *ptr_exec_cmd, *new_exec_cmd;
+ struct t_hashtable *options_cmd;
+ struct t_infolist *ptr_infolist;
+
+ /* 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->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 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)
+ {
+ /* TODO: send signal to the process */
+ }
+ 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)
+ {
+ /* TODO: send KILL signal to the process */
+ }
+ 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)
+ {
+ /* TODO: send KILL signal to the process */
+ }
+ return WEECHAT_RC_OK;
+ }
+
+ /* parse command options */
+ command_index = -1;
+ use_shell = 1;
+ pipe_stdin = 0;
+ timeout = 0;
+ output_to_buffer = 0;
+ ptr_name = NULL;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (weechat_strcasecmp (argv[i], "-nosh") == 0)
+ {
+ use_shell = 0;
+ }
+ else if (weechat_strcasecmp (argv[i], "-stdin") == 0)
+ {
+ pipe_stdin = 1;
+ }
+ else if (weechat_strcasecmp (argv[i], "-o") == 0)
+ {
+ output_to_buffer = 1;
+ }
+ else if (weechat_strcasecmp (argv[i], "-timeout") == 0)
+ {
+ if (i + 1 >= argc)
+ return WEECHAT_RC_ERROR;
+ i++;
+ error = NULL;
+ timeout = strtol (argv[i], &error, 10);
+ if (!error || error[0])
+ return WEECHAT_RC_ERROR;
+ }
+ else if (weechat_strcasecmp (argv[i], "-name") == 0)
+ {
+ if (i + 1 >= argc)
+ return WEECHAT_RC_ERROR;
+ i++;
+ ptr_name = argv[i];
+ }
+ else
+ {
+ command_index = i;
+ break;
+ }
+ }
+ if (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() */
+ options_cmd = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL,
+ NULL);
+ if (!options_cmd)
+ {
+ exec_free (new_exec_cmd);
+ return WEECHAT_RC_ERROR;
+ }
+
+ /* run the command in background */
+ if (use_shell)
+ {
+ /* command will be: sh -c "command arguments..." */
+ weechat_hashtable_set (options_cmd, "arg1", "-c");
+ weechat_hashtable_set (options_cmd, "arg2", argv_eol[command_index]);
+ }
+ if (weechat_exec_plugin->debug >= 1)
+ {
+ weechat_printf (NULL, "%s: executing command: \"%s%s%s\"",
+ EXEC_PLUGIN_NAME,
+ (use_shell) ? "" : "sh -c '",
+ argv_eol[command_index],
+ (use_shell) ? "" : "'");
+ }
+ if (pipe_stdin)
+ weechat_hashtable_set (options_cmd, "stdin", "1");
+ new_exec_cmd->hook = weechat_hook_process_hashtable (
+ (use_shell) ? "sh" : argv_eol[command_index],
+ options_cmd,
+ (int)(timeout * 1000),
+ &exec_process_cb,
+ new_exec_cmd);
+
+ weechat_hashtable_free (options_cmd);
+
+ if (!new_exec_cmd->hook)
+ {
+ exec_free (new_exec_cmd);
+ weechat_printf (NULL,
+ _("%s%s: failed to run command \"%s\""),
+ weechat_prefix ("error"), EXEC_PLUGIN_NAME,
+ argv_eol[command_index]);
+ return WEECHAT_RC_OK;
+ }
+
+ new_exec_cmd->name = (ptr_name) ? strdup (ptr_name) : NULL;
+ new_exec_cmd->command = strdup (argv_eol[command_index]);
+ new_exec_cmd->buffer_plugin = strdup (weechat_buffer_get_string (buffer,
+ "plugin"));
+ new_exec_cmd->buffer_name = strdup (weechat_buffer_get_string (buffer,
+ "name"));
+ new_exec_cmd->output_to_buffer = output_to_buffer;
+ 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);
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Hooks exec commands.
+ */
+
+void
+exec_command_init ()
+{
+ weechat_hook_command (
+ "exec",
+ N_("execute external commands"),
+ N_("-list"
+ " || [-nosh] [-stdin] [-o] [-timeout <timeout>] [-name <name>] <id>"
+ " || -in <id> <text>"
+ " || -signal <id> <signal>"
+ " || -kill <id>"
+ " || -killall"),
+ N_(" -list: list commands\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"
+ " -stdin: create a pipe for sending data to the process (with "
+ "/exec -in)\n"
+ " -o: send output of command to the current buffer\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\n"
+ " -in: send text on standard input of process\n"
+ " -signal: send a signal (integer or name) to the process (example: "
+ "kill, 9, ...)\n"
+ " -kill: alias of \"-signal <id> 9\"\n"
+ "-killall: kill all running processes\n"
+ " number: command number"),
+ "-list"
+ " || -nosh|-stdin|-o|-timeout|-name|%*"
+ " || -in|-signal|-kill %(exec_commands_ids)"
+ " || -killall",
+ &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..312033c41
--- /dev/null
+++ b/src/plugins/exec/exec-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_EXEC_COMMAND_H
+#define __WEECHAT_EXEC_COMMAND_H 1
+
+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..4c5cb1c69
--- /dev/null
+++ b/src/plugins/exec/exec-config.c
@@ -0,0 +1,151 @@
+/*
+ * 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_purge_delay;
+
+/* exec config, color section */
+
+struct t_config_option *exec_config_color_flag_running;
+struct t_config_option *exec_config_color_flag_finished;
+
+/*
+ * 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_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);
+}
diff --git a/src/plugins/exec/exec-config.h b/src/plugins/exec/exec-config.h
new file mode 100644
index 000000000..331f057de
--- /dev/null
+++ b/src/plugins/exec/exec-config.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_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_purge_delay;
+
+extern struct t_config_option *exec_config_color_flag_running;
+extern struct t_config_option *exec_config_color_flag_finished;
+
+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..bbdb33965
--- /dev/null
+++ b/src/plugins/exec/exec.c
@@ -0,0 +1,500 @@
+/*
+ * 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-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 */
+
+
+/*
+ * 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->start_time = time (NULL);
+ new_exec_cmd->end_time = 0;
+ new_exec_cmd->buffer_plugin = NULL;
+ new_exec_cmd->buffer_name = NULL;
+ new_exec_cmd->output_to_buffer = 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, **lines, str_number[32], str_tags[1024];
+ int i, num_lines;
+
+ 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;
+
+ lines = weechat_string_split (ptr_output, "\n", 0, 0, &num_lines);
+ if (!lines)
+ return;
+
+ for (i = 0; i < num_lines; i++)
+ {
+ if (exec_cmd->output_to_buffer)
+ weechat_command (buffer, lines[i]);
+ 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);
+ weechat_printf_tags (buffer, str_tags,
+ "%s%s",
+ (stdout) ? " \t" : weechat_prefix ("error"),
+ lines[i]);
+ }
+ }
+
+ weechat_string_free_split (lines);
+}
+
+/*
+ * 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_plugin,
+ exec_cmd->buffer_name);
+
+ /* display return code (only if output is NOT sent to buffer) */
+ if (!exec_cmd->output_to_buffer)
+ {
+ weechat_printf (ptr_buffer, "");
+ if (return_code >= 0)
+ {
+ weechat_printf (ptr_buffer, "%s: end of command \"%s\" (rc=%d)",
+ EXEC_PLUGIN_NAME, exec_cmd->command,
+ return_code);
+ }
+ else
+ {
+ weechat_printf (ptr_buffer,
+ _("%s%s: unexpected end of command \"%s\""),
+ weechat_prefix ("error"), EXEC_PLUGIN_NAME,
+ exec_cmd->command);
+ }
+ }
+
+ /* 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);
+
+ /* (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_plugin)
+ free (exec_cmd->buffer_plugin);
+ if (exec_cmd->buffer_name)
+ free (exec_cmd->buffer_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 (" start_time. . . . . . . : %ld", ptr_exec_cmd->start_time);
+ weechat_log_printf (" end_time. . . . . . . . : %ld", ptr_exec_cmd->end_time);
+ weechat_log_printf (" buffer_plugin . . . . . : '%s'", ptr_exec_cmd->buffer_plugin);
+ weechat_log_printf (" buffer_name . . . . . . : '%s'", ptr_exec_cmd->buffer_name);
+ weechat_log_printf (" output_to_buffer. . . . : %d", ptr_exec_cmd->output_to_buffer);
+ 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[])
+{
+ /* make C compiler happy */
+ (void) argc;
+ (void) argv;
+
+ 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 ();
+
+ 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..21490aff6
--- /dev/null
+++ b/src/plugins/exec/exec.h
@@ -0,0 +1,67 @@
+/*
+ * 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"
+
+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 */
+ time_t start_time; /* start time */
+ time_t end_time; /* end time */
+
+ /* buffer */
+ char *buffer_plugin; /* buffer plugin (where cmd is exec) */
+ char *buffer_name; /* buffer name (where cmd is exec) */
+ int output_to_buffer; /* 1 if output is sent to buffer */
+
+ /* 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 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 */
diff --git a/weechat.cygport.in b/weechat.cygport.in
index f903d758b..3182f61b8 100644
--- a/weechat.cygport.in
+++ b/weechat.cygport.in
@@ -87,6 +87,7 @@ weechat_CONTENTS="
usr/lib/weechat/plugins/alias.dll
usr/lib/weechat/plugins/aspell.dll
usr/lib/weechat/plugins/charset.dll
+ usr/lib/weechat/plugins/exec.dll
usr/lib/weechat/plugins/fifo.dll
usr/lib/weechat/plugins/irc.dll
usr/lib/weechat/plugins/logger.dll