From 97254780d655dd29b4093e52c6930aecaecca2eb Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Mon, 10 Mar 2014 14:26:23 +0100 Subject: [PATCH 01/35] exec: add exec plugin --- CMakeLists.txt | 1 + configure.ac | 19 ++ debian/weechat-plugins.install | 1 + doc/docgen.py | 1 + po/POTFILES.in | 8 + po/srcfiles.cmake | 8 + src/plugins/CMakeLists.txt | 4 + src/plugins/Makefile.am | 12 +- src/plugins/exec/CMakeLists.txt | 29 ++ src/plugins/exec/Makefile.am | 38 +++ src/plugins/exec/exec-command.c | 409 +++++++++++++++++++++++ src/plugins/exec/exec-command.h | 25 ++ src/plugins/exec/exec-completion.c | 73 +++++ src/plugins/exec/exec-completion.h | 25 ++ src/plugins/exec/exec-config.c | 151 +++++++++ src/plugins/exec/exec-config.h | 38 +++ src/plugins/exec/exec.c | 500 +++++++++++++++++++++++++++++ src/plugins/exec/exec.h | 67 ++++ weechat.cygport.in | 1 + 19 files changed, 1406 insertions(+), 4 deletions(-) create mode 100644 src/plugins/exec/CMakeLists.txt create mode 100644 src/plugins/exec/Makefile.am create mode 100644 src/plugins/exec/exec-command.c create mode 100644 src/plugins/exec/exec-command.h create mode 100644 src/plugins/exec/exec-completion.c create mode 100644 src/plugins/exec/exec-completion.h create mode 100644 src/plugins/exec/exec-config.c create mode 100644 src/plugins/exec/exec-config.h create mode 100644 src/plugins/exec/exec.c create mode 100644 src/plugins/exec/exec.h 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 +# +# 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 . +# + +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 +# +# 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 . +# + +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 + * + * 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 . + */ + +#include +#include +#include +#include + +#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 ] [-name ] " + " || -in " + " || -signal " + " || -kill " + " || -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 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 + * + * 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 . + */ + +#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 + * + * 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 . + */ + +#include +#include +#include + +#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 + * + * 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 . + */ + +#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 + * + * 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 . + */ + +#include +#include +#include + +#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 + * + * 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 . + */ + +#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 + * + * 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 . + */ + +#include +#include +#include +#include + +#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 "); +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 + * + * 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 . + */ + +#ifndef __WEECHAT_EXEC_H +#define __WEECHAT_EXEC_H 1 + +#include + +#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 From 9355c867edc8f894c6e2936eb066f828668168e6 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 08:24:07 +0100 Subject: [PATCH 02/35] exec: add tag "exec_rc" for line with command return code, remove empty line displayed before --- src/plugins/exec/exec.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/plugins/exec/exec.c b/src/plugins/exec/exec.c index bbdb33965..5758485f3 100644 --- a/src/plugins/exec/exec.c +++ b/src/plugins/exec/exec.c @@ -246,19 +246,19 @@ exec_end_command (struct t_exec_cmd *exec_cmd, int return_code) /* 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); + weechat_printf_tags (ptr_buffer, "exec_rc", + "%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); + weechat_printf_tags (ptr_buffer, "exec_rc", + _("%s%s: unexpected end of command \"%s\""), + weechat_prefix ("error"), EXEC_PLUGIN_NAME, + exec_cmd->command); } } From ee2cb329f4c2721bee63ec1966b1a953e47081aa Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 09:52:08 +0100 Subject: [PATCH 03/35] exec: display command return code after output (stdout/stderr) --- src/plugins/exec/exec.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/plugins/exec/exec.c b/src/plugins/exec/exec.c index 5758485f3..9ab44cf9a 100644 --- a/src/plugins/exec/exec.c +++ b/src/plugins/exec/exec.c @@ -243,29 +243,31 @@ exec_end_command (struct t_exec_cmd *exec_cmd, int return_code) ptr_buffer = weechat_buffer_search (exec_cmd->buffer_plugin, exec_cmd->buffer_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 output is NOT sent to buffer) */ if (!exec_cmd->output_to_buffer) { if (return_code >= 0) { weechat_printf_tags (ptr_buffer, "exec_rc", - "%s: end of command \"%s\" (rc=%d)", - EXEC_PLUGIN_NAME, exec_cmd->command, - return_code); + _("%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%s: unexpected end of command \"%s\""), + _("%s%s: unexpected end of command %d " + "(\"%s\")"), weechat_prefix ("error"), EXEC_PLUGIN_NAME, - exec_cmd->command); + exec_cmd->number, 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; From 3f50356a14f299c57ef9b164c0c5a1c1321d5699 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 10:41:55 +0100 Subject: [PATCH 04/35] exec: remove error prefix in case of unexpected end of a command --- src/plugins/exec/exec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/exec/exec.c b/src/plugins/exec/exec.c index 9ab44cf9a..3d9ffd2ff 100644 --- a/src/plugins/exec/exec.c +++ b/src/plugins/exec/exec.c @@ -261,10 +261,10 @@ exec_end_command (struct t_exec_cmd *exec_cmd, int return_code) else { weechat_printf_tags (ptr_buffer, "exec_rc", - _("%s%s: unexpected end of command %d " + _("%s: unexpected end of command %d " "(\"%s\")"), - weechat_prefix ("error"), EXEC_PLUGIN_NAME, - exec_cmd->number, exec_cmd->command); + EXEC_PLUGIN_NAME, exec_cmd->number, + exec_cmd->command); } } From 2527c282c378f7401c0adb80221b691582b84989 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 11:18:07 +0100 Subject: [PATCH 05/35] exec: implement options -signal/-kill/-killall in command /exec --- src/plugins/exec/exec-command.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index 0f07e02c6..016541e3f 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -218,9 +218,7 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, return WEECHAT_RC_ERROR; ptr_exec_cmd = exec_command_search_running_id (argv[2]); if (ptr_exec_cmd) - { - /* TODO: send signal to the process */ - } + weechat_hook_set (ptr_exec_cmd->hook, "signal", argv[3]); return WEECHAT_RC_OK; } @@ -231,9 +229,7 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, 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 */ - } + weechat_hook_set (ptr_exec_cmd->hook, "signal", "kill"); return WEECHAT_RC_OK; } @@ -243,7 +239,10 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd; ptr_exec_cmd = ptr_exec_cmd->next_cmd) { - /* TODO: send KILL signal to the process */ + if (ptr_exec_cmd->hook) + { + weechat_hook_set (ptr_exec_cmd->hook, "signal", "kill"); + } } return WEECHAT_RC_OK; } From 67b892d1fd0602e3ae3d3ea58dae3abcc42f1dfd Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 11:23:35 +0100 Subject: [PATCH 06/35] exec: add list of signal names in /help exec --- src/plugins/exec/exec-command.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index 016541e3f..37221704d 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -395,9 +395,9 @@ exec_command_init () " -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 9\"\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 kill\"\n" "-killall: kill all running processes\n" " number: command number"), "-list" From 29e498d00291d495389edbbaa3694045533ff22b Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 11:27:01 +0100 Subject: [PATCH 07/35] exec: replace number by id in /help exec --- src/plugins/exec/exec-command.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index 37221704d..d50afd206 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -399,7 +399,8 @@ exec_command_init () "or one of these names: hup, int, quit, kill, term, usr1, usr2\n" " -kill: alias of \"-signal kill\"\n" "-killall: kill all running processes\n" - " number: command number"), + " id: command identifier: either its number or name (if set " + "with \"-name xxx\")"), "-list" " || -nosh|-stdin|-o|-timeout|-name|%*" " || -in|-signal|-kill %(exec_commands_ids)" From e3de3e6dda9a945c1d7e6991dc36a22c6bbd3947 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 11:45:19 +0100 Subject: [PATCH 08/35] exec: add option "-set" in command /exec --- src/plugins/exec/exec-command.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index d50afd206..229682a6c 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -247,6 +247,17 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, 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; + } + /* parse command options */ command_index = -1; use_shell = 1; @@ -383,7 +394,8 @@ exec_command_init () " || -in " " || -signal " " || -kill " - " || -killall"), + " || -killall" + " || -set "), 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 " @@ -394,16 +406,21 @@ exec_command_init () "-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" + " id: command identifier: either its number or name (if set " + "with \"-name xxx\")" " -in: send text on standard input of process\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 kill\"\n" "-killall: kill all running processes\n" - " id: command identifier: either its number or name (if set " - "with \"-name xxx\")"), + " -set: set a hook property (see function hook_set in plugin API " + "reference)\n" + "property: property for function hook_set\n" + " value: new value for property"), "-list" " || -nosh|-stdin|-o|-timeout|-name|%*" " || -in|-signal|-kill %(exec_commands_ids)" - " || -killall", + " || -killall" + " || -set %(exec_commands_ids) stdin|stdin_close|signal", &exec_command_exec, NULL); } From 319f2a51ec2ccb7e5a1299008aa9d9185afb1dc6 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 15:40:55 +0100 Subject: [PATCH 09/35] exec: fix typos in /help exec --- src/plugins/exec/exec-command.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index 229682a6c..6e9c2a000 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -407,7 +407,7 @@ exec_command_init () " -name: set a name for the command (to name it later with /exec)\n" " command: the command to execute\n" " id: command identifier: either its number or name (if set " - "with \"-name xxx\")" + "with \"-name xxx\")\n" " -in: send text on standard input of process\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" @@ -415,8 +415,8 @@ exec_command_init () "-killall: kill all running processes\n" " -set: set a hook property (see function hook_set in plugin API " "reference)\n" - "property: property for function hook_set\n" - " value: new value for property"), + "property: hook property\n" + " value: new value for hook property"), "-list" " || -nosh|-stdin|-o|-timeout|-name|%*" " || -in|-signal|-kill %(exec_commands_ids)" From 5708b3977c1e5b9d45639809d54a3d29cd1f92f6 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 15:41:11 +0100 Subject: [PATCH 10/35] exec: update plugin API version --- src/plugins/weechat-plugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/weechat-plugin.h b/src/plugins/weechat-plugin.h index cea485f7b..6bfb54bb8 100644 --- a/src/plugins/weechat-plugin.h +++ b/src/plugins/weechat-plugin.h @@ -57,7 +57,7 @@ struct timeval; * please change the date with current one; for a second change at same * date, increment the 01, otherwise please keep 01. */ -#define WEECHAT_PLUGIN_API_VERSION "20140304-01" +#define WEECHAT_PLUGIN_API_VERSION "20140311-01" /* macros for defining plugin infos */ #define WEECHAT_PLUGIN_NAME(__name) \ From 281cfdda94c164b1e629247a259d7e26a515335e Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 17:51:06 +0100 Subject: [PATCH 11/35] exec: add option "-bg" in command /exec --- src/plugins/exec/exec-command.c | 29 +++++++++++++++++++++++------ src/plugins/exec/exec.c | 9 +++++++-- src/plugins/exec/exec.h | 1 + 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index 6e9c2a000..cc3bfd894 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -172,7 +172,8 @@ 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; + int i, command_index, use_shell, detached, pipe_stdin, output_to_buffer; + int length; long timeout; char *error, *ptr_name, *text; struct t_exec_cmd *ptr_exec_cmd, *new_exec_cmd; @@ -261,6 +262,7 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, /* parse command options */ command_index = -1; use_shell = 1; + detached = 0; pipe_stdin = 0; timeout = 0; output_to_buffer = 0; @@ -272,12 +274,20 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, { use_shell = 0; } + else if (weechat_strcasecmp (argv[i], "-bg") == 0) + { + if (output_to_buffer) + return WEECHAT_RC_ERROR; + detached = 1; + } else if (weechat_strcasecmp (argv[i], "-stdin") == 0) { pipe_stdin = 1; } else if (weechat_strcasecmp (argv[i], "-o") == 0) { + if (detached) + return WEECHAT_RC_ERROR; output_to_buffer = 1; } else if (weechat_strcasecmp (argv[i], "-timeout") == 0) @@ -329,6 +339,10 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, weechat_hashtable_set (options_cmd, "arg1", "-c"); weechat_hashtable_set (options_cmd, "arg2", argv_eol[command_index]); } + if (pipe_stdin) + weechat_hashtable_set (options_cmd, "stdin", "1"); + if (detached) + weechat_hashtable_set (options_cmd, "detached", "1"); if (weechat_exec_plugin->debug >= 1) { weechat_printf (NULL, "%s: executing command: \"%s%s%s\"", @@ -337,8 +351,6 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, 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, @@ -360,6 +372,7 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, new_exec_cmd->name = (ptr_name) ? strdup (ptr_name) : NULL; new_exec_cmd->command = strdup (argv_eol[command_index]); + new_exec_cmd->detached = detached; new_exec_cmd->buffer_plugin = strdup (weechat_buffer_get_string (buffer, "plugin")); new_exec_cmd->buffer_name = strdup (weechat_buffer_get_string (buffer, @@ -390,7 +403,8 @@ exec_command_init () "exec", N_("execute external commands"), N_("-list" - " || [-nosh] [-stdin] [-o] [-timeout ] [-name ] " + " || [-nosh] [-bg] [-stdin] [-o] [-timeout ] [-name ] " + "" " || -in " " || -signal " " || -kill " @@ -400,9 +414,12 @@ exec_command_init () " -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" " -stdin: create a pipe for sending data to the process (with " "/exec -in)\n" - " -o: send output of command to the current buffer\n" + " -o: send output of command to the current buffer " + "(not compatible with option -bg)\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" @@ -418,7 +435,7 @@ exec_command_init () "property: hook property\n" " value: new value for hook property"), "-list" - " || -nosh|-stdin|-o|-timeout|-name|%*" + " || -nosh|-bg|-stdin|-o|-timeout|-name|%*" " || -in|-signal|-kill %(exec_commands_ids)" " || -killall" " || -set %(exec_commands_ids) stdin|stdin_close|signal", diff --git a/src/plugins/exec/exec.c b/src/plugins/exec/exec.c index 3d9ffd2ff..6cc68710a 100644 --- a/src/plugins/exec/exec.c +++ b/src/plugins/exec/exec.c @@ -118,6 +118,7 @@ exec_add () 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->buffer_plugin = NULL; @@ -247,8 +248,11 @@ exec_end_command (struct t_exec_cmd *exec_cmd, int return_code) exec_command_display_output (exec_cmd, ptr_buffer, 1); exec_command_display_output (exec_cmd, ptr_buffer, 0); - /* display return code (only if output is NOT sent to buffer) */ - if (!exec_cmd->output_to_buffer) + /* + * 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) { if (return_code >= 0) { @@ -410,6 +414,7 @@ exec_print_log () 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 (" buffer_plugin . . . . . : '%s'", ptr_exec_cmd->buffer_plugin); diff --git a/src/plugins/exec/exec.h b/src/plugins/exec/exec.h index 21490aff6..6d7c4f5fa 100644 --- a/src/plugins/exec/exec.h +++ b/src/plugins/exec/exec.h @@ -33,6 +33,7 @@ struct t_exec_cmd 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 */ From 2dc6eeaee29ee42fa89dfb6282e1217abcb5e527 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 18:11:52 +0100 Subject: [PATCH 12/35] exec: add option "-del" in command /exec --- src/plugins/exec/exec-command.c | 69 ++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index cc3bfd894..a706c3d63 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -173,10 +173,10 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, char **argv, char **argv_eol) { int i, command_index, use_shell, detached, pipe_stdin, output_to_buffer; - int length; + int length, count; long timeout; char *error, *ptr_name, *text; - struct t_exec_cmd *ptr_exec_cmd, *new_exec_cmd; + struct t_exec_cmd *ptr_exec_cmd, *ptr_next_exec_cmd, *new_exec_cmd; struct t_hashtable *options_cmd; struct t_infolist *ptr_infolist; @@ -259,6 +259,61 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, 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; + } + /* parse command options */ command_index = -1; use_shell = 1; @@ -409,7 +464,8 @@ exec_command_init () " || -signal " " || -kill " " || -killall" - " || -set "), + " || -set " + " || -del |-all [...]"), 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 " @@ -433,11 +489,14 @@ exec_command_init () " -set: set a hook property (see function hook_set in plugin API " "reference)\n" "property: hook property\n" - " value: new value for hook property"), + " value: new value for hook property\n" + " -del: delete a terminated command\n" + " -all: delete all terminated commands"), "-list" " || -nosh|-bg|-stdin|-o|-timeout|-name|%*" " || -in|-signal|-kill %(exec_commands_ids)" " || -killall" - " || -set %(exec_commands_ids) stdin|stdin_close|signal", + " || -set %(exec_commands_ids) stdin|stdin_close|signal" + " || -del %(exec_commands_ids)|-all %(exec_commands_ids)|%*", &exec_command_exec, NULL); } From d5e1a52e24a9a43492820a196074884bea8a24cd Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 20:13:28 +0100 Subject: [PATCH 13/35] exec: fix crash when giving bad id to command /exec -in --- src/plugins/exec/exec-command.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index a706c3d63..e576be871 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -198,7 +198,7 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, if (argc < 4) return WEECHAT_RC_ERROR; ptr_exec_cmd = exec_command_search_running_id (argv[2]); - if (ptr_exec_cmd->hook) + if (ptr_exec_cmd && ptr_exec_cmd->hook) { length = strlen (argv_eol[3]) + 1 + 1; text = malloc (length); From 9543f9c03494a9ced1dd0d5529c43b931e7d9d0a Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Tue, 11 Mar 2014 21:11:54 +0100 Subject: [PATCH 14/35] core: add missing \0 at the end of stderr buffer in hook_process --- ChangeLog.asciidoc | 1 + src/core/wee-hook.c | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ChangeLog.asciidoc b/ChangeLog.asciidoc index a716e0091..579ba5dfb 100644 --- a/ChangeLog.asciidoc +++ b/ChangeLog.asciidoc @@ -15,6 +15,7 @@ http://weechat.org/files/releasenotes/ReleaseNotes-devel.html[release notes] == Version 0.4.4 (under dev) +* core: add missing \0 at the end of stderr buffer in hook_process * core: fix detection of terminated process in hook_process * core: set option weechat.look.buffer_search_where to prefix_message by default * core: fix "/window scroll -N" on a buffer with free content diff --git a/src/core/wee-hook.c b/src/core/wee-hook.c index 415879530..0334464f3 100644 --- a/src/core/wee-hook.c +++ b/src/core/wee-hook.c @@ -1607,15 +1607,15 @@ hook_process_child (struct t_hook *hook_process) void hook_process_send_buffers (struct t_hook *hook_process, int callback_rc) { - int i, size; + int size; /* add '\0' at end of stdout and stderr */ - for (i = 0; i < 2; i++) - { - size = HOOK_PROCESS(hook_process, buffer_size[i]); - if (size > 0) - HOOK_PROCESS(hook_process, buffer[i])[size] = '\0'; - } + size = HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDOUT]); + if (size > 0) + HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDOUT])[size] = '\0'; + size = HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDERR]); + if (size > 0) + HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDERR])[size] = '\0'; /* send buffers to callback */ (void) (HOOK_PROCESS(hook_process, callback)) From 07908366953fbf6bedba565182c84d5c43116977 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Wed, 12 Mar 2014 09:57:39 +0100 Subject: [PATCH 15/35] exec: add option exec.command.default_options --- src/plugins/exec/exec-command.c | 237 ++++++++++++++++++++------------ src/plugins/exec/exec-command.h | 11 ++ src/plugins/exec/exec-config.c | 40 ++++++ src/plugins/exec/exec-config.h | 4 + 4 files changed, 207 insertions(+), 85 deletions(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index e576be871..779ac2e48 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -26,6 +26,7 @@ #include "../weechat-plugin.h" #include "exec.h" +#include "exec-command.h" #include "exec-config.h" @@ -164,6 +165,88 @@ exec_command_search_running_id (const char *id) 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], "-l") == 0) + { + cmd_options->output_to_buffer = 0; + } + else if (weechat_strcasecmp (argv[i], "-o") == 0) + { + cmd_options->output_to_buffer = 1; + } + 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_name = argv[i]; + } + else + { + if (set_command_index) + { + cmd_options->command_index = i; + break; + } + else + return 0; + } + } + + return 1; +} + /* * Callback for command "/exec": manage executed commands. */ @@ -172,12 +255,11 @@ int exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, char **argv, char **argv_eol) { - int i, command_index, use_shell, detached, pipe_stdin, output_to_buffer; - int length, count; - long timeout; - char *error, *ptr_name, *text; + int i, length, count; + char *text; struct t_exec_cmd *ptr_exec_cmd, *ptr_next_exec_cmd, *new_exec_cmd; - struct t_hashtable *options_cmd; + struct t_exec_cmd_options cmd_options; + struct t_hashtable *process_options; struct t_infolist *ptr_infolist; /* make C compiler happy */ @@ -315,60 +397,35 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, } /* parse command options */ - command_index = -1; - use_shell = 1; - detached = 0; - pipe_stdin = 0; - timeout = 0; - output_to_buffer = 0; - ptr_name = NULL; + 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.output_to_buffer = 0; + cmd_options.ptr_name = NULL; - for (i = 1; i < argc; i++) + /* parse default options */ + if (!exec_command_parse_options (&cmd_options, + exec_config_cmd_num_options, + exec_config_cmd_options, + 0, 0)) { - if (weechat_strcasecmp (argv[i], "-nosh") == 0) - { - use_shell = 0; - } - else if (weechat_strcasecmp (argv[i], "-bg") == 0) - { - if (output_to_buffer) - return WEECHAT_RC_ERROR; - detached = 1; - } - else if (weechat_strcasecmp (argv[i], "-stdin") == 0) - { - pipe_stdin = 1; - } - else if (weechat_strcasecmp (argv[i], "-o") == 0) - { - if (detached) - return WEECHAT_RC_ERROR; - 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; - } + weechat_printf (NULL, + _("%s%s: invalid options in option " + "exec.command.default_options"), + weechat_prefix ("error"), EXEC_PLUGIN_NAME); + return WEECHAT_RC_ERROR; } - if (command_index < 0) + if (!exec_command_parse_options (&cmd_options, argc, argv, 1, 1)) + return WEECHAT_RC_ERROR; + + /* "-bg" and "-o" are incompatible options */ + if (cmd_options.detached && cmd_options.output_to_buffer) + return WEECHAT_RC_ERROR; + + /* command not found? */ + if (cmd_options.command_index < 0) return WEECHAT_RC_ERROR; new_exec_cmd = exec_add (); @@ -376,44 +433,45 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, 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) + 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; } /* run the command in background */ - if (use_shell) + if (cmd_options.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]); + weechat_hashtable_set (process_options, "arg1", "-c"); + weechat_hashtable_set (process_options, "arg2", + argv_eol[cmd_options.command_index]); } - if (pipe_stdin) - weechat_hashtable_set (options_cmd, "stdin", "1"); - if (detached) - weechat_hashtable_set (options_cmd, "detached", "1"); + if (cmd_options.pipe_stdin) + weechat_hashtable_set (process_options, "stdin", "1"); + if (cmd_options.detached) + weechat_hashtable_set (process_options, "detached", "1"); 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) ? "" : "'"); + (cmd_options.use_shell) ? "" : "sh -c '", + argv_eol[cmd_options.command_index], + (cmd_options.use_shell) ? "" : "'"); } new_exec_cmd->hook = weechat_hook_process_hashtable ( - (use_shell) ? "sh" : argv_eol[command_index], - options_cmd, - (int)(timeout * 1000), + (cmd_options.use_shell) ? "sh" : argv_eol[cmd_options.command_index], + process_options, + cmd_options.timeout * 1000, &exec_process_cb, new_exec_cmd); - weechat_hashtable_free (options_cmd); + weechat_hashtable_free (process_options); if (!new_exec_cmd->hook) { @@ -421,18 +479,19 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, weechat_printf (NULL, _("%s%s: failed to run command \"%s\""), weechat_prefix ("error"), EXEC_PLUGIN_NAME, - argv_eol[command_index]); + argv_eol[cmd_options.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->detached = detached; + new_exec_cmd->name = (cmd_options.ptr_name) ? + strdup (cmd_options.ptr_name) : NULL; + new_exec_cmd->command = strdup (argv_eol[cmd_options.command_index]); + new_exec_cmd->detached = cmd_options.detached; 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; + new_exec_cmd->output_to_buffer = cmd_options.output_to_buffer; ptr_infolist = weechat_infolist_get ("hook", new_exec_cmd->hook, NULL); if (ptr_infolist) { @@ -458,8 +517,8 @@ exec_command_init () "exec", N_("execute external commands"), N_("-list" - " || [-nosh] [-bg] [-stdin] [-o] [-timeout ] [-name ] " - "" + " || [-sh|-nosh] [-bg|-nobg] [-stdin|-nostdin] [-l|-o] " + "[-timeout ] [-name ] " " || -in " " || -signal " " || -kill " @@ -467,13 +526,18 @@ exec_command_init () " || -set " " || -del |-all [...]"), 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" + " -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" + " -l: display locally output of command on current buffer " + "(default)\n" " -o: send output of command to the current buffer " "(not compatible with option -bg)\n" "-timeout: set a timeout for the command (in seconds)\n" @@ -491,9 +555,12 @@ exec_command_init () "property: hook property\n" " value: new value for hook property\n" " -del: delete a terminated command\n" - " -all: delete all terminated commands"), + " -all: delete all terminated commands\n" + "\n" + "Default options can be set in the option " + "exec.command.default_options."), "-list" - " || -nosh|-bg|-stdin|-o|-timeout|-name|%*" + " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-l|-o|-timeout|-name|%*" " || -in|-signal|-kill %(exec_commands_ids)" " || -killall" " || -set %(exec_commands_ids) stdin|stdin_close|signal" diff --git a/src/plugins/exec/exec-command.h b/src/plugins/exec/exec-command.h index 312033c41..4132fd853 100644 --- a/src/plugins/exec/exec-command.h +++ b/src/plugins/exec/exec-command.h @@ -20,6 +20,17 @@ #ifndef __WEECHAT_EXEC_COMMAND_H #define __WEECHAT_EXEC_COMMAND_H 1 +struct t_exec_cmd_options +{ + int command_index; + int use_shell; + int detached; + int pipe_stdin; + int timeout; + int output_to_buffer; + const char *ptr_name; +}; + extern void exec_command_init (); #endif /* __WEECHAT_EXEC_COMMAND_H */ diff --git a/src/plugins/exec/exec-config.c b/src/plugins/exec/exec-config.c index 4c5cb1c69..05ecc027e 100644 --- a/src/plugins/exec/exec-config.c +++ b/src/plugins/exec/exec-config.c @@ -32,6 +32,7 @@ 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 */ @@ -39,6 +40,30 @@ struct t_config_option *exec_config_command_purge_delay; 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. */ @@ -82,6 +107,14 @@ exec_config_init () 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", @@ -148,4 +181,11 @@ 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 index 331f057de..9963fd092 100644 --- a/src/plugins/exec/exec-config.h +++ b/src/plugins/exec/exec-config.h @@ -25,11 +25,15 @@ 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 (); From e8de09662500543d774430a3aa722cfd7bd16c5f Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Wed, 12 Mar 2014 11:54:00 +0100 Subject: [PATCH 16/35] exec: add options "-n"/"-ns" in command /exec (display output in a new buffer) --- po/POTFILES.in | 2 + po/srcfiles.cmake | 2 + src/plugins/exec/CMakeLists.txt | 1 + src/plugins/exec/Makefile.am | 2 + src/plugins/exec/exec-buffer.c | 130 ++++++++++++++++++++++++++++++++ src/plugins/exec/exec-buffer.h | 27 +++++++ src/plugins/exec/exec-command.c | 106 ++++++++++++++++++-------- src/plugins/exec/exec-command.h | 15 ++-- src/plugins/exec/exec.c | 33 ++++---- src/plugins/exec/exec.h | 3 +- 10 files changed, 268 insertions(+), 53 deletions(-) create mode 100644 src/plugins/exec/exec-buffer.c create mode 100644 src/plugins/exec/exec-buffer.h diff --git a/po/POTFILES.in b/po/POTFILES.in index f3f120ee0..d0dadead2 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -114,6 +114,8 @@ ./src/plugins/aspell/weechat-aspell-speller.h ./src/plugins/charset/charset.c ./src/plugins/exec/exec.c +./src/plugins/exec/exec-buffer.c +./src/plugins/exec/exec-buffer.h ./src/plugins/exec/exec-command.c ./src/plugins/exec/exec-command.h ./src/plugins/exec/exec-completion.c diff --git a/po/srcfiles.cmake b/po/srcfiles.cmake index 2fce5d3d2..886397caa 100644 --- a/po/srcfiles.cmake +++ b/po/srcfiles.cmake @@ -115,6 +115,8 @@ SET(WEECHAT_SOURCES ./src/plugins/aspell/weechat-aspell-speller.h ./src/plugins/charset/charset.c ./src/plugins/exec/exec.c +./src/plugins/exec/exec-buffer.c +./src/plugins/exec/exec-buffer.h ./src/plugins/exec/exec-command.c ./src/plugins/exec/exec-command.h ./src/plugins/exec/exec-completion.c diff --git a/src/plugins/exec/CMakeLists.txt b/src/plugins/exec/CMakeLists.txt index bd86d831f..a5286eabd 100644 --- a/src/plugins/exec/CMakeLists.txt +++ b/src/plugins/exec/CMakeLists.txt @@ -19,6 +19,7 @@ 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) diff --git a/src/plugins/exec/Makefile.am b/src/plugins/exec/Makefile.am index 3fa30374f..cad543311 100644 --- a/src/plugins/exec/Makefile.am +++ b/src/plugins/exec/Makefile.am @@ -25,6 +25,8 @@ 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 \ diff --git a/src/plugins/exec/exec-buffer.c b/src/plugins/exec/exec-buffer.c new file mode 100644 index 000000000..ce36db0a4 --- /dev/null +++ b/src/plugins/exec/exec-buffer.c @@ -0,0 +1,130 @@ +/* + * exec-buffer.c - buffers with output of commands + * + * Copyright (C) 2014 Sébastien Helleu + * + * 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 . + */ + +#include +#include +#include + +#include "../weechat-plugin.h" +#include "exec.h" +#include "exec-buffer.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) +{ + /* make C compiler happy */ + (void) data; + + /* close buffer */ + if (strcmp (input_data, "q") == 0) + { + weechat_buffer_close (buffer); + return WEECHAT_RC_OK; + } + + return WEECHAT_RC_OK; +} + +/* + * Callback called when 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) + return new_buffer; + + 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"); + + 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 + * + * 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 . + */ + +#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 index 779ac2e48..b81e6c181 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -26,6 +26,7 @@ #include "../weechat-plugin.h" #include "exec.h" +#include "exec-buffer.h" #include "exec-command.h" #include "exec-config.h" @@ -210,10 +211,22 @@ exec_command_parse_options (struct t_exec_cmd_options *cmd_options, 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 = 2; + } + else if (weechat_strcasecmp (argv[i], "-ns") == 0) + { + cmd_options->output_to_buffer = 0; + cmd_options->new_buffer = 1; } else if (weechat_strcasecmp (argv[i], "-timeout") == 0) { @@ -256,11 +269,12 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, char **argv, char **argv_eol) { int i, length, count; - char *text; + char *text, str_buffer[512]; struct t_exec_cmd *ptr_exec_cmd, *ptr_next_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; /* make C compiler happy */ (void) data; @@ -403,6 +417,7 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, cmd_options.pipe_stdin = 0; cmd_options.timeout = 0; cmd_options.output_to_buffer = 0; + cmd_options.new_buffer = 0; cmd_options.ptr_name = NULL; /* parse default options */ @@ -420,8 +435,9 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, if (!exec_command_parse_options (&cmd_options, argc, argv, 1, 1)) return WEECHAT_RC_ERROR; - /* "-bg" and "-o" are incompatible options */ - if (cmd_options.detached && cmd_options.output_to_buffer) + /* 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? */ @@ -443,8 +459,6 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, exec_free (new_exec_cmd); return WEECHAT_RC_ERROR; } - - /* run the command in background */ if (cmd_options.use_shell) { /* command will be: sh -c "command arguments..." */ @@ -456,6 +470,40 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, 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_name) ? + strdup (cmd_options.ptr_name) : NULL; + new_exec_cmd->command = strdup (argv_eol[cmd_options.command_index]); + new_exec_cmd->detached = cmd_options.detached; + new_exec_cmd->output_to_buffer = cmd_options.output_to_buffer; + if (cmd_options.new_buffer) + { + /* output in a new buffer */ + 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.new_buffer > 1)); + if (new_buffer) + { + new_exec_cmd->buffer_full_name = + strdup (weechat_buffer_get_string (new_buffer, "full_name")); + } + } + else + { + new_exec_cmd->buffer_full_name = + strdup (weechat_buffer_get_string (buffer, "full_name")); + } + + /* execute the command */ if (weechat_exec_plugin->debug >= 1) { weechat_printf (NULL, "%s: executing command: \"%s%s%s\"", @@ -471,37 +519,30 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, &exec_process_cb, new_exec_cmd); - weechat_hashtable_free (process_options); - - if (!new_exec_cmd->hook) + 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]); - return WEECHAT_RC_OK; } - new_exec_cmd->name = (cmd_options.ptr_name) ? - strdup (cmd_options.ptr_name) : NULL; - new_exec_cmd->command = strdup (argv_eol[cmd_options.command_index]); - new_exec_cmd->detached = cmd_options.detached; - 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 = cmd_options.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); - } + weechat_hashtable_free (process_options); return WEECHAT_RC_OK; } @@ -517,7 +558,7 @@ exec_command_init () "exec", N_("execute external commands"), N_("-list" - " || [-sh|-nosh] [-bg|-nobg] [-stdin|-nostdin] [-l|-o] " + " || [-sh|-nosh] [-bg|-nobg] [-stdin|-nostdin] [-l|-o|-n] " "[-timeout ] [-name ] " " || -in " " || -signal " @@ -531,7 +572,7 @@ exec_command_init () "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" + "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" @@ -540,6 +581,9 @@ exec_command_init () "(default)\n" " -o: send output of command to the current buffer " "(not compatible with option -bg)\n" + " -n: display output of command in a new buffer (not compatible " + "with -bg)\n" + " -ns: same as -n, but don't switch to the new 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" @@ -560,7 +604,7 @@ exec_command_init () "Default options can be set in the option " "exec.command.default_options."), "-list" - " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-l|-o|-timeout|-name|%*" + " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-l|-o|-n|-timeout|-name|%*" " || -in|-signal|-kill %(exec_commands_ids)" " || -killall" " || -set %(exec_commands_ids) stdin|stdin_close|signal" diff --git a/src/plugins/exec/exec-command.h b/src/plugins/exec/exec-command.h index 4132fd853..c62277a80 100644 --- a/src/plugins/exec/exec-command.h +++ b/src/plugins/exec/exec-command.h @@ -22,13 +22,14 @@ struct t_exec_cmd_options { - int command_index; - int use_shell; - int detached; - int pipe_stdin; - int timeout; - int output_to_buffer; - const char *ptr_name; + 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) */ + int output_to_buffer; /* 1 if output is sent to buffer */ + int new_buffer; /* output in a new buffer */ + const char *ptr_name; /* name of command */ }; extern void exec_command_init (); diff --git a/src/plugins/exec/exec.c b/src/plugins/exec/exec.c index 6cc68710a..ec7ebe870 100644 --- a/src/plugins/exec/exec.c +++ b/src/plugins/exec/exec.c @@ -26,6 +26,7 @@ #include "../weechat-plugin.h" #include "exec.h" +#include "exec-buffer.h" #include "exec-command.h" #include "exec-completion.h" #include "exec-config.h" @@ -121,9 +122,8 @@ exec_add () new_exec_cmd->detached = 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->buffer_full_name = NULL; new_exec_cmd->stdout_size = 0; new_exec_cmd->stdout = NULL; new_exec_cmd->stderr_size = 0; @@ -241,8 +241,7 @@ 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); + 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); @@ -368,10 +367,8 @@ exec_free (struct t_exec_cmd *exec_cmd) 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->buffer_full_name) + free (exec_cmd->buffer_full_name); if (exec_cmd->stdout) free (exec_cmd->stdout); if (exec_cmd->stderr) @@ -417,9 +414,8 @@ exec_print_log () 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 (" 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 (" buffer_full_name. . . . : '%s'", ptr_exec_cmd->buffer_full_name); 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); @@ -467,9 +463,7 @@ exec_debug_dump_cb (void *data, const char *signal, const char *type_data, int weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[]) { - /* make C compiler happy */ - (void) argc; - (void) argv; + int i, upgrading; weechat_plugin = plugin; @@ -486,6 +480,19 @@ weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[]) /* 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; } diff --git a/src/plugins/exec/exec.h b/src/plugins/exec/exec.h index 6d7c4f5fa..0b5d2f94c 100644 --- a/src/plugins/exec/exec.h +++ b/src/plugins/exec/exec.h @@ -38,9 +38,8 @@ struct t_exec_cmd 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 */ + char *buffer_full_name; /* buffer where output is displayed */ /* command output */ int stdout_size; /* number of bytes in stdout */ From 2962ce55d642049dd9bf5e40d6e4c83522fc1696 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Wed, 12 Mar 2014 11:56:09 +0100 Subject: [PATCH 17/35] exec: add missing completion of "-ns" in command /exec --- src/plugins/exec/exec-command.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index b81e6c181..135243fad 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -604,7 +604,7 @@ exec_command_init () "Default options can be set in the option " "exec.command.default_options."), "-list" - " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-l|-o|-n|-timeout|-name|%*" + " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-l|-o|-n|-ns|-timeout|-name|%*" " || -in|-signal|-kill %(exec_commands_ids)" " || -killall" " || -set %(exec_commands_ids) stdin|stdin_close|signal" From 442bc80da63e09f51d589cadef0cbf8e1415791c Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Wed, 12 Mar 2014 12:04:23 +0100 Subject: [PATCH 18/35] exec: add option "-inclose" in command /exec --- src/plugins/exec/exec-command.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index 135243fad..39db0565c 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -308,6 +308,30 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, 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) { @@ -561,6 +585,7 @@ exec_command_init () " || [-sh|-nosh] [-bg|-nobg] [-stdin|-nostdin] [-l|-o|-n] " "[-timeout ] [-name ] " " || -in " + " || -inclose []" " || -signal " " || -kill " " || -killall" @@ -590,6 +615,8 @@ exec_command_init () " 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 kill\"\n" @@ -605,7 +632,7 @@ exec_command_init () "exec.command.default_options."), "-list" " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-l|-o|-n|-ns|-timeout|-name|%*" - " || -in|-signal|-kill %(exec_commands_ids)" + " || -in|-inclose|-signal|-kill %(exec_commands_ids)" " || -killall" " || -set %(exec_commands_ids) stdin|stdin_close|signal" " || -del %(exec_commands_ids)|-all %(exec_commands_ids)|%*", From 6c20e402dcb845f864ed07bad78a81077b2c2804 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Wed, 12 Mar 2014 19:13:56 +0100 Subject: [PATCH 19/35] exec: add options "-buffer" and "-sw"/"-nosw" in command /exec --- src/plugins/exec/exec-command.c | 84 ++++++++++++++++++++++++--------- src/plugins/exec/exec-command.h | 5 +- 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index 39db0565c..6fd9d02e8 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -208,6 +208,22 @@ exec_command_parse_options (struct t_exec_cmd_options *cmd_options, { 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; @@ -219,15 +235,18 @@ exec_command_parse_options (struct t_exec_cmd_options *cmd_options, cmd_options->new_buffer = 0; } else if (weechat_strcasecmp (argv[i], "-n") == 0) - { - cmd_options->output_to_buffer = 0; - cmd_options->new_buffer = 2; - } - else if (weechat_strcasecmp (argv[i], "-ns") == 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], "-timeout") == 0) { if (i + 1 >= argc) @@ -243,7 +262,7 @@ exec_command_parse_options (struct t_exec_cmd_options *cmd_options, if (i + 1 >= argc) return 0; i++; - cmd_options->ptr_name = argv[i]; + cmd_options->ptr_command_name = argv[i]; } else { @@ -440,9 +459,12 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, 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.ptr_name = NULL; + cmd_options.switch_to_buffer = 1; + cmd_options.ptr_command_name = NULL; /* parse default options */ if (!exec_command_parse_options (&cmd_options, @@ -496,14 +518,27 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, weechat_hashtable_set (process_options, "detached", "1"); /* set variables in new command (before running the command) */ - new_exec_cmd->name = (cmd_options.ptr_name) ? - strdup (cmd_options.ptr_name) : NULL; + 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; new_exec_cmd->output_to_buffer = cmd_options.output_to_buffer; - if (cmd_options.new_buffer) + if (cmd_options.ptr_buffer_name && !cmd_options.ptr_buffer) { - /* output in a new 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), @@ -514,17 +549,20 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, snprintf (str_buffer, sizeof (str_buffer), "exec.%d", new_exec_cmd->number); } - new_buffer = exec_buffer_new (str_buffer, (cmd_options.new_buffer > 1)); + 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 + else if (cmd_options.ptr_buffer) { new_exec_cmd->buffer_full_name = - strdup (weechat_buffer_get_string (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"); } /* execute the command */ @@ -582,8 +620,8 @@ exec_command_init () "exec", N_("execute external commands"), N_("-list" - " || [-sh|-nosh] [-bg|-nobg] [-stdin|-nostdin] [-l|-o|-n] " - "[-timeout ] [-name ] " + " || [-sh|-nosh] [-bg|-nobg] [-stdin|-nostdin] [-buffer ] " + "[-l|-o|-n] |-sw|-nosw] [-timeout ] [-name ] " " || -in " " || -inclose []" " || -signal " @@ -602,13 +640,16 @@ exec_command_init () " -stdin: create a pipe for sending data to the process (with " "/exec -in)\n" "-nostdin: do not create a pipe for stdin (default)\n" - " -l: display locally output of command on current buffer " - "(default)\n" - " -o: send output of command to the current buffer " + " -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" - " -ns: same as -n, but don't switch to the new buffer\n" + " -sw: switch to the output buffer (default)\n" + " -nosw: don't switch to the output 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" @@ -631,7 +672,8 @@ exec_command_init () "Default options can be set in the option " "exec.command.default_options."), "-list" - " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-l|-o|-n|-ns|-timeout|-name|%*" + " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-buffer|-l|-o|-n|-sw|-nosw|" + "-timeout|-name|%*" " || -in|-inclose|-signal|-kill %(exec_commands_ids)" " || -killall" " || -set %(exec_commands_ids) stdin|stdin_close|signal" diff --git a/src/plugins/exec/exec-command.h b/src/plugins/exec/exec-command.h index c62277a80..4984c1a49 100644 --- a/src/plugins/exec/exec-command.h +++ b/src/plugins/exec/exec-command.h @@ -27,9 +27,12 @@ struct t_exec_cmd_options 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 */ - const char *ptr_name; /* name of command */ + int switch_to_buffer; /* switch to the output buffer */ + const char *ptr_command_name; /* name of command */ }; extern void exec_command_init (); From 08bffd6f5a7a34e556a7efeab58b229fd55e219c Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Wed, 12 Mar 2014 19:18:29 +0100 Subject: [PATCH 20/35] exec: automatically disable shell if command starts with "url:" (URL download) --- src/plugins/exec/exec-command.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index 6fd9d02e8..c56b540b4 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -505,6 +505,9 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, 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..." */ @@ -652,7 +655,9 @@ exec_command_init () " -nosw: don't switch to the output 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" + " 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" From ed6ea18c30176ca0e961a2535837d54adeeef2ec Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Wed, 12 Mar 2014 19:52:04 +0100 Subject: [PATCH 21/35] exec: add options "-ln"/"-noln" (line numbers) in command /exec --- src/plugins/exec/exec-command.c | 18 ++++++++++++++++-- src/plugins/exec/exec-command.h | 1 + src/plugins/exec/exec.c | 26 ++++++++++++++++++++++---- src/plugins/exec/exec.h | 1 + 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index c56b540b4..97f318f01 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -247,6 +247,14 @@ exec_command_parse_options (struct t_exec_cmd_options *cmd_options, { 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], "-timeout") == 0) { if (i + 1 >= argc) @@ -464,6 +472,7 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, cmd_options.output_to_buffer = 0; cmd_options.new_buffer = 0; cmd_options.switch_to_buffer = 1; + cmd_options.line_numbers = -1; cmd_options.ptr_command_name = NULL; /* parse default options */ @@ -567,6 +576,8 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, if (cmd_options.switch_to_buffer) weechat_buffer_set (cmd_options.ptr_buffer, "display", "1"); } + new_exec_cmd->line_numbers = (cmd_options.line_numbers < 0) ? + cmd_options.new_buffer : cmd_options.line_numbers; /* execute the command */ if (weechat_exec_plugin->debug >= 1) @@ -624,7 +635,8 @@ exec_command_init () N_("execute external commands"), N_("-list" " || [-sh|-nosh] [-bg|-nobg] [-stdin|-nostdin] [-buffer ] " - "[-l|-o|-n] |-sw|-nosw] [-timeout ] [-name ] " + "[-l|-o|-n] |-sw|-nosw] [-ln|-noln] [-timeout ] " + "[-name ] " " || -in " " || -inclose []" " || -signal " @@ -653,6 +665,8 @@ exec_command_init () "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" "-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 " @@ -678,7 +692,7 @@ exec_command_init () "exec.command.default_options."), "-list" " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-buffer|-l|-o|-n|-sw|-nosw|" - "-timeout|-name|%*" + "-ln|-noln|-timeout|-name|%*" " || -in|-inclose|-signal|-kill %(exec_commands_ids)" " || -killall" " || -set %(exec_commands_ids) stdin|stdin_close|signal" diff --git a/src/plugins/exec/exec-command.h b/src/plugins/exec/exec-command.h index 4984c1a49..680d60be3 100644 --- a/src/plugins/exec/exec-command.h +++ b/src/plugins/exec/exec-command.h @@ -32,6 +32,7 @@ struct t_exec_cmd_options 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 */ const char *ptr_command_name; /* name of command */ }; diff --git a/src/plugins/exec/exec.c b/src/plugins/exec/exec.c index ec7ebe870..aea5d0454 100644 --- a/src/plugins/exec/exec.c +++ b/src/plugins/exec/exec.c @@ -124,6 +124,7 @@ exec_add () 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->stdout_size = 0; new_exec_cmd->stdout = NULL; new_exec_cmd->stderr_size = 0; @@ -193,8 +194,8 @@ 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; + char *ptr_output, **lines, *line, str_number[32], str_tags[1024]; + int i, num_lines, length; ptr_output = (stdout) ? exec_cmd->stdout : exec_cmd->stderr; if (!ptr_output) @@ -214,17 +215,33 @@ exec_command_display_output (struct t_exec_cmd *exec_cmd, for (i = 0; i < num_lines; i++) { if (exec_cmd->output_to_buffer) - weechat_command (buffer, lines[i]); + { + if (exec_cmd->line_numbers) + { + length = 32 + strlen (lines[i]) + 1; + line = malloc (length); + if (line) + { + snprintf (line, length, "%d. %s", i + 1, lines[i]); + weechat_command (buffer, line); + free (line); + } + } + else + 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); + snprintf (str_number, sizeof (str_number), "%d\t", i + 1); weechat_printf_tags (buffer, str_tags, "%s%s", - (stdout) ? " \t" : weechat_prefix ("error"), + (exec_cmd->line_numbers) ? str_number : " \t", lines[i]); } } @@ -416,6 +433,7 @@ exec_print_log () 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 (" 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); diff --git a/src/plugins/exec/exec.h b/src/plugins/exec/exec.h index 0b5d2f94c..5ae32708f 100644 --- a/src/plugins/exec/exec.h +++ b/src/plugins/exec/exec.h @@ -40,6 +40,7 @@ struct t_exec_cmd /* buffer */ 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 */ /* command output */ int stdout_size; /* number of bytes in stdout */ From 571a7a5dbef307489b496840fcdd07ba44ee1128 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Thu, 13 Mar 2014 07:38:16 +0100 Subject: [PATCH 22/35] exec: execute commands from input of exec buffers --- src/plugins/exec/exec-buffer.c | 15 ++ src/plugins/exec/exec-command.c | 356 +++++++++++++++++--------------- src/plugins/exec/exec-command.h | 3 + 3 files changed, 208 insertions(+), 166 deletions(-) diff --git a/src/plugins/exec/exec-buffer.c b/src/plugins/exec/exec-buffer.c index ce36db0a4..9a220785e 100644 --- a/src/plugins/exec/exec-buffer.c +++ b/src/plugins/exec/exec-buffer.c @@ -26,6 +26,7 @@ #include "../weechat-plugin.h" #include "exec.h" #include "exec-buffer.h" +#include "exec-command.h" #include "exec-config.h" @@ -37,6 +38,9 @@ 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; @@ -47,6 +51,17 @@ exec_buffer_input_cb (void *data, struct t_gui_buffer *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; } diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index 97f318f01..b54904ab1 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -287,6 +287,193 @@ exec_command_parse_options (struct t_exec_cmd_options *cmd_options, 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.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; + new_exec_cmd->output_to_buffer = cmd_options.output_to_buffer; + 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.new_buffer = 1; + } + new_exec_cmd->line_numbers = (cmd_options.line_numbers < 0) ? + cmd_options.new_buffer : cmd_options.line_numbers; + + /* 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. */ @@ -296,12 +483,8 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, char **argv, char **argv_eol) { int i, length, count; - char *text, str_buffer[512]; - struct t_exec_cmd *ptr_exec_cmd, *ptr_next_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; + char *text; + struct t_exec_cmd *ptr_exec_cmd, *ptr_next_exec_cmd; /* make C compiler happy */ (void) data; @@ -461,166 +644,7 @@ exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, return WEECHAT_RC_OK; } - /* 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.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, 1, 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; - new_exec_cmd->output_to_buffer = cmd_options.output_to_buffer; - 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"); - } - new_exec_cmd->line_numbers = (cmd_options.line_numbers < 0) ? - cmd_options.new_buffer : cmd_options.line_numbers; - - /* 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; + return exec_command_run (buffer, argc, argv, argv_eol, 1); } /* diff --git a/src/plugins/exec/exec-command.h b/src/plugins/exec/exec-command.h index 680d60be3..e0cfb7618 100644 --- a/src/plugins/exec/exec-command.h +++ b/src/plugins/exec/exec-command.h @@ -36,6 +36,9 @@ struct t_exec_cmd_options 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 */ From 0eca1fd20bf7216a0deedf01fa08e95535d6a436 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Thu, 13 Mar 2014 07:56:20 +0100 Subject: [PATCH 23/35] exec: add options "-rc"/"-norc" (display return code) in command /exec --- src/plugins/exec/exec-command.c | 16 ++++++++++++++-- src/plugins/exec/exec-command.h | 1 + src/plugins/exec/exec.c | 5 ++++- src/plugins/exec/exec.h | 3 ++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index b54904ab1..304e05140 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -255,6 +255,14 @@ exec_command_parse_options (struct t_exec_cmd_options *cmd_options, { cmd_options->line_numbers = 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) @@ -318,6 +326,7 @@ exec_command_run (struct t_gui_buffer *buffer, cmd_options.new_buffer = 0; cmd_options.switch_to_buffer = 1; cmd_options.line_numbers = -1; + cmd_options.display_rc = 1; cmd_options.ptr_command_name = NULL; /* parse default options */ @@ -429,6 +438,7 @@ exec_command_run (struct t_gui_buffer *buffer, } new_exec_cmd->line_numbers = (cmd_options.line_numbers < 0) ? cmd_options.new_buffer : cmd_options.line_numbers; + new_exec_cmd->display_rc = cmd_options.display_rc; /* execute the command */ if (weechat_exec_plugin->debug >= 1) @@ -659,8 +669,8 @@ exec_command_init () N_("execute external commands"), N_("-list" " || [-sh|-nosh] [-bg|-nobg] [-stdin|-nostdin] [-buffer ] " - "[-l|-o|-n] |-sw|-nosw] [-ln|-noln] [-timeout ] " - "[-name ] " + "[-l|-o|-n] |-sw|-nosw] [-ln|-noln] [-rc|-norc] " + "[-timeout ] [-name ] " " || -in " " || -inclose []" " || -signal " @@ -691,6 +701,8 @@ exec_command_init () " -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" + " -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 " diff --git a/src/plugins/exec/exec-command.h b/src/plugins/exec/exec-command.h index e0cfb7618..b849e6ad4 100644 --- a/src/plugins/exec/exec-command.h +++ b/src/plugins/exec/exec-command.h @@ -33,6 +33,7 @@ struct t_exec_cmd_options 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 display_rc; /* 1 to display return code */ const char *ptr_command_name; /* name of command */ }; diff --git a/src/plugins/exec/exec.c b/src/plugins/exec/exec.c index aea5d0454..f977c3351 100644 --- a/src/plugins/exec/exec.c +++ b/src/plugins/exec/exec.c @@ -125,6 +125,7 @@ exec_add () 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; @@ -268,7 +269,8 @@ exec_end_command (struct t_exec_cmd *exec_cmd, int return_code) * 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) + if (!exec_cmd->detached && !exec_cmd->output_to_buffer + && exec_cmd->display_rc) { if (return_code >= 0) { @@ -434,6 +436,7 @@ exec_print_log () 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); diff --git a/src/plugins/exec/exec.h b/src/plugins/exec/exec.h index 5ae32708f..0f9da1068 100644 --- a/src/plugins/exec/exec.h +++ b/src/plugins/exec/exec.h @@ -37,10 +37,11 @@ struct t_exec_cmd time_t start_time; /* start time */ time_t end_time; /* end time */ - /* buffer */ + /* 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 display_rc; /* 1 if return code is displayed */ /* command output */ int stdout_size; /* number of bytes in stdout */ From c8f574d7305b5a064b084e82d93cd90950ea2306 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Thu, 13 Mar 2014 09:02:36 +0100 Subject: [PATCH 24/35] exec: disable "output to buffer" if command is executed on an exec buffer --- src/plugins/exec/exec-command.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index 304e05140..c6af59fe1 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -388,7 +388,6 @@ exec_command_run (struct t_gui_buffer *buffer, 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; - new_exec_cmd->output_to_buffer = cmd_options.output_to_buffer; if (cmd_options.ptr_buffer_name && !cmd_options.ptr_buffer) { /* output in a new buffer using given name */ @@ -434,8 +433,10 @@ exec_command_run (struct t_gui_buffer *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->display_rc = cmd_options.display_rc; From cd65198c713577e3fee2232a10984fedfc274aa9 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Thu, 13 Mar 2014 10:59:58 +0100 Subject: [PATCH 25/35] api: add callback in function string_replace_regex --- doc/en/weechat_plugin_api.en.txt | 16 ++++++++-- doc/fr/weechat_plugin_api.fr.txt | 18 ++++++++--- doc/it/weechat_plugin_api.it.txt | 16 ++++++++-- doc/ja/weechat_plugin_api.ja.txt | 16 ++++++++-- src/core/wee-string.c | 43 ++++++++++++++++++++++---- src/core/wee-string.h | 4 ++- src/plugins/trigger/trigger-callback.c | 3 +- src/plugins/weechat-plugin.h | 12 ++++--- 8 files changed, 103 insertions(+), 25 deletions(-) diff --git a/doc/en/weechat_plugin_api.en.txt b/doc/en/weechat_plugin_api.en.txt index 91631663d..a57636d20 100644 --- a/doc/en/weechat_plugin_api.en.txt +++ b/doc/en/weechat_plugin_api.en.txt @@ -1268,14 +1268,17 @@ This function is not available in scripting API. _WeeChat ≥ 0.4.4._ -Replace text in a string using a regular expression and replacement text. +Replace text in a string using a regular expression, replacement text and +optional callback. Prototype: [source,C] ---- char *weechat_string_replace_regex (const char *string, void *regex, - const char *replace, const char reference_char); + const char *replace, const char reference_char, + char *(*callback)(void *data, const char *text), + void *callback_data); ---- Arguments: @@ -1291,6 +1294,13 @@ Arguments: ** `$.*N`: match `N` (can be `+` or `0` to `99`), with all chars replaced by `*` (the `*` char can be any char between space (32) and `~` (126)) * 'reference_char': the char used for reference to match (commonly '$') +* 'callback': an optional callback called for each reference in 'replace' + (except for matches replaced by a char); the callback must return: +** newly allocated string: it is used as replacement text (it is freed after + use) +** NULL: the text received in callback is used as replacement text (without + changes) +* 'callback_data': pointer given to callback when it is called Return value: @@ -1307,7 +1317,7 @@ if (weechat_string_regcomp (&my_regex, "([0-9]{4})-([0-9]{2})-([0-9]{2})", REG_EXTENDED) == 0) { string = weechat_string_replace_regex ("date: 2014-02-14", &my_regex, - "$3/$2/$1", '$'); + "$3/$2/$1", '$', NULL, NULL); /* string == "date: 14/02/2014" */ if (string) free (string); diff --git a/doc/fr/weechat_plugin_api.fr.txt b/doc/fr/weechat_plugin_api.fr.txt index 60cca1461..20ecc200a 100644 --- a/doc/fr/weechat_plugin_api.fr.txt +++ b/doc/fr/weechat_plugin_api.fr.txt @@ -1286,15 +1286,17 @@ Cette fonction n'est pas disponible dans l'API script. _WeeChat ≥ 0.4.4._ -Remplacer du texte dans une chaîne en utilisant une expression régulière et du -texte de remplacement. +Remplacer du texte dans une chaîne en utilisant une expression régulière, du +texte de remplacement et un "callback" optionnel. Prototype : [source,C] ---- char *weechat_string_replace_regex (const char *string, void *regex, - const char *replace, const char reference_char); + const char *replace, const char reference_char, + char *(*callback)(void *data, const char *text), + void *callback_data); ---- Paramètres : @@ -1314,6 +1316,14 @@ Paramètres : caractère entre l'espace (32) et `~` (126)) * 'reference_char' : le caractère utilisé pour les références aux correspondances (en général '$') +* 'callback' : un "callback" optionnel appelé pour chaque référence dans + 'replace' (sauf pour les correspondances remplacées par un caractère); le + "callback" doit retourner : +** une chaîne nouvellement allouée : elle est utilisée en texte de remplacement + (elle est libérée après utilisation) +** NULL : le texte reçu dans le "callback" est utilisé comme texte de + remplacement (sans changement) +* 'callback_data' : pointeur donné au "callback" lorsqu'il est appelé Valeur de retour : @@ -1330,7 +1340,7 @@ if (weechat_string_regcomp (&my_regex, "([0-9]{4})-([0-9]{2})-([0-9]{2})", REG_EXTENDED) == 0) { string = weechat_string_replace_regex ("date: 2014-02-14", &my_regex, - "$3/$2/$1", '$'); + "$3/$2/$1", '$', NULL, NULL); /* string == "date: 14/02/2014" */ if (string) free (string); diff --git a/doc/it/weechat_plugin_api.it.txt b/doc/it/weechat_plugin_api.it.txt index 62ae887e6..eaccf8cc0 100644 --- a/doc/it/weechat_plugin_api.it.txt +++ b/doc/it/weechat_plugin_api.it.txt @@ -1302,14 +1302,17 @@ Questa funzione non è disponibile nelle API per lo scripting. _WeeChat ≥ 0.4.4._ // TRANSLATION MISSING -Replace text in a string using a regular expression and replacement text. +Replace text in a string using a regular expression, replacement text and +optional callback. Prototipo: [source,C] ---- char *weechat_string_replace_regex (const char *string, void *regex, - const char *replace, const char reference_char); + const char *replace, const char reference_char, + char *(*callback)(void *data, const char *text), + void *callback_data); ---- Argomenti: @@ -1326,6 +1329,13 @@ Argomenti: ** `$.*N`: match `N` (can be `+` or `0` to `99`), with all chars replaced by `*` (the `*` char can be any char between space (32) and `~` (126)) * 'reference_char': the char used for reference to match (commonly '$') +* 'callback': an optional callback called for each reference in 'replace' + (except for matches replaced by a char); the callback must return: +** newly allocated string: it is used as replacement text (it is freed after + use) +** NULL: the text received in callback is used as replacement text (without + changes) +* 'callback_data': pointer given to callback when it is called Valore restituito: @@ -1343,7 +1353,7 @@ if (weechat_string_regcomp (&my_regex, "([0-9]{4})-([0-9]{2})-([0-9]{2})", REG_EXTENDED) == 0) { string = weechat_string_replace_regex ("date: 2014-02-14", &my_regex, - "$3/$2/$1", '$'); + "$3/$2/$1", '$', NULL, NULL); /* string == "date: 14/02/2014" */ if (string) free (string); diff --git a/doc/ja/weechat_plugin_api.ja.txt b/doc/ja/weechat_plugin_api.ja.txt index 42032beef..136950763 100644 --- a/doc/ja/weechat_plugin_api.ja.txt +++ b/doc/ja/weechat_plugin_api.ja.txt @@ -1269,14 +1269,17 @@ free (str); _WeeChat バージョン 0.4.4 以上で利用可。_ // TRANSLATION MISSING -Replace text in a string using a regular expression and replacement text. +Replace text in a string using a regular expression, replacement text and +optional callback. プロトタイプ: [source,C] ---- char *weechat_string_replace_regex (const char *string, void *regex, - const char *replace, const char reference_char); + const char *replace, const char reference_char, + char *(*callback)(void *data, const char *text), + void *callback_data); ---- 引数: @@ -1293,6 +1296,13 @@ char *weechat_string_replace_regex (const char *string, void *regex, ** `$.*N`: match `N` (can be `+` or `0` to `99`), with all chars replaced by `*` (the `*` char can be any char between space (32) and `~` (126)) * 'reference_char': the char used for reference to match (commonly '$') +* 'callback': an optional callback called for each reference in 'replace' + (except for matches replaced by a char); the callback must return: +** newly allocated string: it is used as replacement text (it is freed after + use) +** NULL: the text received in callback is used as replacement text (without + changes) +* 'callback_data': pointer given to callback when it is called 戻り値: @@ -1310,7 +1320,7 @@ if (weechat_string_regcomp (&my_regex, "([0-9]{4})-([0-9]{2})-([0-9]{2})", REG_EXTENDED) == 0) { string = weechat_string_replace_regex ("date: 2014-02-14", &my_regex, - "$3/$2/$1", '$'); + "$3/$2/$1", '$', NULL, NULL); /* string == "date: 14/02/2014" */ if (string) free (string); diff --git a/src/core/wee-string.c b/src/core/wee-string.c index ff3d143e9..e3a8f1184 100644 --- a/src/core/wee-string.c +++ b/src/core/wee-string.c @@ -1154,7 +1154,9 @@ string_replace (const char *string, const char *search, const char *replace) char * string_replace_regex_get_replace (const char *string, regmatch_t *regex_match, int last_match, const char *replace, - const char reference_char) + const char reference_char, + char *(*callback)(void *data, const char *text), + void *callback_data) { int length, length_current, length_add, match; const char *ptr_replace, *ptr_add; @@ -1208,8 +1210,26 @@ string_replace_regex_get_replace (const char *string, regmatch_t *regex_match, } if (regex_match[match].rm_so >= 0) { - ptr_add = string + regex_match[match].rm_so; - length_add = regex_match[match].rm_eo - regex_match[match].rm_so; + if (callback) + { + temp = string_strndup (string + regex_match[match].rm_so, + regex_match[match].rm_eo - regex_match[match].rm_so); + if (temp) + { + modified_replace = (*callback) (callback_data, temp); + if (modified_replace) + { + ptr_add = modified_replace; + length_add = strlen (modified_replace); + } + free (temp); + } + } + if (!ptr_add) + { + ptr_add = string + regex_match[match].rm_so; + length_add = regex_match[match].rm_eo - regex_match[match].rm_so; + } } } else if ((ptr_replace[1] == '.') @@ -1308,6 +1328,11 @@ string_replace_regex_get_replace (const char *string, regmatch_t *regex_match, * (the char '*' can be replaced by any char between space (32) * and '~' (126)) * + * If the callback is not NULL, it is called for every reference to a match + * (except for matches replaced by a char). + * If not NULL, the string returned by the callback (which must have been newly + * allocated) is used and freed after use. + * * Examples: * * string | regex | replace | result @@ -1322,7 +1347,9 @@ string_replace_regex_get_replace (const char *string, regmatch_t *regex_match, char * string_replace_regex (const char *string, void *regex, const char *replace, - const char reference_char) + const char reference_char, + char *(*callback)(void *data, const char *text), + void *callback_data) { char *result, *result2, *str_replace; int length, length_replace, start_offset, i, rc, end, last_match; @@ -1373,9 +1400,13 @@ string_replace_regex (const char *string, void *regex, const char *replace, /* check if the regex matched the end of string */ end = !result[regex_match[0].rm_eo]; - str_replace = string_replace_regex_get_replace (result, regex_match, + str_replace = string_replace_regex_get_replace (result, + regex_match, last_match, - replace, reference_char); + replace, + reference_char, + callback, + callback_data); length_replace = (str_replace) ? strlen (str_replace) : 0; length = regex_match[0].rm_so + length_replace + diff --git a/src/core/wee-string.h b/src/core/wee-string.h index 1ae8cb9b2..bf34cb2a3 100644 --- a/src/core/wee-string.h +++ b/src/core/wee-string.h @@ -59,7 +59,9 @@ extern int string_has_highlight_regex_compiled (const char *string, extern int string_has_highlight_regex (const char *string, const char *regex); extern char *string_replace_regex (const char *string, void *regex, const char *replace, - const char reference_char); + const char reference_char, + char *(*callback)(void *data, const char *text), + void *callback_data); extern char **string_split (const char *string, const char *separators, int keep_eol, int num_items_max, int *num_items); extern char **string_split_shared (const char *string, const char *separators, diff --git a/src/plugins/trigger/trigger-callback.c b/src/plugins/trigger/trigger-callback.c index d9d82101e..00a941b20 100644 --- a/src/plugins/trigger/trigger-callback.c +++ b/src/plugins/trigger/trigger-callback.c @@ -213,7 +213,8 @@ trigger_callback_replace_regex (struct t_trigger *trigger, value = weechat_string_replace_regex (ptr_value, trigger->regex[i].regex, replace_eval, - '$'); + '$', + NULL, NULL); if (value) { /* display debug info on trigger buffer */ diff --git a/src/plugins/weechat-plugin.h b/src/plugins/weechat-plugin.h index 6bfb54bb8..9f3d219d0 100644 --- a/src/plugins/weechat-plugin.h +++ b/src/plugins/weechat-plugin.h @@ -57,7 +57,7 @@ struct timeval; * please change the date with current one; for a second change at same * date, increment the 01, otherwise please keep 01. */ -#define WEECHAT_PLUGIN_API_VERSION "20140311-01" +#define WEECHAT_PLUGIN_API_VERSION "20140313-01" /* macros for defining plugin infos */ #define WEECHAT_PLUGIN_NAME(__name) \ @@ -250,7 +250,9 @@ struct t_weechat_plugin int (*string_has_highlight_regex) (const char *string, const char *regex); char *(*string_replace_regex) (const char *string, void *regex, const char *replace, - const char reference_char); + const char reference_char, + char *(*callback)(void *data, const char *text), + void *callback_data); char **(*string_split) (const char *string, const char *separators, int keep_eol, int num_items_max, int *num_items); char **(*string_split_shell) (const char *string, int *num_items); @@ -1017,9 +1019,11 @@ extern int weechat_plugin_end (struct t_weechat_plugin *plugin); #define weechat_string_has_highlight_regex(__string, __regex) \ weechat_plugin->string_has_highlight_regex(__string, __regex) #define weechat_string_replace_regex(__string, __regex, __replace, \ - __reference_char) \ + __reference_char, __callback, \ + __callback_data) \ weechat_plugin->string_replace_regex(__string, __regex, __replace, \ - __reference_char) + __reference_char, __callback, \ + __callback_data) #define weechat_string_split(__string, __separator, __eol, __max, \ __num_items) \ weechat_plugin->string_split(__string, __separator, __eol, \ From 7b31d397b7a6b5202fbacd1b9a47e4890bb158b7 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Thu, 13 Mar 2014 12:08:33 +0100 Subject: [PATCH 26/35] exec: add missing empty lines in output of commands The function weechat_string_split() collapses many separators (here "\n"), so empty lines were removed and not displayed. For example, if output is "Line1\n\nEnd\n", the empty line between "Line1" and "End" was not displayed. --- src/plugins/exec/exec.c | 48 +++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/plugins/exec/exec.c b/src/plugins/exec/exec.c index f977c3351..7d70ccaf2 100644 --- a/src/plugins/exec/exec.c +++ b/src/plugins/exec/exec.c @@ -195,8 +195,9 @@ void exec_command_display_output (struct t_exec_cmd *exec_cmd, struct t_gui_buffer *buffer, int stdout) { - char *ptr_output, **lines, *line, str_number[32], str_tags[1024]; - int i, num_lines, length; + 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) @@ -209,27 +210,36 @@ exec_command_display_output (struct t_exec_cmd *exec_cmd, 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++) + 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->output_to_buffer) { if (exec_cmd->line_numbers) { - length = 32 + strlen (lines[i]) + 1; - line = malloc (length); - if (line) + length = 32 + strlen (line) + 1; + line2 = malloc (length); + if (line2) { - snprintf (line, length, "%d. %s", i + 1, lines[i]); - weechat_command (buffer, line); - free (line); + snprintf (line2, length, "%d. %s", line_nb, line); + weechat_command (buffer, line2); + free (line2); } } else - weechat_command (buffer, lines[i]); + weechat_command (buffer, (line[0]) ? line : " "); } else { @@ -239,15 +249,17 @@ exec_command_display_output (struct t_exec_cmd *exec_cmd, "exec_%s,exec_cmd_%s", (stdout) ? "stdout" : "stderr", (exec_cmd->name) ? exec_cmd->name : str_number); - snprintf (str_number, sizeof (str_number), "%d\t", i + 1); + 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", - lines[i]); + line); } - } - weechat_string_free_split (lines); + free (line); + line_nb++; + ptr_line = (pos) ? pos + 1 : NULL; + } } /* From d791e76e1b5683d541f55d8f0ab3876928d21420 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Thu, 13 Mar 2014 14:50:38 +0100 Subject: [PATCH 27/35] exec: switch to exec buffer even if it is already opened --- src/plugins/exec/exec-buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/exec/exec-buffer.c b/src/plugins/exec/exec-buffer.c index 9a220785e..1a4019fde 100644 --- a/src/plugins/exec/exec-buffer.c +++ b/src/plugins/exec/exec-buffer.c @@ -122,7 +122,7 @@ exec_buffer_new (const char *name, int switch_to_buffer) new_buffer = weechat_buffer_search (EXEC_PLUGIN_NAME, name); if (new_buffer) - return new_buffer; + goto end; new_buffer = weechat_buffer_new (name, &exec_buffer_input_cb, NULL, @@ -138,6 +138,7 @@ exec_buffer_new (const char *name, int switch_to_buffer) 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"); From a97bcd1ca13807283fea99c29d4061d744ba2a08 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Fri, 14 Mar 2014 19:16:13 +0100 Subject: [PATCH 28/35] core: add functions to convert RGB to terminal color (and vice versa) --- src/gui/gui-color.c | 111 ++++++++++++++++++++++++++++++++++++++++++++ src/gui/gui-color.h | 2 + 2 files changed, 113 insertions(+) diff --git a/src/gui/gui-color.c b/src/gui/gui-color.c index 13b4ea8ff..59bf114c1 100644 --- a/src/gui/gui-color.c +++ b/src/gui/gui-color.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "../core/weechat.h" #include "../core/wee-config.h" @@ -51,6 +52,54 @@ struct t_hashtable *gui_color_hash_palette_color = NULL; struct t_hashtable *gui_color_hash_palette_alias = NULL; struct t_weelist *gui_color_list_with_alias = NULL; +/* terminal colors */ +int gui_color_term256[256] = +{ + 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, /* 0-5 */ + 0x008080, 0xc0c0c0, 0x808080, 0xff0000, 0x00ff00, 0xffff00, /* 6-11 */ + 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, 0x000000, 0x00005f, /* 12-17 */ + 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, /* 18-23 */ + 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, /* 24-29 */ + 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, /* 30-35 */ + 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, 0x00d700, 0x00d75f, /* 36-41 */ + 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, /* 42-47 */ + 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, /* 48-53 */ + 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, /* 54-59 */ + 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 0x5f8700, 0x5f875f, /* 60-65 */ + 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, /* 66-71 */ + 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, /* 72-77 */ + 0x5fd787, 0x5fd7af, 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, /* 78-83 */ + 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 0x870000, 0x87005f, /* 84-89 */ + 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, /* 90-95 */ + 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, /* 96-101 */ + 0x878787, 0x8787af, 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, /* 102-107 */ + 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, /* 108-113 */ + 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, /* 114-119 */ + 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, /* 120-125 */ + 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, /* 126-131 */ + 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 0xaf8700, 0xaf875f, /* 132-137 */ + 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, /* 138-143 */ + 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, /* 144-149 */ + 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, /* 150-155 */ + 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, 0xd70000, 0xd7005f, /* 156-161 */ + 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, /* 162-167 */ + 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, /* 168-173 */ + 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, /* 174-179 */ + 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, /* 180-185 */ + 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, /* 186-191 */ + 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, /* 192-197 */ + 0xff0087, 0xff00af, 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, /* 198-203 */ + 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 0xff8700, 0xff875f, /* 204-209 */ + 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, /* 210-215 */ + 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, /* 216-221 */ + 0xffd787, 0xffd7af, 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, /* 222-227 */ + 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, 0x080808, 0x121212, /* 228-233 */ + 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, /* 234-239 */ + 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, /* 240-245 */ + 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, /* 246-251 */ + 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee, /* 252-255 */ +}; + /* * Searches for a color with configuration option name. @@ -400,6 +449,68 @@ gui_color_get_custom (const char *color_name) return color[index_color]; } +/* + * Converts a terminal color to its RGB value. + * + * Returns a RGB color as integer. + */ + +int +gui_color_convert_term_to_rgb (int color) +{ + if ((color < 0) || (color > 255)) + return 0; + + return gui_color_term256[color]; +} + +/* + * Converts a RGB color to the closest terminal color. + * + * Argument "limit" is the number of colors to check in the table of terminal + * colors (starting from 0). So for example 256 will return any of the 256 + * colors, 16 will return a color between 0 and 15. + * + * Returns the closest terminal color (0-255). + */ + +int +gui_color_convert_rgb_to_term (int rgb, int limit) +{ + int i, r1, g1, b1, r2, g2, b2, diff, best_diff, best_color; + + r1 = rgb >> 16; + g1 = (rgb >> 8) & 0xFF; + b1 = rgb & 0xFF; + + best_diff = INT_MAX; + best_color = 0; + + for (i = 0; i < limit; i++) + { + r2 = gui_color_term256[i] >> 16; + g2 = (gui_color_term256[i] >> 8) & 0xFF; + b2 = gui_color_term256[i] & 0xFF; + + diff = ((r2 - r1) * (r2 - r1)) + + ((g2 - g1) * (g2 - g1)) + + ((b2 - b1) * (b2 - b1)); + + /* exact match! */ + if (diff == 0) + return i; + + if (diff < best_diff) + { + best_color = i; + best_diff = diff; + } + } + + /* return the closest color */ + return best_color; +} + /* * Removes WeeChat color codes from a message. * diff --git a/src/gui/gui-color.h b/src/gui/gui-color.h index 598b22555..a40527ca9 100644 --- a/src/gui/gui-color.h +++ b/src/gui/gui-color.h @@ -170,6 +170,8 @@ extern int gui_color_attr_get_flag (char c); extern void gui_color_attr_build_string (int color, char *str_attr); extern const char *gui_color_get_custom (const char *color_name); extern char *gui_color_decode (const char *string, const char *replacement); +extern int gui_color_convert_term_to_rgb (int color); +extern int gui_color_convert_rgb_to_term (int rgb, int limit); extern char *gui_color_emphasize (const char *string, const char *search, int case_sensitive, regex_t *regex); extern void gui_color_free (struct t_gui_color *color); From 9e659d9f2eb303e7d3bd77b69015e3f742610a92 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Fri, 14 Mar 2014 19:51:52 +0100 Subject: [PATCH 29/35] core: add options "term2rgb" and "rgb2term" in command /color --- src/core/wee-command.c | 68 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/src/core/wee-command.c b/src/core/wee-command.c index e29d4cd03..2faf34276 100644 --- a/src/core/wee-command.c +++ b/src/core/wee-command.c @@ -1157,7 +1157,8 @@ COMMAND_CALLBACK(color) { char *str_alias, *str_rgb, *pos, *error; char str_color[1024], str_command[1024]; - long number; + long number, limit; + unsigned int rgb; int i; struct t_gui_color_palette *color_palette; @@ -1302,6 +1303,46 @@ COMMAND_CALLBACK(color) return WEECHAT_RC_OK; } + /* convert terminal color to RGB color */ + if (string_strcasecmp (argv[1], "term2rgb") == 0) + { + if (argc < 3) + return WEECHAT_RC_ERROR; + error = NULL; + number = strtol (argv[2], &error, 10); + if (!error || error[0] || (number < 0) || (number > 255)) + return WEECHAT_RC_ERROR; + gui_chat_printf (NULL, + "%ld -> #%06x", + number, + gui_color_convert_term_to_rgb (number)); + return WEECHAT_RC_OK; + } + + /* convert RGB color to terminal color */ + if (string_strcasecmp (argv[1], "rgb2term") == 0) + { + if (argc < 3) + return WEECHAT_RC_ERROR; + if (sscanf ((argv[2][0] == '#') ? argv[2] + 1 : argv[2], "%x", &rgb) != 1) + return WEECHAT_RC_ERROR; + if (rgb > 0xFFFFFF) + return WEECHAT_RC_ERROR; + limit = 256; + if (argc > 3) + { + error = NULL; + limit = strtol (argv[3], &error, 10); + if (!error || error[0] || (limit < 1) || (limit > 256)) + return WEECHAT_RC_ERROR; + } + gui_chat_printf (NULL, + "#%06x -> %d", + rgb, + gui_color_convert_rgb_to_term (rgb, limit)); + return WEECHAT_RC_OK; + } + return WEECHAT_RC_ERROR; } @@ -6612,16 +6653,25 @@ command_init () hook_command ( NULL, "color", N_("define color aliases and display palette of colors"), - N_("alias || unalias || reset || -o"), - N_(" alias: add an alias for a color\n" - "unalias: delete an alias\n" - " color: color number (greater than or equal to 0, max depends on " + N_("alias " + " || unalias " + " || reset" + " || term2rgb " + " || rgb2term []" + " || -o"), + N_(" alias: add an alias for a color\n" + " unalias: delete an alias\n" + " color: color number (greater than or equal to 0, max depends on " "terminal, commonly 63 or 255)\n" - " name: alias name for color (for example: \"orange\")\n" - " reset: reset all color pairs (required when no more color pairs " + " name: alias name for color (for example: \"orange\")\n" + " reset: reset all color pairs (required when no more color pairs " "are available if automatic reset is disabled, see option " "weechat.look.color_pairs_auto_reset)\n" - " -o: send terminal/colors info to current buffer as input\n" + "term2rgb: convert a terminal color (0-255) to RGB color\n" + "rgb2term: convert a RGB color to terminal color (0-255)\n" + " limit: number of colors to use in terminal table (starting from " + "0); default is 256\n" + " -o: send terminal/colors info to current buffer as input\n" "\n" "Without argument, this command displays colors in a new buffer.\n" "\n" @@ -6633,6 +6683,8 @@ command_init () "alias %(palette_colors)" " || unalias %(palette_colors)" " || reset" + " || term2rgb" + " || rgb2term" " || -o", &command_color, NULL); /* From e38f437ad739967c5e8e5388ff47d38fb88b2a9f Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Sat, 15 Mar 2014 11:06:30 +0100 Subject: [PATCH 30/35] core: add modifier and infos to decode ANSI colors New modifier: - color_decode_ansi: convert ANSI colors to WeeChat colors (or remove colors). New infos: - color_ansi_regex: regex used to parse ANSI colors in a string - color_term2rgb: convert a terminal color (0-255) to RGB - color_rgb2term: convert a RGB color to terminal color (0-255) --- doc/en/weechat_plugin_api.en.txt | 10 +- doc/fr/weechat_plugin_api.fr.txt | 12 +- doc/it/weechat_plugin_api.it.txt | 13 +- doc/ja/weechat_plugin_api.ja.txt | 13 +- src/gui/curses/gui-curses-color.c | 17 --- src/gui/curses/gui-curses.h | 1 - src/gui/gui-color.c | 239 ++++++++++++++++++++++++++++++ src/gui/gui-color.h | 12 +- src/plugins/plugin-api.c | 69 ++++++++- 9 files changed, 357 insertions(+), 29 deletions(-) diff --git a/doc/en/weechat_plugin_api.en.txt b/doc/en/weechat_plugin_api.en.txt index a57636d20..6d2fb9bb4 100644 --- a/doc/en/weechat_plugin_api.en.txt +++ b/doc/en/weechat_plugin_api.en.txt @@ -9132,12 +9132,12 @@ List of modifiers used by WeeChat and plugins: | irc_color_decode | "1" to keep colors, "0" to remove colors | Any string | - String with WeeChat color codes, or without color + String with IRC colors converted to WeeChat colors (or IRC colors removed) | irc_color_encode | "1" to keep colors, "0" to remove colors | Any string | - String with IRC color codes, or without color + String with IRC colors (or IRC colors removed) | irc_command_auth + _(WeeChat ≥ 0.4.1)_ | @@ -9175,6 +9175,12 @@ List of modifiers used by WeeChat and plugins: fit in 512 bytes) | New content of message +| color_decode_ansi + + _(WeeChat ≥ 0.4.4)_ | + "1" to keep colors, "0" to remove colors | + Any string | + String with ANSI colors converted to WeeChat colors (or ANSI colors removed) + | bar_condition_yyy ^(2)^ | String with window pointer ("0x123..") | Empty string | diff --git a/doc/fr/weechat_plugin_api.fr.txt b/doc/fr/weechat_plugin_api.fr.txt index 20ecc200a..1cc76476f 100644 --- a/doc/fr/weechat_plugin_api.fr.txt +++ b/doc/fr/weechat_plugin_api.fr.txt @@ -9300,12 +9300,13 @@ Liste des modificateurs utilisés par WeeChat et les extensions : | irc_color_decode | "1" pour garder les couleurs, "0" pour les supprimer | Toute chaîne | - Chaîne avec des codes couleur WeeChat, ou sans couleur + Chaîne avec les couleurs IRC converties en couleurs WeeChat (ou avec les + couleurs IRC supprimées) | irc_color_encode | "1" pour garder les couleurs, "0" pour les supprimer | Toute chaîne | - Chaîne avec des codes couleur IRC, ou sans couleur + Chaîne avec les couleurs IRC (ou avec les couleurs IRC supprimées) | irc_command_auth + _(WeeChat ≥ 0.4.1)_ | @@ -9343,6 +9344,13 @@ Liste des modificateurs utilisés par WeeChat et les extensions : automatique pour tenir dans les 512 octets) | Nouveau contenu du message +| color_decode_ansi + + _(WeeChat ≥ 0.4.4)_ | + "1" pour garder les couleurs, "0" pour les supprimer | + Toute chaîne | + Chaîne avec les couleurs ANSI converties en couleurs WeeChat (ou avec les + couleurs ANSI supprimées) + | bar_condition_yyy ^(2)^ | Chaîne avec un pointeur vers la fenêtre ("0x123..") | Chaîne vide | diff --git a/doc/it/weechat_plugin_api.it.txt b/doc/it/weechat_plugin_api.it.txt index eaccf8cc0..c9cd4f236 100644 --- a/doc/it/weechat_plugin_api.it.txt +++ b/doc/it/weechat_plugin_api.it.txt @@ -9356,15 +9356,17 @@ List of modifiers used by WeeChat and plugins: Qualsiasi stringa | Stringa codificata da UTF-8 al set caratteri trovato per il plugin/buffer +// TRANSLATION MISSING | irc_color_decode | "1" per mantenere i colori, "0" per rimuovere i colori | Qualsiasi stringa | - Stringa con i codici colori di Weechat, o senza colore + String with IRC colors converted to WeeChat colors (or IRC colors removed) +// TRANSLATION MISSING | irc_color_encode | "1" per mantenere i colori, "0" per rimuovere i colori | Qualsiasi stringa | - Stringa con i codici colori IRC, o senza colore + String with IRC colors (or IRC colors removed) // TRANSLATION MISSING | irc_command_auth + @@ -9402,6 +9404,13 @@ List of modifiers used by WeeChat and plugins: Contenuto del messaggio che sta per essere inviato al server IRC (dopo la divisione automatica da adattare in 512 byte) | Nuovo contenuto del messaggio +// TRANSLATION MISSING +| color_decode_ansi + + _(WeeChat ≥ 0.4.4)_ | + "1" per mantenere i colori, "0" per rimuovere i colori | + Qualsiasi stringa | + String with ANSI colors converted to WeeChat colors (or ANSI colors removed) + | bar_condition_yyy ^(2)^ | Stringa con puntatore alla finestra ("0x123..") | Stringa vuota | diff --git a/doc/ja/weechat_plugin_api.ja.txt b/doc/ja/weechat_plugin_api.ja.txt index 136950763..796303068 100644 --- a/doc/ja/weechat_plugin_api.ja.txt +++ b/doc/ja/weechat_plugin_api.ja.txt @@ -9148,15 +9148,17 @@ WeeChat とプラグインが使う修飾子のリスト: 任意の文字列 | UTF-8 からプラグインおよびバッファの文字セットにエンコードされた文字列 +// TRANSLATION MISSING | irc_color_decode | 色を保持する場合は "1"、削除する場合は "0" | 任意の文字列 | - WeeChat 色コードを含む文字列および含まない文字列 + String with IRC colors converted to WeeChat colors (or IRC colors removed) +// TRANSLATION MISSING | irc_color_encode | 色を保持する場合は "1"、削除する場合は "0" | 任意の文字列 | - IRC 色コードを含む文字列および含まない文字列 + String with IRC colors (or IRC colors removed) | irc_command_auth + _(WeeChat バージョン 0.4.1 以上で利用可)_ | @@ -9192,6 +9194,13 @@ WeeChat とプラグインが使う修飾子のリスト: IRC サーバに送信するメッセージの内容 (512 バイトを超えないように自動分割した後) | メッセージの新しい内容 +// TRANSLATION MISSING +| color_decode_ansi + + _(WeeChat バージョン 0.4.4 以上で利用可)_ | + 色を保持する場合は "1"、削除する場合は "0" | + 任意の文字列 | + String with ANSI colors converted to WeeChat colors (or ANSI colors removed) + | bar_condition_yyy ^(2)^ | ウィンドウへのポインタの文字列 ("0x123..") | 空文字列 | diff --git a/src/gui/curses/gui-curses-color.c b/src/gui/curses/gui-curses-color.c index 41ce2b54a..60291421b 100644 --- a/src/gui/curses/gui-curses-color.c +++ b/src/gui/curses/gui-curses-color.c @@ -1539,20 +1539,3 @@ gui_color_dump () } } } - -/* - * Ends GUI colors. - */ - -void -gui_color_end () -{ - int i; - - for (i = 0; i < GUI_COLOR_NUM_COLORS; i++) - { - gui_color_free (gui_color[i]); - } - gui_color_palette_free_structs (); - gui_color_free_vars (); -} diff --git a/src/gui/curses/gui-curses.h b/src/gui/curses/gui-curses.h index 35c4b9b20..7553e842f 100644 --- a/src/gui/curses/gui-curses.h +++ b/src/gui/curses/gui-curses.h @@ -86,7 +86,6 @@ extern int gui_color_get_pair (int fg, int bg); extern int gui_color_weechat_get_pair (int weechat_color); extern void gui_color_pre_init (); extern void gui_color_init (); -extern void gui_color_end (); /* chat functions */ extern void gui_chat_calculate_line_diff (struct t_gui_window *window, diff --git a/src/gui/gui-color.c b/src/gui/gui-color.c index 59bf114c1..e62f6bc24 100644 --- a/src/gui/gui-color.c +++ b/src/gui/gui-color.c @@ -100,6 +100,11 @@ int gui_color_term256[256] = 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee, /* 252-255 */ }; +/* ANSI colors */ +regex_t *gui_color_regex_ansi = NULL; +char *gui_color_ansi[8] = +{ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" }; + /* * Searches for a color with configuration option name. @@ -704,6 +709,216 @@ gui_color_decode (const char *string, const char *replacement) return (char *)out; } +/* + * Converts ANSI color codes to WeeChat colors (or removes them). + * + * This callback is called by gui_color_decode_ansi, it must not be called + * directly. + */ + +char * +gui_color_decode_ansi_cb (void *data, const char *text) +{ + unsigned long keep_colors; + char *text2, **items, *output, str_color[128]; + int i, length, num_items, value; + + keep_colors = (unsigned long)data; + + /* if we don't keep colors of if text is empty, just return empty string */ + if (!keep_colors || !text || !text[0]) + return strdup (""); + + /* only sequences ending with 'm' are used, the others are discarded */ + length = strlen (text); + if (text[length - 1] != 'm') + return strdup (""); + + /* sequence "\33[m" resets color */ + if (length < 4) + return strdup (gui_color_get_custom ("reset")); + + text2 = NULL; + items = NULL; + output = NULL; + + /* extract text between "\33[" and "m" */ + text2 = string_strndup (text + 2, length - 3); + if (!text2) + goto end; + + items = string_split (text2, ";", 0, 0, &num_items); + if (!items) + goto end; + + output = malloc ((32 * num_items) + 1); + if (!output) + goto end; + output[0] = '\0'; + + for (i = 0; i < num_items; i++) + { + value = atoi (items[i]); + switch (value) + { + case 0: /* reset */ + strcat (output, gui_color_get_custom ("reset")); + break; + case 1: /* bold */ + strcat (output, gui_color_get_custom ("bold")); + break; + case 2: /* remove bold */ + case 21: + case 22: + strcat (output, gui_color_get_custom ("-bold")); + break; + case 3: /* italic */ + strcat (output, gui_color_get_custom ("italic")); + break; + case 4: /* underline */ + strcat (output, gui_color_get_custom ("underline")); + break; + case 23: /* remove italic */ + strcat (output, gui_color_get_custom ("-italic")); + break; + case 24: /* remove underline */ + strcat (output, gui_color_get_custom ("-underline")); + break; + case 30: /* text color */ + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + strcat (output, + gui_color_get_custom (gui_color_ansi[value - 30])); + break; + case 38: /* text color */ + if (i + 1 < num_items) + { + switch (atoi (items[i + 1])) + { + case 2: /* RGB color */ + if (i + 4 < num_items) + { + snprintf (str_color, sizeof (str_color), + "|%d", + gui_color_convert_rgb_to_term ( + (atoi (items[i + 2]) << 16) | + (atoi (items[i + 3]) << 8) | + atoi (items[i + 4]), + 256)); + strcat (output, gui_color_get_custom (str_color)); + i += 4; + } + break; + case 5: /* terminal color (0-255) */ + if (i + 2 < num_items) + { + snprintf (str_color, sizeof (str_color), + "|%d", atoi (items[i + 2])); + strcat (output, gui_color_get_custom (str_color)); + i += 2; + } + break; + } + } + break; + case 39: /* default text color */ + strcat (output, gui_color_get_custom ("default")); + break; + case 40: /* background color */ + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + snprintf (str_color, sizeof (str_color), + "|,%s", + gui_color_ansi[value - 40]); + strcat (output, gui_color_get_custom (str_color)); + break; + case 48: /* background color */ + if (i + 1 < num_items) + { + switch (atoi (items[i + 1])) + { + case 2: /* RGB color */ + if (i + 4 < num_items) + { + snprintf (str_color, sizeof (str_color), + "|,%d", + gui_color_convert_rgb_to_term ( + (atoi (items[i + 2]) << 16) | + (atoi (items[i + 3]) << 8) | + atoi (items[i + 4]), + 256)); + strcat (output, gui_color_get_custom (str_color)); + i += 4; + } + break; + case 5: /* terminal color (0-255) */ + if (i + 2 < num_items) + { + snprintf (str_color, sizeof (str_color), + "|,%d", atoi (items[i + 2])); + strcat (output, gui_color_get_custom (str_color)); + i += 2; + } + break; + } + } + break; + case 49: /* default background color */ + strcat (output, gui_color_get_custom (",default")); + break; + } + } + +end: + if (items) + string_free_split (items); + if (text2) + free (text2); + + return (output) ? output : strdup (""); +} + +/* + * Converts ANSI color codes to WeeChat colors (or removes them). + * + * Note: result must be freed after use. + */ + +char * +gui_color_decode_ansi (const char *string, int keep_colors) +{ + /* allocate/compile regex if needed (first call) */ + if (!gui_color_regex_ansi) + { + gui_color_regex_ansi = malloc (sizeof (*gui_color_regex_ansi)); + if (!gui_color_regex_ansi) + return NULL; + if (string_regcomp (gui_color_regex_ansi, + GUI_COLOR_REGEX_ANSI_DECODE, + REG_EXTENDED) != 0) + { + free (gui_color_regex_ansi); + gui_color_regex_ansi = NULL; + return NULL; + } + } + + return string_replace_regex (string, gui_color_regex_ansi, + "$0", '$', + &gui_color_decode_ansi_cb, + (void *)((unsigned long)keep_colors)); +} + /* * Emphasizes a string or regular expression in a string (which can contain * colors). @@ -1027,3 +1242,27 @@ gui_color_palette_free_structs () if (gui_color_list_with_alias) weelist_free (gui_color_list_with_alias); } + +/* + * Ends GUI colors. + */ + +void +gui_color_end () +{ + int i; + + for (i = 0; i < GUI_COLOR_NUM_COLORS; i++) + { + gui_color_free (gui_color[i]); + } + gui_color_palette_free_structs (); + gui_color_free_vars (); + + if (gui_color_regex_ansi) + { + regfree (gui_color_regex_ansi); + free (gui_color_regex_ansi); + gui_color_regex_ansi = NULL; + } +} diff --git a/src/gui/gui-color.h b/src/gui/gui-color.h index a40527ca9..b19a500f7 100644 --- a/src/gui/gui-color.h +++ b/src/gui/gui-color.h @@ -138,6 +138,12 @@ enum t_gui_color_enum #define GUI_COLOR_EXTENDED_MASK 0x00FFFFF #define GUI_COLOR_EXTENDED_MAX 99999 +#define GUI_COLOR_REGEX_ANSI_DECODE \ + "\33(" \ + "([()].)|" \ + "([<>])|" \ + "(\\[[0-9;?]*[A-Za-z]))" + /* color structure */ struct t_gui_color @@ -169,9 +175,10 @@ extern const char *gui_color_search_config (const char *color_name); extern int gui_color_attr_get_flag (char c); extern void gui_color_attr_build_string (int color, char *str_attr); extern const char *gui_color_get_custom (const char *color_name); -extern char *gui_color_decode (const char *string, const char *replacement); extern int gui_color_convert_term_to_rgb (int color); extern int gui_color_convert_rgb_to_term (int rgb, int limit); +extern char *gui_color_decode (const char *string, const char *replacement); +extern char *gui_color_decode_ansi (const char *string, int keep_colors); extern char *gui_color_emphasize (const char *string, const char *search, int case_sensitive, regex_t *regex); extern void gui_color_free (struct t_gui_color *color); @@ -180,7 +187,7 @@ extern int gui_color_palette_get_alias (const char *alias); extern struct t_gui_color_palette *gui_color_palette_get (int number); extern void gui_color_palette_add (int number, const char *value); extern void gui_color_palette_remove (int number); -extern void gui_color_palette_free_structs (); +extern void gui_color_end (); /* color functions (GUI dependent) */ @@ -191,6 +198,7 @@ extern int gui_color_assign_by_diff (int *color, const char *color_name, extern int gui_color_get_weechat_colors_number (); extern int gui_color_get_term_colors (); extern const char *gui_color_get_name (int num_color); +extern void gui_color_free_vars (); extern void gui_color_init_weechat (); extern void gui_color_display_terminal_colors (); extern void gui_color_info_term_colors (char *buffer, int size); diff --git a/src/plugins/plugin-api.c b/src/plugins/plugin-api.c index 8a1c4dfb7..5dd4a983a 100644 --- a/src/plugins/plugin-api.c +++ b/src/plugins/plugin-api.c @@ -277,6 +277,25 @@ plugin_api_command (struct t_weechat_plugin *plugin, free (command2); } +/* + * Modifier to decode ANSI colors. + */ + +char * +plugin_api_modifier_color_decode_ansi (void *data, + const char *modifier, + const char *modifier_data, + const char *string) +{ + /* make C compiler happy */ + (void) data; + (void) modifier; + + return gui_color_decode_ansi (string, + (modifier_data && (strcmp (modifier_data, "1") == 0)) ? + 1: 0); +} + /* * Gets info about WeeChat. */ @@ -288,10 +307,11 @@ plugin_api_info_get_internal (void *data, const char *info_name, time_t inactivity; static char value[32], version_number[32] = { '\0' }; static char weechat_dir_absolute_path[PATH_MAX] = { '\0' }; + int rgb, limit; + char *pos, *color; /* make C compiler happy */ (void) data; - (void) arguments; if (!info_name) return NULL; @@ -397,6 +417,43 @@ plugin_api_info_get_internal (void *data, const char *info_name, snprintf (value, sizeof (value), "%d", gui_window_get_height ()); return value; } + else if (string_strcasecmp (info_name, "color_ansi_regex") == 0) + { + return GUI_COLOR_REGEX_ANSI_DECODE; + } + else if (string_strcasecmp (info_name, "color_term2rgb") == 0) + { + if (arguments && arguments[0]) + { + snprintf (value, sizeof (value), + "%d", + gui_color_convert_term_to_rgb (atoi (arguments))); + return value; + } + } + else if (string_strcasecmp (info_name, "color_rgb2term") == 0) + { + if (arguments && arguments[0]) + { + limit = 256; + pos = strchr (arguments, ','); + if (pos) + { + color = string_strndup (arguments, pos - arguments); + if (!color) + return NULL; + rgb = atoi (color); + limit = atoi (pos + 1); + free (color); + } + else + rgb = atoi (arguments); + snprintf (value, sizeof (value), + "%d", + gui_color_convert_rgb_to_term (rgb, limit)); + return value; + } + } /* info not found */ return NULL; @@ -1097,6 +1154,10 @@ plugin_api_infolist_free (struct t_infolist *infolist) void plugin_api_init () { + /* WeeChat core modifiers */ + hook_modifier (NULL, "color_decode_ansi", + &plugin_api_modifier_color_decode_ansi, NULL); + /* WeeChat core info hooks */ hook_info (NULL, "version", N_("WeeChat version"), NULL, &plugin_api_info_get_internal, NULL); @@ -1141,6 +1202,12 @@ plugin_api_init () &plugin_api_info_get_internal, NULL); hook_info (NULL, "term_height", N_("height of terminal"), NULL, &plugin_api_info_get_internal, NULL); + hook_info (NULL, "color_ansi_regex", N_("regular expression to match ANSI escape codes"), NULL, + &plugin_api_info_get_internal, NULL); + hook_info (NULL, "color_term2rgb", N_("terminal color (0-255) converted to RGB color"), NULL, + &plugin_api_info_get_internal, NULL); + hook_info (NULL, "color_rgb2term", N_("RGB color converted to terminal color (0-255)"), NULL, + &plugin_api_info_get_internal, NULL); /* WeeChat core infolist hooks */ hook_infolist (NULL, "bar", N_("list of bars"), From d3c85c920c367d23820d58e448dfec30873a488c Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Sat, 15 Mar 2014 11:12:49 +0100 Subject: [PATCH 31/35] irc: add modifier "irc_color_decode_ansi" --- doc/en/weechat_plugin_api.en.txt | 6 + doc/fr/weechat_plugin_api.fr.txt | 7 + doc/it/weechat_plugin_api.it.txt | 7 + doc/ja/weechat_plugin_api.ja.txt | 7 + src/plugins/irc/irc-color.c | 380 ++++++++++++++++++++++++++++++- src/plugins/irc/irc-color.h | 11 + src/plugins/irc/irc.c | 3 + 7 files changed, 419 insertions(+), 2 deletions(-) diff --git a/doc/en/weechat_plugin_api.en.txt b/doc/en/weechat_plugin_api.en.txt index 6d2fb9bb4..9812abc2e 100644 --- a/doc/en/weechat_plugin_api.en.txt +++ b/doc/en/weechat_plugin_api.en.txt @@ -9139,6 +9139,12 @@ List of modifiers used by WeeChat and plugins: Any string | String with IRC colors (or IRC colors removed) +| irc_color_decode_ansi + + _(WeeChat ≥ 0.4.4)_ | + "1" to keep colors, "0" to remove colors | + Any string | + String with ANSI colors converted to IRC colors (or ANSI colors removed) + | irc_command_auth + _(WeeChat ≥ 0.4.1)_ | Server name | diff --git a/doc/fr/weechat_plugin_api.fr.txt b/doc/fr/weechat_plugin_api.fr.txt index 1cc76476f..b8b334994 100644 --- a/doc/fr/weechat_plugin_api.fr.txt +++ b/doc/fr/weechat_plugin_api.fr.txt @@ -9308,6 +9308,13 @@ Liste des modificateurs utilisés par WeeChat et les extensions : Toute chaîne | Chaîne avec les couleurs IRC (ou avec les couleurs IRC supprimées) +| irc_color_decode_ansi + + _(WeeChat ≥ 0.4.4)_ | + "1" pour garder les couleurs, "0" pour les supprimer | + Toute chaîne | + Chaîne avec les couleurs ANSI converties en couleurs IRC (ou avec les couleurs + ANSI supprimées) + | irc_command_auth + _(WeeChat ≥ 0.4.1)_ | Nom du serveur | diff --git a/doc/it/weechat_plugin_api.it.txt b/doc/it/weechat_plugin_api.it.txt index c9cd4f236..1ce753ae0 100644 --- a/doc/it/weechat_plugin_api.it.txt +++ b/doc/it/weechat_plugin_api.it.txt @@ -9368,6 +9368,13 @@ List of modifiers used by WeeChat and plugins: Qualsiasi stringa | String with IRC colors (or IRC colors removed) +// TRANSLATION MISSING +| irc_color_decode_ansi + + _(WeeChat ≥ 0.4.4)_ | + "1" per mantenere i colori, "0" per rimuovere i colori | + Qualsiasi stringa | + String with ANSI colors converted to IRC colors (or ANSI colors removed) + // TRANSLATION MISSING | irc_command_auth + _(WeeChat ≥ 0.4.1)_ | diff --git a/doc/ja/weechat_plugin_api.ja.txt b/doc/ja/weechat_plugin_api.ja.txt index 796303068..b610b8fe9 100644 --- a/doc/ja/weechat_plugin_api.ja.txt +++ b/doc/ja/weechat_plugin_api.ja.txt @@ -9160,6 +9160,13 @@ WeeChat とプラグインが使う修飾子のリスト: 任意の文字列 | String with IRC colors (or IRC colors removed) +// TRANSLATION MISSING +| irc_color_decode_ansi + + _(WeeChat バージョン 0.4.4 以上で利用可)_ | + 色を保持する場合は "1"、削除する場合は "0" | + 任意の文字列 | + String with ANSI colors converted to IRC colors (or ANSI colors removed) + | irc_command_auth + _(WeeChat バージョン 0.4.1 以上で利用可)_ | サーバ名 | diff --git a/src/plugins/irc/irc-color.c b/src/plugins/irc/irc-color.c index 69b0381eb..bd1895b1f 100644 --- a/src/plugins/irc/irc-color.c +++ b/src/plugins/irc/irc-color.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "../weechat-plugin.h" #include "irc.h" @@ -45,9 +46,29 @@ char *irc_color_to_weechat[IRC_NUM_COLORS] = /* 11 */ "lightcyan", /* 12 */ "lightblue", /* 13 */ "lightmagenta", - /* 14 */ "gray", - /* 15 */ "white" + /* 14 */ "darkgray", + /* 15 */ "gray" }; +char irc_color_term2irc[IRC_COLOR_TERM2IRC_NUM_COLORS] = +{ /* term > IRC */ + 1, /* 0 1 (black) */ + 5, /* 1 5 (red) */ + 3, /* 2 3 (green) */ + 7, /* 3 7 (brown) */ + 2, /* 4 2 (blue) */ + 6, /* 5 6 (magenta) */ + 10, /* 6 10 (cyan) */ + 15, /* 7 15 (gray) */ + 14, /* 8 14 (darkgray) */ + 4, /* 9 4 (lightred) */ + 9, /* 10 9 (lightgreen) */ + 8, /* 11 8 (yellow) */ + 12, /* 12 12 (lightblue) */ + 13, /* 13 13 (lightmagenta) */ + 11, /* 14 11 (lightcyan) */ + 0, /* 15 0 (white) */ +}; +regex_t *irc_color_regex_ansi = NULL; /* @@ -361,6 +382,343 @@ irc_color_encode (const char *string, int keep_colors) return (char *)out; } +/* + * Converts a RGB color to IRC color. + * + * Returns a IRC color number (between 0 and 15), -1 if error. + */ + +int +irc_color_convert_rgb2irc (int rgb) +{ + char str_color[64], *error; + const char *info_color; + long number; + + snprintf (str_color, sizeof (str_color), + "%d,%d", + rgb, + IRC_COLOR_TERM2IRC_NUM_COLORS); + + info_color = weechat_info_get ("color_rgb2term", str_color); + if (!info_color || !info_color[0]) + return -1; + + error = NULL; + number = strtol (info_color, &error, 10); + if (!error || error[0] + || (number < 0) || (number >= IRC_COLOR_TERM2IRC_NUM_COLORS)) + { + return -1; + } + + return irc_color_term2irc[number]; +} + +/* + * Converts a terminal color to IRC color. + * + * Returns a IRC color number (between 0 and 15), -1 if error. + */ + +int +irc_color_convert_term2irc (int color) +{ + char str_color[64], *error; + const char *info_color; + long number; + + snprintf (str_color, sizeof (str_color), "%d", color); + + info_color = weechat_info_get ("color_term2rgb", str_color); + if (!info_color || !info_color[0]) + return -1; + + error = NULL; + number = strtol (info_color, &error, 10); + if (!error || error[0] || (number < 0) || (number > 0xFFFFFF)) + return -1; + + return irc_color_convert_rgb2irc (number); +} + +/* + * Replaces ANSI colors by IRC colors (or removes them). + * + * This callback is called by irc_color_decode_ansi, it must not be called + * directly. + */ + +char * +irc_color_decode_ansi_cb (void *data, const char *text) +{ + struct t_irc_color_ansi_state *ansi_state; + char *text2, **items, *output, str_color[128]; + int i, length, num_items, value, color; + + ansi_state = (struct t_irc_color_ansi_state *)data; + + /* if we don't keep colors of if text is empty, just return empty string */ + if (!ansi_state->keep_colors || !text || !text[0]) + return strdup (""); + + /* only sequences ending with 'm' are used, the others are discarded */ + length = strlen (text); + if (text[length - 1] != 'm') + return strdup (""); + + /* sequence "\33[m" resets color */ + if (length < 4) + return strdup (weechat_color ("reset")); + + text2 = NULL; + items = NULL; + output = NULL; + + /* extract text between "\33[" and "m" */ + text2 = weechat_strndup (text + 2, length - 3); + if (!text2) + goto end; + + items = weechat_string_split (text2, ";", 0, 0, &num_items); + if (!items) + goto end; + + output = malloc ((32 * num_items) + 1); + if (!output) + goto end; + output[0] = '\0'; + + for (i = 0; i < num_items; i++) + { + value = atoi (items[i]); + switch (value) + { + case 0: /* reset */ + strcat (output, IRC_COLOR_RESET_STR); + ansi_state->bold = 0; + ansi_state->underline = 0; + ansi_state->italic = 0; + break; + case 1: /* bold */ + if (!ansi_state->bold) + { + strcat (output, IRC_COLOR_BOLD_STR); + ansi_state->bold = 1; + } + break; + case 2: /* remove bold */ + case 21: + case 22: + if (ansi_state->bold) + { + strcat (output, IRC_COLOR_BOLD_STR); + ansi_state->bold = 0; + } + break; + case 3: /* italic */ + if (!ansi_state->italic) + { + strcat (output, IRC_COLOR_ITALIC_STR); + ansi_state->italic = 1; + } + break; + case 4: /* underline */ + if (!ansi_state->underline) + { + strcat (output, IRC_COLOR_UNDERLINE_STR); + ansi_state->underline = 1; + } + break; + case 23: /* remove italic */ + if (ansi_state->italic) + { + strcat (output, IRC_COLOR_ITALIC_STR); + ansi_state->italic = 0; + } + break; + case 24: /* remove underline */ + if (ansi_state->underline) + { + strcat (output, IRC_COLOR_UNDERLINE_STR); + ansi_state->underline = 0; + } + break; + case 30: /* text color */ + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + snprintf (str_color, sizeof (str_color), + "%c%02d", + IRC_COLOR_COLOR_CHAR, + irc_color_term2irc[value - 30]); + strcat (output, str_color); + break; + case 38: /* text color */ + if (i + 1 < num_items) + { + switch (atoi (items[i + 1])) + { + case 2: /* RGB color */ + if (i + 4 < num_items) + { + color = irc_color_convert_rgb2irc ( + (atoi (items[i + 2]) << 16) | + (atoi (items[i + 3]) << 8) | + atoi (items[i + 4])); + if (color >= 0) + { + snprintf (str_color, sizeof (str_color), + "%c%02d", + IRC_COLOR_COLOR_CHAR, + color); + strcat (output, str_color); + } + i += 4; + } + break; + case 5: /* terminal color (0-255) */ + if (i + 2 < num_items) + { + color = irc_color_convert_term2irc (atoi (items[i + 2])); + if (color >= 0) + { + snprintf (str_color, sizeof (str_color), + "%c%02d", + IRC_COLOR_COLOR_CHAR, + color); + strcat (output, str_color); + } + i += 2; + } + break; + } + } + break; + case 39: /* default text color */ + snprintf (str_color, sizeof (str_color), + "%c15", + IRC_COLOR_COLOR_CHAR); + strcat (output, str_color); + break; + case 40: /* background color */ + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + snprintf (str_color, sizeof (str_color), + "%c,%02d", + IRC_COLOR_COLOR_CHAR, + irc_color_term2irc[value - 40]); + strcat (output, str_color); + break; + case 48: /* background color */ + if (i + 1 < num_items) + { + switch (atoi (items[i + 1])) + { + case 2: /* RGB color */ + if (i + 4 < num_items) + { + color = irc_color_convert_rgb2irc ( + (atoi (items[i + 2]) << 16) | + (atoi (items[i + 3]) << 8) | + atoi (items[i + 4])); + if (color >= 0) + { + snprintf (str_color, sizeof (str_color), + "%c,%02d", + IRC_COLOR_COLOR_CHAR, + color); + strcat (output, str_color); + } + i += 4; + } + break; + case 5: /* terminal color (0-255) */ + if (i + 2 < num_items) + { + color = irc_color_convert_term2irc (atoi (items[i + 2])); + if (color >= 0) + { + snprintf (str_color, sizeof (str_color), + "%c,%02d", + IRC_COLOR_COLOR_CHAR, + color); + strcat (output, str_color); + } + i += 2; + } + break; + } + } + break; + case 49: /* default background color */ + snprintf (str_color, sizeof (str_color), + "%c,01", + IRC_COLOR_COLOR_CHAR); + strcat (output, str_color); + break; + } + } + +end: + if (items) + weechat_string_free_split (items); + if (text2) + free (text2); + + return (output) ? output : strdup (""); +} + +/* + * Replaces ANSI colors by IRC colors. + * + * If keep_colors == 0: removes any color/style in message otherwise keeps + * colors. + * + * Note: result must be freed after use. + */ + +char * +irc_color_decode_ansi (const char *string, int keep_colors) +{ + struct t_irc_color_ansi_state ansi_state; + + /* allocate/compile regex if needed (first call) */ + if (!irc_color_regex_ansi) + { + irc_color_regex_ansi = malloc (sizeof (*irc_color_regex_ansi)); + if (!irc_color_regex_ansi) + return NULL; + if (weechat_string_regcomp (irc_color_regex_ansi, + weechat_info_get ("color_ansi_regex", NULL), + REG_EXTENDED) != 0) + { + free (irc_color_regex_ansi); + irc_color_regex_ansi = NULL; + return NULL; + } + } + + ansi_state.keep_colors = keep_colors; + ansi_state.bold = 0; + ansi_state.underline = 0; + ansi_state.italic = 0; + + return weechat_string_replace_regex (string, irc_color_regex_ansi, + "$0", '$', + &irc_color_decode_ansi_cb, + &ansi_state); +} + /* * Callback for modifiers "irc_color_decode" and "irc_color_encode". * @@ -385,6 +743,9 @@ irc_color_modifier_cb (void *data, const char *modifier, if (strcmp (modifier, "irc_color_encode") == 0) return irc_color_encode (string, keep_colors); + if (strcmp (modifier, "irc_color_decode_ansi") == 0) + return irc_color_decode_ansi (string, keep_colors); + /* unknown modifier */ return NULL; } @@ -403,3 +764,18 @@ irc_color_for_tags (const char *color) return weechat_string_replace (color, ",", ":"); } + +/* + * Ends IRC colors. + */ + +void +irc_color_end () +{ + if (irc_color_regex_ansi) + { + regfree (irc_color_regex_ansi); + free (irc_color_regex_ansi); + irc_color_regex_ansi = NULL; + } +} diff --git a/src/plugins/irc/irc-color.h b/src/plugins/irc/irc-color.h index 0224bd11d..5136d7690 100644 --- a/src/plugins/irc/irc-color.h +++ b/src/plugins/irc/irc-color.h @@ -59,6 +59,8 @@ #define IRC_COLOR_UNDERLINE_CHAR '\x1F' /* underlined text */ #define IRC_COLOR_UNDERLINE_STR "\x1F" /* [1F]...[1F] */ +#define IRC_COLOR_TERM2IRC_NUM_COLORS 16 + /* macros for WeeChat core and IRC colors */ #define IRC_COLOR_BAR_FG weechat_color("bar_fg") @@ -92,11 +94,20 @@ #define IRC_COLOR_ITEM_LAG_COUNTING weechat_color(weechat_config_string(irc_config_color_item_lag_counting)) #define IRC_COLOR_ITEM_LAG_FINISHED weechat_color(weechat_config_string(irc_config_color_item_lag_finished)) +struct t_irc_color_ansi_state +{ + char keep_colors; + char bold; + char underline; + char italic; +}; + extern char *irc_color_decode (const char *string, int keep_colors); extern char *irc_color_encode (const char *string, int keep_colors); extern char *irc_color_modifier_cb (void *data, const char *modifier, const char *modifier_data, const char *string); extern char *irc_color_for_tags (const char *color); +extern void irc_color_end (); #endif /* __WEECHAT_IRC_COLOR_H */ diff --git a/src/plugins/irc/irc.c b/src/plugins/irc/irc.c index 2b341b99f..6c807ddaf 100644 --- a/src/plugins/irc/irc.c +++ b/src/plugins/irc/irc.c @@ -189,6 +189,7 @@ weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[]) /* modifiers */ weechat_hook_modifier ("irc_color_decode", &irc_color_modifier_cb, NULL); weechat_hook_modifier ("irc_color_encode", &irc_color_modifier_cb, NULL); + weechat_hook_modifier ("irc_color_decode_ansi", &irc_color_modifier_cb, NULL); /* hook completions */ irc_completion_init (); @@ -281,5 +282,7 @@ weechat_plugin_end (struct t_weechat_plugin *plugin) irc_redirect_end (); + irc_color_end (); + return WEECHAT_RC_OK; } From 298f0211c1add0f451d77581319c1c696d1ba62e Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Sat, 15 Mar 2014 11:30:08 +0100 Subject: [PATCH 32/35] exec: add option "-color" in command /exec (decode ANSI colors by default) The ANSI colors are decoded by default to WeeChat colors (for local display), or IRC colors (if output is sent to buffer with "-o"). --- src/plugins/exec/exec-command.c | 21 ++++++++++++++--- src/plugins/exec/exec-command.h | 1 + src/plugins/exec/exec.c | 40 +++++++++++++++++++++++++++++++++ src/plugins/exec/exec.h | 10 +++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c index c6af59fe1..09230c195 100644 --- a/src/plugins/exec/exec-command.c +++ b/src/plugins/exec/exec-command.c @@ -255,6 +255,15 @@ exec_command_parse_options (struct t_exec_cmd_options *cmd_options, { 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; @@ -326,6 +335,7 @@ exec_command_run (struct t_gui_buffer *buffer, 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; @@ -439,6 +449,7 @@ exec_command_run (struct t_gui_buffer *buffer, 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 */ @@ -670,8 +681,8 @@ exec_command_init () N_("execute external commands"), N_("-list" " || [-sh|-nosh] [-bg|-nobg] [-stdin|-nostdin] [-buffer ] " - "[-l|-o|-n] |-sw|-nosw] [-ln|-noln] [-rc|-norc] " - "[-timeout ] [-name ] " + "[-l|-o|-n] |-sw|-nosw] [-ln|-noln] [-color off|decode|strip] " + "[-rc|-norc] [-timeout ] [-name ] " " || -in " " || -inclose []" " || -signal " @@ -702,6 +713,10 @@ exec_command_init () " -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" @@ -729,7 +744,7 @@ exec_command_init () "exec.command.default_options."), "-list" " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-buffer|-l|-o|-n|-sw|-nosw|" - "-ln|-noln|-timeout|-name|%*" + "-ln|-noln|-color|-timeout|-name|%*" " || -in|-inclose|-signal|-kill %(exec_commands_ids)" " || -killall" " || -set %(exec_commands_ids) stdin|stdin_close|signal" diff --git a/src/plugins/exec/exec-command.h b/src/plugins/exec/exec-command.h index b849e6ad4..252350ea1 100644 --- a/src/plugins/exec/exec-command.h +++ b/src/plugins/exec/exec-command.h @@ -33,6 +33,7 @@ struct t_exec_cmd_options 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 */ }; diff --git a/src/plugins/exec/exec.c b/src/plugins/exec/exec.c index 7d70ccaf2..a8184d680 100644 --- a/src/plugins/exec/exec.c +++ b/src/plugins/exec/exec.c @@ -44,6 +44,33 @@ 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. @@ -225,6 +252,19 @@ exec_command_display_output (struct t_exec_cmd *exec_cmd, 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) diff --git a/src/plugins/exec/exec.h b/src/plugins/exec/exec.h index 0f9da1068..9958ed7a3 100644 --- a/src/plugins/exec/exec.h +++ b/src/plugins/exec/exec.h @@ -25,6 +25,14 @@ #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 */ @@ -41,6 +49,7 @@ struct t_exec_cmd 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 */ @@ -59,6 +68,7 @@ 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, From 6749ed354d0671e5a60646b36b0a1cf573159124 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Sat, 15 Mar 2014 12:09:31 +0100 Subject: [PATCH 33/35] core: add decoding of bright ANSI colors (90-97, 100-107) --- src/gui/gui-color.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/gui/gui-color.c b/src/gui/gui-color.c index e62f6bc24..a851b15da 100644 --- a/src/gui/gui-color.c +++ b/src/gui/gui-color.c @@ -102,8 +102,14 @@ int gui_color_term256[256] = /* ANSI colors */ regex_t *gui_color_regex_ansi = NULL; -char *gui_color_ansi[8] = -{ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" }; +char *gui_color_ansi[16] = +{ + /* 0-7 */ + "black", "red", "green", "brown", "blue", "magenta", "cyan", "gray", + /* 8-15 */ + "darkgray", "lightred", "lightgreen", "yellow", "lightblue", "lightmagenta", + "lightcyan", "white", +}; /* @@ -876,6 +882,30 @@ gui_color_decode_ansi_cb (void *data, const char *text) case 49: /* default background color */ strcat (output, gui_color_get_custom (",default")); break; + case 90: /* text color (bright) */ + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + strcat (output, + gui_color_get_custom (gui_color_ansi[value - 90 + 8])); + break; + case 100: /* background color (bright) */ + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + snprintf (str_color, sizeof (str_color), + "|,%s", + gui_color_ansi[value - 100 + 8]); + strcat (output, gui_color_get_custom (str_color)); + break; } } From 0bfbe8ef4038fd3195cad7b8e3e002a1ab20603e Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Sat, 15 Mar 2014 12:09:51 +0100 Subject: [PATCH 34/35] irc: add decoding of bright ANSI colors (90-97, 100-107) --- src/plugins/irc/irc-color.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/plugins/irc/irc-color.c b/src/plugins/irc/irc-color.c index bd1895b1f..6c0713aec 100644 --- a/src/plugins/irc/irc-color.c +++ b/src/plugins/irc/irc-color.c @@ -666,6 +666,34 @@ irc_color_decode_ansi_cb (void *data, const char *text) IRC_COLOR_COLOR_CHAR); strcat (output, str_color); break; + case 90: /* text color (bright) */ + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + snprintf (str_color, sizeof (str_color), + "%c%02d", + IRC_COLOR_COLOR_CHAR, + irc_color_term2irc[value - 90 + 8]); + strcat (output, str_color); + break; + case 100: /* background color (bright) */ + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + snprintf (str_color, sizeof (str_color), + "%c,%02d", + IRC_COLOR_COLOR_CHAR, + irc_color_term2irc[value - 100 + 8]); + strcat (output, str_color); + break; } } From a62430b744bd7115b31bc3bd51c6f0b504028ea5 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Sat, 15 Mar 2014 12:14:20 +0100 Subject: [PATCH 35/35] doc: fix IRC colors 14/15 in user's guide --- doc/de/weechat_user.de.txt | 4 ++-- doc/en/weechat_user.en.txt | 4 ++-- doc/fr/weechat_user.fr.txt | 4 ++-- doc/it/weechat_user.it.txt | 4 ++-- doc/ja/weechat_user.ja.txt | 4 ++-- doc/pl/weechat_user.pl.txt | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/de/weechat_user.de.txt b/doc/de/weechat_user.de.txt index 50444c52e..7e44f280d 100644 --- a/doc/de/weechat_user.de.txt +++ b/doc/de/weechat_user.de.txt @@ -1395,8 +1395,8 @@ Farbtabelle für key[ctrl-c,c]: | 11 | hell türkis | lightcyan | 12 | hellblau | lightblue | 13 | hell magenta | lightmagenta -| 14 | grau | gray -| 15 | hellgrau | white +| 14 | grau | darkgray +| 15 | hellgrau | gray |=== Beispiel: Im Buffer wird "Hallo an alle!" ausgegeben. Dabei wird "Hallo" in fett und hellblau diff --git a/doc/en/weechat_user.en.txt b/doc/en/weechat_user.en.txt index 792353adf..0bbbd9cc4 100644 --- a/doc/en/weechat_user.en.txt +++ b/doc/en/weechat_user.en.txt @@ -1377,8 +1377,8 @@ Color codes for key[ctrl-c,c] are: | 11 | light cyan | lightcyan | 12 | light blue | lightblue | 13 | light magenta | lightmagenta -| 14 | gray | gray -| 15 | light gray | white +| 14 | gray | darkgray +| 15 | light gray | gray |=== Example: display of "hello everybody!" with "hello" in light blue bold and diff --git a/doc/fr/weechat_user.fr.txt b/doc/fr/weechat_user.fr.txt index a7738e4a9..dc30ebe2a 100644 --- a/doc/fr/weechat_user.fr.txt +++ b/doc/fr/weechat_user.fr.txt @@ -1417,8 +1417,8 @@ Les codes couleur pour key[ctrl-c,c] sont : | 11 | cyan clair | lightcyan | 12 | bleu clair | lightblue | 13 | violet clair | lightmagenta -| 14 | gris | gray -| 15 | gris clair | white +| 14 | gris | darkgray +| 15 | gris clair | gray |=== Exemple : affichage de "bonjour tout le monde !" avec "bonjour" en bleu clair diff --git a/doc/it/weechat_user.it.txt b/doc/it/weechat_user.it.txt index d6fcf6c1e..9ba076a81 100644 --- a/doc/it/weechat_user.it.txt +++ b/doc/it/weechat_user.it.txt @@ -1429,8 +1429,8 @@ I codici colore per key[ctrl-c,c] sono: | 11 | azzurro chiaro | lightcyan | 12 | blu chiaro | lightblue | 13 | rosa chiaro | lightmagenta -| 14 | grigio | gray -| 15 | grigio chiaro | white +| 14 | grigio | darkgray +| 15 | grigio chiaro | gray |=== Esempio: visualizza "ciao a tutti!" con "ciao" scritto in blu chiaro grassetto diff --git a/doc/ja/weechat_user.ja.txt b/doc/ja/weechat_user.ja.txt index 383a11649..469fd2946 100644 --- a/doc/ja/weechat_user.ja.txt +++ b/doc/ja/weechat_user.ja.txt @@ -1379,8 +1379,8 @@ key[ctrl-c,c] 用の色コード: | 11 | 明るい青緑色 | lightcyan | 12 | 明るい青 | lightblue | 13 | 明るい赤紫色 | lightmagenta -| 14 | 灰色 | gray -| 15 | 明るい灰色 | white +| 14 | 灰色 | darkgray +| 15 | 明るい灰色 | gray |=== 例: "こんにちは皆さん!" の "こんにちは" を太字の明るい青、"皆さん" diff --git a/doc/pl/weechat_user.pl.txt b/doc/pl/weechat_user.pl.txt index de5ab9260..606fcf9d5 100644 --- a/doc/pl/weechat_user.pl.txt +++ b/doc/pl/weechat_user.pl.txt @@ -1387,8 +1387,8 @@ Kody kolorów dla key[ctrl-c,c]: | 11 | light cyan | lightcyan | 12 | light blue | lightblue | 13 | light magenta | lightmagenta -| 14 | gray | gray -| 15 | light gray | white +| 14 | gray | darkgray +| 15 | light gray | gray |=== Przykład: wyświetlenie "hello everybody!" z pogrubionym jasno niebieskim "hello"