diff --git a/ble.pp b/ble.pp index 49e3a33b..6854b4fa 100644 --- a/ble.pp +++ b/ble.pp @@ -1376,7 +1376,7 @@ function ble/dispatch/.help { } function ble/dispatch { if (($#==0)); then - [[ $_ble_attach && ! $_ble_edit_exec_inside_userspace ]] + [[ $_ble_attached && ! $_ble_edit_exec_inside_userspace ]] return $? fi diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md index d6092d53..0e944fc8 100644 --- a/docs/ChangeLog.md +++ b/docs/ChangeLog.md @@ -302,6 +302,7 @@ - global: work around `shopt -s compat42` `#D1754` a75bb25 - global (`ble/builtin/*`): work around `set -eu` in NixOS initialization (reported by SuperSandro2000) `#D1743` 001c595 - util, edit, contrib: add support for `bash-preexec` (motivated by SuperSandro2000) `#D1744` e85f52c + - util (`ble/builtin/trap`): fix resetting `$?` and `$_` (reported by SuperSandro2000) `#D1757` 0000000 - main: check `IN_NIX_SHELL` to inactivate ble.sh in nix-shell (suggested by SuperSandro2000) `#D1747` b4bd955 - canvas: test the terminal for the sequence of clearing `DECSTBM` `#D1748` 4b1601d - main: check `/dev/tty` on startup (reported by andychu) `#D1749` 711c69f diff --git a/note.txt b/note.txt index 5d464791..1e48a7b8 100644 --- a/note.txt +++ b/note.txt @@ -5942,6 +5942,74 @@ bash_tips Done (実装ログ) ------------------------------------------------------------------------------- +2022-02-01 + + * util (trap): SIGWINCH を沢山呼び出した後にどんどん重くなる (reported by SuperSandro2000) [#D1757] + https://github.com/akinomyoga/ble.sh/issues/173#issuecomment-1026891422 + + これは _ble_builtin_trap_postproc を出力したら分かった。trap handler が走る + 度にどんどん $_ の中身が長くなっている。 + + > ble/util/setexit 1 '' + > ble/util/setexit 1 'ble/util/setexit 1 '\'''\''' + > ble/util/setexit 1 'ble/util/setexit 1 '\''ble/util/setexit 1 '\''\'\'''\'''\''\'\'''\'''\''' + + この様になっている。先ず、builtin eval の中から $_ を再設定しようとしても + eval に渡した引数が $_ になってしまうという事が問題である。これはどの様にし + たら良いだろうか。一つの方法は eval "postproc" '#' "lastarg" と呼び出すという事。 + + x postproc に文法的に壊れた物が入っていた時に変な事になるのではないか。と思っ + たが、postproc を設定しているのは trap handler の枠組み自身なのでちゃんと + した物しか入っていないというのは保証される気がする。 + + x lastarg に改行が含まれている場合には復元できないのではないか。これはどう + しようもない。 + + というか今回の問題を除いても $?, $_ を正しく復元できている様に見えない。 + trap handler の各ケース done, return, break, continue のそれぞれについて + $?, $_ を正しく保存しなければならない。 + + 先ず return/break/continue の時の $? $_ の振る舞いについて確認する必要がある。 + + $ trap 'break 1' INT + $ for ((i=0;i<1000000;i++)); do ((a++)); done; echo "_=$_" + ^C + _=1 + $ trap 'break' INT + $ for ((i=0;i<1000000;i++)); do ((a++)); done; echo "_=$_" + ^C + _=break + + この結果を見ると loop の外で $?, $_ を見ることができる。更に言うと break, + continue に渡された引数ですら取得できる。 for (()) は $? $_ を書き換えない + 様に見える。 + + ? break/continue に引数として許される形式は? 普通の整数はOK 負の整数もOK +1 + などもOK 少数は駄目だった。空文字列も駄目だった筈。その様な判定にした。 + + 取り敢えず実装してみたが全然正しく復元できていない。 + + x fixed: 次に setexit が結局 $_ に設定されてしまっている。何故。と思ったら + '#' が本当に # になってしまっていた。これは直した→すぐに動く様になった。 + + x fixed: 先ず $_ で取得した lastarg には gexec に使った begin ... end が入っ + ている。これはそのままでも良いのかもしれないし、或いはコマンド実行中かそ + うでないかで振る舞いを変更するべきなのかもしれない。コマンド実行中でない + 時にはユーザートラップを呼び出す前に復元してユーザートラップを呼び出した + 後に保存する? + + % 或いはそもそも保存しなくても良い気がする。本来 trap が別のコマンドに影響 + % を与えるというのも変な話である。→と思ったが或る handler の影響は次の + % handler に残っていて欲しい気もする。つまり、ユーザーコマンドに影響が残る + % のは変な気がするが一方で同じ handler の間で $?, $_ がちゃんと保持されてい + % なければ変だという事。やはり保存するべきの様な気がしてきた。 + →ユーザートラップによって変更された $_ はちゃんと反映させるべきの気がする。 + →うーん。結局トラップ専用の lastarg,lastexit 変数に記録する事にした。 + + x おまけで ble/dispatch のモード判定にミスを発見した。修正した。 + + 取り敢えず制限はあるがこれで大丈夫の筈。 + 2022-01-24 * edit: コマンド実行時間計測 [#D1756] diff --git a/src/util.sh b/src/util.sh index 76bf9786..8ec4b233 100644 --- a/src/util.sh +++ b/src/util.sh @@ -2056,15 +2056,26 @@ function ble/builtin/trap { } function trap { ble/builtin/trap "$@"; } +# user trap handler 専用の $?, $_ の記録。 +_ble_builtin_trap_user_lastcmd= +_ble_builtin_trap_user_lastarg= +_ble_builtin_trap_user_lastexit= +## @fn ble/builtin/trap/invoke.sandbox +## @var[in] _ble_trap_handler +## @var[out] _ble_trap_done function ble/builtin/trap/invoke.sandbox { local _ble_trap_count for ((_ble_trap_count=0;_ble_trap_count<1;_ble_trap_count++)); do _ble_trap_done=return + # Note #D1757: そのまま制御を変更せずに trap handler の実行が終わっ + # た時は $? $_ を保存する。同じ eval の中でないと $_ が eval を抜 + # けた時に eval の最終引数に置き換えられてしまう事に注意する。 ble/util/setexit "$_ble_trap_lastexit" "$_ble_trap_lastarg" - builtin eval -- "$_ble_trap_handler" 2>&3 + builtin eval -- "$_ble_trap_handler"$'\n_ble_trap_lastexit=$? _ble_trap_lastarg=$_' 2>&3 _ble_trap_done=done - return "$_ble_trap_lastexit" + return 0 done + _ble_trap_lastexit=$? _ble_trap_lastarg=$_ # break/continue 検出 if ((_ble_trap_count==0)); then @@ -2072,7 +2083,7 @@ function ble/builtin/trap/invoke.sandbox { else _ble_trap_done=continue fi - return "$_ble_trap_lastexit" + return 0 } ## @fn ble/builtin/trap/invoke sig ## @param[in] sig @@ -2088,24 +2099,53 @@ function ble/builtin/trap/invoke { fi local _ble_trap_handler=${_ble_builtin_trap_handlers[_ble_trap_sig]-} - if [[ $_ble_trap_handler ]]; then - local _ble_trap_done= - ble/builtin/trap/invoke.sandbox; local ext=$? - case $_ble_trap_done in - (done) - _ble_builtin_trap_postproc="ble/util/setexit $ext" ;; - (break) - _ble_builtin_trap_postproc=break ;; - (continue) - _ble_builtin_trap_postproc=continue ;; - (return) - _ble_builtin_trap_postproc="return $ext" ;; - esac + [[ $_ble_trap_handler ]] || return 0 + + # restore $_ and $? for user trap handlers + if [[ $_ble_attached && ! $_ble_edit_exec_inside_userspace ]]; then + if [[ $_ble_builtin_trap_user_lastcmd != $_ble_edit_CMD ]]; then + _ble_builtin_trap_user_lastcmd=$_ble_edit_CMD + _ble_builtin_trap_user_lastexit=$_ble_edit_exec_lastexit + _ble_builtin_trap_user_lastarg=$_ble_edit_exec_lastarg + fi + _ble_trap_lastexit=$_ble_builtin_trap_user_lastexit + _ble_trap_lastarg=$_ble_builtin_trap_user_lastarg + fi + + local _ble_trap_done= + ble/builtin/trap/invoke.sandbox; local ext=$? + case $_ble_trap_done in + (done) + _ble_builtin_trap_lastarg=$_ble_trap_lastarg + _ble_builtin_trap_postproc="ble/util/setexit $_ble_trap_lastext" ;; + (break | continue) + _ble_builtin_trap_lastarg=$_ble_trap_lastarg + if ble/string#match "$_ble_trap_lastarg" '^-?[0-9]+$'; then + _ble_builtin_trap_postproc="$_ble_trap_done $_ble_trap_lastarg" + else + _ble_builtin_trap_postproc=$_ble_trap_done + fi ;; + (return) + # Note #D1757: return 自体の lastarg は最早取得できないが、もし + # 仮に直接 builtin trap で実行されたとしても、return で関数を抜 + # けた時に lastarg は書き換えられるので取得できない。精々関数を + # 呼び出す前の lastarg を設定して置いて return が失敗した時に前 + # の状態を keep するぐらいしかない気がする。 + _ble_builtin_trap_lastarg=$ext + _ble_builtin_trap_postproc="return $ext" ;; + esac + + # save $_ and $? for user trap handlers + if [[ $_ble_attached && ! $_ble_edit_exec_inside_userspace ]]; then + _ble_builtin_trap_user_lastexit=$_ble_trap_lastexit + _ble_builtin_trap_user_lastarg=$_ble_trap_lastarg fi + } 3>&2 2>/dev/null # set -x 対策 #D0930 ## @fn ble/builtin/trap/.handler sig signame ## @var[out] _ble_builtin_trap_postproc +## @var[out] _ble_builtin_trap_lastarg function ble/builtin/trap/.handler { local _ble_trap_lastexit=$? _ble_trap_lastarg=$_ FUNCNEST= local _ble_trap_sig=$1 _ble_trap_name=$2 @@ -2113,7 +2153,8 @@ function ble/builtin/trap/.handler { # 透過 _ble_builtin_trap_postproc を設定 local _ble_local_q=\' _ble_local_Q="'\''" - _ble_builtin_trap_postproc="ble/util/setexit $_ble_trap_lastexit '${_ble_trap_lastarg//$_ble_local_q/$_ble_local_Q}'" + _ble_builtin_trap_lastarg=$_ble_trap_lastarg + _ble_builtin_trap_postproc="ble/util/setexit $_ble_trap_lastexit" # ble.sh hook ble/util/joblist.check @@ -2125,6 +2166,14 @@ function ble/builtin/trap/.handler { ble/util/setexit "$_ble_trap_lastexit" "$_ble_trap_lastarg" ble/builtin/trap/invoke "$_ble_trap_sig" + # Note #D1757: 現在 eval が終わった後の $_ を設定する為には eval に + # '#' "$lastarg" を余分に渡すしかないので改行を含める事はできない。 + # 中途半端な値を設定するよりは最初から何も設定しない事にする。ここ設 + # 定する lastarg は一見して誰も使わない様な気がするが、裸で設定され + # た user trap が参照するかもしれないので一応設定する。 + [[ $_ble_builtin_trap_lastarg == *$'\n'* ]] && + _ble_builtin_trap_lastarg= + ble/base/.restore-bash-options set shopt } @@ -2135,7 +2184,7 @@ function ble/builtin/trap/install-hook { local sig=$ret name=${_ble_builtin_trap_signames[ret]} ble/builtin/trap/reserve "$sig" - local handler="ble/builtin/trap/.handler $sig ${name#SIG}; builtin eval -- \"\$_ble_builtin_trap_postproc\"" + local handler="ble/builtin/trap/.handler $sig ${name#SIG}; builtin eval -- \"\$_ble_builtin_trap_postproc\" \\# \"\$_ble_builtin_trap_lastarg\"" local trap_command="trap -- '$handler' $name" if [[ $opts == *:readline:* ]] && ! ble/util/is-running-in-subshell; then # Note #D1345: ble.sh の内部で "builtin trap -- WINCH" 等とすると