diff --git a/ble.pp b/ble.pp index 002a1cf6..ff66c676 100644 --- a/ble.pp +++ b/ble.pp @@ -323,7 +323,7 @@ function ble/variable#copy-state { function ble/base/.adjust-bash-options { builtin eval -- "$1=\$-" - set +exvuk -B + set +exvukT -B [[ $2 == shopt ]] || local shopt if ((_ble_bash>=40100)); then @@ -343,6 +343,7 @@ function ble/base/.restore-bash-options { local set=${!1} shopt=${!2} [[ :$shopt: == *:nocasematch:* ]] && shopt -s nocasematch [[ $set == *B* ]] || set +B + [[ $set == *T* ]] && set -T [[ $set == *k* ]] && set -k [[ $set == *u* ]] && set -u [[ $set == *v* ]] && set -v @@ -933,6 +934,8 @@ function ble/base/.create-user-directory { ## ## @var _ble_base +## @var _ble_base_blesh +## @var _ble_base_blesh_raw ## ## ble.sh のインストール先ディレクトリ。 ## 読み込んだ ble.sh の実体があるディレクトリとして解決される。 @@ -942,9 +945,11 @@ function ble/base/initialize-base-directory { local defaultDir=${2-} # resolve symlink - if [[ -h $src ]] && type -t readlink &>/dev/null; then + _ble_base_blesh_raw=$src + if [[ -h $src ]]; then local ret; ble/util/readlink "$src"; src=$ret fi + _ble_base_blesh=$src if [[ -s $src && $src != */* ]]; then _ble_base=$PWD @@ -1587,6 +1592,7 @@ function ble/base/install-prompt-attach { _ble_base_attach_PROMPT_COMMAND[save_index]=${PROMPT_COMMAND-} ble/function#lambda PROMPT_COMMAND \ "ble/base/attach-from-PROMPT_COMMAND $save_index \"\$FUNCNAME\"" + ble/function#trace "$PROMPT_COMMAND" if [[ $_ble_edit_detach_flag == reload ]]; then _ble_edit_detach_flag=prompt-attach blehook PRECMD+="$PROMPT_COMMAND" @@ -1607,12 +1613,14 @@ function ble/base/attach-from-PROMPT_COMMAND { local save_index=$1 lambda=$2 # 待避していた内容を復元・実行 - [[ $PROMPT_COMMAND == "$lambda" ]] || local PROMPT_COMMAND + local PROMPT_COMMAND_local= + [[ $PROMPT_COMMAND == "$lambda" ]] || local PROMPT_COMMAND PROMPT_COMMAND_local=1 PROMPT_COMMAND=${_ble_base_attach_PROMPT_COMMAND[save_index]} local ble_base_attach_from_prompt_command=processing ble/prompt/update/.eval-prompt_command 2>&3 ble/util/unlocal ble_base_attach_from_prompt_command _ble_base_attach_PROMPT_COMMAND[save_index]=$PROMPT_COMMAND + [[ ! $PROMPT_COMMAND_local ]] || ble/util/unlocal PROMPT_COMMAND blehook PRECMD-="$lambda" || ((1)) # set -e 対策 # #D1354: 入れ子の ble/base/attach-from-PROMPT_COMMAND の時は一番 @@ -1748,6 +1756,15 @@ function ble/base/sub:clear-cache { ble/debug/measure-set-timeformat ble.pp/epilogue; } #%end +# Note: ble-attach 及びそれを呼び出す可能性がある物には DEBUG trap を +# 継承させる。これはユーザーの設定した user trap を正しく抽出する為 +# に必要。現在は ble-attach から呼び出される ble-edit/attach で処理 +# している。 +ble/function#trace ble-attach +ble/function#trace ble +ble/function#trace ble/dispatch +ble/function#trace ble/base/attach-from-PROMPT_COMMAND + ble-import -f lib/_package if [[ $_ble_init_command ]]; then ble/base/sub:"$_ble_init_command"; _ble_init_exit=$? diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md index 8b5728db..397593b9 100644 --- a/docs/ChangeLog.md +++ b/docs/ChangeLog.md @@ -318,6 +318,7 @@ - main: check `/dev/tty` on startup (reported by andychu) `#D1749` 711c69f - util: add identification of Windows Terminal `wt` `#D1758` e332dc5 - complete: evaluate words for `noquote` (motivated by SuperSandro2000) `#D1767` 0a42299 +- edit (TRAPDEBUG): preserve original `DEBUG` trap and enabled it in `PROMPT_COMMAND` (motivated by ammarooo) `#D1772` `#D1773` 0000000 ## Internal changes and fixes diff --git a/make_command.sh b/make_command.sh index 4413fa91..257d10b6 100755 --- a/make_command.sh +++ b/make_command.sh @@ -1319,6 +1319,7 @@ function sub:scan { \Zline = "bind"Zd \Zlocal trap_command="trap -- Zd \Zlocal trap$Zd + \Z\$\{content#"trap -- '\''"\}Zd g' sub:scan/a.txt diff --git a/memo/D1772.bashrc b/memo/D1772.bashrc new file mode 100644 index 00000000..c50a4630 --- /dev/null +++ b/memo/D1772.bashrc @@ -0,0 +1,229 @@ +#! bash config + +shopt -s extglob +#opts=builtin-elapsed +#opts=no-timer + +#type=none +type=initial +#type=initial-prompt +#type=final +#type=final-prompt + +#type=check-DEBUG-behavior +#type=unset-self-func +#type=check-bashbug-tmpenv +#type=check-unset-trap-DEBUG + +#set -T + +if [[ $type == initial ]]; then + source out/ble.sh --attach=none +elif [[ $type == initial-prompt ]]; then + source out/ble.sh +fi + +if [[ ${BLE_VERSION-} && :$opts: == *:builtin-elapsed:* ]]; then + ble-import contrib/prompt-elapsed + _ps1_time='\q{contrib/elapsed}' +elif [[ :$opts: != *:no-timer:* ]]; then + function timer_start { +#echo $FUNCNAME >/dev/tty + timer=${timer:-$SECONDS} + } + + function timer_stop { +#echo $FUNCNAME >/dev/tty + timer_show=$(($SECONDS - $timer)) + #timer_show=$(($SECONDS - timer)) + unset timer + } + + trap 'timer_start "$BASH_COMMAND"' DEBUG + + if [ "$PROMPT_COMMAND" == "" ]; then + PROMPT_COMMAND="timer_stop" + else + PROMPT_COMMAND="$PROMPT_COMMAND; timer_stop" + fi + + _ps1_time='${timer_show}s' +fi + +case $type in +(check-DEBUG-behavior) + echo "direct:1" + builtin trap -p DEBUG + echo "direct:2" + + # Q サブシェルの中でも DEBUG trap の定義を参照できるか? + # A 参照できる。 + echo "subshell:1" + echo "[$(builtin trap -p DEBUG)]" + echo "subshell:2" + + # Q 関数の中でも DEBUG trap の定義を参照できるか? + # A 参照できない。-t を付加していれば見られる。 + function inside-function { + echo "$FUNCNAME:1" + builtin trap -p DEBUG + echo "$FUNCNAME:2" + } >/dev/tty + declare -ft inside-function + inside-function + + # Q 関数内サブシェルでも DEBUG trap の定義を参照できるか? + # A できない。-t を付加していれば見られる。 + function inside-func-subshell { + echo "$FUNCNAME[$(builtin trap -p DEBUG)]" + } >/dev/tty + declare -ft inside-func-subshell + inside-func-subshell + + # Q 関数内 eval でも DEBUG trap の定義を参照できるか? + # A できない。-t を付加していれば見られる。 + function inside-func-eval { + echo "$FUNCNAME:1" + eval 'builtin trap -p DEBUG' + echo "$FUNCNAME:2" + } >/dev/tty + declare -ft inside-func-eval + inside-func-eval + + # Q 関数の更に内側の関数で DEBUG trap の定義を参照できるか? + # A できない。外側と内側の両方に -t を指定して初めて見られる。 + function inside-func-nest/1 { + builtin trap -p DEBUG + } + function inside-func-nest { + echo "$FUNCNAME:1" + inside-func-nest/1 + echo "$FUNCNAME:2" + } >/dev/tty + declare -ft inside-func-nest/1 + declare -ft inside-func-nest + inside-func-nest + + # Q 関数の中でリダイレクトした時に DEBUG trap の定義を参照できるか。 + # A できない。-t を付加していれば見られる。 + function inside-func-redir { + builtin trap -p DEBUG > a.tmp + echo "$FUNCNAME[$(/dev/tty + declare -ft inside-func-redir + inside-func-redir + + # Q 内側の関数から呼び出された後で外側の関数に対して -t 属性を付加して参照できる様になるだろうか。 + # A ならない。 + function nest-afterward-trace/1 { + declare -ft "${FUNCNAME[1]}" + builtin trap -p DEBUG + } + function nest-afterward-trace { + echo "$FUNCNAME:1" + nest-afterward-trace/1 + echo "$FUNCNAME:2" + } + declare -ft nest-afterward-trace/1 + nest-afterward-trace + + # Q 内側の関数で set -o functrace を設定して見られる様になるだろうか。 + # A ならない。 + function nest-afterward-functrace/1 { + set -o functrace + builtin trap -p DEBUG + set +o functrace + } + function nest-afterward-functrace { + echo "$FUNCNAME:1" + nest-afterward-functrace/1 + echo "$FUNCNAME:2" + } + nest-afterward-functrace + + # Q source xxx の中から DEBUG trap は見えるだろうか。 + # A 見えない。但し set -T (-o functrace) が設定されていれば見える。 + #set -T + { + echo echo inside-source:1 + echo builtin trap -p DEBUG + echo echo inside-source:2 + } >| a.tmp + source ./a.tmp + + exit ;; + +(unset-self-func) + function test-unset-self-function { + unset -f "$FUNCNAME" + echo hello + echo world + } + test-unset-self-function + + if declare -f test-unset-self-function; then + echo $'test-unset-self-function: \e[1;31mfound\e[m (unexpected)' + else + echo $'test-unset-self-function: \e[1;32mnot found\e[m (expected)' + fi + + exit ;; + +(check-bashbug-tmpenv) + set +T + + function hello { + echo "result1:$a" + echo "result2:$a" + } + a=value hello + a=value eval hello + a=value builtin eval hello + + function print { echo "v=${v:-(not found)}"; } + function trapdebug { echo "$FUNCNAME:1:v=($v)"; } + builtin trap 'trapdebug' DEBUG + v=xxxx builtin eval 'echo v=${v:-(not found)}' + v=xxxx eval 'echo v=${v:-(not found)}' + echo "[print with debugtrap]" + v=xxxx print + v=xxxx eval print + v=xxxx builtin eval print + + exit ;; + +(check-unset-trap-DEBUG) + # Q trace 属性のついた関数内から DEBUG trap を削除する事は可能か? + # A 削除できる。 + function unset-trap { + builtin trap - DEBUG + } + declare -ft unset-trap + + echo check-unset-trap-DEBUG:1 + builtin trap -p DEBUG + echo check-unset-trap-DEBUG:2 + unset-trap + echo check-unset-trap-DEBUG:3 + builtin trap -p DEBUG + echo check-unset-trap-DEBUG:4 + + exit ;; +esac + +if [[ $type == final ]]; then + source out/ble.sh --attach=none +elif [[ $type == final-prompt ]]; then + source out/ble.sh +fi +PS1='[last: '$_ps1_time'][\w]$ ' + +if [[ ${BLE_VERSION-} && $type == @(final|initial) ]]; then + ble-attach + # { echo A + # builtin trap -p DEBUG + # echo B + # trap -p DEBUG + # echo C + # } >/dev/tty +fi diff --git a/note.txt b/note.txt index b498cfe5..712ef2fb 100644 --- a/note.txt +++ b/note.txt @@ -1312,6 +1312,21 @@ bash_tips bug-bash, third-party bugs & reviews ------------------------------------------------------------------------------- +2022-02-15 + + * tmpenv builtin eval vs DEBUG trap + Ref #D1772 + + function print { echo "v=${v:-(not found)}"; } + function trapdebug { echo "$FUNCNAME:1:v=($v)"; } + builtin trap 'trapdebug' DEBUG + v=xxxx eval print + v=xxxx builtin eval print + + 以上に於いて builtin eval print で実行される print に対する DEBUG trap の中 + から v=xxxx が見えない。eval print の時にはちゃんと見えているので意図的な振 + る舞いとも思われない。 + 2022-01-23 * wezterm, bash-preexec に対する patch を終結させる。 @@ -1752,6 +1767,18 @@ bash_tips ToDo ------------------------------------------------------------------------------- +2022-02-14 + + * ble/builtin/trap DEBUG は関数の入れ子や trace 属性などは考慮していない。こ + の事によって問題が起こる可能性もある。 + + * bash-3.0 でコマンドを実行するとプロンプトの前に変な文字列 '' が出力される + + * DEBUG trap 以外についても元からあった trap を拾う様にしたい + Ref #D1772 + + * 関数の定義位置を DEBUG trap 等を使って抽出する事が可能なのではないか? + 2022-02-12 * highlight: ls -l ~/.c{onfig,a/b} @@ -1899,8 +1926,6 @@ bash_tips これを使って処理するのはもしかしたら外部プログラムに頼らなければならない かもしれない。 - * 元々存在した DEBUG trap を保持する - * bash-preexec に API 安定化のお願いをする。 * "for ((i=0;i<10;i++)); do sleep 1; done" で C-c すると ble.sh 全体がその場で停止する @@ -6087,9 +6112,896 @@ bash_tips Done (実装ログ) ------------------------------------------------------------------------------- +2022-02-15 + + * 2022-01-23 [解消] 元々存在した DEBUG trap を保持する [#D1773] + Ref #D1772 + + * exec: 既存の DEBUG trap を保持する (motivated by ammarooo) [#D1772] + https://github.com/akinomyoga/ble.sh/issues/176 + + * 然し、上記で報告されている物を修正するには PROMPT_COMMAND の実行時にも + DEBUG trap を有効にしなければならない。これは ble.sh の想定していない事で + ある。うーん。面倒であるが有効化する事にした方が良いだろうか。と、思った + が局所的に有効化して後で削除する事はできただろうか。同じ関数 scope 内で削 + 除すれば良いのだったか。何れにしても既存の DEBUG trap は捕まえる事にする。 + + 速度を計測して問題なければ PROMPT_COMMAND 実行時に DEBUG trap を有効にす + る事にする。その時には ble.sh の内部で直接実行している時にはユーザー trap + は無効になる様に調整する。 + + 時間を計測して無視できないという判断の場合には bleopt で有効化できる様に + する。 + + →attach 時の既存 trap かそれとも ble.sh load 時の trap か。load した時には + 既に trap は置き換えられているので、それ以降 attach 迄に設定された物に関し + ては気にしなくて良い。というか単に無視するしかない。なので、ble.sh load 時 + に設定されている trap を復元する方向で考える。 + + * ble-reload した時にもちゃんと上書きして塗り潰さない様に注意する。一つの手 + は trap を復元する事の気がする。これは ble/base/unload で実行すれば良い。 + + 取り敢えずロード時の DEBUG trap を記録する事にする。他にもロード時の INT 等 + を保持する様にしたい。 + + | うーん。trap -p DEBUG を実行すると最初のコマンド実行まではちゃんと存在して + | いる。然し、コマンドを実行した時点で消滅してしまう。 + | + | という事を考えると、何処かの時点で消えているという事になる筈だが、実際に + | trap DEBUG を上書きする瞬間で trap -p DEBUG を実行しても何も表示されていな + | い。或いは関数の中からだと外側の trap -p を読み取る事ができないという事なの + | だろうか→試してみるとサブシェルの中からだと見えないが本体のシェルからは例 + | え関数内であったとしても読み出す事ができている。 + | + | * うーん。何処で trap が削除されているかは分かった。一方で ble/util/assign + | の中だと DEBUG の trap string は正確に読み取れていないという事が判明した。 + | + | 何故だろう。eval があると DEBUG が伝播しないという事なのだろうか。 + | + | ? ble/util/assign で DEBUG trap の中身を取得できない理由について調べる。うー + | ん。 + + 色々試したがどうやらそもそも関数の中から trap DEBUG が取れるというのは幻だっ + た様だ。一番外側で設定されている DEBUG trap を読み取る場合、全ての呼び出し + 階層の関数に -t 属性が予めついている必要がある (呼び出しの最中に -t 属性を + 付加しても無駄である。これは set -o functrace についても同様である)。 + + これが意味する所は何だろうか。うーん。 + + * ble.sh が最初に DEBUG trap を上書きしようとする時までに外側の DEBUG trap + を読み取る必要がある。source ble.sh した瞬間には、ble.sh の一番外のレベル + であれば ble.sh が source された文脈での trap DEBUG を取得できるかもしれ + ないが、ble.sh 自体が関数内で source された場合には読み取る事ができない。 + + a reject: という事を考えると、やはり一番最初に DEBUG trap を設定しようとす + る機会に読み取るのが良い気がする。基本的に DEBUG trap を設定するのはコマ + ンド実行の直前であり、コマンド実行は一番外側の文脈で行われるからである。 + (然し、将来的に line-editor のプロセスとコマンドを実行するプロセスを分離 + した時にどうしたら良いのかについては不明である。というか分離した時には + trap 関連は全て再設計する必要があるので今ここで気にしても仕方がない気がす + る)。 + + と思って改めて調べてみたが、builtin trap DEBUG が初めて設定されるのは、ユー + ザーが ble/builtin/trap DEBUG を呼び出した時かまたは C-c を押した時である。 + 従って必ずしも通過するとは限らない。問題点は実際に DEBUG trap が設定され + ているかどうかに拘らず .epilogue 及び .end で trap -- - DEBUG trap を除去 + しようとしている事である (但し、.epilogue / .end で実行している trap - + DEBUG は結局何の効果もない)。 + + a1 そう考えると例えば TRAPDEBUG/enter に関しては毎回コマンドが実行される + 前に呼び出されるので、初回にこれが実行された時点で、この中で trap - + DEBUG を読み出すという事が考えられる。 + + a2 でもそうなって来るとそもそもコマンド実行時に DEBUG trap を読み取る必要 + すらない。初回の ble-decode/.hook の直後か直前に読み出してしまえば良い + のではないか? + + と思ったがそもそもコマンド実行直前である必要すらない。 + + b reject: やはり ble.sh がグローバルで読み出された時にはその時点で既存の + trap を読み取るのが安全である。もしグローバルでなかったら代替手段に頼る。 + + 実際に実装してみたが動かない。試しに ble.sh の先頭に以下を記述してみた所、 + 実は source の中ですら DEBUG は伝播しないのだという事が判明した。 + + | echo ble.sh:1 + | builtin trap -p DEBUG + | echo ble.sh:2 + + 駄目だ。この方法はそもそも使えなかったのである。改めて man bash を確認し + てみる。set -T の説明を読むと、これが設定されていなければ基本的には、関数 + もコマンド置換もサブシェルも DEBUG を継承しないと書かれている。然し + source に関しては述べられていない。何故だろう。実際に改めて小さなスクリプ + トを作って試してみると、やはり source の中では DEBUG trap は見えない。 + functrace を設定すると source の中でも見える様になる。functrace は man に + は書かれていないけれども source についても DEBUG が継承される様になる。 + + うーん。ble.sh の外側で set -T を実行させる様な方法は存在しないし、やはり + source ble.sh の中から元々の trap DEBUG を読み取るのは困難である。 + + 結局一旦は実装した以下の変更はなかった事にする。 + + | diff --git a/src/edit.sh b/src/edit.sh + | index c32bed0..29c96c4 100644 + | --- a/src/edit.sh + | +++ b/src/edit.sh + | @@ -5769,6 +5772,17 @@ function ble/builtin/trap:DEBUG { + | ble-edit/exec:gexec/.TRAPDEBUG/trap + | fi + | } + | +function ble/builtin/trap:DEBUG/.initialize { + | + builtin unset -f "$FUNCNAME" + | + if ((!_ble_bash_loaded_in_function)); then + | + local tmp=$_ble_base_run/$$.trap.DEBUG ret + | + builtin trap -p DEBUG >| "$tmp" + | + source "$tmp" # ble/builtin/trap に処理させる + | + ble/util/put '' >| "$tmp" + | + fi + | +} + | +declare -ft ble/builtin/trap:DEBUG/.initialize + | +ble/builtin/trap:DEBUG/.initialize + | + | function ble-edit/exec:gexec/.TRAPINT { + | local sig=130 + + 或いは functrace を on にしてもらうしかないのだ。然し常に functrace を on + にする訳にも行かないし、だからと言って勝手に ble.sh の中から functrace を + off にすると本当に functrace を on にしたい場合に使えなくなる。そうすると、 + ble.sh を呼び出す前後で set -T set +T を実行してもらう事になるが、其処ま + でするぐらいであれば、普通に先に ble.sh を先に source してもらうべきであ + る。また、一回でもキー入力をすれば治るのだが最初の一回だけは我慢してもら + うしかないと諦めるのである。 + + functrace を on にしてもらう様にお願いするのはおかしいとしても、ユーザー + が偶 functrace を on にしていたらそれを利用するべきなのでは? そういう意味 + でやはり上のコードは一応含めて置いて良いのではないか? + + c 最初のキー入力の直後に解決するという手がある。 + + グローバルで実行する必要があるので _ble_decode_bind_hook に指定する必要が + ある。然し、他の箇所からも同時に _ble_decode_bind_hook を実行しようとした + 時に問題になる。うーん。確認してみた所 _ble_decode_bind_hook に値を設定し + ているのは現在一箇所だけである。なので今までは何も気にする必要はなかった。 + + c1 _ble_decode_bind_hook には追記式でコマンドを書いていく方式にする可能性。 + そして複数の箇所から書き込む事を許す。 + + c2 _ble_decode_bind_hook を現在書き込んでいる箇所 (gexec/.setup) で処理す + る。 + + c3 reject: 或いは通常のコマンドとして最初に登録してしまう? と考えたがそう + するとコマンドのカウントが余分に増えてしまうので駄目の気がする。 + + % 他にも C-x C-v 等、コマンド実行として取り扱っている物があるのではなかっ + % たか。そういった物についてはコマンドのカウントが乱れない様になっている + % のか? と思って確認したがこれらは実は gexec で実行してはいなかった。その + % まま関数の中で実行していた。その時に必要に応じてシェル状態の保存・復元 + % をその場で実行して処理しているのだった。 + + 上記 c2 の方法が現状で一番綺麗の気がする。今後、もっと様々な箇所から bind + hook を設定したいという状況になったら c1 の様な方式にすれば良い。 + + % 取り敢えず、上記の b と c の方法を組み合わせる事にする。先ずは b から実装す + % る事にする→と思ったら b は不可能な事が判明してしまった。 + + 仕方がないので c の方針で実装する事にする。とにかく未だ DEBUG trap が存在し + ていない時にそれをチェックするのが目的。 + + というか最初のキー入力の時にようやく DEBUG trap を読み取る事ができるという + のが意味する事は…最初のプロンプトの表示の時点では DEBUG trap を正しく動作 + させるのは無理という事になるのでは。うーん。もしそうだとすれば報告を受けた + 場合に関しては、結局 PROMPT_COMMAND を実行させるしかないという事になる。 + + 或いは、attach した時の DA2 等の返答の際に取得はできる。とは言いつつ DA2 の + 返答が戻ってくるまで PROMPT_COMMAND の実行を遅延させる訳にも行かない。とい + う事を考えると、やはり最初の一回は必ず失敗するという事は認めてもらうしかな + い。或いは、最初に ble.sh を source して貰ってそれから最後に attach すると + いうのを徹底してもらうしかない。 + + * done: 取り敢えず c の方針で早く実装する。実装した。というか b の実装で用 + 意した関数をそのまま呼び出せば良いのだった。 + + * done: PROMPT_COMMAND で TRAPDEBUG を有効にする。 + + * done: functrace も待避・復元オプションに含める。 + + 追加した。これで特に問題が起こるとは思えない。DEBUG で余分に何か呼び出さ + れるという事はあるかもしれない。 + + ? [保留] bash-4.4..dev まで tmpenv の builtin eval の中から見える値が変。と。これ + は実は既に知っている問題なのではないか。 + + うーん。逆に bash-4.3 で問題が起こるという事は判明した。bash-4.4 以降では + ちゃんと値が見える筈である。それにも拘らず実際の ble.sh の中では逆に 4.4 + 以降で値が見えなくなっていた。これは一体どういう事だろうか。 + + →うーん。これはどうもまた別のバグの様だ。再現した。以下で再現させる事が + できる。これは trap 関連でしか再現しないのだろうか。分からない。 + + | function print { echo "v=${v:-(not found)}"; } + | function trapdebug { echo "$FUNCNAME:1:v=($v)"; } + | builtin trap 'trapdebug' DEBUG + | v=xxxx eval print + | v=xxxx builtin eval print + + と、思ったが、もしかすると DEBUG trap の中からは意図的に tmpenv が見えな + い様になっているのかもしれない。然し、builtin eval の時と eval の時の振る + 舞いを比べるとやはり異なるのでこれは意やはり意図したものではない。 + + これは別項目にする事にする。今は取り敢えず Note だけ残して放置する事にす + る。 + + [動作確認] + + * C1 取り敢えず動作はしている。然し微妙な振る舞いが色々ある。 + + x C2 fixed: 何故か起動した時に点滅する様になってしまった。一旦表示がクリア + されている? →これはコマンド実行をした時は bind/.tail をコマンド実行後に + 実行する為に bind/.tail を抑制するのが原因。今回 DEBUG initialize でも + return 0 をする様に変更してしまったので、DEBUG initialize の時にも + bind/.tail が省略されてしまってしかし DEBUG initialize の場合は自分で + bind/.tail が呼び出されないので、bind/.tail が欠けて bash -x によるプロン + プトの消去が発生して点滅していた。 + + DEBUG initialize だけの時は return 1 を返す事にした。 + + x C3 fixed: 後から ble.sh を source した場合、エラーメッセージが出る事も問 + 題であるが、最初のコマンドの実行時間がシェル開始時刻からコマンド終了時刻 + までの時間になってしまっている。 + + ble.sh なしだと期待通りに時間を計測する事ができている。先に ble.sh を + source した場合でも特に問題は起こっていない。 + + これは何故だろうか。先に trap を実行しているとどういう事になるかというと、 + bashrc の中で time_start されて timer が設定される事になるが最終的に + PROMPT_COMMAND が呼び出されて timer は clear される事になる。然し、その後 + で様々の処理が走ってその過程で改めて timer が設定される? と思ったが何によっ + て設定されるのかは謎である。ble-attach した段階で DEBUG は解除されている + のではないのか。と思ったが、違う。実際に解除されていないのだ。それでは全 + 然駄目だ。恐らく最初の ble-decode/.hook の瞬間に DEBUG trap が呼び出され + て結果として時間にずれが生じているという事だろう。 + + というか、trap - DEBUG で解除すれば良いのではないか? + →OK. これで治った。 + + x C4 fixed: 最初に ble.sh を source した場合でもエラーを検出している。これ + はどういう事か。うーん。最初の PROMPT_COMMAND を実行する時点で未だ attach + が完了していないから DEBUG trap の設定が省略されているという可能性? しか + し、それだとエラーが画面ではなく vbell に出力されている理由が分からない。 + これは要するに bash が取り敢えず裏で実行した PS1 の評価によって引き起こさ + れているのではないか。 + + と思ったが、bash が裏で PROMPT_COMMAND を実行するという事があるのだろう + か…。あー。分かった。PS1 については待避しているが、PROMPT_COMMAND につい + ては待避していないのである。 + + PROMPT_COMMAND が二重に実行されている問題は別枠で修正するべきの気がする。 + これは ble-0.3 にも適用する必要がある。と思って動作確認したが二重実行され + るのは最初に呼び出した時だけなので内部で処理する事にする。これは軽微な差 + 異なので 0.3 に適用する必要はない。 + + * PROMPT_COMMAND が ble.sh と bash で二重実行される事について + + 特に PROMPT_COMMAND を待避していた記憶はない。という事は PROMPT_COMMAND + が裏で二重に実行されているのではないか。更に言うと、裏の PROMPT_COMMAND + によって裏の PS1 が設定されてしまっているのではないか。 + + 現状で実際にその様になってしまっているかどうかを確認する事にする。→確 + 認してみたが別にそういう困った事にはなっていなかった。よく考えてみたら + PROMPT_COMMAND はコマンドが実行された後に実行されるのであって、bash の + 枠組みの方でコマンドを実行していないのだから余分に呼び出される事はない + のである。 + + 但し、初回の PROMPT_COMMAND に限っては bash によって実行されるという事 + に留意しなければならない。PS1 を待避・復元している所で PROMPT_COMMAND + も待避・復元すれば良いのではないだろうか。但し、初回だけで良い。否、 + ble-attach の時に何回 PROMPT_COMMAND が呼び出されるか分からないので、何 + 回かは実行する必要があるだろうか。これは実際に試して何回実行されるか確 + 認すれば良い。 + + 確認した所4回呼び出されている。これは確かに呼び出し過ぎである。その内の + 1回は bash から直接呼び出されている。残り3回が何処から来ているかは確認 + する必要がある。一番最後の物は端末テスト後の再配置だろうと思われるが、 + 再配置の時に改めて PROMPT_COMMAND を呼び出す必要もないだろうという気が + する。 + + ? そんなに重い処理でなければ PS1 と一緒に毎回保存・復元すれば良いのでは? + と思ったが、PROMPT_COMMAND 配列を保存・復元するのは面倒なのでやはり保存・ + 復元は最初だけにするのが良い様に思われる。 + + PROMPT_COMMAND 配列の場合には特に設定下側がどの key に対して設定した + かを覚えているかもしれないので、key をずらしてしまうのは良くないので、 + 特に key も一緒に記録する必要がある。 + + 保存・復元は ble/util/declare-print-definitions PROMPT_COMMAND を用い + る方法と各要素を一つずつ保存復元する方法の二通り考えられる。前者は + fork を沢山するので重い。後者も要素が大量になってくると大変である。 + + うーん。取り敢えず各要素毎にコピーする事にする。 + + 取り敢えず対応した。ちゃんと動く様になった。 + + * reject: 待避は bashrc の中から実行している時だけで充分なのではないか。 + + そんなに重い処理でもないし PS1 を毎回待避している事を思うと + PROMPT_COMMAND を毎回待避しても対した処理にはならないので、気にしなくて + も良いのではないか。 + + a 然し、bashrc の中から実行しているという事が分かるのであればその時にだ + け待避するのは尤もな気がする。 + + 一方でその様な方法が存在するかどうかはまた別である。以前その様な方法 + を思いついた様な気もするし、結局動かなかった様な気もする。思い出せな + い。bash のソースコードを覗いて、何かの振る舞いで interactive / + non-interactive で振る舞いの変わる物があってそれを巧妙に組み合わせれ + ば判定可能なのではないかと思ったが、結局異なる振る舞いを再現させる事 + ができなくて諦めた気がする。 + + b もしくは attach 後の初回の PROMPT_COMMAND の評価の時だけ待避する? し + かし、それだと複数回 attach もしくは reload を行った時に (そもそもそ + の様な使い方は想定していないとは言え)、PROMPT_COMMAND が余分に実行さ + れてしまう事になる。 + + 更にコマンドラインから直接 ble-attach した時や prompt attach した時に + は初回でも PROMPT_ATTACH を待避する必要もない。という事を考えると初回 + かどうかというのは本質ではない様に思われる。 + + c 或いはコマンドを既に一回でも実行していたら OK という考え方もある。こ + れならば複数回 reload した時に PROMPT_COMMAND が重複して実行されると + いうこともない。 + + 一方でやはり初回には余計に PROMPT_COMMAND を待避する事になるが仕方の + ない事なのかもしれない。 + + また条件によって待避したりしなかったりする場合、現在 PROMPT_COMMAND が + 設定されているかどうかを判定する方法が面倒になる。という事を考えると現 + 状の様に常に PROMPT_COMMAND を待避する様にしておくのが無難の様な気がす + る。或いは、退避はしなくて常にコピーだけはしておくという事にしても良い + が、それだと結局コストは存在しているので中途半端な事をするよりは本当に + 待避した方が良い様に思われる。 + + 取り敢えずは現状の様に常に待避する事にして、後で問題になる様であれば条 + 件を考える事にする。そもそもコマンド実行の時にだけ待避は起こるのだから、 + 滅多に実行しない操作だし処理のコストが問題になるという事は考えにくい。 + もし問題が起こるとすればもっと別の何かのエラーが起こった時の状況復帰に + 関係する物だろうと思われる。 + + ? C5 ok: PS1= とするのは bleopt_internal_suppress_output が設定されていない + 時だけになっているが、これも常に実行する様にした方が良いのでは。裏で毎回 + コマンド置換などが実行されている様な状態になっているのは無駄である。それ + にもしユーザーが PS1= に何か破壊的な操作・副作用のある操作を実装していた + 場合やはり振る舞いが意図しない物になる気がする。なので PS1= を指定するべ + き気がする。 + + と思って振る舞いを確認してみたがそもそも PS1 は余分に評価されてはいない。 + 確か端末の状態に応じて Bash はプロンプトの評価を控えるのだった。そしてそ + れに伴って PS1 の評価も実行していない? と思ったが、そもそもコマンドを実行 + していないのだからプロンプトの内容も変わらず、従って PS1 の中のコマンド置 + 換を再評価する機会もないというだけの事の気がする。PROMPT_COMMAND が二重に + 実行されない事と同様の事である。 + + 何れにしても PS1 を空にする理由も特にない気がするので PS1= を実行する。と + 思ったが、元々不意に ble.sh が落ちた時に不自然な振る舞いにならない為に + PS1= を設定しているのだった。という事を考えるとやはりそのままにして置いた + 方が良い? と思ったが、現在中途半端な状態にあるという事が分かる様にした方 + が良い気もする。 + + a 例えば PS2 を代わりに設定する? + + x と思ったが、PS2 は PS2 でコマンド置換を含んでいる可能性もあるし、そも + そも異なる物なのでユーザーが意図的に何か PS2 に仕組んでいた時に変な事 + になる。 + + x というか、将来的に PS2 を編集時の行番号等に用いるなどした時に棲み分け + が改めて必要になるのでやはり変な利用方法はしない方が良い。 + + b [press any key to continue] 等として変な状態にある事が分かる様にするの + が良いのでは。 + + →具体的に問題を起こしてみてどうなるか試そうと思ったが再現できない。うー + ん。配列の添字関係で実行が停止していた気がするが…。 + + echo ${_ble + + 等の中途半端なコマンド文字列で無理やり実行してもちゃんと元の状態に戻る + 事ができている。コマンド実行がその場で終了してしまう様な設定が存在した + 気がするがどの様にすれば良いのだろう。 + + * 例えば ${hello?world} などか → 問題なかった。 + * set -u だろうか → 問題なかった。 + * set -e だろうか → これはそもそもシェルを終了するので関係ない。 + * 算術式中の a[-1] だろうか → これも問題ない。 + + 何処かに記録として残っていないだろうか。 + + * "for ((i=0;i<10;i++)); do sleep 1; done" これだ + + 然し試してみたがこちらで指定した PS1 が反映される訳でもない。 + + ! 恐らく最初に PS1 を表示した時の計算結果がずっと残っているという事なの + ! だろう。なので、実は中で PS1 を設定しようがどうしようが関係ないという + ! 事。但し、bashrc で直接 attach する時に限ってはその時の PS1 に意味が + ! ある。 + ! + ! →と思って bashrc で直接 attach する場合についても試したがそれでも駄 + ! 目だった。よく考えたら bash が変な状態になった時にプロンプトを表示す + ! るとして、その時の PS1 の値を使って表示するのだとしたら、コマンド実行 + ! 中に失敗したのだとしたらコマンド実行中の PS1 を使って表示するのだから、 + ! 待避した PS1 が使われる事はない。という事を考えると待避中の PS1 が使 + ! われる事はない様な気がする。 + + うーん。コマンド実行中の失敗の場合はコマンド実行中の PS1 が使われるので、 + 待避中の PS1 の値に何を設定するかは関係ない。そもそも PS1 を待避する必 + 要性があるのかどうかも何だか分からなくなって来た。 + + * ok: WINCH の時に問題になるのではないか: ble.sh では WINCH した時に、 + PROMPT_COMMAND で現在の端末幅に合わせた内容を計算している可能性を考えて、 + PROMPT_COMMAND を再度呼び出す事にしている。これで問題が生じることはないか? + + →報告されたコードの場合には結局 PROMPT_COMMAND のコード自体に対して + DEBUG trap が走る事によって問題が回避されているので、問題は起こらない筈。 + →実際に問題が起こらないという事を確かめた。 + + 一方で PROMPT_COMMAND が余分に呼び出される事で他の問題が生じる可能性はあ + るだろうか。うーん。そもそも PROMPT_COMMAND がコマンド実行後に1回だけしか + 呼び出されないという想定は bash のマニュアルにも書いていないし、意味を考 + えればプロンプトの再表示時に複数回呼び出されてもおかしくはない気がする。 + 具体的にそういう動作に依存している枠組みが現れる迄は気にしない事にする。 + + ? C6 reject: 効率を考えたら実は TRAPDEBUG/enter の時には TRAPDEBUG ではなく + て元のユーザーの trap を直接設定しておけば良いのでは? + + 現在の TRAPDEBUG の処理 (sandbox でのユーザートラップの実行を含め) は複雑 + で重いので直接実行できるのであればその方が良い気がする。 + + 現在 TRAPDEBUG は ble.sh 的には INT の処理の為にしか使っていない。将来的 + にもっと別の事に使いたくなる可能性もあるが、取り敢えずは特別な処理は挟ま + ないのだからユーザートラップを直接設定して良い気がする。 + + 一方で TRAPDEBUG は ble.sh の処理自体に対する DEBUG trap を阻止するという + 目的もあった。うーん。そうは言っても gexec/.{restore,save}-lastarg 及び + epilogue 等に対する DEBUG trap 程度であれば許しても良い気がする。その直後 + に builtin trap - DEBUG で解除しているので、gexec/.end 以降に対してユーザー + の DEBUG trap が実行される事は結局ないと思われる。 + + .TRAPDEBUG/trap に於いて現在介入する必要がなければ介入しない様にする事にした。 + + x と思ったら報告者の設定で問題が起こる。やはり ble.sh 内部での処理に対し + ては TRAPDEBUG が発生しない様に制御が必要なのだろうか。 + + うーん。PROMPT_COMMAND の実行の為の TRAPDEBUG に関しては DEBUG を + filter する為に明示的に TRAPDEBUG で trap する事にした。 + + * というか単にユーザートラップを実行するだけなのであれば、単に postproc + にユーザートラップをしかければ良いだけでは? その様に実装した。但し、本 + 来は user trap がなければそもそも DEBUG trap も存在しない筈ではある。但 + し、複数コマンドを実行していて最初のコマンドを C-c で殺した時に、次のコ + マンドには TRAPDEBUG が残った状態で実行される。この時にそういう事が起こ + る可能性がある。 + + o C-c がちゃんと動くか確認する。OK 動いている。 + + 2022-02-16 うーん。やはり直接 trap すると問題が色々発生する。 + + x 先ず初めに報告にあった物が動かなくなった。報告にあった設定だと + PROMOPT_COMMAND より後に初めて実行した DEBUG trap で時刻が決まる。然し、 + 直接 trap していると、ble-attach よりも前に設定した trap が + ble-decode/.hook 等にも反応してしまって正しい時刻が決定できない。 + + 従って attach していない状態で ble/builtin/trap が受け取った DEBUG trap + は結局 TRAPDEBUG 経由で実行するのが無難なのではないか。 + + x 更に、コマンド実行で再設定している DEBUG trap についても今回は問題になっ + ていないが、やはり問題になる可能性がある。直接 user trap を設定すると、 + exec:gexec/.* に対して軒並み発火してしまうが、本来これらに対しては発火 + して欲しくはないのである。 + + なのでこの場合にもやはり user trap を直接設定するのは避ける。 + + x C7 fixed: prompt-attach をした場合全く値が更新されない。これは何故だろう + か。PROMPT_COMMAND が失われている可能性? 確認した。元からあった + PROMPT_COMMAND が消滅している。→修正した。ble/idict#copy の引数が逆だっ + た。 + + x C8 fixed: 現在は先程まで動いていた物も動かなくなっている。何故? + + x fixed: 先ず、起動時に trap が削除できていない所為でコマンドを実行する前は表示 + される時刻が前のプロンプトを表示した時刻からの遅延になってしまっている。 + これは type=initial でも final でも起こっている。 + + これの原因は簡単で user trap を直接 trap する様にした為に、ble.sh の内 + 部 (具体的には最初の ble-decode/.hook) に対して DEBUG trap が動作してし + まっている。 + + x fixed: 次に、final で実行した場合にはそもそも user trap すら正しく抽出 + できていない。少なくとも2コマンド目以降には元々取得できていた筈だがそれ + も動かなくなっている。 + + これの原因は謎である。もしかすると TRAPDEBUG/.initialize が実行されてい + ない? 先に adjusted が正しく動作しているとすると確かに + TRAPDEBUG/.initialize は呼び出されなくなってしまう。現在の構成の場合に + は TRAPDEBUG/.initialize と adjust は別々に処理する必要がある気がする。 + + これは試しに attach で adjust を呼び出す様にした所に問題がある。attach + の時点で user trap の初期化が終わっていないので、この時点で adjust を実 + 行してしまうと user trap が失われてしまう。…と思ったが、それも何だか変 + な気がする。adjust では trap - DEBUG を実行していて ble-attach は + declare -ft していないので、ble-attach 内部での trap 解除は外に漏れ出さ + ない筈である。或いは、同じ文脈で TRAPDEBUG/.initialize が実行されてしまっ + ている可能性はある? + + 分かった、adjust で trap - DEBUG したのは外に伝播しないが、adjusted=1 + に設定する為に user trap 読み取りのコードが不活性化してしまっている。 + adjusted=1 になる為には user trap も読み取った後でなければならないので + ある。 + + x C9 fixed: prompt-attach (memo/D1772.bashrc type=initial-prompt) した後に + user trap が設定されたまま残っている。うーん。ble-attach する時に削除する + べきなのではないか。。と思ったが、それだと未だ user trap を読み取っていな + い時に元から存在した設定を削除してしまう事になるので駄目である。 + + うーん。つまり最初に読み取った時に取り敢えず trap を削除しておくべきとい + う事なのだろうか。コマンド実行中でなければ。 + + というより prompt-attach の場合、何処でユーザー trap を読み取っているのだ + ろう。と思ったが、initial-prompt の場合には最初に trap DEBUG をユーザーが + 呼び出した時点でユーザー trap が読み取られる事になる。 + + 結局初回は必ず trap - DEBUG を実行する事になるという事なのだろうか。初回 + の判定はどの様にしたら良いのか。うーん。また新しく変数を増やすのか? 或い + は既存の変数を用いて判定する事はできるだろうか。 + + そもそも userTrapInitialized という変数はその場合必要になるのだろうか。こ + の変数は実際 gexec/.setup で調整をする必要があるかどうかだけの為に用いら + れている。という事を考えるとこの変数の意味を変えれば良い気がする。 + + 或いは TRAPDEBUG の adjust/restore の状態をちゃんと管理しておいて、一旦 + restore したのに未だ adjust していない状態になっていたり、未だそもそも + adjust した状態になっていなかったら元に戻す等の対処が必要になるのではない + か。 + + →その様に修正した。これで無駄に DEBUG trap が内部で呼び出される事はなく + なった筈。 + + しかし報告された設定の場合に、初回のコマンド実行の実行時間が正しくない。 + これは一回でも無駄な DEBUG trap が PROMPT_COMMAND 実行後に実行されるとも + う駄目なので、実は最初に現れた時点でもう正常動作しないのである。 + + a そうなると attach の時点で trap を削除するべきなのだろうか。その為には + attach で trap の削除に関係する全ての経路で declare -ft を指定しておく + 必要がある。一応どの様な経路で adjust が行われるのかについて確認してお + く事にする。 + + 取り敢えず adjust-PS1 は以下の経路で呼び出されている。 + + ble-edit/adjust-PS1 + ble-edit/attach/.attach + ble-edit/attach + ble-attach + ble/base/attach-from-PROMPT_COMMAND + + うーん。ble-edit/attach の直下で TRAPDEBUG/adjust を呼び出してみる事に + する。 + + x 然し関数内や source 内部で ble-attach した時には結局元の trap を削除 + するのに失敗する事になる。一応一番外側が ble-attach かどうかで判定は + できる。また set -T が有効になっている場合にも削除は可能だろう。 + + 他、ble-attach を呼び出す可能性のある関数は全て列挙する。 + + - ble -> ble/dispatch + - ble/base/attach-from-PROMPT_COMMAND + - ble/base/process-blesh-arguments ... これはいきなり attach する時 + + 色々面倒である。ble/base/attach-from-PROMPT_COMMAND についても、これは + PROMPT_COMMAND に設定して使う事を想定しているが、他の枠組みが何処かの変 + 数に待避して関数内から呼び出した場合にはやはり trap が見えなくなってし + まう。結局 attach の時点で確実に trap を削除しようとするのは困難である。 + + b initial-attach の時に問題が起こらない様にするだけであれば、attach の外 + の状態であっても trap を実行した時には filter を入れる様にするという手 + もある。 + + うーん。方針が定まらないとどの様に対処するべきか分からない。再度現状につ + いて整理する事にする。 + + "ble/builitin/trap ... DEBUG" が bashrc で呼び出された時はどの様に振る舞 + うべきか。取り敢えず、実際に ble-attach するまでは普通に DEBUG trap が動 + 作するのが自然である。また、ble-attach した時に如何にして元から存在した + DEBUG trap を不活性にするのかという事。 + + ! a ble/builtin/trap でユーザーが DEBUG trap を設定した時は、未だ attach し + ! ていなければ user trap を直接設置する。ble-attach を実行した問にそれを + ! 削除する。 + ! + ! 然し、trap - DEBUG によって本当の意味で削除するのは条件が限られている。 + ! 現在の関数呼び出し経路の全てに trace 属性がついているか、set -T が設定 + ! されていてかつ現在の関数呼び出し経路の全てが既知の (set -T を弄らない) + ! 関数であるという事が保証できる場合のみである。取り敢えず trap '' DEBUG + ! とすれば削除はできる気がする。 + ! + ! 従って、 + ! + ! 1 ble.sh の枠組みの中で ble-attach を最終的に呼び出す可能性のありそうな + ! 関数の全てに declare -ft を付加しておく。 + ! + ! 2 ble-attach が呼び出された時に既に user trap が既知の状態になっている + ! 時には trap DEBUG を削除する。 + ! + ! 2a 経路上が全て DEBUG を継承していると判断できる時には trap - DEBUG + ! を実行して直接削除する。 + ! + ! 2b 経路上で DEBUG が途切れているかもしれない時には仕方がないので trap + ! '' DEBUG を実行する。途中で別の DEBUG を設置したりしない限りはこの + ! DEBUG trap は一番上の階層にまで適用される筈である。 + ! + ! 然し面倒なので ble-attach した瞬間には常に trap '' DEBUG で良い気もす + ! る。その様にしておけばわざわざ declare -ft 等の事を考える必要もない。 + ! + ! 3 ble-attach が呼び出された時に user trap が既知の状態になっていない時 + ! は厄介である。 + ! + ! 3a 経路上が全て DEBUG を継承していると判断できる時にはその場で user + ! trap を読み取ることができる。その時には単に trap - DEBUG を追加で実 + ! 行して trap を削除すれば良い。 + ! + ! 3b それ以外の場合には user trap を取得できない。trap '' DEBUG で削除 + ! すると元の user trap を破壊してしまうので実行できない。 + ! + ! この方法で気になるのは様々な関数に trace 属性を設定して変な問題が起こら + ! ないのかという事である。これは他の人が設定した DEBUG trap の侵入を許可 + ! するという事に他ならない。本来他の人が設定した DEBUG trap でその枠組の + ! 動作に干渉してはならない (したら保証外) なので気にしなくて良い筈という + ! 気もするが、DEBUG trap を使っている側がちゃんと動かない可能性は常に存在 + ! する。まあ余り気にしても仕方がないのかもしれない。 + ! + ! b ble/buitlin/trap は attach 状態にない時には TRAPDEBUG を設置する。つま + ! り、ble.sh の外側でも TRAPDEBUG で filter しながら動作するという事。こ + ! れはいざ ble-attach を実行した時に意図しない DEBUG を防ぐ目的がある。 + ! + ! 然し、これは元から設置されていた DEBUG に対しては無力である。結局、元か + ! ら設置されていた DEBUG に対しても対策をするのであればわざわざ DEBUTRAP + ! 経由にする必要もないのでは? → 然し、元から設置されていた DEBUG に対し + ! ては完全な対策ができない。ble/builtin/trap 経由の時には完全な対策が可能 + ! な方法に切り替えるのは妥当である。従って、論点は元から設置されていた + ! DEBUG に対してどれだけ完全な対策を行う事ができるかという事になる。 + + c 結局 ble/builtin/trap 及び .TRAPDEBUG/restore では必ず TRAPDEBUG 経由で + bind する事にしたので改めて整理し直す。また、現在形路上が全て DEBUG を + 継承しているかどうかを判定する関数を作成した。 + + 1 ble.sh の枠組みの中で ble-attach を最終的に呼び出す可能性のありそうな関 + 数の全てに予め ble/function#trace を実行しておく。 + + 2a global DEBUG が見える状態にある時には、user trap が未知であればその場 + で読み取りを行う (__initialize)。そして DEBUG trap を削除する + (__TRAPDEBUG_adjust)。 + + 2b それ以外で user trap が既に読み取り済みもしくは設定済みであれば + DEBUG trap を削除する (__TRAPDEBUG_adjust)。 + + 2c それ以外の時には user trap を保持しなければならないので仕方がないが + そのまま通過。 + + この方針で行く事にする。実装した。取り敢えず動いている様に見える。後で + 制限について書く事にする。 + + * C10 done: 元から設定されている trap が TRAPDEBUG の類だった時。これは無視 + する必要がある。そうしないと最悪無限ループになる。 + + x C11 fixed: initial-prompt で core-dump する様になってしまった。。何故? 再 + 帰 trap の可能性を疑って先に対応する事にしたが直らなかった。関係なかった + 様だ。 + + 取り敢えずどういう制御パスになっているかだけは確認する。 + + と思ったら再帰 trap になっていた。再帰判定のコードが間違っていた。修正した。 + + 然し、そもそも何故再帰 trap になってしまうのかというのは謎である。あー分 + かった。ble/builtin/trap 経由で user trap を読み取ってもその後で + ble-edit/attach から呼び出した _ble_builtin_trap_DEBUG__initialize で再び + user trap を読み出そうとしてしまうのが問題なのである。これも修正した。 + + x C12 fixed: bash-5.0 以下で PROMPT_COMMAND が消滅してしまっている。何故か。 + copy-state の使い方を間違えている? + + これは分かった。attach-from-PROMPT の時は PROMPT_COMMAND の書き換えをチェッ + クしている。なので、中で attach して PROMPT_COMMAND を待避するとそれが保 + 存されてしまう。 + + と思ったがそれでも何だか変な気がする。ble-attach は何れにしてもその + PROMPT_COMMAND の保存復元の外側で実施される筈だから。変だ。 + + うーん。分かった。local PROMPT_COMMAND としていた為に異なるレベルの + PROMPT_COMMAND を触っていて変な事になっていた様だ。unlocal をちゃんと実行 + する様にしたら問題なくなった。と、思ったがこれは本当に意図した動作なのだ + ろうか? 新しい PROMPT_COMMAND を実行したくて lcoal PROMPT_COMMAND を実行 + していたのではあるまいか? + + 改めてコードを確認する。うーん。多分大丈夫意図した振る舞いになっている筈。 + + x C13 wontfix: bash-5.0 以下で type=initial-prompt (memo/D1772.bashrc) に対 + し attach 時に vbell で報告者の設定がエラーメッセージを出す。 + + て、__initialize での DEBUG trap 読み取りに失敗している気がする。というか、 + initial なので直接 ble/builtin/trap を経由して trap を設定している筈で、 + 間違う筈はない…。 + + うーん。vbell でエラーが表示されるという事は attach した後に bash が + PROMPT_COMMAND を実行しようとして失敗しているという事になる気がするが…。 + うーん。つまり? 外側の PROMPT_COMMAND の文字列を eval している時に、 + ble-attach の入った ble/function#lambda/0 を実行する所までは良くてその後 + に続いている timer_stop の呼び出しに於いて問題が生じているという構図であ + る。うーん。これを回避するのは難しい。 + + + つまり何が起こっているかというと、本来は PROMPT_COMMAND の実行の最初に + timer が設定されてその実行の終わりに timer_stop が呼び出されるのでそれで + 良いのだが、prompt attach の瞬間には bash の + + PROMPT_COMMAND='ble-prompt-attach ; timer_stop' + + の最初に timer が設定されるが、それは ble-attach 内部の PROMPT_COMMAND 呼 + び出しで消費される。その後で timer_stop を実行しようとするが、その時点で + 既に DEBUG trap は除去されているので対応する DEBUG trap が一度も呼び出さ + れない儘に timer_stop が呼び出されてエラーメッセージが発生する。 + + うーん。prompt attach は仕組みからして処理の実行順序を変更する事になるの + でこれに対してどの様に処理するべきかは謎である。本当は PROMPT_COMMAND の + 最後に実際の attach 操作を持って来たいところだがそういう訳にも行かない。 + + うーん。其処まで実行順序が厳密でなければならないというのは困る。というか、 + そもそも元々のスクリプトが DEBUG が必ず PROMPT_COMMAND よりも前に実行され + る筈という前提で設計されているのがよくない。というか、そもそも DEBUG を用 + いた hack を使っている時点でよくない。然し、そうは言ってもうーん。 + + 例えば prompt-attach の時には DEBUG trap はその場では削除しないという事を + 考えたが、そうすると結局また問題が生じる。timer_stop が処理するまでは良い + が、その後も DEBUG trap が有効のままなので、DEBUG trap が + ble-decode/.hook 等に対して呼び出されて予期しない時刻になる。また、もし逆 + に timer_stop がその PROMPT_COMMAND の中で attach-fromp-PROMPT_COMMAND よ + りも後に呼び出されなかったら結局それも DEBUG trap で設定された timer が残 + 存してしまう。 + + これは現状 vbell が出るだけで全く使えない訳ではないので無視する事にする。 + それに $timer ではなく timer と書いていれば特に問題もなく利用できる筈であ + る。 + + * C14 desolved: trap DEBUG より後に ble.sh を source した場合に対する対策 + + 最初の PROMPT_COMMAND 実行の時には特に問題にならない。然しその時に timer + 変数が削除される。2回目の PROMPT_COMMAND 実行までに trap が実行されれば問 + 題は起こらないが、実際は未だ trap DEBUG は取得できていないので自前で設定 + することもないし、だからと言ってずっと関数を出ないので既に設定されている + 物が発火することもない。 + + * C15 desolved: prompt-attach の場合の振る舞いについても確認する必要がある。 + + x C16 ok: bash-3.2 で type=final (memo/D1772.bashrc) の時に初回のコマンド実 + 行の時間がずれる + + 他の 3つの attach 方法の場合には問題は生じていない。物によっては最初のプ + ロンプトの時間が 0s になったり 1s になったり 2s になったりして、初期化の + 時間が含まれたり含まれなかったりしているがそれは対した事ではない様に思わ + れる。 + + これは何故だろうか。4.0 では特に何も起こっていない。final の時には + ble-attach を直接呼び出して attach している。うーん。もしかして DEBUG + trap を除去できていない? + + うーん。というか 4.0 でも 3.2 でも bashrc の内部では trap を取得できてい + ない様だ。うーん? というか 5.1 ですらもできていない。何故だろう。 + is-global-traceable がちゃんと動いていないという事? + + →と思ったらこれは単にテスト用の rcfile の名前が .bashrc ではなかった為に + 棄却されていただけの事だった。棄却条件を緩めたらちゃんと期待通りに動く様 + になった。気にしなくて良い。 + + x C17 desolved: bash-3.0, 3.1 で後に ble.sh を source した時、何回かコマン + ドを実行してもエラーが出続ける。どうやら trap が消滅している。 + + →これは今試したら再現しない。ちゃんと動いている。うーん。多分他の物を修 + 正した時に一緒に直ったのだという事だと思われる。 + + x C18 wontfix: bash-3.1 で後から ble.sh を source した時に最初のコマンド実行 + の時間がずれている。うーん。これは ble-attach 等が DEBUG を継承させられな + いという事に由来する制限である。 + + うーん。bash-3.1 以下で declare -ft を適用する方法はあるのだろうか。或い + は set -T を設定するしかないのかもしれない。 + + 念の為先に source ble.sh したときについてはちゃんと動く事を確認した。OK + + * C19 別項目: その他の trap についても元からあった trap を拾う様にしたい。 + うーん。この commit は巨大になり過ぎたし、DEBUG trap 専用の物なので、他の + trap に関しては別項目で対応するのが正解の気がする。 + + x C20 別項目: bash-3.0 で変な文字列 '' が出力される。 + + この変なエラーを直さない限り、そもそもちゃんと動いているかどうかまともに + 修正する事ができない。というかこの謎の出力は一体何処から来ているのだろう + か。DEBUG trap 関係だろうか。 + + うーん。これはそもそも今回の話と関係なく起こっている問題の様だ。これは別 + 枠で修正するべき。そもそも master で、plain bash + plain ble.sh でも再現 + する事を確認した。 + + x C21 fixed: bash-3.0 で結果が常に 0s + + 調べてみると DEBUG trap が何故かコマンドが終了してからしか呼び出されてい + ない様だ。更に調べると TRAPDEBUG は呼び出されている。と此処で分かった。原 + 因は BASH_COMMAND が bash-3.0 で設定されていないという事だ。 + + これは関数内で BASH_COMMAND を参照した時だけでない。trap string で直接 + BASH_COMMAND を参照しても同様に BASH_COMMAND には現在実行しているコマンド + の文字列が格納されている (という事を確認した)。 + + $ func() { echo heloo; }; declare -ft func; trap 'trapdebug "$BASH_COMMAND"' DEBUG; trapdebug() { echo "[$1]"; } + + これは仕方のない事である。bash-3.0 では不当に DEBUG trap が握りつぶされな + い様にした。bash-3.0 では余分に DEBUG trap が呼び出される事になるが、これ + は仕方がない。DEBUG trap をしかける側はいつでも大量の関係ない DEBUG trap + を捨てる事が求められているので許容してもらう。 + + x C22 fixed: bash-3.1 以下で type=final-prompt (memo/D1772.bashrc) に於いて + trap が消滅している? + + 先ず _ble_builtin_trap_DEBUG_userTrapInitialized によると初期化は実行され + ていない。一方で builtin trap -p の時点でもう消えている。うーん。 + + どうやら bash-3.0 では bind -x の top レベルでも何故か + attach-from-PROMPT_COMMAND の中で実行している設定になっている様だ。謎。取 + り敢えず work around を加える事にした。ちゃんとトップレベルの trap を読み + 取る事ができているので OK + + * C23 done: refactor name ble/function/is-global-traceable + + 取り敢えず何れの bash version でも少なくともコマンドを一回実行すればちゃん + と動く様になった様に思われる。これで良しとする。 + + + * C24 一旦何処かに push して diff を観察する。 + + [制限] + + 最後に現状の実装の制限についてまとめる。これは何処かにまとめて置いた方が良 + い気がする。ソースコードの中に記述するのが最も分かりやすいのではないか。転 + 記した。 + + * 先に ble.sh を source した場合は殆どの場合動く。 + + bash-5.0 以下で先に ble.sh を source して prompt-attach を行い、更に + PROMPT_COMMAND を書き換えた場合には、PROMPT_COMMAND の中で + attach-from-PROMPT_COMMAND よりも後に実行している処理は DEBUG trap が無 + 効化された状態で実行される事になる。 + + * 後に ble.sh を source した場合には以下の場合にロード直後は DEBUG trap が + ble.sh 内部の処理に対しても有効になっている。 + + - rcfile が .bashrc でも .profile でも .bash_profile でもない場合 (これ + は現在 rcfile の中にいるかどうかを判定する方法が bash にはない事から、 + rcfile かどうかをファイル名と行番号だけから判定しなければならない事に + 由来する) + + - source ~/.bashrc 等の様にして手動で bashrc を読み込んだ時 (これは + source が DEBUG trap を継承しないという Bash の制限に由来する) + + - rcfile から一旦別のファイルを source してそのファイルから ble.sh を + source した時。または関数内から ble.sh を source した時 (これも DEBUG + trap の継承に関する Bash の制限に由来する) + + - bash-3.1 以下の時 (これは declare -ft で trace を付加できる関数の関数 + 名に対する制限に由来する) + + * というか結局報告者の提示した設定が不安定すぎるという事に尽きている様な + 気もする。この設定は例えば bash-preexec.sh と一緒に使ったとしても崩れる + し (とはいいつつも bash-preexec.sh は他のあらゆるものを壊す気がする)、 + また、PROMPT_COMMAND+=$'\n'追加設定 を実行するあらゆる設定に対して脆弱 + である。その様ないい加減な設定を使っている時点で完全には対応しきれない。 + 2022-02-13 - * util: EXIT trap を設定していると実際に exit する時に return に空文字列が渡される [#D1771] + * util: EXIT trap を設定していると実際に exit する時に return に空文字列が渡される (reported by SuperSandro2000) [#D1771] https://github.com/akinomyoga/ble.sh/issues/175 これは明らかに最近書き直した trap handler 周りのの処理の問題である。と思っ @@ -6193,7 +7105,7 @@ bash_tips 2022-02-12 - * complete: _fzf_path_completion 経由だと ~ で始まるパスのディレクトリの後に / が挿入されない [#D1767] + * complete: _fzf_path_completion 経由だと ~ で始まるパスのディレクトリの後に / が挿入されない (reported by SuperSandro2000) [#D1767] https://github.com/akinomyoga/ble.sh/issues/171#issuecomment-1030962166 file: memo/D1760.fzf-completion.bashrc diff --git a/src/edit.sh b/src/edit.sh index c32bed07..16de4968 100644 --- a/src/edit.sh +++ b/src/edit.sh @@ -1493,19 +1493,30 @@ else fi function ble/prompt/update/.has-prompt_command { - [[ ${PROMPT_COMMAND[*]} ]] -} -function ble/prompt/update/.eval-prompt_command.1 { - # Note: return 等と記述されていた時対策として関数内評価する。 + [[ ${_ble_edit_PROMPT_COMMAND[*]} == *[![:space:]]* ]] +} +function _ble_prompt_update__eval_prompt_command_1 { + # Note: return 等と記述されていた時の対策として関数内評価する。 + # Note #D1772: 本来は tempenv として _ble_edit_exec_TRAPDEBUG_enabled=1 も指 + # 定すれば ble-edit/exec/.setexit や builtin eval に対する DEBUG trap の除外 + # を明示的に確認しなくても済むはずだが、bash-4.4..5.2(少なくとも) にはバグが + # あって builtin eval を使うと DEBUG trap の中から tmpenv が見えなくなってし + # まう。仕方がないので local で _ble_edit_exec_TRAPDEBUG_enabled=1 を設定する。 + local _ble_edit_exec_TRAPDEBUG_enabled=1 ble-edit/exec/.setexit "$_ble_edit_exec_lastarg" BASH_COMMAND=$_ble_edit_exec_BASH_COMMAND \ builtin eval -- "$1" } +ble/function#trace _ble_prompt_update__eval_prompt_command_1 function ble/prompt/update/.eval-prompt_command { - local _command + ((${#PROMPT_COMMAND[@]})) || return 0 + local _command _ble_edit_exec_TRAPDEBUG_adjusted=1 + ble-edit/exec:gexec/.TRAPDEBUG/restore filter for _command in "${PROMPT_COMMAND[@]}"; do - ble/prompt/update/.eval-prompt_command.1 "$_command" + [[ $_command ]] || continue + _ble_prompt_update__eval_prompt_command_1 "$_command" done + _ble_edit_exec_gexec__TRAPDEBUG_adjust } ## @fn ble/prompt/update opts ## _ble_edit_PS1 からプロンプトを構築します。 @@ -2347,16 +2358,38 @@ function ble-edit/content/push-kill-ring { _ble_edit_PS1_adjusted= _ble_edit_PS1='\s-\v\$ ' +_ble_edit_PROMPT_COMMAND= function ble-edit/adjust-PS1 { [[ $_ble_edit_PS1_adjusted ]] && return 0 _ble_edit_PS1_adjusted=1 _ble_edit_PS1=$PS1 - [[ $bleopt_internal_suppress_bash_output ]] || PS1= + if [[ $bleopt_internal_suppress_bash_output ]]; then + # Note #D1772: ble.sh の処理中に落ちた場合に表示されるプロンプト。現状でそ + # の様な事が起こった事はない気がするし、実際にそうなった時の動作確認もでき + # ていないが念の為設定しておく。 + PS1='[ble: press RET to continue]' + else + # suppress_bash_output をしていない時はそのまま bash のプロンプトが表示され + # てしまわない様に PS1 は空にしておく。 + PS1= + fi + + if ble/is-array PROMPT_COMMAND; then + ble/idict#copy _ble_edit_PROMPT_COMMAND PROMPT_COMMAND + else + ble/variable#copy-state PROMPT_COMMAND _ble_edit_PROMPT_COMMAND + fi + builtin unset -v PROMPT_COMMAND } function ble-edit/restore-PS1 { [[ $_ble_edit_PS1_adjusted ]] || return 1 _ble_edit_PS1_adjusted= PS1=$_ble_edit_PS1 + if ble/is-array _ble_edit_PROMPT_COMMAND; then + ble/idict#copy PROMPT_COMMAND _ble_edit_PROMPT_COMMAND + else + ble/variable#copy-state _ble_edit_PROMPT_COMMAND PROMPT_COMMAND + fi } _ble_edit_IGNOREEOF_adjusted= @@ -5701,37 +5734,75 @@ function ble/exec/time#start { #-------------------------------------- _ble_edit_exec_TRAPDEBUG_enabled= +_ble_edit_exec_TRAPDEBUG_lastexit= +_ble_edit_exec_TRAPDEBUG_lastarg= _ble_edit_exec_TRAPDEBUG_postproc= _ble_edit_exec_inside_begin= _ble_edit_exec_inside_prologue= _ble_edit_exec_inside_userspace= ble/builtin/trap/reserve DEBUG + +## @fn ble-edit/exec:gexec/.TRAPDEBUG/trap [opts] +## @param[in] opts +## filter +## DEBUG trap の filter を (TRAPDEBUG の特別処理がなくても) 明示的に強制 +## する事を示します。PROMPT_COMMAND の処理などで、PROMPT_COMMAND の処理の +## みに対して DEBUG trap を走らせる為に指定します。 function ble-edit/exec:gexec/.TRAPDEBUG/trap { + # Note #D1772: 本来は ! $_ble_attached の時には user trap を直接 trap したい + # が、それだと ble-attach 直後に ble.sh の関数 (特に ble-decode/.hook) に対 + # して意図しない DEBUG trap が発火する事を防げないので TRAPDEBUG 経由にして、 + # DEBUG を選別することにする。 + # Note #D1772: コマンド実行の為の TRAPDEBUG の場合でも、やはり + # ble-edit/exec:gexec/.* を除外する為に TRAPDEBUG 経由で user trap を実行す + # る事にする。もし FUNCNAME, BASH_SOURCE 等を DEBUG trap から参照したいユー + # ザーがいれば、コマンド実行の時には既定で user trap を直接 trap する様にし + # ても良い。 builtin trap -- 'ble-edit/exec:gexec/.TRAPDEBUG "$*"; builtin eval -- "$_ble_edit_exec_TRAPDEBUG_postproc"' DEBUG + + # Note: 以下は条件付きで user trap を直接 trap するコード。 + # if [[ $_ble_attached && _ble_edit_exec_INT -ne 0 || :$1: == *:filter:* ]]; then + # builtin trap -- 'ble-edit/exec:gexec/.TRAPDEBUG "$*"; builtin eval -- "$_ble_edit_exec_TRAPDEBUG_postproc"' DEBUG + # else + # local user_trap=${_ble_builtin_trap_handlers[_ble_builtin_trap_DEBUG]} + # builtin trap -- "$user_trap" DEBUG + # fi +} + +_ble_edit_exec_TRAPDEBUG_adjusted= +# Note: bash-3.1 以下では特殊な関数名の関数には declare -ft を付加する事ができない。 +function _ble_edit_exec_gexec__TRAPDEBUG_adjust { + builtin trap - DEBUG + _ble_edit_exec_TRAPDEBUG_adjusted=1 } -function ble-edit/exec:gexec/.TRAPDEBUG/enter { +ble/function#trace _ble_edit_exec_gexec__TRAPDEBUG_adjust +function ble-edit/exec:gexec/.TRAPDEBUG/restore { + _ble_edit_exec_TRAPDEBUG_adjusted= + local opts=$1 ble/builtin/trap/.initialize if [[ ${_ble_builtin_trap_handlers[_ble_builtin_trap_DEBUG]} ]]; then - ble-edit/exec:gexec/.TRAPDEBUG/trap + ble-edit/exec:gexec/.TRAPDEBUG/trap "$opts" fi } + function ble-edit/exec:gexec/.TRAPDEBUG { - local __lastexit=$? __lastarg=$_ + local __lastexit=$? __lastarg=$_ bash_command=$BASH_COMMAND # Note XXXX: bash-3.0 では BASH_COMMAND が異なる。 _ble_edit_exec_TRAPDEBUG_postproc= [[ $_ble_edit_exec_TRAPDEBUG_enabled || ! $_ble_attached ]] || return 0 - [[ $BASH_COMMAND != ble-edit/exec:gexec/.* ]] || return 0 + [[ $bash_command != *ble-edit/exec:gexec/.* ]] || return 0 + [[ ! ( ${FUNCNAME[1]-} == _ble_prompt_update__eval_prompt_command_1 && ( $bash_command == 'ble-edit/exec/.setexit '* || $bash_command == 'BASH_COMMAND='*' builtin eval -- '* ) ) ]] || return 0 [[ ! ${_ble_builtin_trap_inside-} ]] || return 0 - local __set __shopt; ble/base/.adjust-bash-options __set __shopt + if [[ $_ble_attached ]] && ((_ble_edit_exec_INT!=0)); then + local __set __shopt; ble/base/.adjust-bash-options __set __shopt - # Run user DEBUG trap - local _ble_builtin_trap_postproc - ble/util/setexit "$__lastexit" "$__lastarg" - ble/builtin/trap/invoke DEBUG - _ble_edit_exec_TRAPDEBUG_postproc=$_ble_builtin_trap_postproc + # Run user DEBUG trap in the sandbox + local _ble_builtin_trap_postproc + ble/util/setexit "$__lastexit" "$__lastarg" + ble/builtin/trap/invoke DEBUG + _ble_edit_exec_TRAPDEBUG_postproc=$_ble_builtin_trap_postproc # unused - # Handle INT - if [[ $_ble_attached ]] && ((_ble_edit_exec_INT!=0)); then + # Handle INT local IFS=$_ble_term_IFS local depth=${#FUNCNAME[*]} if ((depth>=2)) && ! ble/string#match "${FUNCNAME[*]:1}" '^ble-edit/exec:gexec/\.|(^| )ble/builtin/trap/\.handler'; then @@ -5742,27 +5813,37 @@ function ble-edit/exec:gexec/.TRAPDEBUG { local func=${_ble_term_setaf[6]}' ('${_ble_term_setaf[4]}${FUNCNAME[1]}${1:+ $1}${_ble_term_setaf[6]}')' ble/util/print "${_ble_term_setaf[9]}[SIGINT]$_ble_term_sgr0 $source$sep$lineno$func$_ble_term_sgr0" >/dev/tty _ble_edit_exec_TRAPDEBUG_postproc="{ return $_ble_edit_exec_INT || break; } &>/dev/null" - elif ((depth==1)) && ! ble/string#match "$BASH_COMMAND" '^ble-edit/exec:gexec/\.'; then + elif ((depth==1)) && ! ble/string#match "$bash_command" '^ble-edit/exec:gexec/\.'; then # 一番外側で、ble-edit/exec:gexec/. 関数ではない時 local source=${_ble_term_setaf[5]}global local sep=${_ble_term_setaf[6]}: - ble/util/print "${_ble_term_setaf[9]}[SIGINT]$_ble_term_sgr0 $source$sep$_ble_term_sgr0 $BASH_COMMAND" >/dev/tty + ble/util/print "${_ble_term_setaf[9]}[SIGINT]$_ble_term_sgr0 $source$sep$_ble_term_sgr0 $bash_command" >/dev/tty _ble_edit_exec_TRAPDEBUG_postproc="{ return $_ble_edit_exec_INT || break; } &>/dev/null" fi + ble/base/.restore-bash-options __set __shopt - return 0 - fi - if [[ ! ${_ble_builtin_trap_handlers[_ble_builtin_trap_DEBUG]+set} ]]; then + elif [[ ${_ble_builtin_trap_handlers[_ble_builtin_trap_DEBUG]+set} ]]; then + # ユーザートラップだけの場合は、ユーザートラップを外で実行 + _ble_edit_exec_TRAPDEBUG_lastexit=$__lastexit + _ble_edit_exec_TRAPDEBUG_lastarg=$__lastarg + _ble_edit_exec_TRAPDEBUG_postproc='ble/util/setexit "$_ble_edit_exec_TRAPDEBUG_lastexit" "$_ble_edit_exec_TRAPDEBUG_lastarg"' + local user_trap=${_ble_builtin_trap_handlers[_ble_builtin_trap_DEBUG]} + if [[ $user_trap == *[![:space:]]* ]]; then + _ble_edit_exec_TRAPDEBUG_postproc="$_ble_edit_exec_TRAPDEBUG_postproc;$user_trap" + fi + + else # ユーザー DEBUG trap がなくかつ INT 処理中でもない場合は DEBUG は削除して # 良い [ Note: builtin trap - DEBUG は此処では効かない ] _ble_edit_exec_TRAPDEBUG_postproc='builtin trap -- - DEBUG' fi - ble/base/.restore-bash-options __set __shopt return 0 } +_ble_builtin_trap_DEBUG_userTrapInitialized= function ble/builtin/trap:DEBUG { + _ble_builtin_trap_DEBUG_userTrapInitialized=1 # Note (#D1155): ユーザコマンド実行中に新しく ble/builtin/trap DEBUG # が設定された場合は builtin trap DEBUG を仕掛ける。 if [[ $1 != - && ( $_ble_edit_exec_TRAPDEBUG_enabled || ! $_ble_attached ) ]]; then @@ -5770,6 +5851,72 @@ function ble/builtin/trap:DEBUG { fi } +## @fn _ble_builtin_trap_DEBUG__initialize +## ユーザーの設定した DEBUG trap を何処かの時点で読み取る。 +## +## DEBUG trap は基本的には ble.sh 内部では無効化される。但し、PROMPT_COMMAND を +## 評価する時には一時的に有効化される。ble/builtin/trap による DEBUG は関数の入 +## れ子等は考慮に入れていない。 +## +## Note: 関数名が POSIX の要求する物になっているのは bash-3.1 以下で特殊文字を +## 含む関数名に対して declare -ft を実行することができない為。 +## +## Note: 先に ble.sh を source した場合は殆どの場合は上の trap:DEBUG 経由で正し +## い trap string が登録するので殆どの場合動く。 +## +## 但し、bash-5.0 以下で先に ble.sh を source して prompt-attach を行い、更に +## PROMPT_COMMAND を書き換えた場合には、PROMPT_COMMAND の中で +## attach-from-PROMPT_COMMAND よりも後に実行している処理は DEBUG trap が無効 +## 化された状態で実行される事になる。これは PROMPT_COMMAND 内で DEBUG trap を +## 有効にしている動作とずれる。 +## +## Note: 先に DEBUG trap を設定した後に ble.sh を source した場合には、以下の場 +## 合にロード直後は DEBUG trap が ble.sh 内部の処理に対しても有効になっている +## 事に注意する。最初のユーザー入力または端末による DA2 応答等の時に改めて +## DEBUG trap の読み取り +## +## - rcfile の名前が .bashrc でも .profile でも .bash_profile でもない場合 +## (これは現在 rcfile の中にいるかどうかを判定する方法が bash にはない事か +## ら、rcfile かどうかをファイル名と行番号だけから判定しなければならない事 +## に由来する) +## +## - コマンドラインから source ~/.bashrc 等の様にして手動で bashrc を読み込ん +## だ時 (これは source が DEBUG trap を継承しないという Bash の制限に由来す +## る) +## +## - rcfile から一旦別のファイルを source してそのファイルから ble.sh を +## source した時。または関数内から ble.sh を source した時 (これも DEBUG +## trap の継承に関する Bash の制限に由来する) +## +## - bash-3.1 以下の時 (これは declare -ft で trace を付加できる関数の関数名 +## に対する制限に由来する) +function _ble_builtin_trap_DEBUG__initialize { + if [[ $_ble_builtin_trap_DEBUG_userTrapInitialized ]]; then + # Note: 既に ble/builtin/trap:DEBUG 等によって user trap が設定されている場 + # 合は改めて読み取る事はしない (読み取っても TRAPDEBUG が見えるだけ)。 + builtin eval -- "function $FUNCNAME() ((1))" + return 0 + elif [[ $1 == force ]] || ble/function/is-global-trace-context; then + _ble_builtin_trap_DEBUG_userTrapInitialized=1 + builtin eval -- "function $FUNCNAME() ((1))" + local tmp=$_ble_base_run/$$.trap.DEBUG ret + builtin trap -p DEBUG >| "$tmp" + local content; ble/util/readfile content "$tmp" + ble/util/put '' >| "$tmp" + + # ble.sh の設定した DEBUG trap は無視する。 + case ${content#"trap -- '"} in + (ble-edit/exec:gexec/.TRAPDEBUG*) ;; # ble-0.4 + (ble-edit/exec:exec/.eval-TRAPDEBUG*|ble-edit/exec:gexec/.eval-TRAPDEBUG*) ;; # ble-0.2 + (.ble-edit/exec:exec/eval-TRAPDEBUG*|.ble-edit/exec:gexec/eval-TRAPDEBUG*) ;; # ble-0.1 + (*) builtin eval -- "$content" ;; # ble/builtin/trap に処理させる + esac + return 0 + fi +} +ble/function#trace _ble_builtin_trap_DEBUG__initialize +_ble_builtin_trap_DEBUG__initialize + function ble-edit/exec:gexec/.TRAPINT { local sig=130 ((_ble_bash>=40300)) || sig=128 # bash-4.2 以下は 128 @@ -5839,7 +5986,6 @@ function ble-edit/exec:gexec/TERM/enter { function ble-edit/exec:gexec/.begin { _ble_edit_exec_inside_begin=1 local IFS=$_ble_term_IFS - _ble_decode_bind_hook= _ble_edit_exec_PWD=$PWD ble-edit/exec:gexec/TERM/leave ble/term/leave @@ -5849,7 +5995,7 @@ function ble-edit/exec:gexec/.begin { # C-c に対して ble/builtin/trap/install-hook INT # 何故か改めて実行しないと有効にならない blehook INT+='ble-edit/exec:gexec/.TRAPINT' - ble-edit/exec:gexec/.TRAPDEBUG/enter + ble-edit/exec:gexec/.TRAPDEBUG/restore } function ble-edit/exec:gexec/.end { _ble_edit_exec_inside_begin= @@ -6015,16 +6161,26 @@ function ble-edit/exec:gexec/.setup { # declare -a arr=(a b c) の様な特殊な構文の物は上書きできない。 # この所為で、例えば source 内で declare した配列などが壊れる。 # - ((${#_ble_edit_exec_lines[@]}==0)) && return 1 - ble/util/buffer.flush >&2 + ((${#_ble_edit_exec_lines[@]})) || [[ ! $_ble_edit_exec_TRAPDEBUG_adjusted ]] || return 1 + + local buff='_ble_decode_bind_hook=' ibuff=1 - local q=\' Q="'\\''" - local cmd - local -a buff=() - local count=0 - buff[${#buff[@]}]=ble-edit/exec:gexec/.begin - for cmd in "${_ble_edit_exec_lines[@]}"; do - if [[ "$cmd" == *[^' ']* ]]; then + if [[ ! $_ble_edit_exec_TRAPDEBUG_adjusted ]]; then + # Note #D1772: bash-3.1 以下で prompt attach すると、何故か一番外側で実行し + # ていても attach-from-PROMPT_COMMAND の中で実行している事になっているので、 + # 明示的に force を指定して DEBUG trap を読み取らせる。 + buff[ibuff++]='_ble_builtin_trap_DEBUG__initialize force' + buff[ibuff++]=_ble_edit_exec_gexec__TRAPDEBUG_adjust + fi + + ble/array#filter-by-glob _ble_edit_exec_lines '*[![:space:]]*' + local count=${#_ble_edit_exec_lines[@]} + if ((count)); then + ble/util/buffer.flush >&2 + + local q=\' Q="'\''" cmd + buff[ibuff++]=ble-edit/exec:gexec/.begin + for cmd in "${_ble_edit_exec_lines[@]}"; do # Note: restore-lastarg の $_ble_edit_exec_lastarg は $_ を設定するための # ものである。 # Note #D0465: restore-lastarg と実際のコマンドを同じ eval の中に入れるの @@ -6034,28 +6190,30 @@ function ble-edit/exec:gexec/.setup { # のは、同じ eval の中でないと $_ が失われてしまうから (特に eval を出 # る時に eval の最終引数になってしまう)。 local prologue="" - buff[${#buff[@]}]="ble-edit/exec:gexec/.prologue '${cmd//$q/$Q}'" - buff[${#buff[@]}]='{ time builtin eval -- "ble-edit/exec:gexec/.restore-lastarg \"\$_ble_edit_exec_lastarg\"' - buff[${#buff[@]}]='$_ble_edit_exec_BASH_COMMAND' - buff[${#buff[@]}]='{ ble-edit/exec:gexec/.save-lastarg; } &>/dev/null' # Note: &>/dev/null は set -x 対策 #D0930 - buff[${#buff[@]}]='" 2>&$_ble_util_fd_stderr; } 2>| "$_ble_exec_time_TIMEFILE"' - buff[${#buff[@]}]='{ ble-edit/exec:gexec/.epilogue; } 3>&2 &>/dev/null' - ((count++)) + buff[ibuff++]="ble-edit/exec:gexec/.prologue '${cmd//$q/$Q}'" + buff[ibuff++]='{ time builtin eval -- "ble-edit/exec:gexec/.restore-lastarg \"\$_ble_edit_exec_lastarg\"' + buff[ibuff++]='$_ble_edit_exec_BASH_COMMAND' + buff[ibuff++]='{ ble-edit/exec:gexec/.save-lastarg; } &>/dev/null' # Note: &>/dev/null は set -x 対策 #D0930 + buff[ibuff++]='" 2>&$_ble_util_fd_stderr; } 2>| "$_ble_exec_time_TIMEFILE"' + buff[ibuff++]='{ ble-edit/exec:gexec/.epilogue; } 3>&2 &>/dev/null' # ※直接 $cmd と書き込むと文法的に破綻した物を入れた時に # 続きの行が実行されない事になってしまう。 - fi - done - _ble_edit_exec_lines=() + done + _ble_edit_exec_lines=() - ((count==0)) && return 1 + # Note: 現在は _ble_decode_bind_hook 経由で処理しているので問題ないが、 + # builtin trap - INT DEBUG を使う時一番外側 (此処) でないと効かない + buff[ibuff++]=_ble_edit_exec_gexec__TRAPDEBUG_adjust + buff[ibuff++]=ble-edit/exec:gexec/.end + fi - # Note: 現在は blehook 経由で処理しているので問題ないが、 - # builtin trap - INT DEBUG を使う時一番外側 (此処) でないと効かない - buff[${#buff[@]}]='builtin trap -- - DEBUG' - buff[${#buff[@]}]=ble-edit/exec:gexec/.end - IFS=$'\n' builtin eval '_ble_decode_bind_hook="${buff[*]}"' - return 0 + if ((ibuff>=2)); then + IFS=$'\n' builtin eval '_ble_decode_bind_hook="${buff[*]}"' + fi + + # コマンド実行をする場合は ble-edit/bind/.tail は遅延する + ((count>=1)); return $? } function ble-edit/exec:gexec/process { @@ -9459,6 +9617,12 @@ function ble-edit/initialize { ble/prompt/initialize } function ble-edit/attach { + # user DEBUG trap 取得を試行 + _ble_builtin_trap_DEBUG__initialize + # user DEBUG trap が取得済みなら DEBUG trap 削除 + [[ $_ble_builtin_trap_DEBUG_userTrapInitialized ]] && + _ble_edit_exec_gexec__TRAPDEBUG_adjust + ble-edit/attach/.attach _ble_canvas_x=0 _ble_canvas_y=0 ble/util/buffer "$_ble_term_cr" @@ -9466,5 +9630,7 @@ function ble-edit/attach { function ble-edit/detach { ble-edit/bind/stdout.finalize ble-edit/attach/.detach - ble-edit/exec:gexec/.TRAPDEBUG/enter + ble-edit/exec:gexec/.TRAPDEBUG/restore } + +ble/function#trace ble-edit/attach diff --git a/src/util.sh b/src/util.sh index 378409d5..db9faa01 100644 --- a/src/util.sh +++ b/src/util.sh @@ -2823,6 +2823,36 @@ function ble/function#evaldef { return "$ext" } +builtin eval -- "${_ble_util_gdict_declare//NAME/_ble_util_function_traced}" +function ble/function#trace { + declare -ft "$1" &>/dev/null && + ble/gdict#set _ble_util_function_traced "$1" 1 +} +function ble/function#has-trace { + ble/gdict#has _ble_util_function_traced "$1" +} +## @fn ble/function/is-global-trace-context +## この関数の呼び出し元の文脈で確実に global の DEBUG が見えているかどうかを +## 判定します。 +function ble/function/is-global-trace-context { + # Note: 例え set -T が設定されていたとしても、それが global で設定された物な + # のか呼び出しの何処かの深さで設定された物なのか分からない。なので、set -T + # が設定されていたからと言って無条件に global が見えているとは限らない。 + # Note: ble に属する関数は勝手に set -T を一時的に有効にしたりする事は基本的 + # にないので許可する。但し、内部で一時的に restore-bash-options している時 + # はあるが、その内部で ble-attach 乃至は ble/function/is-global-trace-context等 + # を実行する事はないと仮定する。 + local func depth=1 ndepth=${#FUNCNAME[*]} + for func in "${FUNCNAME[@]:1}"; do + local src=${BASH_SOURCE[depth]} + [[ $- == *T* && ( $func == ble || $func == ble[-/]* || $func == source && $src == "$_ble_base_blesh_raw" ) ]] || + [[ $func == source && depth -eq ndepth-1 && BASH_LINENO[depth] -eq 0 && ( ${src##*/} == .bashrc || ${src##*/} == .bash_profile || ${src##*/} == .profile ) ]] || + ble/gdict#has _ble_util_function_traced "$func" || return 1 + ((depth++)) + done + return 0 +} + ## @fn ble/function#try function args... ## 関数 function が存在している時に限り関数を呼び出します。 ##