Skip to content

Commit

Permalink
util (ble/builtin/trap): fix resetting $? and $_
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Feb 2, 2022
1 parent 2b28bec commit dfc6221
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 19 deletions.
2 changes: 1 addition & 1 deletion ble.pp
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions docs/ChangeLog.md
Expand Up @@ -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
Expand Down
68 changes: 68 additions & 0 deletions note.txt
Expand Up @@ -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]
Expand Down
85 changes: 67 additions & 18 deletions src/util.sh
Expand Up @@ -2056,23 +2056,34 @@ 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
_ble_trap_done=break
else
_ble_trap_done=continue
fi
return "$_ble_trap_lastexit"
return 0
}
## @fn ble/builtin/trap/invoke sig
## @param[in] sig
Expand All @@ -2088,32 +2099,62 @@ 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
local set shopt; ble/base/.adjust-bash-options set shopt

# 透過 _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
Expand All @@ -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
}

Expand All @@ -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" 等とすると
Expand Down

0 comments on commit dfc6221

Please sign in to comment.