Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
global: fix bugs of the adjustments for bash-5.2 "patsub_replacement"…
… (fixup 4590997)

* global: work around compat42 quoting of "${v/pat/"$rep"}" #D1751
* prompt: fix a bug of ble/prompt/print redundantly quoting '$' #D1752
* global: identify bash-4.2 bug that internal quoting of ${v/%$empty/"$rep"} remains #D1753
* global: work around "shopt -s compat42" #D1754
  • Loading branch information
akinomyoga committed Jan 24, 2022
1 parent e199bee commit a75bb25
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 33 deletions.
4 changes: 4 additions & 0 deletions docs/ChangeLog.md
Expand Up @@ -294,6 +294,10 @@
- menu (`menu-style:desc`): work around xenl quirks for relative cursor movements (reported by telometto) `#D1728` 3e136a6
- global: work around the arithmetic syntax error of `10#` in Bash-5.1 `#D1734` 7545ea3
- global: adjust implementations for Bash 5.2 `patsub_replacement` `#D1738` 4590997
- global: work around compat42 quoting of "${v/pat/"$rep"}" `#D1751` 0000000
- prompt: fix a bug of ble/prompt/print redundantly quoting '$' `#D1752` 0000000
- global: identify bash-4.2 bug that internal quoting of `${v/%$empty/"$rep"}` remains `#D1753` 0000000
- global: work around `shopt -s compat42` `#D1754` 0000000
- 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
- main: check `IN_NIX_SHELL` to inactivate ble.sh in nix-shell `#D1747` 0000000
Expand Down
9 changes: 6 additions & 3 deletions keymap/vi_test.sh
Expand Up @@ -40,10 +40,13 @@ function ble/keymap:vi_test/check {
((ntest++,nsuccess++))
else
((ntest++))
local esc_in=${in//$nl/"$NL"}
local esc_fin=${fin//$nl/"$NL"}
local esc_str=${_ble_edit_str//$nl/"$NL"}
ble/util/print "test($section/$id): keys = ($kspecs)"
ble/util/print " initial = \"$i:${in//$nl/"$NL"}\""
ble/util/print " expected = \"$f:${fin//$nl/"$NL"}\""
ble/util/print " result = \"$_ble_edit_ind:${_ble_edit_str//$nl/"$NL"}\""
ble/util/print " initial = \"$i:$esc_in\""
ble/util/print " expected = \"$f:$esc_fin\""
ble/util/print " result = \"$_ble_edit_ind:$esc_str\""
fi >&2

# restore states
Expand Down
7 changes: 6 additions & 1 deletion lib/core-complete.sh
Expand Up @@ -3429,7 +3429,12 @@ function ble/complete/progcomp/.compgen {
done
[[ $has_desc ]] && bleopt complete_menu_style=desc
else
[[ $progcomp_prefix ]] && cands=("${cands[@]/#/"$progcomp_prefix"}") # WA #D1570 safe
[[ $progcomp_prefix ]] &&
if ((_ble_bash>=40300)) && ! shopt -q compat42; then
cands=("${cands[@]/#/"$progcomp_prefix"}") # WA #D1570 #D1751 safe
else
cands=("${cands[@]/#/$progcomp_prefix}") # WA #D1570 #D1738 safe
fi
fi
ble/complete/cand/yield.batch "$action" "$comp_opts"

Expand Down
20 changes: 10 additions & 10 deletions lib/core-syntax.sh
Expand Up @@ -4411,10 +4411,10 @@ function ble/syntax:bash/ctx-heredoc-word/remove-quotes {
if rex='^\$?"(([^\"]|\\.)*)(\\?$|")'; [[ $text =~ $rex ]]; then
local str=${BASH_REMATCH[1]}
local a b
b='\`' a='`'; str="${str//"$b"/"$a"}"
b='\"' a='"'; str="${str//"$b"/"$a"}"
b='\$' a='$'; str="${str//"$b"/"$a"}"
b='\\' a='\'; str="${str//"$b"/"$a"}"
b='\`' a='`'; str=${str//"$b"/"$a"}
b='\"' a='"'; str=${str//"$b"/"$a"} # WA #D1751 safe
b='\$' a='$'; str=${str//"$b"/"$a"}
b='\\' a='\'; str=${str//"$b"/"$a"}
result=$result$str
text=${text:${#BASH_REMATCH}}
continue
Expand Down Expand Up @@ -4450,12 +4450,12 @@ function ble/syntax:bash/ctx-heredoc-word/escape-delimiter {
local ret=$1
if [[ $ret == *[\\\'$_ble_term_IFS$_ble_term_FS]* ]]; then
local a b fs=$_ble_term_FS
a=\\ ; b="\\$a"; ret="${ret//"$a"/"$b"}"
a=\' ; b="\\$a"; ret="${ret//"$a"/"$b"}"
a=' ' ; b="$_ble_syntax_bash_heredoc_EscSP"; ret="${ret//"$a"/"$b"}"
a=$'\t'; b="$_ble_syntax_bash_heredoc_EscHT"; ret="${ret//"$a"/"$b"}"
a=$'\n'; b="$_ble_syntax_bash_heredoc_EscLF"; ret="${ret//"$a"/"$b"}"
a=$fs ; b="$_ble_syntax_bash_heredoc_EscFS"; ret="${ret//"$a"/"$b"}"
a=\\ ; b='\'$a; ret=${ret//"$a"/"$b"}
a=\' ; b='\'$a; ret=${ret//"$a"/"$b"}
a=' ' ; b=$_ble_syntax_bash_heredoc_EscSP; ret=${ret//"$a"/"$b"}
a=$'\t'; b=$_ble_syntax_bash_heredoc_EscHT; ret=${ret//"$a"/"$b"}
a=$'\n'; b=$_ble_syntax_bash_heredoc_EscLF; ret=${ret//"$a"/"$b"}
a=$fs ; b=$_ble_syntax_bash_heredoc_EscFS; ret=${ret//"$a"/"$b"}
fi
escaped=$ret
}
Expand Down
5 changes: 5 additions & 0 deletions make_command.sh
Expand Up @@ -1164,6 +1164,11 @@ function sub:scan/bash502-patsub_replacement {
\Z#D1738Zd
\Z\$\{_ble_edit_str//\$'\''\\n'\''/\$'\''\\n'\''"\$comment_begin"\}Zd # edit.sh
g'

grc --color '"[^"]*\$\{[[:alnum:]_]+(\[[^][]*\])?//?([^{}]|\{[^{}]*\})+/[^{}"'\'']*"[^"]*([&$]|\\)' --exclude=./test |
sed -E 'h;s/'"$esc"'//g;s/^[^:]*:[0-9]+:[[:space:]]*//
\Z#D1751Zd
g'
}

function sub:scan/gawk402bug-regex-check {
Expand Down
135 changes: 135 additions & 0 deletions note.txt
Expand Up @@ -208,6 +208,12 @@

bash 実装上で注意するべき事

* bash-5.2 以上の patsub_replacement に注意する。

任意の文字列に置換する場合は & が勝手に解釈されない様に "${var/x/"$s"}" の
様に quote すると良い。但し、bash-4.2 では "${var/x/"$s"}" の " は literal
に解釈されてしまう事に注意する。一番安全なのは一旦変数に代入するという事。

* 変数の代入は基本的に quote は必要ないが、

1 チルダで始まる時はチルダ展開を防ぐ為に quote が必要。
Expand Down Expand Up @@ -5894,6 +5900,135 @@ bash_tips

2022-01-24

* patsubWA: compat42 での振る舞い [#D1754]

それとは別に compat42 の効果についても考察する必要がある。compat42 の時には
結局問題が生じるのではないだろうか?? quote しても無駄である。という事を考え
ると compat42 の時は単純に実装を切り替えるというのは動作しない。とは言いつ
つ惨事を防ぐ為にはやはり配慮が必要である。

* patsubWA: bash-dev で \q{} が動かない [#D1753]

うーん。これも patsub が原因の気がする。或いは bash-dev の振る舞いの変化に
よって引き起こされている? 取り敢えず調べる → やはり patsub WA によって動か
なくなっている。bash 側の振る舞いの変化による物ではなかった。

実際の動作を調べる。先ずそもそも \q{} が呼び出されているのか? うーん。呼び
出されていない。つまり @P による展開になってしまっているという事だろうか →
否、@P による展開にはなっていない。

うーん。どうやら \q{} はいつの間にかに \\q{} に変換されてしまっている様子。
何故だろうか。うーん。bleopt_prompt_rps1 の値の時点で \\q{} になってしまっ
ている。つまり、bleopt が悪い。

うーん。引数読み取りの時点で駄目になっている。

$ ble/array#push specs "${var[@]/%/"=$value"}" # #D1570 #D1751 WA checked
$ ble/debug/print-variables var value specs |& cat -v >/dev/tty
var=('bleopt_prompt_rps1') value='\q{blerc/rps1}' specs=('bleopt_prompt_rps1=\\q{blerc/rps1}')

うーん。不思議だ。bash-5.2 のバグだろうか。うーん。これはバグである。という
か /% 等の様にパターンが空の時には escape は処理されないのである。

$ var= rep='&'; echo "${var/%/"$rep"}"

うーん。Bash に報告しようと思ったが思ったよりも複雑である。続きは
bug-report/bash/report31 で議論する事にした。取り敢えず patch は作った。最
終的にどうなるかは分からないが取り敢えずは patch 付きでコンパイルしていれば
今の ble.sh の実装で大丈夫のはず。

* patsubWA: bash-4.3 で \$ になる [#D1752]

これもつい最近までは見られなかった問題である。bash-3.0 迄影響を受けている。
これも patsub_replacement 対策が悪さをしていると見える。

% うーん。_ble_prompt_const_root を出力している箇所から既に問題が起こっている
% 気がするが、_ble_prompt_const_root の値は昔から変わらず "\$" だった様だ。
% 然し、patsub 修正よりも前にはちゃんと単一の $ になっていた。
%
% $ declare -p _ble_prompt_const_root
% declare -- _ble_prompt_const_root="\$"
%
% →これは declare の出力の quote であって、_ble_prompt_const_root の値自体
% は常に単一の '$' だった。

というよりそもそも ble/prompt/print は $ を解釈しないのではなかったのか。だ
とすれば \$ の様にする意味がない。うーん。つまり以前の実装が間違っていたと
いう事。

うーん。分かった。確かに以前の実装は間違っている。escape-characters に渡す
文字集合は '$\`"' ではなく '\$`"' でなければならない。つまり一番最初に '\'
を置換しなければ他の文字の置換結果の '\' も二重に置換してしまう事になる。こ
の様な実装に切り替えたのは patsub の事だと思ったが

% よく考えたらそれよりも前から二重のバグによってたまたま動いていただけに過
% ぎない → これも勘違い。以前は '$' を正しく quote して正しく最終的に '$'
% になっていた。
%
% | function ble/prompt/print {
% | - local text=$1 a b
% | - if [[ ! $prompt_noesc && $text == *['$\"`']* ]]; then
% | - a='\' b='\\' text=${text//"$a"/$b}
% | - a='$' b='\$' text=${text//"$a"/$b}
% | - a='"' b='\"' text=${text//"$a"/$b}
% | - a='`' b='\`' text=${text//"$a"/$b}
% | - fi
% | - ble/canvas/put.draw "$text"
% | + local ret=$1 a b
% | + [[ $prompt_noesc ]] ||
% | + ble/string#escape-characters "$ret" '$\"`'
% | + ble/canvas/put.draw "$ret"
% | }
%
% と思ったがこの編集を見ると以前はちゃんと動いていた筈なのである。という事は、
% 先ず初めに '\$' が \\\$ になって、更に評価時に \$ になる筈。不思議である。
% うーん。以前は \$ は \\\$ になって、更に評価された時に \$ になるという仕組
% みだった筈。

何れにしても問題の部分を修正したらちゃんと動く様になった。

% 然し何故これで動くのかは未だに謎である。うーん。何故?
%
% →ずっと遡っていくと実はずっと単一の '$' である。。そして
% _ble_prompt_const_root も単一の '$' である。declare の出力が double
% quoted だから \$ と表示していたけれども実は単一の $ だったという事である。
% つまり勘違いだった。

結局これで万事大丈夫になった。

* patsubWA: bash-4.2 で文法エラーが発生する (bind.delay 周り) [#D1751]

何と、bash-4.2 では "${var//xx/"yy"}" の " は literal になる。テストで検出
された物を少し修正したが他にも沢山ありそうだ。

$ grc '"[^"]*\$\{[[:alnum:]_]+(\[[^][]*\])?//?([^{}]|\{[^{}]*\})+/[^{}"'\'']*"[^"]*([&$]|\\)' --exclude=./test
done: ./keymap/vi_test.sh:44: ble/util/print " initial = \"$i:${in//$nl/"$NL"}\""
done: ./keymap/vi_test.sh:45: ble/util/print " expected = \"$f:${fin//$nl/"$NL"}\""
done: ./keymap/vi_test.sh:46: ble/util/print " result = \"$_ble_edit_ind:${_ble_edit_str//$nl/"$NL"}\""
done: ./lib/core-complete.sh:3432: [[ $progcomp_prefix ]] && cands=("${cands[@]/#/"$progcomp_prefix"}") # WA #D1570 safe
done: ./lib/core-syntax.sh:4414: b='\`' a='`'; str="${str//"$b"/"$a"}"
done: ./lib/core-syntax.sh:4415: b='\"' a='"'; str="${str//"$b"/"$a"}"
done: ./lib/core-syntax.sh:4416: b='\$' a='$'; str="${str//"$b"/"$a"}"
done: ./lib/core-syntax.sh:4417: b='\\' a='\'; str="${str//"$b"/"$a"}"
done: ./lib/core-syntax.sh:4453: a=\\ ; b="\\$a"; ret="${ret//"$a"/"$b"}"
done: ./lib/core-syntax.sh:4454: a=\' ; b="\\$a"; ret="${ret//"$a"/"$b"}"
done: ./lib/core-syntax.sh:4455: a=' ' ; b="$_ble_syntax_bash_heredoc_EscSP"; ret="${ret//"$a"/"$b"}"
done: ./lib/core-syntax.sh:4456: a=$'\t'; b="$_ble_syntax_bash_heredoc_EscHT"; ret="${ret//"$a"/"$b"}"
done: ./lib/core-syntax.sh:4457: a=$'\n'; b="$_ble_syntax_bash_heredoc_EscLF"; ret="${ret//"$a"/"$b"}"
done: ./lib/core-syntax.sh:4458: a=$fs ; b="$_ble_syntax_bash_heredoc_EscFS"; ret="${ret//"$a"/"$b"}"
done: ./src/util.sh:118: ble/array#push specs "${var[@]/%/"=$value"}" # #D1570 WA checked
done: ./src/util.sh:777: ARR=("${ARR[@]::$2}" "${sARR[@]/#/"$4"}" "${ARR[@]:$3}")' # WA #D1570 checked
done: ./src/util.sh:1138: a='!' b='"\!"' ret=${ret//"$a"/"$b"}
done: ./src/util.sh:1165: a=$'\n' b="\$'\n'" ret=${ret//"$a"/"$b"}
done: ./src/util.sh:1401: builtin eval -- "${_ble_local_script//opts/"$1"}"
done: ./src/util.sh:1405: builtin eval -- "${_ble_local_script//opts/"$1"}"
done: ./src/util.sh:1413: builtin eval -- "${_ble_local_script//opts/"$1"}"
done: ./src/util.sh:1421: builtin eval -- "${_ble_local_script//opts/"$1"}"
done: ./src/util.sh:1574: ret=("${ret[@]//$_ble_term_FS,/"$_ble_term_FS"}") # WA #D1570 checked
done: ./src/util.sh:5371: ble/util/put "${_ble_term_visible_bell_show//'%message%'/"$sgr$message"}" >&

取り敢えず全て修正した。一応動作はしている。

* prompt: wezterm shell-integration が PRECMD で何かを出力するがずれる [#D1750]

先ず precmd を呼び出す瞬間のカーソル位置が前回のプロンプトの後になっている
Expand Down
2 changes: 1 addition & 1 deletion src/edit.sh
Expand Up @@ -605,7 +605,7 @@ _ble_prompt_term_status_data=()
function ble/prompt/print {
local ret=$1 a b
[[ $prompt_noesc ]] ||
ble/string#escape-characters "$ret" '$\"`'
ble/string#escape-characters "$ret" '\$"`'
ble/canvas/put.draw "$ret"
}

Expand Down
47 changes: 29 additions & 18 deletions src/util.sh
Expand Up @@ -115,7 +115,11 @@ function bleopt/.read-arguments {

if [[ $op ]]; then
var=("${var[@]}") # #D1570: WA bash-3.0 ${scal[@]/x} bug
ble/array#push specs "${var[@]/%/"=$value"}" # #D1570 WA checked
if ((_ble_bash>=40300)) && ! shopt -q compat42; then
ble/array#push specs "${var[@]/%/"=$value"}" # #D1570 #D1751 WA checked
else
ble/array#push specs "${var[@]/%/=$value}" # #D1570 #D1738 WA checked
fi
else
ble/array#push pvars "${var[@]}"
fi
Expand Down Expand Up @@ -773,8 +777,10 @@ function ble/array#replace {
function ble/dense-array#fill-range {
ble/array#reserve-prototype $(($3-$2))
local _ble_script='
local -a sARR; sARR=("${_ble_array_prototype[@]::$3-$2}")
ARR=("${ARR[@]::$2}" "${sARR[@]/#/"$4"}" "${ARR[@]:$3}")' # WA #D1570 checked
local -a sARR; sARR=("${_ble_array_prototype[@]::$3-$2}")
ARR=("${ARR[@]::$2}" "${sARR[@]/#/$4}" "${ARR[@]:$3}")' # WA #D1570 #D1738 checked
((_ble_bash>=40300)) && ! shopt -q compat42 &&
_ble_script=${_ble_script//'$4'/'"$4"'}
builtin eval -- "${_ble_script//ARR/$1}"
}

Expand Down Expand Up @@ -803,7 +809,7 @@ function ble/string#reserve-prototype {
function ble/string#repeat {
ble/string#reserve-prototype "$2"
ret=${_ble_string_prototype::$2}
ret="${ret// /"$1"}"
ret=${ret// /"$1"}
}

## @fn ble/string#common-prefix a b
Expand Down Expand Up @@ -1135,7 +1141,7 @@ function ble/string#escape-for-bash-single-quote {
function ble/string#escape-for-bash-double-quote {
ble/string#escape-characters "$1" '\"$`'
local a b
a='!' b='"\!"' ret=${ret//"$a"/"$b"}
a='!' b='"\!"' ret=${ret//"$a"/"$b"} # WA #D1751 checked
}
function ble/string#escape-for-bash-escape-string {
ble/string#escape-characters "$1" $'\\\a\b\e\f\n\r\t\v'\' '\abefnrtv'\'
Expand All @@ -1162,7 +1168,7 @@ function ble/string#escape-for-bash-specialchars {
if [[ $ret == *[$']\n\t']* ]]; then
local a b
a=']' b=\\$a ret=${ret//"$a"/"$b"}
a=$'\n' b="\$'\n'" ret=${ret//"$a"/"$b"}
a=$'\n' b="\$'\n'" ret=${ret//"$a"/"$b"} # WA #D1751 checked
a=$'\t' b=$'\\\t' ret=${ret//"$a"/"$b"}
fi

Expand Down Expand Up @@ -1246,8 +1252,8 @@ else
function ble/string#quote-words {
local q=\' Q="'\''" IFS=$_ble_term_IFS
ret=("${@//$q/$Q}")
ret=("${ret[@]/%/"$q"}") # WA #D1570 checked
ret="${ret[*]/#/"$q"}" # WA #D1570 checked
ret=("${ret[@]/%/$q}") # WA #D1570 #D1738 ok
ret="${ret[*]/#/$q}" # WA #D1570 #D1738 ok
}
function ble/string#quote-command {
if (($#<=1)); then
Expand All @@ -1256,9 +1262,9 @@ else
fi
local q=\' Q="'\''" IFS=$_ble_term_IFS
ret=("${@:2}")
ret=("${ret[@]//$q/$Q}") # WA #D1570 checked
ret=("${ret[@]/%/"$q"}") # WA #D1570 checked
ret="$1 ${ret[*]/#/"$q"}" # WA #D1570 checked
ret=("${ret[@]//$q/$Q}") # WA #D1570 #D1738 ok
ret=("${ret[@]/%/$q}") # WA #D1570 #D1738 ok
ret="$1 ${ret[*]/#/$q}" # WA #D1570 #D1738 ok
}
fi
## @fn ble/string#quote-word text opts
Expand Down Expand Up @@ -1398,27 +1404,31 @@ function ble/util/substr {

function ble/path#append {
local _ble_local_script='opts=$opts${opts:+:}$2'
builtin eval -- "${_ble_local_script//opts/"$1"}"
_ble_local_script=${_ble_local_script//opts/"$1"}
builtin eval -- "$_ble_local_script"
}
function ble/path#prepend {
local _ble_local_script='opts=$2${opts:+:}$opts'
builtin eval -- "${_ble_local_script//opts/"$1"}"
_ble_local_script=${_ble_local_script//opts/"$1"}
builtin eval -- "$_ble_local_script"
}
function ble/path#remove {
[[ $2 ]] || return 1
local _ble_local_script='
opts=:${opts//:/::}:
opts=${opts//:"$2":}
opts=${opts//::/:} opts=${opts#:} opts=${opts%:}'
builtin eval -- "${_ble_local_script//opts/"$1"}"
_ble_local_script=${_ble_local_script//opts/"$1"}
builtin eval -- "$_ble_local_script"
}
function ble/path#remove-glob {
[[ $2 ]] || return 1
local _ble_local_script='
opts=:${opts//:/::}:
opts=${opts//:$2:}
opts=${opts//::/:} opts=${opts#:} opts=${opts%:}'
builtin eval -- "${_ble_local_script//opts/"$1"}"
_ble_local_script=${_ble_local_script//opts/"$1"}
builtin eval -- "$_ble_local_script"
}
function ble/path#contains {
builtin eval "[[ :\${$1}: == *:\"\$2\":* ]]"
Expand Down Expand Up @@ -1570,8 +1580,8 @@ function ble/adict#keys {
_ble_local_keylist=${!_ble_local_keylist%:}
ble/string#split ret : "$_ble_local_keylist"
if [[ $_ble_local_keylist == *"$_ble_term_FS"* ]]; then
ret=("${ret[@]//$_ble_term_FS./:}") # WA #D1570 checked
ret=("${ret[@]//$_ble_term_FS,/"$_ble_term_FS"}") # WA #D1570 checked
ret=("${ret[@]//$_ble_term_FS./:}") # WA #D1570 checked
ret=("${ret[@]//$_ble_term_FS,/$_ble_term_FS}") # WA #D1570 #D1738 checked
fi

# filter out unset elements
Expand Down Expand Up @@ -5368,7 +5378,8 @@ function ble/term/visible-bell:term/init {
}
function ble/term/visible-bell:term/show {
local sgr=$1 message=${_ble_term_visible_bell_prev[1]}
ble/util/put "${_ble_term_visible_bell_show//'%message%'/"$sgr$message"}" >&2
message=${_ble_term_visible_bell_show//'%message%'/"$sgr$message"}
ble/util/put "$message" >&2
}
function ble/term/visible-bell:term/update {
ble/term/visible-bell:term/show "$@"
Expand Down

0 comments on commit a75bb25

Please sign in to comment.