OSDN Git Service

Post-prompt command
authorWATANABE Yuki <magicant@wonderwand.net>
Wed, 17 Apr 2024 15:31:09 +0000 (00:31 +0900)
committerWATANABE Yuki <magicant@wonderwand.net>
Wed, 17 Apr 2024 15:31:09 +0000 (00:31 +0900)
15 files changed:
NEWS
NEWS.ja
doc/_set.txt
doc/interact.txt
doc/ja/_set.txt
doc/ja/interact.txt
doc/ja/params.txt
doc/ja/posix.txt
doc/params.txt
doc/posix.txt
input.c
share/initialization/common
tests/prompt-y.tst
tests/run-test.sh
variable.h

diff --git a/NEWS b/NEWS
index 725c077..e57aa47 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,8 @@ History of Yash
 
 ## Yash 2.57 (Unreleased)
 
+  - Added support for the "$POST_PROMPT_COMMAND" variable, whose value
+    is executed after reading a command line in the interactive shell.
   - [line-editing] Fixed the spurious error message printed when
     completing after `git config alias.` with the nounset shell option
     enabled.
diff --git a/NEWS.ja b/NEWS.ja
index 905dcd3..84a8ac0 100644 (file)
--- a/NEWS.ja
+++ b/NEWS.ja
@@ -2,6 +2,8 @@ Yash 更新履歴
 
 ## Yash 2.57 (未リリース)
 
+  - "$POST_PROMPT_COMMAND" に対応。対話モードでコマンドを読むごとに
+    変数の値が実行される
   - [行編集] nounset オプション有効時に `git config alias.` に続けて
     補完をしようとするとエラーが出るのを修正
   - [行編集] カーソルがバックスラッシュの直後にある時に補完をすると
index b0fbfbc..7e62e76 100644 (file)
@@ -202,6 +202,7 @@ This option enables the link:posix.html[POSIXly-correct mode].
 When this option is disabled, the <<so-xtrace,x-trace option>> is temporarily
 disabled while the shell is executing commands defined in the
 link:params.html#sv-command_not_found_handler[+COMMAND_NOT_FOUND_HANDLER+],
+link:params.html#sv-post_prompt_command[+POST_PROMPT_COMMAND+],
 link:params.html#sv-prompt_command[+PROMPT_COMMAND+], or
 link:params.html#sv-yash_after_cd[+YASH_AFTER_CD+] variable.
 
