diff options
Diffstat (limited to 'src/plugins/exec')
-rw-r--r-- | src/plugins/exec/CMakeLists.txt | 30 | ||||
-rw-r--r-- | src/plugins/exec/Makefile.am | 40 | ||||
-rw-r--r-- | src/plugins/exec/exec-buffer.c | 146 | ||||
-rw-r--r-- | src/plugins/exec/exec-buffer.h | 27 | ||||
-rw-r--r-- | src/plugins/exec/exec-command.c | 753 | ||||
-rw-r--r-- | src/plugins/exec/exec-command.h | 46 | ||||
-rw-r--r-- | src/plugins/exec/exec-completion.c | 73 | ||||
-rw-r--r-- | src/plugins/exec/exec-completion.h | 25 | ||||
-rw-r--r-- | src/plugins/exec/exec-config.c | 191 | ||||
-rw-r--r-- | src/plugins/exec/exec-config.h | 42 | ||||
-rw-r--r-- | src/plugins/exec/exec.c | 587 | ||||
-rw-r--r-- | src/plugins/exec/exec.h | 79 |
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 */ |