diff options
46 files changed, 3223 insertions, 86 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index b1301cc82..da61424a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,7 @@ OPTION(ENABLE_ALIAS "Enable Alias plugin" ON) OPTION(ENABLE_ASPELL "Enable Aspell plugin" ON) OPTION(ENABLE_ENCHANT "Enable Enchant lib for Aspell plugin" OFF) OPTION(ENABLE_CHARSET "Enable Charset plugin" ON) +OPTION(ENABLE_EXEC "Enable Exec plugin" ON) OPTION(ENABLE_FIFO "Enable FIFO plugin" ON) OPTION(ENABLE_IRC "Enable IRC plugin" ON) OPTION(ENABLE_LOGGER "Enable Logger plugin" ON) diff --git a/ChangeLog.asciidoc b/ChangeLog.asciidoc index 9dade5ba7..9509860af 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 highlight problem with "(?-i)" and upper case letters in option weechat.look.highlight (closes #24) * core: use glibtoolize on Mac OS X (autotools) (closes #22) diff --git a/configure.ac b/configure.ac index 953e6dc63..414e82052 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/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/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/doc/en/weechat_plugin_api.en.txt b/doc/en/weechat_plugin_api.en.txt index 91631663d..9812abc2e 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); @@ -9122,12 +9132,18 @@ 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_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)_ | @@ -9165,6 +9181,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/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_plugin_api.fr.txt b/doc/fr/weechat_plugin_api.fr.txt index 60cca1461..b8b334994 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); @@ -9290,12 +9300,20 @@ 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_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)_ | @@ -9333,6 +9351,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/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_plugin_api.it.txt b/doc/it/weechat_plugin_api.it.txt index 62ae887e6..1ce753ae0 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); @@ -9346,15 +9356,24 @@ 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_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 + @@ -9392,6 +9411,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/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_plugin_api.ja.txt b/doc/ja/weechat_plugin_api.ja.txt index 42032beef..b610b8fe9 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); @@ -9138,15 +9148,24 @@ 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) + +// 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 以上で利用可)_ | @@ -9182,6 +9201,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/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" diff --git a/po/POTFILES.in b/po/POTFILES.in index 82aec229f..d0dadead2 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -113,6 +113,16 @@ ./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-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 +./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..886397caa 100644 --- a/po/srcfiles.cmake +++ b/po/srcfiles.cmake @@ -114,6 +114,16 @@ 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-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 +./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/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 <color> <name> || unalias <color> || 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 <color> <name>" + " || unalias <color>" + " || reset" + " || term2rgb <color>" + " || rgb2term <rgb> [<limit>]" + " || -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); /* 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)) diff --git a/src/core/wee-string.c b/src/core/wee-string.c index ae8292290..e8f9ed589 100644 --- a/src/core/wee-string.c +++ b/src/core/wee-string.c @@ -1146,7 +1146,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; @@ -1200,8 +1202,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] == '.') @@ -1300,6 +1320,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 @@ -1314,7 +1339,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; @@ -1365,9 +1392,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/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 13b4ea8ff..a851b15da 100644 --- a/src/gui/gui-color.c +++ b/src/gui/gui-color.c @@ -31,6 +31,7 @@ #include <signal.h> #include <time.h> #include <ctype.h> +#include <limits.h> #include "../core/weechat.h" #include "../core/wee-config.h" @@ -51,6 +52,65 @@ 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 */ +}; + +/* ANSI colors */ +regex_t *gui_color_regex_ansi = NULL; +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", +}; + /* * Searches for a color with configuration option name. @@ -401,6 +461,68 @@ gui_color_get_custom (const char *color_name) } /* + * 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. * * If replacement is not NULL and not empty, it is used to replace color codes @@ -594,6 +716,240 @@ gui_color_decode (const char *string, const char *replacement) } /* + * 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; + 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; + } + } + +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). * @@ -916,3 +1272,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 598b22555..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,7 +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 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); @@ -178,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) */ @@ -189,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/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..a5286eabd --- /dev/null +++ b/src/plugins/exec/CMakeLists.txt @@ -0,0 +1,30 @@ +# +# Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> +# +# This file is part of WeeChat, the extensible chat client. +# +# WeeChat is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# WeeChat is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with WeeChat. If not, see <http://www.gnu.org/licenses/>. +# + +ADD_LIBRARY(exec MODULE +exec.c exec.h +exec-buffer.c exec-buffer.h +exec-command.c exec-command.h +exec-completion.c exec-completion.h +exec-config.c exec-config.h) +SET_TARGET_PROPERTIES(exec PROPERTIES PREFIX "") + +TARGET_LINK_LIBRARIES(exec) + +INSTALL(TARGETS exec LIBRARY DESTINATION ${LIBDIR}/plugins) diff --git a/src/plugins/exec/Makefile.am b/src/plugins/exec/Makefile.am new file mode 100644 index 000000000..cad543311 --- /dev/null +++ b/src/plugins/exec/Makefile.am @@ -0,0 +1,40 @@ +# +# Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> +# +# This file is part of WeeChat, the extensible chat client. +# +# WeeChat is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# WeeChat is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with WeeChat. If not, see <http://www.gnu.org/licenses/>. +# + +AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/locale\" $(EXEC_CFLAGS) + +libdir = ${weechat_libdir}/plugins + +lib_LTLIBRARIES = exec.la + +exec_la_SOURCES = exec.c \ + exec.h \ + exec-buffer.c \ + exec-buffer.h \ + exec-command.c \ + exec-command.h \ + exec-completion.c \ + exec-completion.h \ + exec-config.c \ + exec-config.h + +exec_la_LDFLAGS = -module -no-undefined +exec_la_LIBADD = $(EXEC_LFLAGS) + +EXTRA_DIST = CMakeLists.txt diff --git a/src/plugins/exec/exec-buffer.c b/src/plugins/exec/exec-buffer.c new file mode 100644 index 000000000..1a4019fde --- /dev/null +++ b/src/plugins/exec/exec-buffer.c @@ -0,0 +1,146 @@ +/* + * exec-buffer.c - buffers with output of commands + * + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "../weechat-plugin.h" +#include "exec.h" +#include "exec-buffer.h" +#include "exec-command.h" +#include "exec-config.h" + + +/* + * Callback for user data in an exec buffer. + */ + +int +exec_buffer_input_cb (void *data, struct t_gui_buffer *buffer, + const char *input_data) +{ + char **argv, **argv_eol; + int argc; + + /* make C compiler happy */ + (void) data; + + /* close buffer */ + if (strcmp (input_data, "q") == 0) + { + weechat_buffer_close (buffer); + return WEECHAT_RC_OK; + } + + argv = weechat_string_split (input_data, " ", 0, 0, &argc); + argv_eol = weechat_string_split (input_data, " ", 1, 0, NULL); + + if (argv && argv_eol) + exec_command_run (buffer, argc, argv, argv_eol, 0); + + if (argv) + weechat_string_free_split (argv); + if (argv_eol) + weechat_string_free_split (argv_eol); + + return WEECHAT_RC_OK; +} + +/* + * Callback called when an exec buffer is closed. + */ + +int +exec_buffer_close_cb (void *data, struct t_gui_buffer *buffer) +{ + /* make C compiler happy */ + (void) data; + (void) buffer; + + return WEECHAT_RC_OK; +} + +/* + * Restore buffer callbacks (input and close) for buffers created by exec + * plugin. + */ + +void +exec_buffer_set_callbacks () +{ + struct t_infolist *ptr_infolist; + struct t_gui_buffer *ptr_buffer; + const char *plugin_name; + + ptr_infolist = weechat_infolist_get ("buffer", NULL, ""); + if (ptr_infolist) + { + while (weechat_infolist_next (ptr_infolist)) + { + ptr_buffer = weechat_infolist_pointer (ptr_infolist, "pointer"); + plugin_name = weechat_infolist_string (ptr_infolist, "plugin_name"); + if (ptr_buffer && plugin_name + && (strcmp (plugin_name, EXEC_PLUGIN_NAME) == 0)) + { + weechat_buffer_set_pointer (ptr_buffer, "close_callback", + &exec_buffer_close_cb); + weechat_buffer_set_pointer (ptr_buffer, "input_callback", + &exec_buffer_input_cb); + } + } + weechat_infolist_free (ptr_infolist); + } +} + +/* + * Creates a new exec buffer for a command. + */ + +struct t_gui_buffer * +exec_buffer_new (const char *name, int switch_to_buffer) +{ + struct t_gui_buffer *new_buffer; + + new_buffer = weechat_buffer_search (EXEC_PLUGIN_NAME, name); + if (new_buffer) + goto end; + + new_buffer = weechat_buffer_new (name, + &exec_buffer_input_cb, NULL, + &exec_buffer_close_cb, NULL); + + /* failed to create buffer ? then return */ + if (!new_buffer) + return NULL; + + weechat_buffer_set (new_buffer, "title", _("Executed commands")); + weechat_buffer_set (new_buffer, "localvar_set_type", "exec"); + weechat_buffer_set (new_buffer, "localvar_set_no_log", "1"); + weechat_buffer_set (new_buffer, "time_for_each_line", "0"); + weechat_buffer_set (new_buffer, "input_get_unknown_commands", "0"); + +end: + if (switch_to_buffer) + weechat_buffer_set (new_buffer, "display", "1"); + + return new_buffer; +} diff --git a/src/plugins/exec/exec-buffer.h b/src/plugins/exec/exec-buffer.h new file mode 100644 index 000000000..42e519888 --- /dev/null +++ b/src/plugins/exec/exec-buffer.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_EXEC_BUFFER_H +#define __WEECHAT_EXEC_BUFFER_H 1 + +extern void exec_buffer_set_callbacks (); +extern struct t_gui_buffer *exec_buffer_new (const char *name, + int switch_to_buffer); + +#endif /* __WEECHAT_EXEC_BUFFER_H */ diff --git a/src/plugins/exec/exec-command.c b/src/plugins/exec/exec-command.c new file mode 100644 index 000000000..09230c195 --- /dev/null +++ b/src/plugins/exec/exec-command.c @@ -0,0 +1,753 @@ +/* + * exec-command.c - exec command + * + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "../weechat-plugin.h" +#include "exec.h" +#include "exec-buffer.h" +#include "exec-command.h" +#include "exec-config.h" + + +/* + * Displays a list of executed commands. + */ + +void +exec_command_list () +{ + struct t_exec_cmd *ptr_exec_cmd; + char str_elapsed[32], str_time1[256], str_time2[256]; + time_t elapsed_time; + struct tm *local_time; + + weechat_printf (NULL, ""); + + if (!exec_cmds) + { + weechat_printf (NULL, _("No command is running")); + return; + } + + weechat_printf (NULL, _("Commands:")); + + for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd; + ptr_exec_cmd = ptr_exec_cmd->next_cmd) + { + elapsed_time = (ptr_exec_cmd->end_time == 0) ? + time (NULL) - ptr_exec_cmd->start_time : + ptr_exec_cmd->end_time - ptr_exec_cmd->start_time; + if (elapsed_time >= 3600) + { + snprintf (str_elapsed, sizeof (str_elapsed), + /* TRANSLATORS: format: hours + minutes, for example: 3h59 */ + _("%dh%02d"), + elapsed_time / 3600, + elapsed_time % 3600); + } + else if (elapsed_time >= 60) + { + snprintf (str_elapsed, sizeof (str_elapsed), + /* TRANSLATORS: format: minutes + seconds, for example: 3m59 */ + _("%dm%02d"), + elapsed_time / 60, + elapsed_time % 60); + } + else + { + snprintf (str_elapsed, sizeof (str_elapsed), + /* TRANSLATORS: format: seconds, for example: 59s */ + _("%ds"), + elapsed_time); + } + if (ptr_exec_cmd->end_time == 0) + { + /* running command */ + weechat_printf (NULL, + /* TRANSLATORS: %s before "ago" is elapsed time, for example: "3m59" */ + _(" %s%s%s %d%s%s%s: %s\"%s%s%s\"%s (pid: %d, " + "started %s ago)"), + weechat_color (weechat_config_string (exec_config_color_flag_running)), + ">>", + weechat_color ("reset"), + ptr_exec_cmd->number, + (ptr_exec_cmd->name) ? " (" : "", + (ptr_exec_cmd->name) ? ptr_exec_cmd->name : "", + (ptr_exec_cmd->name) ? ")" : "", + weechat_color ("chat_delimiters"), + weechat_color ("reset"), + ptr_exec_cmd->command, + weechat_color ("chat_delimiters"), + weechat_color ("reset"), + ptr_exec_cmd->pid, + str_elapsed); + } + else + { + /* process has ended */ + local_time = localtime (&ptr_exec_cmd->start_time); + strftime (str_time1, sizeof (str_time1), + "%Y-%m-%d %H:%M:%S", local_time); + local_time = localtime (&ptr_exec_cmd->end_time); + strftime (str_time2, sizeof (str_time2), + "%Y-%m-%d %H:%M:%S", local_time); + weechat_printf (NULL, + " %s%s%s %d%s%s%s: %s\"%s%s%s\"%s (%s -> %s, %s)", + weechat_color (weechat_config_string (exec_config_color_flag_finished)), + "[]", + weechat_color ("reset"), + ptr_exec_cmd->number, + (ptr_exec_cmd->name) ? " (" : "", + (ptr_exec_cmd->name) ? ptr_exec_cmd->name : "", + (ptr_exec_cmd->name) ? ")" : "", + weechat_color ("chat_delimiters"), + weechat_color ("reset"), + ptr_exec_cmd->command, + weechat_color ("chat_delimiters"), + weechat_color ("reset"), + str_time1, + str_time2, + str_elapsed); + } + } +} + +/* + * Searches a running command by id, and displays an error if command is not + * found or not running any more. + * + * Returns the command found, or NULL if not found or not running. + */ + +struct t_exec_cmd * +exec_command_search_running_id (const char *id) +{ + struct t_exec_cmd *ptr_exec_cmd; + + ptr_exec_cmd = exec_search_by_id (id); + if (!ptr_exec_cmd) + { + weechat_printf (NULL, _("%s%s: command id \"%s\" not found"), + weechat_prefix ("error"), EXEC_PLUGIN_NAME, id); + return NULL; + } + + if (!ptr_exec_cmd->hook) + { + weechat_printf (NULL, + _("%s%s: command with id \"%s\" is not running any " + "more"), + weechat_prefix ("error"), EXEC_PLUGIN_NAME, id); + return NULL; + } + + return ptr_exec_cmd; +} + +/* + * Parse command options. + * + * Returns: + * 1: options parsed successfully + * 0: error parsing options + */ + +int +exec_command_parse_options (struct t_exec_cmd_options *cmd_options, + int argc, char **argv, int start_arg, + int set_command_index) +{ + int i; + char *error; + + for (i = start_arg; i < argc; i++) + { + if (weechat_strcasecmp (argv[i], "-sh") == 0) + { + cmd_options->use_shell = 1; + } + else if (weechat_strcasecmp (argv[i], "-nosh") == 0) + { + cmd_options->use_shell = 0; + } + else if (weechat_strcasecmp (argv[i], "-bg") == 0) + { + cmd_options->detached = 1; + } + else if (weechat_strcasecmp (argv[i], "-nobg") == 0) + { + cmd_options->detached = 0; + } + else if (weechat_strcasecmp (argv[i], "-stdin") == 0) + { + cmd_options->pipe_stdin = 1; + } + else if (weechat_strcasecmp (argv[i], "-nostdin") == 0) + { + cmd_options->pipe_stdin = 0; + } + else if (weechat_strcasecmp (argv[i], "-buffer") == 0) + { + if (i + 1 >= argc) + return 0; + i++; + cmd_options->ptr_buffer_name = argv[i]; + cmd_options->ptr_buffer = weechat_buffer_search ("==", argv[i]); + if (cmd_options->ptr_buffer + && (weechat_buffer_get_integer (cmd_options->ptr_buffer, "type") != 0)) + { + /* only a buffer with formatted content is allowed */ + return 0; + } + if (!cmd_options->ptr_buffer) + cmd_options->new_buffer = 1; + } + else if (weechat_strcasecmp (argv[i], "-l") == 0) + { + cmd_options->output_to_buffer = 0; + cmd_options->new_buffer = 0; + } + else if (weechat_strcasecmp (argv[i], "-o") == 0) + { + cmd_options->output_to_buffer = 1; + cmd_options->new_buffer = 0; + } + else if (weechat_strcasecmp (argv[i], "-n") == 0) + { + cmd_options->output_to_buffer = 0; + cmd_options->new_buffer = 1; + } + else if (weechat_strcasecmp (argv[i], "-sw") == 0) + { + cmd_options->switch_to_buffer = 1; + } + else if (weechat_strcasecmp (argv[i], "-nosw") == 0) + { + cmd_options->switch_to_buffer = 0; + } + else if (weechat_strcasecmp (argv[i], "-ln") == 0) + { + cmd_options->line_numbers = 1; + } + else if (weechat_strcasecmp (argv[i], "-noln") == 0) + { + cmd_options->line_numbers = 0; + } + else if (weechat_strcasecmp (argv[i], "-color") == 0) + { + if (i + 1 >= argc) + return 0; + i++; + cmd_options->color = exec_search_color (argv[i]); + if (cmd_options->color < 0) + return 0; + } + else if (weechat_strcasecmp (argv[i], "-rc") == 0) + { + cmd_options->display_rc = 1; + } + else if (weechat_strcasecmp (argv[i], "-norc") == 0) + { + cmd_options->display_rc = 0; + } + else if (weechat_strcasecmp (argv[i], "-timeout") == 0) + { + if (i + 1 >= argc) + return 0; + i++; + error = NULL; + cmd_options->timeout = strtol (argv[i], &error, 10); + if (!error || error[0]) + return 0; + } + else if (weechat_strcasecmp (argv[i], "-name") == 0) + { + if (i + 1 >= argc) + return 0; + i++; + cmd_options->ptr_command_name = argv[i]; + } + else + { + if (set_command_index) + { + cmd_options->command_index = i; + break; + } + else + return 0; + } + } + + return 1; +} + +/* + * Runs a command. + * + * Returns: + * WEECHAT_RC_OK: command run successfully + * WEECHAT_RC_ERROR: error running command + */ + +int +exec_command_run (struct t_gui_buffer *buffer, + int argc, char **argv, char **argv_eol, int start_arg) +{ + char str_buffer[512]; + struct t_exec_cmd *new_exec_cmd; + struct t_exec_cmd_options cmd_options; + struct t_hashtable *process_options; + struct t_infolist *ptr_infolist; + struct t_gui_buffer *new_buffer; + + /* parse command options */ + cmd_options.command_index = -1; + cmd_options.use_shell = 1; + cmd_options.detached = 0; + cmd_options.pipe_stdin = 0; + cmd_options.timeout = 0; + cmd_options.ptr_buffer_name = NULL; + cmd_options.ptr_buffer = buffer; + cmd_options.output_to_buffer = 0; + cmd_options.new_buffer = 0; + cmd_options.switch_to_buffer = 1; + cmd_options.line_numbers = -1; + cmd_options.color = EXEC_COLOR_DECODE; + cmd_options.display_rc = 1; + cmd_options.ptr_command_name = NULL; + + /* parse default options */ + if (!exec_command_parse_options (&cmd_options, + exec_config_cmd_num_options, + exec_config_cmd_options, + 0, 0)) + { + weechat_printf (NULL, + _("%s%s: invalid options in option " + "exec.command.default_options"), + weechat_prefix ("error"), EXEC_PLUGIN_NAME); + return WEECHAT_RC_ERROR; + } + if (!exec_command_parse_options (&cmd_options, argc, argv, start_arg, 1)) + return WEECHAT_RC_ERROR; + + /* options "-bg" and "-o"/"-n" are incompatible */ + if (cmd_options.detached + && (cmd_options.output_to_buffer || cmd_options.new_buffer)) + return WEECHAT_RC_ERROR; + + /* command not found? */ + if (cmd_options.command_index < 0) + return WEECHAT_RC_ERROR; + + new_exec_cmd = exec_add (); + if (!new_exec_cmd) + return WEECHAT_RC_ERROR; + + /* create hashtable for weechat_hook_process_hashtable() */ + process_options = weechat_hashtable_new (32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, + NULL); + if (!process_options) + { + exec_free (new_exec_cmd); + return WEECHAT_RC_ERROR; + } + /* automatically disable shell if we are downloading an URL */ + if (strncmp (argv_eol[cmd_options.command_index], "url:", 4) == 0) + cmd_options.use_shell = 0; + if (cmd_options.use_shell) + { + /* command will be: sh -c "command arguments..." */ + weechat_hashtable_set (process_options, "arg1", "-c"); + weechat_hashtable_set (process_options, "arg2", + argv_eol[cmd_options.command_index]); + } + if (cmd_options.pipe_stdin) + weechat_hashtable_set (process_options, "stdin", "1"); + if (cmd_options.detached) + weechat_hashtable_set (process_options, "detached", "1"); + + /* set variables in new command (before running the command) */ + new_exec_cmd->name = (cmd_options.ptr_command_name) ? + strdup (cmd_options.ptr_command_name) : NULL; + new_exec_cmd->command = strdup (argv_eol[cmd_options.command_index]); + new_exec_cmd->detached = cmd_options.detached; + if (cmd_options.ptr_buffer_name && !cmd_options.ptr_buffer) + { + /* output in a new buffer using given name */ + new_exec_cmd->output_to_buffer = 0; + snprintf (str_buffer, sizeof (str_buffer), + "exec.%s", cmd_options.ptr_buffer_name); + new_buffer = exec_buffer_new (str_buffer, cmd_options.switch_to_buffer); + if (new_buffer) + { + new_exec_cmd->buffer_full_name = + strdup (weechat_buffer_get_string (new_buffer, "full_name")); + } + } + else if (cmd_options.new_buffer) + { + /* output in a new buffer using automatic name */ + if (new_exec_cmd->name) + { + snprintf (str_buffer, sizeof (str_buffer), + "exec.%s", new_exec_cmd->name); + } + else + { + snprintf (str_buffer, sizeof (str_buffer), + "exec.%d", new_exec_cmd->number); + } + new_buffer = exec_buffer_new (str_buffer, cmd_options.switch_to_buffer); + if (new_buffer) + { + new_exec_cmd->buffer_full_name = + strdup (weechat_buffer_get_string (new_buffer, "full_name")); + } + } + else if (cmd_options.ptr_buffer) + { + new_exec_cmd->buffer_full_name = + strdup (weechat_buffer_get_string (cmd_options.ptr_buffer, + "full_name")); + if (cmd_options.switch_to_buffer) + weechat_buffer_set (cmd_options.ptr_buffer, "display", "1"); + } + if (cmd_options.ptr_buffer + && (strcmp (weechat_buffer_get_string (cmd_options.ptr_buffer, "plugin"), + EXEC_PLUGIN_NAME) == 0)) + { + cmd_options.output_to_buffer = 0; + cmd_options.new_buffer = 1; + } + new_exec_cmd->output_to_buffer = cmd_options.output_to_buffer; + new_exec_cmd->line_numbers = (cmd_options.line_numbers < 0) ? + cmd_options.new_buffer : cmd_options.line_numbers; + new_exec_cmd->color = cmd_options.color; + new_exec_cmd->display_rc = cmd_options.display_rc; + + /* execute the command */ + if (weechat_exec_plugin->debug >= 1) + { + weechat_printf (NULL, "%s: executing command: \"%s%s%s\"", + EXEC_PLUGIN_NAME, + (cmd_options.use_shell) ? "" : "sh -c '", + argv_eol[cmd_options.command_index], + (cmd_options.use_shell) ? "" : "'"); + } + new_exec_cmd->hook = weechat_hook_process_hashtable ( + (cmd_options.use_shell) ? "sh" : argv_eol[cmd_options.command_index], + process_options, + cmd_options.timeout * 1000, + &exec_process_cb, + new_exec_cmd); + + if (new_exec_cmd->hook) + { + /* get PID of command */ + ptr_infolist = weechat_infolist_get ("hook", new_exec_cmd->hook, NULL); + if (ptr_infolist) + { + if (weechat_infolist_next (ptr_infolist)) + { + new_exec_cmd->pid = weechat_infolist_integer (ptr_infolist, + "child_pid"); + } + weechat_infolist_free (ptr_infolist); + } + } + else + { + exec_free (new_exec_cmd); + weechat_printf (NULL, + _("%s%s: failed to run command \"%s\""), + weechat_prefix ("error"), EXEC_PLUGIN_NAME, + argv_eol[cmd_options.command_index]); + } + + weechat_hashtable_free (process_options); + + return WEECHAT_RC_OK; +} + +/* + * Callback for command "/exec": manage executed commands. + */ + +int +exec_command_exec (void *data, struct t_gui_buffer *buffer, int argc, + char **argv, char **argv_eol) +{ + int i, length, count; + char *text; + struct t_exec_cmd *ptr_exec_cmd, *ptr_next_exec_cmd; + + /* make C compiler happy */ + (void) data; + (void) buffer; + + /* list running commands */ + if ((argc == 1) + || ((argc == 2) && (weechat_strcasecmp (argv[1], "-list") == 0))) + { + exec_command_list (); + return WEECHAT_RC_OK; + } + + /* send text to a running process */ + if (weechat_strcasecmp (argv[1], "-in") == 0) + { + if (argc < 4) + return WEECHAT_RC_ERROR; + ptr_exec_cmd = exec_command_search_running_id (argv[2]); + if (ptr_exec_cmd && ptr_exec_cmd->hook) + { + length = strlen (argv_eol[3]) + 1 + 1; + text = malloc (length); + if (text) + { + snprintf (text, length, "%s\n", argv_eol[3]); + weechat_hook_set (ptr_exec_cmd->hook, "stdin", text); + free (text); + } + } + return WEECHAT_RC_OK; + } + + /* send text to a running process (if given), then close stdin */ + if (weechat_strcasecmp (argv[1], "-inclose") == 0) + { + if (argc < 3) + return WEECHAT_RC_ERROR; + ptr_exec_cmd = exec_command_search_running_id (argv[2]); + if (ptr_exec_cmd && ptr_exec_cmd->hook) + { + if (argc > 3) + { + length = strlen (argv_eol[3]) + 1 + 1; + text = malloc (length); + if (text) + { + snprintf (text, length, "%s\n", argv_eol[3]); + weechat_hook_set (ptr_exec_cmd->hook, "stdin", text); + free (text); + } + } + weechat_hook_set (ptr_exec_cmd->hook, "stdin_close", "1"); + } + return WEECHAT_RC_OK; + } + + /* send a signal to a running process */ + if (weechat_strcasecmp (argv[1], "-signal") == 0) + { + if (argc < 4) + return WEECHAT_RC_ERROR; + ptr_exec_cmd = exec_command_search_running_id (argv[2]); + if (ptr_exec_cmd) + weechat_hook_set (ptr_exec_cmd->hook, "signal", argv[3]); + return WEECHAT_RC_OK; + } + + /* send a KILL signal to a running process */ + if (weechat_strcasecmp (argv[1], "-kill") == 0) + { + if (argc < 3) + return WEECHAT_RC_ERROR; + ptr_exec_cmd = exec_command_search_running_id (argv[2]); + if (ptr_exec_cmd) + weechat_hook_set (ptr_exec_cmd->hook, "signal", "kill"); + return WEECHAT_RC_OK; + } + + /* send a KILL signal to all running processes */ + if (weechat_strcasecmp (argv[1], "-killall") == 0) + { + for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd; + ptr_exec_cmd = ptr_exec_cmd->next_cmd) + { + if (ptr_exec_cmd->hook) + { + weechat_hook_set (ptr_exec_cmd->hook, "signal", "kill"); + } + } + return WEECHAT_RC_OK; + } + + /* set a hook property */ + if (weechat_strcasecmp (argv[1], "-set") == 0) + { + if (argc < 5) + return WEECHAT_RC_ERROR; + ptr_exec_cmd = exec_command_search_running_id (argv[2]); + if (ptr_exec_cmd) + weechat_hook_set (ptr_exec_cmd->hook, argv[3], argv_eol[4]); + return WEECHAT_RC_OK; + } + + /* delete terminated command(s) */ + if (weechat_strcasecmp (argv[1], "-del") == 0) + { + if (argc < 3) + return WEECHAT_RC_ERROR; + if (weechat_strcasecmp (argv[2], "-all") == 0) + { + count = 0; + ptr_exec_cmd = exec_cmds; + while (ptr_exec_cmd) + { + ptr_next_exec_cmd = ptr_exec_cmd->next_cmd; + if (!ptr_exec_cmd->hook) + { + exec_free (ptr_exec_cmd); + count++; + } + ptr_exec_cmd = ptr_next_exec_cmd; + } + weechat_printf (NULL, _("%d commands removed"), count); + } + else + { + for (i = 2; i < argc; i++) + { + ptr_exec_cmd = exec_search_by_id (argv[i]); + if (ptr_exec_cmd) + { + if (ptr_exec_cmd->hook) + { + weechat_printf (NULL, + _("%s%s: command with id \"%s\" is still " + "running"), + weechat_prefix ("error"), EXEC_PLUGIN_NAME, + argv[i]); + } + else + { + exec_free (ptr_exec_cmd); + weechat_printf (NULL, + _("Command \"%s\" removed"), argv[i]); + } + } + else + { + weechat_printf (NULL, + _("%s%s: command id \"%s\" not found"), + weechat_prefix ("error"), EXEC_PLUGIN_NAME, + argv[i]); + } + } + } + return WEECHAT_RC_OK; + } + + return exec_command_run (buffer, argc, argv, argv_eol, 1); +} + +/* + * Hooks exec commands. + */ + +void +exec_command_init () +{ + weechat_hook_command ( + "exec", + N_("execute external commands"), + N_("-list" + " || [-sh|-nosh] [-bg|-nobg] [-stdin|-nostdin] [-buffer <name>] " + "[-l|-o|-n] |-sw|-nosw] [-ln|-noln] [-color off|decode|strip] " + "[-rc|-norc] [-timeout <timeout>] [-name <name>] <command>" + " || -in <id> <text>" + " || -inclose <id> [<text>]" + " || -signal <id> <signal>" + " || -kill <id>" + " || -killall" + " || -set <id> <property> <value>" + " || -del <id>|-all [<id>...]"), + N_(" -list: list commands\n" + " -sh: use the shell to execute the command (default)\n" + " -nosh: do not use the shell to execute the command (required if " + "the command has some unsafe data, for example the content of a " + "message from another user)\n" + " -bg: run process in background: do not display process output " + "neither return code (not compatible with option -o/-n)\n" + " -nobg: catch process output and display return code (default)\n" + " -stdin: create a pipe for sending data to the process (with " + "/exec -in)\n" + "-nostdin: do not create a pipe for stdin (default)\n" + " -buffer: display/send output of command on this buffer (if the " + "buffer is not found, a new buffer with name \"exec.exec.xxx\" is " + "created)\n" + " -l: display locally output of command on buffer (default)\n" + " -o: send output of command to the buffer " + "(not compatible with option -bg)\n" + " -n: display output of command in a new buffer (not compatible " + "with -bg)\n" + " -sw: switch to the output buffer (default)\n" + " -nosw: don't switch to the output buffer\n" + " -ln: display line numbers (default in new buffer only)\n" + " -noln: don't display line numbers\n" + " -color: action on ANSI colors in output:\n" + " off: keep ANSI codes as-is\n" + " decode: convert ANSI colors to WeeChat/IRC (default)\n" + " strip: remove ANSI colors\n" + " -rc: display return code (default)\n" + " -norc: don't display return code\n" + "-timeout: set a timeout for the command (in seconds)\n" + " -name: set a name for the command (to name it later with /exec)\n" + " command: the command to execute; if beginning with \"url:\", the " + "shell is disabled and the content of URL is downloaded and sent as " + "output\n" + " id: command identifier: either its number or name (if set " + "with \"-name xxx\")\n" + " -in: send text on standard input of process\n" + "-inclose: same a -in, but stdin is closed after (and text is " + "optional: without text, the stdin is just closed)\n" + " -signal: send a signal to the process; the signal can be an integer " + "or one of these names: hup, int, quit, kill, term, usr1, usr2\n" + " -kill: alias of \"-signal <id> kill\"\n" + "-killall: kill all running processes\n" + " -set: set a hook property (see function hook_set in plugin API " + "reference)\n" + "property: hook property\n" + " value: new value for hook property\n" + " -del: delete a terminated command\n" + " -all: delete all terminated commands\n" + "\n" + "Default options can be set in the option " + "exec.command.default_options."), + "-list" + " || -sh|-nosh|-bg|-nobg|-stdin|-nostdin|-buffer|-l|-o|-n|-sw|-nosw|" + "-ln|-noln|-color|-timeout|-name|%*" + " || -in|-inclose|-signal|-kill %(exec_commands_ids)" + " || -killall" + " || -set %(exec_commands_ids) stdin|stdin_close|signal" + " || -del %(exec_commands_ids)|-all %(exec_commands_ids)|%*", + &exec_command_exec, NULL); +} diff --git a/src/plugins/exec/exec-command.h b/src/plugins/exec/exec-command.h new file mode 100644 index 000000000..252350ea1 --- /dev/null +++ b/src/plugins/exec/exec-command.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_EXEC_COMMAND_H +#define __WEECHAT_EXEC_COMMAND_H 1 + +struct t_exec_cmd_options +{ + int command_index; /* index of command in arguments */ + int use_shell; /* 1 to use shell (sh -c "command") */ + int detached; /* 1 if detached (no output) */ + int pipe_stdin; /* 1 to create a pipe for stdin */ + int timeout; /* timeout (in seconds) */ + const char *ptr_buffer_name; /* name of buffer */ + struct t_gui_buffer *ptr_buffer; /* pointer to buffer */ + int output_to_buffer; /* 1 if output is sent to buffer */ + int new_buffer; /* output in a new buffer */ + int switch_to_buffer; /* switch to the output buffer */ + int line_numbers; /* 1 to display line numbers */ + int color; /* what to do with ANSI colors */ + int display_rc; /* 1 to display return code */ + const char *ptr_command_name; /* name of command */ +}; + +extern int exec_command_run (struct t_gui_buffer *buffer, + int argc, char **argv, char **argv_eol, + int start_arg); +extern void exec_command_init (); + +#endif /* __WEECHAT_EXEC_COMMAND_H */ diff --git a/src/plugins/exec/exec-completion.c b/src/plugins/exec/exec-completion.c new file mode 100644 index 000000000..80333279e --- /dev/null +++ b/src/plugins/exec/exec-completion.c @@ -0,0 +1,73 @@ +/* + * exec-completion.c - completion for exec commands + * + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "../weechat-plugin.h" +#include "exec.h" + + +/* + * Adds executed commands ids to completion list. + */ + +int +exec_completion_commands_ids_cb (void *data, const char *completion_item, + struct t_gui_buffer *buffer, + struct t_gui_completion *completion) +{ + struct t_exec_cmd *ptr_exec_cmd; + char str_number[32]; + + /* make C compiler happy */ + (void) data; + (void) completion_item; + (void) buffer; + + for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd; + ptr_exec_cmd = ptr_exec_cmd->next_cmd) + { + snprintf (str_number, sizeof (str_number), "%d", ptr_exec_cmd->number); + weechat_hook_completion_list_add (completion, str_number, + 0, WEECHAT_LIST_POS_SORT); + if (ptr_exec_cmd->name) + { + weechat_hook_completion_list_add (completion, ptr_exec_cmd->name, + 0, WEECHAT_LIST_POS_SORT); + } + } + + return WEECHAT_RC_OK; +} + +/* + * Hooks completions. + */ + +void +exec_completion_init () +{ + weechat_hook_completion ("exec_commands_ids", + N_("ids (numbers and names) of executed commands"), + &exec_completion_commands_ids_cb, NULL); +} diff --git a/src/plugins/exec/exec-completion.h b/src/plugins/exec/exec-completion.h new file mode 100644 index 000000000..a01bb03cf --- /dev/null +++ b/src/plugins/exec/exec-completion.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_EXEC_COMPLETION_H +#define __WEECHAT_EXEC_COMPLETION_H 1 + +extern void exec_completion_init (); + +#endif /* __WEECHAT_EXEC_COMPLETION_H */ diff --git a/src/plugins/exec/exec-config.c b/src/plugins/exec/exec-config.c new file mode 100644 index 000000000..05ecc027e --- /dev/null +++ b/src/plugins/exec/exec-config.c @@ -0,0 +1,191 @@ +/* + * exec-config.c - exec configuration options (file exec.conf) + * + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "../weechat-plugin.h" +#include "exec.h" +#include "exec-config.h" + + +struct t_config_file *exec_config_file = NULL; + +/* exec config, command section */ + +struct t_config_option *exec_config_command_default_options; +struct t_config_option *exec_config_command_purge_delay; + +/* exec config, color section */ + +struct t_config_option *exec_config_color_flag_running; +struct t_config_option *exec_config_color_flag_finished; + +char **exec_config_cmd_options = NULL; +int exec_config_cmd_num_options = 0; + + +/* + * Callback for changes on option "exec.command.default_options". + */ + +void +exec_config_change_command_default_options (void *data, + struct t_config_option *option) +{ + /* make C compiler happy */ + (void) data; + (void) option; + + if (exec_config_cmd_options) + weechat_string_free_split (exec_config_cmd_options); + + exec_config_cmd_options = weechat_string_split ( + weechat_config_string (exec_config_command_default_options), + " ", 0, 0, &exec_config_cmd_num_options); +} + +/* + * Reloads exec configuration file. + */ + +int +exec_config_reload_cb (void *data, struct t_config_file *config_file) +{ + /* make C compiler happy */ + (void) data; + + return weechat_config_reload (config_file); +} + +/* + * Initializes exec configuration file. + * + * Returns: + * 1: OK + * 0: error + */ + +int +exec_config_init () +{ + struct t_config_section *ptr_section; + + exec_config_file = weechat_config_new (EXEC_CONFIG_NAME, + &exec_config_reload_cb, NULL); + if (!exec_config_file) + return 0; + + /* command */ + ptr_section = weechat_config_new_section (exec_config_file, "command", + 0, 0, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL); + if (!ptr_section) + { + weechat_config_free (exec_config_file); + return 0; + } + + exec_config_command_default_options = weechat_config_new_option ( + exec_config_file, ptr_section, + "default_options", "string", + N_("default options for command /exec (see /help exec); example: " + "\"-nosh -bg\" to run all commands in background (no output), and " + "without using the shell"), + NULL, 0, 0, "", NULL, 0, NULL, NULL, + &exec_config_change_command_default_options, NULL, NULL, NULL); + exec_config_command_purge_delay = weechat_config_new_option ( + exec_config_file, ptr_section, + "purge_delay", "integer", + N_("delay for purging finished commands (in seconds, 0 = purge " + "commands immediately, -1 = never purge)"), + NULL, -1, 36000 * 24 * 30, "0", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL); + + /* color */ + ptr_section = weechat_config_new_section (exec_config_file, "color", + 0, 0, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL); + if (!ptr_section) + { + weechat_config_free (exec_config_file); + return 0; + } + + exec_config_color_flag_running = weechat_config_new_option ( + exec_config_file, ptr_section, + "flag_running", "color", + N_("text color for a running command flag (in exec buffer and " + "/exec -list)"), + NULL, 0, 0, "lightgreen", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL); + exec_config_color_flag_finished = weechat_config_new_option ( + exec_config_file, ptr_section, + "flag_finished", "color", + N_("text color for a finished command flag (in exec buffer and " + "/exec -list)"), + NULL, 0, 0, "lightred", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL); + + return 1; +} + +/* + * Reads exec configuration file. + */ + +int +exec_config_read () +{ + return weechat_config_read (exec_config_file); +} + +/* + * Writes exec configuration file. + */ + +int +exec_config_write () +{ + return weechat_config_write (exec_config_file); +} + +/* + * Frees exec configuration. + */ + +void +exec_config_free () +{ + weechat_config_free (exec_config_file); + + if (exec_config_cmd_options) + { + weechat_string_free_split (exec_config_cmd_options); + exec_config_cmd_options = NULL; + exec_config_cmd_num_options = 0; + } +} diff --git a/src/plugins/exec/exec-config.h b/src/plugins/exec/exec-config.h new file mode 100644 index 000000000..9963fd092 --- /dev/null +++ b/src/plugins/exec/exec-config.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_EXEC_CONFIG_H +#define __WEECHAT_EXEC_CONFIG_H 1 + +#define EXEC_CONFIG_NAME "exec" +#define EXEC_CONFIG_SECTION_EXEC "exec" + +extern struct t_config_file *exec_config_file; + +extern struct t_config_option *exec_config_command_default_options; +extern struct t_config_option *exec_config_command_purge_delay; + +extern struct t_config_option *exec_config_color_flag_running; +extern struct t_config_option *exec_config_color_flag_finished; + +extern char **exec_config_cmd_options; +extern int exec_config_cmd_num_options; + +extern int exec_config_init (); +extern int exec_config_read (); +extern int exec_config_write (); +extern void exec_config_free (); + +#endif /* __WEECHAT_EXEC_CONFIG_H */ diff --git a/src/plugins/exec/exec.c b/src/plugins/exec/exec.c new file mode 100644 index 000000000..a8184d680 --- /dev/null +++ b/src/plugins/exec/exec.c @@ -0,0 +1,587 @@ +/* + * exec.c - execution of external commands in WeeChat + * + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "../weechat-plugin.h" +#include "exec.h" +#include "exec-buffer.h" +#include "exec-command.h" +#include "exec-completion.h" +#include "exec-config.h" + + +WEECHAT_PLUGIN_NAME(EXEC_PLUGIN_NAME); +WEECHAT_PLUGIN_DESCRIPTION(N_("Execution of external commands in WeeChat")); +WEECHAT_PLUGIN_AUTHOR("Sébastien Helleu <flashcode@flashtux.org>"); +WEECHAT_PLUGIN_VERSION(WEECHAT_VERSION); +WEECHAT_PLUGIN_LICENSE(WEECHAT_LICENSE); + +struct t_weechat_plugin *weechat_exec_plugin = NULL; + +struct t_exec_cmd *exec_cmds = NULL; /* first executed command */ +struct t_exec_cmd *last_exec_cmd = NULL; /* last executed command */ +int exec_cmds_count = 0; /* number of executed commands */ + +char *exec_color_string[EXEC_NUM_COLORS] = +{ "off", "decode", "strip" }; + + +/* + * Searches for a color action name. + * + * Returns index of color in enum t_exec_color, -1 if not found. + */ + +int +exec_search_color (const char *color) +{ + int i; + + if (!color) + return -1; + + for (i = 0; i < EXEC_NUM_COLORS; i++) + { + if (weechat_strcasecmp (exec_color_string[i], color) == 0) + return i; + } + + /* color not found */ + return -1; +} + +/* + * Searches for an executed command by id, which can be a number or a name. + * + * Returns pointer to executed command found, NULL if not found. + */ + +struct t_exec_cmd * +exec_search_by_id (const char *id) +{ + struct t_exec_cmd* ptr_exec_cmd; + char *error; + long number; + + error = NULL; + number = strtol (id, &error, 10); + if (!error || error[0]) + number = -1; + + for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd; + ptr_exec_cmd = ptr_exec_cmd->next_cmd) + { + /* check if number is matching */ + if ((number >= 0) && (ptr_exec_cmd->number == (int)number)) + return ptr_exec_cmd; + + /* check if name is matching */ + if (ptr_exec_cmd->name && (strcmp (ptr_exec_cmd->name, id) == 0)) + return ptr_exec_cmd; + } + + /* executed command not found */ + return NULL; +} + +/* + * Adds a command in list of executed commands. + */ + +struct t_exec_cmd * +exec_add () +{ + struct t_exec_cmd *new_exec_cmd, *ptr_exec_cmd; + int number; + + /* find first available number */ + number = (last_exec_cmd) ? last_exec_cmd->number + 1 : 0; + for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd; + ptr_exec_cmd = ptr_exec_cmd->next_cmd) + { + if (ptr_exec_cmd->prev_cmd + && (ptr_exec_cmd->number > ptr_exec_cmd->prev_cmd->number + 1)) + { + number = ptr_exec_cmd->prev_cmd->number + 1; + break; + } + } + + new_exec_cmd = malloc (sizeof (*new_exec_cmd)); + if (!new_exec_cmd) + return NULL; + + new_exec_cmd->prev_cmd = last_exec_cmd; + new_exec_cmd->next_cmd = NULL; + if (!exec_cmds) + exec_cmds = new_exec_cmd; + else + last_exec_cmd->next_cmd = new_exec_cmd; + last_exec_cmd = new_exec_cmd; + + new_exec_cmd->number = number; + new_exec_cmd->name = NULL; + new_exec_cmd->hook = NULL; + new_exec_cmd->command = NULL; + new_exec_cmd->pid = 0; + new_exec_cmd->detached = 0; + new_exec_cmd->start_time = time (NULL); + new_exec_cmd->end_time = 0; + new_exec_cmd->output_to_buffer = 0; + new_exec_cmd->buffer_full_name = NULL; + new_exec_cmd->line_numbers = 0; + new_exec_cmd->display_rc = 0; + new_exec_cmd->stdout_size = 0; + new_exec_cmd->stdout = NULL; + new_exec_cmd->stderr_size = 0; + new_exec_cmd->stderr = NULL; + new_exec_cmd->return_code = -1; + + exec_cmds_count++; + + return new_exec_cmd; +} + +/* + * Timer callback to delete a command. + */ + +int +exec_timer_delete_cb (void *data, int remaining_calls) +{ + struct t_exec_cmd *exec_cmd, *ptr_exec_cmd; + + /* make C compiler happy */ + (void) remaining_calls; + + exec_cmd = (struct t_exec_cmd *)data; + if (!exec_cmd) + return WEECHAT_RC_OK; + + for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd; + ptr_exec_cmd = ptr_exec_cmd->next_cmd) + { + if (ptr_exec_cmd == exec_cmd) + { + exec_free (ptr_exec_cmd); + break; + } + } + + return WEECHAT_RC_OK; +} + +/* + * Concatenates some text to stdout/stderr of a command. + */ + +void +exec_command_concat_output (int *size, char **output, const char *text) +{ + int length, new_size; + char *new_output; + + length = strlen (text); + new_size = *size + length; + new_output = realloc (*output, new_size + 1); + if (!new_output) + return; + + *output = new_output; + memcpy (*output + *size, text, length + 1); + *size = new_size; +} + +/* + * Displays output of a command. + */ + +void +exec_command_display_output (struct t_exec_cmd *exec_cmd, + struct t_gui_buffer *buffer, int stdout) +{ + char *ptr_output, *ptr_line, *line, *line2, *pos; + char str_number[32], str_tags[1024]; + int line_nb, length; + + ptr_output = (stdout) ? exec_cmd->stdout : exec_cmd->stderr; + if (!ptr_output) + return; + + /* + * if output is sent to the buffer, the buffer must exist + * (we don't send output by default to core buffer) + */ + if (exec_cmd->output_to_buffer && !buffer) + return; + + ptr_line = ptr_output; + line_nb = 1; + while (ptr_line) + { + /* ignore last empty line */ + if (!ptr_line[0]) + break; + + /* search end of line */ + pos = strchr (ptr_line, '\n'); + line = (pos) ? + weechat_strndup (ptr_line, pos - ptr_line) : strdup (ptr_line); + if (!line) + break; + + if (exec_cmd->color != EXEC_COLOR_OFF) + { + line2 = weechat_hook_modifier_exec ( + (exec_cmd->output_to_buffer) ? + "irc_color_decode_ansi" : "color_decode_ansi", + (exec_cmd->color == EXEC_COLOR_DECODE) ? "1" : "0", + line); + free (line); + if (!line2) + break; + line = line2; + } + + if (exec_cmd->output_to_buffer) + { + if (exec_cmd->line_numbers) + { + length = 32 + strlen (line) + 1; + line2 = malloc (length); + if (line2) + { + snprintf (line2, length, "%d. %s", line_nb, line); + weechat_command (buffer, line2); + free (line2); + } + } + else + weechat_command (buffer, (line[0]) ? line : " "); + } + else + { + + snprintf (str_number, sizeof (str_number), "%d", exec_cmd->number); + snprintf (str_tags, sizeof (str_tags), + "exec_%s,exec_cmd_%s", + (stdout) ? "stdout" : "stderr", + (exec_cmd->name) ? exec_cmd->name : str_number); + snprintf (str_number, sizeof (str_number), "%d\t", line_nb); + weechat_printf_tags (buffer, str_tags, + "%s%s", + (exec_cmd->line_numbers) ? str_number : " \t", + line); + } + + free (line); + line_nb++; + ptr_line = (pos) ? pos + 1 : NULL; + } +} + +/* + * Ends a command. + */ + +void +exec_end_command (struct t_exec_cmd *exec_cmd, int return_code) +{ + struct t_gui_buffer *ptr_buffer; + + ptr_buffer = weechat_buffer_search ("==", exec_cmd->buffer_full_name); + + /* display stdout/stderr (if output to buffer, the buffer must exist) */ + exec_command_display_output (exec_cmd, ptr_buffer, 1); + exec_command_display_output (exec_cmd, ptr_buffer, 0); + + /* + * display return code (only if command is not detached and if output is + * NOT sent to buffer) + */ + if (!exec_cmd->detached && !exec_cmd->output_to_buffer + && exec_cmd->display_rc) + { + if (return_code >= 0) + { + weechat_printf_tags (ptr_buffer, "exec_rc", + _("%s: end of command %d (\"%s\"), " + "return code: %d"), + EXEC_PLUGIN_NAME, exec_cmd->number, + exec_cmd->command, return_code); + } + else + { + weechat_printf_tags (ptr_buffer, "exec_rc", + _("%s: unexpected end of command %d " + "(\"%s\")"), + EXEC_PLUGIN_NAME, exec_cmd->number, + exec_cmd->command); + } + } + + /* (re)set some variables after the end of command */ + exec_cmd->hook = NULL; + exec_cmd->pid = 0; + exec_cmd->end_time = time (NULL); + exec_cmd->return_code = return_code; + + /* schedule a timer to remove the executed command */ + if (weechat_config_integer (exec_config_command_purge_delay) >= 0) + { + weechat_hook_timer (1 + (1000 * weechat_config_integer (exec_config_command_purge_delay)), + 0, 1, + &exec_timer_delete_cb, exec_cmd); + } +} + +/* + * Callback for hook process. + */ + +int +exec_process_cb (void *data, const char *command, int return_code, + const char *out, const char *err) +{ + struct t_exec_cmd *ptr_exec_cmd; + + /* make C compiler happy */ + (void) command; + + ptr_exec_cmd = (struct t_exec_cmd *)data; + if (!ptr_exec_cmd) + return WEECHAT_RC_ERROR; + + if (weechat_exec_plugin->debug >= 2) + { + weechat_printf (NULL, + "%s: process_cb: command=\"%s\", rc=%d, " + "out: %d bytes, err: %d bytes", + EXEC_PLUGIN_NAME, + ptr_exec_cmd->command, + return_code, + (out) ? strlen (out) : 0, + (err) ? strlen (err) : 0); + } + + if (return_code == WEECHAT_HOOK_PROCESS_ERROR) + { + exec_end_command (ptr_exec_cmd, -1); + return WEECHAT_RC_OK; + } + + if (out) + { + exec_command_concat_output (&ptr_exec_cmd->stdout_size, + &ptr_exec_cmd->stdout, + out); + } + if (err) + { + exec_command_concat_output (&ptr_exec_cmd->stderr_size, + &ptr_exec_cmd->stderr, + err); + } + + if (return_code >= 0) + exec_end_command (ptr_exec_cmd, return_code); + + return WEECHAT_RC_OK; +} + +/* + * Deletes a command. + */ + +void +exec_free (struct t_exec_cmd *exec_cmd) +{ + if (!exec_cmd) + return; + + /* remove command from commands list */ + if (exec_cmd->prev_cmd) + (exec_cmd->prev_cmd)->next_cmd = exec_cmd->next_cmd; + if (exec_cmd->next_cmd) + (exec_cmd->next_cmd)->prev_cmd = exec_cmd->prev_cmd; + if (exec_cmds == exec_cmd) + exec_cmds = exec_cmd->next_cmd; + if (last_exec_cmd == exec_cmd) + last_exec_cmd = exec_cmd->prev_cmd; + + /* free data */ + if (exec_cmd->hook) + weechat_unhook (exec_cmd->hook); + if (exec_cmd->name) + free (exec_cmd->name); + if (exec_cmd->command) + free (exec_cmd->command); + if (exec_cmd->buffer_full_name) + free (exec_cmd->buffer_full_name); + if (exec_cmd->stdout) + free (exec_cmd->stdout); + if (exec_cmd->stderr) + free (exec_cmd->stderr); + + free (exec_cmd); + + exec_cmds_count--; +} + +/* + * Deletes all commands. + */ + +void +exec_free_all () +{ + while (exec_cmds) + { + exec_free (exec_cmds); + } +} + +/* + * Prints exec infos in WeeChat log file (usually for crash dump). + */ + +void +exec_print_log () +{ + struct t_exec_cmd *ptr_exec_cmd; + + for (ptr_exec_cmd = exec_cmds; ptr_exec_cmd; + ptr_exec_cmd = ptr_exec_cmd->next_cmd) + { + weechat_log_printf (""); + weechat_log_printf ("[exec command (addr:0x%lx)]", ptr_exec_cmd); + weechat_log_printf (" number. . . . . . . . . : %d", ptr_exec_cmd->number); + weechat_log_printf (" name. . . . . . . . . . : '%s'", ptr_exec_cmd->name); + weechat_log_printf (" hook. . . . . . . . . . : 0x%lx", ptr_exec_cmd->hook); + weechat_log_printf (" command . . . . . . . . : '%s'", ptr_exec_cmd->command); + weechat_log_printf (" pid . . . . . . . . . . : %d", ptr_exec_cmd->pid); + weechat_log_printf (" detached. . . . . . . . : %d", ptr_exec_cmd->detached); + weechat_log_printf (" start_time. . . . . . . : %ld", ptr_exec_cmd->start_time); + weechat_log_printf (" end_time. . . . . . . . : %ld", ptr_exec_cmd->end_time); + weechat_log_printf (" output_to_buffer. . . . : %d", ptr_exec_cmd->output_to_buffer); + weechat_log_printf (" buffer_full_name. . . . : '%s'", ptr_exec_cmd->buffer_full_name); + weechat_log_printf (" line_numbers. . . . . . : %d", ptr_exec_cmd->line_numbers); + weechat_log_printf (" display_rc. . . . . . . : %d", ptr_exec_cmd->display_rc); + weechat_log_printf (" stdout_size . . . . . . : %d", ptr_exec_cmd->stdout_size); + weechat_log_printf (" stdout. . . . . . . . . : '%s'", ptr_exec_cmd->stdout); + weechat_log_printf (" stderr_size . . . . . . : %d", ptr_exec_cmd->stderr_size); + weechat_log_printf (" stderr. . . . . . . . . : '%s'", ptr_exec_cmd->stderr); + weechat_log_printf (" return_code . . . . . . : %d", ptr_exec_cmd->return_code); + weechat_log_printf (" prev_cmd. . . . . . . . : 0x%lx", ptr_exec_cmd->prev_cmd); + weechat_log_printf (" next_cmd. . . . . . . . : 0x%lx", ptr_exec_cmd->next_cmd); + } +} + +/* + * Callback for signal "debug_dump". + */ + +int +exec_debug_dump_cb (void *data, const char *signal, const char *type_data, + void *signal_data) +{ + /* make C compiler happy */ + (void) data; + (void) signal; + (void) type_data; + + if (!signal_data + || (weechat_strcasecmp ((char *)signal_data, EXEC_PLUGIN_NAME) == 0)) + { + weechat_log_printf (""); + weechat_log_printf ("***** \"%s\" plugin dump *****", + weechat_plugin->name); + + exec_print_log (); + + weechat_log_printf (""); + weechat_log_printf ("***** End of \"%s\" plugin dump *****", + weechat_plugin->name); + } + + return WEECHAT_RC_OK; +} + +/* + * Initializes exec plugin. + */ + +int +weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[]) +{ + int i, upgrading; + + weechat_plugin = plugin; + + exec_command_init (); + + if (!exec_config_init ()) + return WEECHAT_RC_ERROR; + + exec_config_read (); + + /* hook some signals */ + weechat_hook_signal ("debug_dump", &exec_debug_dump_cb, NULL); + + /* hook completions */ + exec_completion_init (); + + /* look at arguments */ + upgrading = 0; + for (i = 0; i < argc; i++) + { + if (weechat_strcasecmp (argv[i], "--upgrade") == 0) + { + upgrading = 1; + } + } + + if (upgrading) + exec_buffer_set_callbacks (); + + return WEECHAT_RC_OK; +} + +/* + * Ends exec plugin. + */ + +int +weechat_plugin_end (struct t_weechat_plugin *plugin) +{ + /* make C compiler happy */ + (void) plugin; + + exec_config_write (); + exec_free_all (); + exec_config_free (); + + return WEECHAT_RC_OK; +} diff --git a/src/plugins/exec/exec.h b/src/plugins/exec/exec.h new file mode 100644 index 000000000..9958ed7a3 --- /dev/null +++ b/src/plugins/exec/exec.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_EXEC_H +#define __WEECHAT_EXEC_H 1 + +#include <time.h> + +#define weechat_plugin weechat_exec_plugin +#define EXEC_PLUGIN_NAME "exec" + +enum t_exec_color { + EXEC_COLOR_OFF = 0, + EXEC_COLOR_DECODE, + EXEC_COLOR_STRIP, + /* number of color actions */ + EXEC_NUM_COLORS, +}; + +struct t_exec_cmd +{ + /* command/process */ + int number; /* command number */ + char *name; /* name of command */ + struct t_hook *hook; /* pointer to process hook */ + char *command; /* command (with arguments) */ + pid_t pid; /* process id */ + int detached; /* 1 if command is detached */ + time_t start_time; /* start time */ + time_t end_time; /* end time */ + + /* display */ + int output_to_buffer; /* 1 if output is sent to buffer */ + char *buffer_full_name; /* buffer where output is displayed */ + int line_numbers; /* 1 if lines numbers are displayed */ + int color; /* what to do with ANSI colors */ + int display_rc; /* 1 if return code is displayed */ + + /* command output */ + int stdout_size; /* number of bytes in stdout */ + char *stdout; /* stdout of command */ + int stderr_size; /* number of bytes in stderr */ + char *stderr; /* stderr of command */ + int return_code; /* command return code */ + + struct t_exec_cmd *prev_cmd; /* link to previous command */ + struct t_exec_cmd *next_cmd; /* link to next command */ +}; + +extern struct t_weechat_plugin *weechat_exec_plugin; +extern struct t_exec_cmd *exec_cmds; +extern struct t_exec_cmd *last_exec_cmd; +extern int exec_cmds_count; + +extern int exec_search_color (const char *color); +extern struct t_exec_cmd *exec_search_by_id (const char *id); +extern struct t_exec_cmd *exec_add (); +extern int exec_process_cb (void *data, const char *command, int return_code, + const char *out, const char *err); +extern void exec_free (struct t_exec_cmd *exec_cmd); +extern void exec_free_all (); + +#endif /* __WEECHAT_EXEC_H */ diff --git a/src/plugins/irc/irc-color.c b/src/plugins/irc/irc-color.c index 69b0381eb..6c0713aec 100644 --- a/src/plugins/irc/irc-color.c +++ b/src/plugins/irc/irc-color.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <string.h> #include <ctype.h> +#include <regex.h> #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; /* @@ -362,6 +383,371 @@ irc_color_encode (const char *string, int keep_colors) } /* + * 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; + 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; + } + } + +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". * * This modifier can be used by other plugins to decode/encode IRC colors in @@ -385,6 +771,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 +792,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; } 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 @@ -278,6 +278,25 @@ plugin_api_command (struct t_weechat_plugin *plugin, } /* + * 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"), 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 cea485f7b..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 "20140304-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, \ 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 |