index 7d8fb0d..25c73cd 100644 (file)
@@ -178,9 +178,16 @@ variables can be defined with a name prefixed with +YASH_+ (e.g.
 link:params.html#sv-yash_ps1[+YASH_PS1+]). This allows using a different
 prompt string than that in the POSIXly-correct mode.
 
-When the shell is not in the link:posix.html[POSIXly-correct mode],
-the value of the link:params.html#sv-prompt_command[+PROMPT_COMMAND+ variable]
-is executed before each prompt.
+When the shell is not in the link:posix.html[POSIXly-correct mode]:
+
+- The value of the link:params.html#sv-prompt_command[+PROMPT_COMMAND+ variable]
+  is executed before each prompt.
+- The value of the
+  link:params.html#sv-post_prompt_command[+POST_PROMPT_COMMAND+ variable] is
+  executed after each line is input. While the execution, the
+  link:params.html#sv-command[+COMMAND+ variable] is set to the just input
+  line. You can even modify the variable to manipulate the command to be
+  executed. If you unset the variable, the command will not be executed.
 
 [[history]]
 == Command history
index b27fb7e..0cb3709 100644 (file)
@@ -134,6 +134,7 @@ link:syntax.html#for[For ループ]が{zwsp}link:exec.html#function[関数]の
 [[so-traceall]]trace-all::
 このオプションは、補助コマンド実行中も <<so-xtrace,x-trace オプション>>を機能させるかどうかを指定します。補助コマンドとは、
 link:params.html#sv-command_not_found_handler[+COMMAND_NOT_FOUND_HANDLER+]、
+link:params.html#sv-post_prompt_command[+POST_PROMPT_COMMAND+]、
 link:params.html#sv-prompt_command[+PROMPT_COMMAND+]、および
 link:params.html#sv-yash_after_cd[+YASH_AFTER_CD+]
 変数の値として定義され、特定のタイミングで解釈・実行されるコマンドです。
index 517a256..3de6c7e 100644 (file)
@@ -98,7 +98,10 @@ link:lineedit.html#prediction[コマンドライン推定]を使用している
 
 link:posix.html[POSIX 準拠モード]でないときは、上記の変数は名前に +YASH_+ を付けた名前 (例えば link:params.html#sv-yash_ps1[+YASH_PS1+]) で定義することもできます。これにより、POSIX 準拠モードとは異なるプロンプトを使い分けることができます。
 
-link:posix.html[POSIX 準拠モード]でないときは、プロンプトを出す前に link:params.html#sv-prompt_command[+PROMPT_COMMAND+ 変数]の値がコマンドとして実行されます。
+link:posix.html[POSIX 準拠モード]でないときは、以下の変数が評価されます。
+
+- プロンプトを出す前に link:params.html#sv-prompt_command[+PROMPT_COMMAND+ 変数]の値がコマンドとして実行されます。
+- コマンドが一行分入力されるたびに link:params.html#sv-post_prompt_command[+POST_PROMPT_COMMAND+ 変数]の値がコマンドとして実行されます。実行中は入力されたコマンドが link:params.html#sv-command[+COMMAND+ 変数]に代入されます。この変数の値を変更することで実行されるコマンドを改変することもできます。変数を削除するとコマンドは実行されません。
 
 [[history]]
 == コマンド履歴
index a7afea5..55fbbb0 100644 (file)
@@ -82,6 +82,10 @@ dfn:[変数]とはユーザが自由に代入可能なパラメータです。
 [[sv-columns]]+COLUMNS+::
 この変数は端末ウィンドウの横幅 (文字数) を指定します。この変数が設定されている場合、デフォルトの横幅ではなくこの変数の値で指定された横幅が{zwsp}link:lineedit.html[行編集]で使われます。
 
+[[sv-command]]+COMMAND+::
+<<sv-post_prompt_command,+POST_PROMPT_COMMAND+>> 変数が実行される間、この変数は直前に入力されたコマンドを示します。
++POST_PROMPT_COMMAND+ の実行が終わるとこの変数は消去されます。
+
 [[sv-command_not_found_handler]]+COMMAND_NOT_FOUND_HANDLER+::
 シェルが実行しようとしたコマンドが見つからなかったとき、この変数の値がコマンドとして実行されます。不明なコマンドを実行したときに何か別のコマンドを実行させたい時に便利です。{zwsp}link:exec.html#simple[単純コマンドの実行]を参照。
 +
@@ -163,11 +167,14 @@ link:_getopts.html[Getopts 組込みコマンド]で引数付きのオプショ
 [[sv-path]]+PATH+::
 この変数は、{zwsp}link:exec.html#search[コマンドの検索時]にコマンドのありかを示すパスを指定します。
 
+[[sv-post_prompt_command]]+POST_PROMPT_COMMAND+::
+link:posix.html[POSIX 準拠モード]でない{zwsp}link:interact.html[対話モード]のシェルにおいて、シェルがコマンドを一行読み込むたびに、この変数の値がコマンドとして解釈・実行されます。詳細は{zwsp}link:interact.html#prompt[プロンプト]を参照してください。
+
 [[sv-ppid]]+PPID+::
 この変数の値は、シェルの親プロセスのプロセス ID を表す正の整数です。この変数はシェルの起動時に初期化されます。この変数の値は{zwsp}link:exec.html#subshell[サブシェル]においても変わりません。
 
 [[sv-prompt_command]]+PROMPT_COMMAND+::
-link:posix.html[POSIX 準拠モード]でない{zwsp}link:interact.html[対話モード]のシェルにおいて、シェルが各コマンドのプロンプトを出す直前に、この変数の値がコマンドとして解釈・実行されます。これは、プロンプトを出す直前に毎回
+link:posix.html[POSIX 準拠モード]でない{zwsp}link:interact.html[対話モード]のシェルにおいて、シェルが各コマンドの{zwsp}link:interact.html#prompt[プロンプト]を出す直前に、この変数の値がコマンドとして解釈・実行されます。これは、プロンプトを出す直前に毎回
 ifdef::basebackend-html[]
 pass:[<code><a href="_eval.html">eval</a> -i -- "${PROMPT_COMMAND-}"</code>]
 endif::basebackend-html[]
index feb762d..581542a 100644 (file)
@@ -35,7 +35,7 @@ POSIX 準拠モードを有効にすると、yash は POSIX の規定にでき
 - link:builtin.html#types[任意組込みコマンドおよび拡張組込みコマンド]は実行できません。
 - いくつかの{zwsp}link:builtin.html[組込みコマンド]で特定のオプションが使えなくなるなど挙動が変わります。特に、長いオプションは使えなくなります。
 - link:interact.html[対話モード]でないとき、{zwsp}link:builtin.html#types[特殊組込みコマンド]のオプションやオペランドの使い方が間違っているとシェルは直ちに終了します。また特殊組込みコマンドで代入エラーやリダイレクトエラーが発生したときも直ちに終了します。
-- link:interact.html[対話モード]のプロンプトを出す前に link:params.html#sv-prompt_command[+PROMPT_COMMAND+ 変数]の値を実行しません。{zwsp}link:params.html#sv-ps1[+PS1+ 変数]・{zwsp}link:params.html#sv-ps2[+PS2+ 変数]・{zwsp}link:params.html#sv-ps4[+PS4+ 変数]の値の解釈の仕方が違います。{zwsp}link:params.html#sv-yash_ps1[+YASH_PS1+] など +YASH_+ で始まる名前のプロンプト変数は使用されません。
+- link:interact.html[対話モード]のプロンプトを出す前後に link:params.html#sv-prompt_command[+PROMPT_COMMAND+ 変数]および link:params.html#sv-post_prompt_command[+POST_PROMPT_COMMAND+ 変数]の値を実行しません。{zwsp}link:params.html#sv-ps1[+PS1+ 変数]・{zwsp}link:params.html#sv-ps2[+PS2+ 変数]・{zwsp}link:params.html#sv-ps4[+PS4+ 変数]の値の解釈の仕方が違います。{zwsp}link:params.html#sv-yash_ps1[+YASH_PS1+] など +YASH_+ で始まる名前のプロンプト変数は使用されません。
 - link:interact.html#mailcheck[メールチェック]において、ファイルが更新されている場合はファイルが空でも新着メールメッセージを出力します。
 
 // vim: set filetype=asciidoc expandtab:
index 243e86b..130e642 100644 (file)
@@ -145,6 +145,11 @@ This variable specifies the width (the number of character columns) of the
 terminal screen. The value affects the display of
 link:lineedit.html[line-editing].
 
+[[sv-command]]+COMMAND+::
+While a <<sv-post_prompt_command,post-prompt command>> is being executed,
+this variable is set to the just input command line. It will be unset after
+the post-prompt command finishes.
+
 [[sv-command_not_found_handler]]+COMMAND_NOT_FOUND_HANDLER+::
 When the shell cannot find a command to be executed,
 the value of this variable is interpreted and executed instead.
@@ -284,6 +289,12 @@ This variable is initialized to +1+ when the shell is started.
 This variable specifies paths that are searched for a command in
 link:exec.html#search[command search].
 
+[[sv-post_prompt_command]]+POST_PROMPT_COMMAND+::
+The shell interprets and executes the value of this variable after reading
+each command if the shell is link:interact.html[interactive] and not in
+the link:posix.html[POSIXly-correct mode].
+See link:interact.html#prompt[Prompts] for details.
+
 [[sv-ppid]]+PPID+::
 The value of this variable is the process ID of the shell's parent process,
 which is a positive integer.
@@ -293,7 +304,7 @@ link:exec.html#subshell[subshell].
 
 [[sv-prompt_command]]+PROMPT_COMMAND+::
 The shell interprets and executes the value of this variable before printing
-each command prompt if the shell is link:interact.html[interactive] and not in
+each command link:interact.html#prompt[prompt] if the shell is link:interact.html[interactive] and not in
 the link:posix.html[POSIXly-correct mode].
 This behavior is equivalent to executing the command
 ifdef::basebackend-html[]
index 5abf88c..02c4ae3 100644 (file)
@@ -87,8 +87,9 @@ When the POSIXly-correct mode is enabled:
   arguments or when an error occurs in assignment or redirection with a
   special built-in.
 - An link:interact.html[interactive] shell does not execute the
-  link:params.html#sv-prompt_command[+PROMPT_COMMAND+ variable] before
-  printing a prompt.
+  link:params.html#sv-prompt_command[+PROMPT_COMMAND+] or
+  link:params.html#sv-post_prompt_command[+POST_PROMPT_COMMAND+ variable]
+  before and after a prompt, respectively.
   The values of the link:params.html#sv-ps1[+PS1+],
   link:params.html#sv-ps2[+PS2+], and link:params.html#sv-ps4[+PS4+] variables
   are parsed differently.
diff --git a/input.c b/input.c
index 25ef354..8002f4f 100644 (file)
--- a/input.c
+++ b/input.c
@@ -64,6 +64,8 @@ static wchar_t *expand_ps1_posix(wchar_t *s)
     __attribute__((nonnull,malloc,warn_unused_result));
 static inline wchar_t get_euid_marker(void)
     __attribute__((pure));
+static wchar_t *post_prompt_command(wchar_t *line)
+    __attribute__((nonnull,malloc,warn_unused_result));
 
 /* An input function that inputs from a wide string.
  * `inputinfo' must be a pointer to a `struct input_wcs_info_T'.
@@ -288,6 +290,8 @@ success:
 #endif
     if (info->prompttype == 1)
         info->prompttype = 2;
+    if (!posixly_correct)
+        line = post_prompt_command(line);
 #if YASH_ENABLE_HISTORY
     add_history(line);
 #endif
@@ -481,6 +485,39 @@ wchar_t get_euid_marker(void)
     return geteuid() == 0 ? L'#' : L'$';
 }
 
+/* Executes $POST_PROMPT_COMMAND, if any.
+ * `line' is the just input command line, which will be assigned to $COMMAND
+ * during the execution. The post-prompt command may modify or unset the
+ * variable. The final value of the variable is returned as a newly malloced
+ * string. `line' is freed in this function. */
+wchar_t *post_prompt_command(wchar_t *line)
+{
+    // If `line` ends with a newline, trim it here and append it back later.
+    size_t linelen = wcslen(line);
+    bool newline = linelen > 0 && line[linelen - 1] == L'\n';
+    if (newline)
+        line[linelen - 1] = L'\0';
+
+    open_new_environment(false);
+    set_positional_parameters((void *[]) { NULL });
+    set_variable(L VAR_COMMAND, line, SCOPE_LOCAL, false);
+
+    exec_variable_as_auxiliary_(VAR_POST_PROMPT_COMMAND);
+    const wchar_t *c = getvar(L VAR_COMMAND);
+    if (c == NULL)
+        c = L"";
+
+    xwcsbuf_T linebuf;
+    wb_init(&linebuf);
+    wb_cat(&linebuf, c);
+    if (newline)
+        wb_wccat(&linebuf, L'\n');
+
+    close_current_environment();
+
+    return wb_towcs(&linebuf);
+}
+
 /* Unsets O_NONBLOCK flag of the specified file descriptor.
  * If `fd' is negative, does nothing.
  * Returns true if successful. On error, `errno' is set and false is returned.*/
index 405a425..feafb38 100644 (file)
@@ -199,6 +199,7 @@ export SHLVL
 # initialize event handlers
 COMMAND_NOT_FOUND_HANDLER=()
 PROMPT_COMMAND=()
+POST_PROMPT_COMMAND=()
 YASH_AFTER_CD=()
 
 # define prompt
index e9042a4..524a31b 100644 (file)
@@ -102,6 +102,14 @@ $
 $ 
 __ERR__
 
+test_Oe 'POST_PROMPT_COMMAND is ignored in POSIX mode' -i +m
+POST_PROMPT_COMMAND='echo not printed'; echo >&2
+echo >&2; exit
+__IN__
+$ 
+$ 
+__ERR__
+
 )
 
 test_e 'YASH_PSx precedes PSx (non-POSIX)' -i +m
@@ -193,6 +201,34 @@ $
 123$ 1
 __ERR__
 
+test_oe 'value of $COMMAND in post-prompt command' -i +m
+POST_PROMPT_COMMAND='printf "[%s]\n" "$COMMAND" >&2'
+echo foo\
+bar; exit
+__IN__
+foobar
+__OUT__
+$ $ [echo foo\]
+> [bar; exit]
+__ERR__
+
+test_o 'modifying $COMMAND in post-prompt command' -i +m
+POST_PROMPT_COMMAND='COMMAND="$COMMAND; echo post"'
+echo foo
+exit
+__IN__
+foo
+post
+__OUT__
+
+test_O 'unsetting $COMMAND in post-prompt command' -i +m
+POST_PROMPT_COMMAND='if [ "$COMMAND" != exit ]; then unset COMMAND; fi'
+echo foo\
+bar
+exit
+echo not reached
+__IN__
+
 test_e '\$ in PS1 and PS2 (non-root)' -i +m
 PS1='\$ ' PS2='\$_'; echo >&2
 e\
index 2678b2b..6faeb3c 100644 (file)
@@ -77,10 +77,10 @@ exec >|"${test_file%.*}.trs"
 export LC_CTYPE="${LC_ALL-${LC_CTYPE-$LANG}}"
 export LANG=C
 export YASH_LOADPATH= # ignore default yashrc
-unset -v CDPATH COLUMNS COMMAND_NOT_FOUND_HANDLER DIRSTACK ECHO_STYLE ENV
-unset -v FCEDIT HANDLED HISTFILE HISTRMDUP HISTSIZE HOME IFS LC_ALL
+unset -v CDPATH COLUMNS COMMAND COMMAND_NOT_FOUND_HANDLER DIRSTACK ECHO_STYLE
+unset -v ENV FCEDIT HANDLED HISTFILE HISTRMDUP HISTSIZE HOME IFS LC_ALL
 unset -v LC_COLLATE LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME LINES MAIL
-unset -v MAILCHECK MAILPATH NLSPATH OLDPWD PROMPT_COMMAND
+unset -v MAILCHECK MAILPATH NLSPATH OLDPWD POST_PROMPT_COMMAND PROMPT_COMMAND
 unset -v PS1 PS1R PS1S PS2 PS2R PS2S PS3 PS3R PS3S PS4 PS4R PS4S 
 unset -v RANDOM TERM YASH_AFTER_CD YASH_LE_TIMEOUT YASH_VERSION
 unset -v A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _
index 671be55..24dcc13 100644 (file)
@@ -1,6 +1,6 @@
 /* Yash: yet another shell */
 /* variable.h: deals with shell variables and parameters */
-/* (C) 2007-2020 magicant */
+/* (C) 2007-2024 magicant */
 
 /* This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@ extern char **environ;
 /* variable names */
 #define VAR_CDPATH                    "CDPATH"
 #define VAR_COLUMNS                   "COLUMNS"
+#define VAR_COMMAND                   "COMMAND"
 #define VAR_COMMAND_NOT_FOUND_HANDLER "COMMAND_NOT_FOUND_HANDLER"
 #define VAR_DIRSTACK                  "DIRSTACK"
 #define VAR_ECHO_STYLE                "ECHO_STYLE"
@@ -57,6 +58,7 @@ extern char **environ;
 #define VAR_OPTARG                    "OPTARG"
 #define VAR_OPTIND                    "OPTIND"
 #define VAR_PATH                      "PATH"
+#define VAR_POST_PROMPT_COMMAND       "POST_PROMPT_COMMAND"
 #define VAR_PPID                      "PPID"
 #define VAR_PROMPT_COMMAND            "PROMPT_COMMAND"
 #define VAR_PS1                       "PS1"