Skip to content

Commit a3b94bb

Browse files
committed
global: work around case-interleaving collation
1 parent 0fdbe3b commit a3b94bb

File tree

11 files changed

+178
-45
lines changed

11 files changed

+178
-45
lines changed

ble.pp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2128,7 +2128,7 @@ function ble/dispatch {
21282128
(version|--version) ble/util/print "ble.sh, version $BLE_VERSION (noarch)" ;;
21292129
(check|--test) ble/base/sub:test "$@" ;;
21302130
(*)
2131-
if ble/string#match "$cmd" '^[-a-z0-9]+$' && ble/is-function "ble-$cmd"; then
2131+
if ble/string#match "$cmd" '^[-a-zA-Z0-9]+$' && ble/is-function "ble-$cmd"; then
21322132
"ble-$cmd" "$@"
21332133
elif ble/is-function ble/bin/ble; then
21342134
ble/bin/ble "$cmd" "$@"

docs/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
- term: add workarounds for `eterm` `#D2087` a643f0ea
6868
- global: adjust bash options for utilities outside the ble context (motivated by jkemp814) `#D2092` 6b144de7
6969
- decode,syntax: quote `$#` in arguments properly `#D2097` 40a625d3
70+
- global: work around case-interleaving collation (reported by dongxi8) `#D2103` xxxxxxxx
7071

7172
## Contrib
7273

lib/core-complete.sh

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6993,6 +6993,13 @@ function ble/complete/insert-braces/.compose {
69936993
return substr(str, 1, length(head)) == head;
69946994
}
69956995
6996+
# Note: value ~ /[[:lower:]]/ cannot be used in mawk. value ~ /[a-z]/ may
6997+
# match uppercase characters in some strange locales, e.g., en_US.UTF-8 in
6998+
# Ubuntu 16.04 LTS.
6999+
function islower(s) {
7000+
return s == tolower(s);
7001+
}
7002+
69967003
BEGIN {
69977004
RS = '"$char_RS"';
69987005
rex_atom = ENVIRON["rex_atom"];
@@ -7104,7 +7111,7 @@ function ble/complete/insert-braces/.compose {
71047111
for (i = 0; i < ikey; i++) {
71057112
while (dict[value = keys[i]]--) {
71067113
if (value ~ /^([a-zA-Z])$/) {
7107-
alpha = (value ~ /^[a-z]$/) ? lower : upper;
7114+
alpha = islower(value) ? lower : upper;
71087115
beg = end = value;
71097116
b = e = index(alpha, value);
71107117
while (b > 1 && dict[tmp = substr(alpha, b - 1, 1)]) {

lib/core-syntax.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,7 +1648,7 @@ function ble/syntax:bash/simple-word/detect-separated-path {
16481648
[[ $word ]] || return 1
16491649

16501650
local rex_url='^[a-z]+://'
1651-
[[ :$opts: == *:url:* && $word =~ $rex_url ]] && return 1
1651+
[[ :$opts: == *:url:* ]] && ble/string#match-safe "$word" "$rex_url" && return 1
16521652

16531653
# read eval options
16541654
local eval_opts=$opts notilde=
@@ -1695,7 +1695,7 @@ function ble/syntax:bash/simple-word/locate-filename/.exists {
16951695
[[ $path == // ]]
16961696
else
16971697
[[ -e $path || -h $path ]]
1698-
fi || [[ :$opts: == *:url:* && $path =~ $rex_url ]]
1698+
fi || [[ :$opts: == *:url:* ]] && ble/string#match-safe "$path" "$rex_url"
16991699
}
17001700
## @fn ble/syntax:bash/simple-word/locate-filename word [sep] [opts]
17011701
## @param[in] word

lib/keymap.vi.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,10 @@ function ble/keymap:vi/string#encode-rot13 {
8787
local -a buff=() ch
8888
for ((i=0;i<${#text};i++)); do
8989
ch=${text:i:1}
90-
if [[ $ch == [A-Z] ]]; then
90+
if ble/string#isupper "$ch"; then
9191
ch=${_ble_util_string_upper_list%%"$ch"*}
9292
ch=${_ble_util_string_upper_list:(${#ch}+13)%26:1}
93-
elif [[ $ch == [a-z] ]]; then
93+
elif ble/string#islower "$ch"; then
9494
ch=${_ble_util_string_lower_list%%"$ch"*}
9595
ch=${_ble_util_string_lower_list:(${#ch}+13)%26:1}
9696
fi

lib/test-util.sh

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,8 @@ function is-global { (builtin readonly "$1"; ! local "$1" 2>/dev/null); }
912912
ble/test code:'ret=xyz:xyz:xyz; '$cmd' ret xyz' ret=
913913
done
914914

915+
export LC_ALL= LC_COLLATE=C 2>/dev/null # suppress locale error #D1440
916+
915917
ble/test code:'ret=a; ble/path#remove ret \?' ret=a
916918
ble/test code:'ret=aa; ble/path#remove ret \?' ret=aa
917919
ble/test code:'ret=a:b; ble/path#remove ret \?' ret=a:b
@@ -1007,7 +1009,7 @@ function is-global { (builtin readonly "$1"; ! local "$1" 2>/dev/null); }
10071009
$Dict#set $dict banana yellow
10081010
$Dict#set $dict orange orange
10091011
$Dict#set $dict melon green
1010-
1012+
10111013
ret=unchanged
10121014
ble/test $Dict'#has '$dict' banana' ret=unchanged # 先頭
10131015
ble/test $Dict'#has '$dict' apple' ret=unchanged #
@@ -1017,7 +1019,7 @@ function is-global { (builtin readonly "$1"; ! local "$1" 2>/dev/null); }
10171019
ble/test $Dict'#get '$dict' apple' ret=red #
10181020
ble/test $Dict'#get '$dict' melon' ret=green # 末尾
10191021
ble/test '! '$Dict'#get '$dict' pear' ret= # 存在しない項目
1020-
1022+
10211023
# 空白類
10221024
ble/test '! '$Dict'#has '$dict' ""' # 末尾空要素で引けるか
10231025
ble/test '! '$Dict'#get '$dict' ""' # 末尾空要素で引けるか
@@ -1034,7 +1036,7 @@ function is-global { (builtin readonly "$1"; ! local "$1" 2>/dev/null); }
10341036
ble/test $Dict'#has '$dict' apple' # 既存項目を破壊していないか
10351037
ble/test $Dict'#get '$dict' " apple "' ret=' red ' # 空白で trim されないか
10361038
ble/test $Dict'#get '$dict' apple' ret=red # 既存項目を破壊していないか
1037-
1039+
10381040
# FS, colon
10391041
ble/test '! '$Dict'#has '$dict' "${_ble_term_FS}"' # 単一FS
10401042
ble/test '! '$Dict'#has '$dict' ":"' # 単一コロン
@@ -1056,7 +1058,7 @@ function is-global { (builtin readonly "$1"; ! local "$1" 2>/dev/null); }
10561058
ble/test $Dict'#get '$dict' ":"' ret=Colon # 単一コロン
10571059
ble/test $Dict'#get '$dict' "apple${_ble_term_FS}banana"' ret=RedYellow # FSを含む見出し
10581060
ble/test $Dict'#get '$dict' apple:banana' ret=__red_yellow__ # コロンを含む見出し
1059-
1061+
10601062
# unset
10611063
$Dict#unset $dict banana
10621064
$Dict#unset $dict apple
@@ -1372,7 +1374,7 @@ function is-global { (builtin readonly "$1"; ! local "$1" 2>/dev/null); }
13721374
ble/util/print [; ble/function#advice/do; ble/util/print ]
13731375
ADVICE_EXIT=99'
13741376
ble/test f1 stdout={A,[,'original quick',],B} exit=99
1375-
1377+
13761378
ble/function#advice remove f1
13771379
ble/test f1 stdout='original' exit=0
13781380
ble/test 'f1 1' stdout='original 1' exit=0
@@ -2048,16 +2050,16 @@ fi
20482050
ble/test "ble/util/chars2keyseq 98 $char 99" ret="b${keyseq}c"
20492051
ble/test "ble/util/keyseq2chars 'b${keyseq}c'; ret=\"\${ret[*]}\"" ret="98 ${3:-$char} 99"
20502052
}
2051-
check1 '7' '\a'
2052-
check1 '8' '\b'
2053-
check1 '9' '\t'
2054-
check1 '10' '\n'
2055-
check1 '11' '\v'
2056-
check1 '12' '\f'
2057-
check1 '13' '\r'
2058-
check1 '27' '\e'
2053+
check1 '7' '\a'
2054+
check1 '8' '\b'
2055+
check1 '9' '\t'
2056+
check1 '10' '\n'
2057+
check1 '11' '\v'
2058+
check1 '12' '\f'
2059+
check1 '13' '\r'
2060+
check1 '27' '\e'
20592061
check1 '127' '\d'
2060-
check1 '92' '\\'
2062+
check1 '92' '\\'
20612063
check1 '28' '\x1c' # workaround bashbug \C-\, \C-\\
20622064
check1 '156' '\x9c' # workaround bashbug \C-\, \C-\\
20632065

make_command.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
umask 022
44
shopt -s nullglob
5+
LC_ALL= LC_COLLATE=C
56

67
function mkd {
78
[[ -d $1 ]] || mkdir -p "$1"

note.txt

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7063,6 +7063,119 @@ bash_tips
70637063
Done (実装ログ)
70647064
-------------------------------------------------------------------------------
70657065

7066+
2023-12-22
7067+
7068+
* util(readonly): work around non-standard collation order (reported by dongxi8) [#D2103]
7069+
https://github.com/akinomyoga/ble.sh/issues/335#issuecomment-1598485650
7070+
7071+
恐らく中国語の collation order は aAbBcC 的な何かになっている。なので小文字
7072+
である事を判定するのに [a-z] とすると失敗する。
7073+
7074+
他に類似の物があるだろうかと思ったが多くの場合には [a-zA-Z] となっているの
7075+
で即座に問題は置きない。但し、diacritical marks のついているアルファベット
7076+
が混ざっている場合は多くあってそれが混入すると困る場合は実は結構ある。そう
7077+
いう場合には対応しきれていない気がする。
7078+
7079+
以下は既に対策済みだった物。よく考えた見たら _ble_util_string_lower_list 等
7080+
の変数に全てのアルファベットを格納しているのだから単にそれを使って判定すれ
7081+
ば良い。26文字の一文字ずつについて判定するのが大変に思うかもしれないが、
7082+
locale の切り替えの方が格段に重いので、寧ろその様にした方が高速である。関数
7083+
として ble/string#isupper, ble/string#islower を用意する事にした。指定した
7084+
引数が一文字かつ大文字または小文字であるかを判定する。
7085+
7086+
./src/util.sh:928:[[ $ch == [A-Z] ]]
7087+
./src/util.sh:931:[[ $ch == [a-z] ]]
7088+
./src/util.sh:955:[[ $ch == [A-Z] ]]
7089+
./src/util.sh:969:[[ $ch == [a-z] ]]
7090+
7091+
これは encode-rot13 での判定である。そして上と同様なので、単に
7092+
ble/string#isupper もしくは ble/string#islower を使えば良い。
7093+
7094+
./lib/keymap.vi.sh:93:[[ $ch == [a-z] ]]
7095+
7096+
これも ble/string#isupper を用いれば良い。
7097+
7098+
./src/edit.sh:745:[[ $c == [A-Z] ]]
7099+
7100+
以下はそれぞれ a または A を除くアルファベットという物だった。範囲の指定の
7101+
仕方を工夫して予期せぬ一致を回避する事にした。範囲指定を修正した。但し、
7102+
LC_COLLATE を調整しない限りは diacritical mark のついている変な物も原理上範
7103+
囲に含まれてしまうが、そもそも一致対象の文字列は declare -p の出力なので変
7104+
な文字が含まれている可能性は排除して良い。
7105+
7106+
./src/util.sh:515:[b-zA-Z]
7107+
./src/util.sh:523:[a-zB-Z]
7108+
7109+
これはそもそも使用文脈が限られているので小文字しかチェックしていなかったが
7110+
大文字が混入しても良いもの。ややこしいので A-Z0-9 も許容する事にする。
7111+
7112+
./src/util.sh:3403:[12])(-[a-z]+)?$'; [[ $msleep_type =~ $rex ]]
7113+
./ble.pp:2131:[-a-z0-9]
7114+
7115+
これは今回の問題箇所。外側で LC_COLLATE を調整する事で対策した。
7116+
7117+
./src/util.sh:7194:[[ $1 != *[a-z]* ]]
7118+
7119+
これらは blehook において lowercase letter が含まれる hook 名は private
7120+
hook として既定で除外する機能の為に使われている物である。blehook でも
7121+
LC_COLLATE の調整を行う事にした。
7122+
7123+
./src/util.hook.sh:140:[[ $pat == *[a-z]* || $flags == *a* ]]
7124+
./src/util.hook.sh:141:[a-z]
7125+
./src/util.hook.sh:164:[[ $pat == *[a-z]* || $flags == *a* ]]
7126+
./src/util.hook.sh:165:[a-z]
7127+
./src/util.hook.sh:209:[[ $flags == *a* ]] || ble/array#remove-by-glob print '_ble_hook_h_*[a-z]
7128+
7129+
これは awk スクリプトに含まれる物だった。他の方法で小文字判定をする事にした。
7130+
7131+
./lib/core-complete.sh:7115:[a-z]
7132+
7133+
これは https:// 等の URL 先頭のスキームを検出するのに使っている。大文字を含
7134+
んでいる物も一致させると C: 等の様な物も一致してしまって不都合である。なの
7135+
で、小文字に限定したい。然し毎回 LC_COLLATE 等を調整するのは面倒なので
7136+
ble/string#match-safe という関数を新しく追加する事にした。
7137+
7138+
./lib/core-syntax.sh:1661:[a-z]
7139+
./lib/core-syntax.sh:1729:[a-z]
7140+
./lib/core-syntax.sh:1754:[a-z]
7141+
7142+
これは簡易判定に用いている物であって後でまたちゃんと判定し直しているので気
7143+
にしなくて良い。
7144+
7145+
./lib/core-syntax.sh:3530:[a-z]+|^\[\[?|^[{}!]'; [[ $tail =~ $rex ]]
7146+
7147+
make_command.sh に関してはスクリプト全体で LC_COLLATE=C を設定すれば良い。
7148+
7149+
./make_command.sh:235:[/[:space:]<>"'\''][a-z]\.txt|/dev/(pts/|pty)[0-9]
7150+
./make_command.sh:303:[A-Z]
7151+
./make_command.sh:491:[A-Z]
7152+
./make_command.sh:492:[_A-Z0-9]
7153+
./make_command.sh:493:[_A-Z0-9]
7154+
./make_command.sh:494:[_A-Z0-9]
7155+
./make_command.sh:496:[_A-Z0-9]
7156+
./make_command.sh:497:[_A-Z0-9]
7157+
./make_command.sh:617:[a-z]
7158+
7159+
実験用スクリプトについては気にしない
7160+
7161+
./memo/D1341.locale-and-casematch.sh:7:[[ A == [a-z] ]]
7162+
./memo/D1341.locale-and-casematch.sh:27:[[ A == [a-z] ]]
7163+
./memo/D1760.fzf-completion-reduced.sh:10:[[a-z0-9.,:-]
7164+
./memo/D1760.fzf-completion-reduced.sh:27:[[a-z0-9.,:-]
7165+
./memo/benchmark/array-is-array.sh:84:[b-zA-Z]
7166+
7167+
これは LC_COLLATE=C を設定する事にした。subshell なので解除しなくて良い。設
7168+
定する時にはコマンドを何も指定せずに変数代入だけで LC_COLLATE=C 2>/dev/null
7169+
としてもエラーを抑制できない。代わりに export LC_COLLATE=C という形で指定す
7170+
る事にした。
7171+
7172+
./lib/test-util.sh:920:[a-zX-Z]
7173+
./lib/test-util.sh:921:[a-zX-Z]
7174+
./lib/test-util.sh:922:[a-zX-Z]
7175+
./lib/test-util.sh:929:[a-zX-Z]
7176+
./lib/test-util.sh:930:[a-zX-Z]
7177+
./lib/test-util.sh:931:[a-zX-Z]
7178+
70667179
2023-12-17
70677180

70687181
* 2023-12-11 ble-import に関する説明を書く? (motivated by Dominiquini) [#D2102]

src/edit.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ function ble/prompt/initialize {
742742
if [[ $WINDIR == [a-zA-Z]:\\* ]]; then
743743
local bsl='\' sl=/
744744
local c=${WINDIR::1} path=${WINDIR:3}
745-
if [[ $c == [A-Z] ]]; then
745+
if ble/string#isupper "$c"; then
746746
if ((_ble_bash>=40000)); then
747747
c=${c,?}
748748
else

src/util.hook.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ function blehook {
187187
local set shopt
188188
ble/base/adjust-BASH_REMATCH
189189
ble/base/.adjust-bash-options set shopt
190+
local LC_ALL= LC_COLLATE=C 2>/dev/null # suppress locale error #D1440
190191

191192
local flags print process
192193
local rex1='^([_a-zA-Z@][_a-zA-Z0-9@]*)$'
@@ -199,6 +200,7 @@ function blehook {
199200
blehook/.print-help
200201
fi
201202
[[ $flags != *E* ]]; local ext=$?
203+
ble/util/unlocal LC_ALL LC_COLLATE 2>/dev/null # suppress locale error #D1440
202204
ble/base/.restore-bash-options set shopt
203205
ble/base/restore-BASH_REMATCH
204206
return "$ext"
@@ -248,6 +250,7 @@ function blehook {
248250
blehook/.print "${print[@]}"
249251
fi
250252

253+
ble/util/unlocal LC_ALL LC_COLLATE 2>/dev/null # suppress locale error #D1440
251254
ble/base/.restore-bash-options set shopt
252255
ble/base/restore-BASH_REMATCH
253256
return "$ext"

0 commit comments

Comments
 (0)