util.sh (245748B)
1 # -*- mode: sh; mode: sh-bash -*- 2 # bash script to be sourced from interactive shell 3 4 #------------------------------------------------------------------------------ 5 # ble.sh options 6 7 function bleopt/.read-arguments/process-option { 8 local name=$1 9 case $name in 10 (help) 11 flags=H$flags ;; 12 (color|color=always) 13 flags=c${flags//[cn]} ;; 14 (color=never) 15 flags=n${flags//[cn]} ;; 16 (color=auto) 17 flags=${flags//[cn]} ;; 18 (color=*) 19 ble/util/print "bleopt: '${name#*=}': unrecognized option argument for '--color'." >&2 20 flags=E$flags ;; 21 (reset) flags=r$flags ;; 22 (changed) flags=u$flags ;; 23 (initialize) flags=I$flags ;; 24 (*) 25 ble/util/print "bleopt: unrecognized long option '--$name'." >&2 26 flags=E$flags ;; 27 esac 28 } 29 30 ## @fn bleopt/expand-variable-pattern pattern opts 31 ## @var[out] ret 32 function bleopt/expand-variable-pattern { 33 ret=() 34 local pattern=$1 35 if [[ $pattern == *@* ]]; then 36 builtin eval -- "ret=(\"\${!${pattern%%@*}@}\")" 37 ble/array#filter-by-glob ret "${pattern//@/?*}" 38 elif [[ ${!pattern+set} || :$opts: == :allow-undefined: ]]; then 39 ret=("$pattern") 40 fi 41 ((${#ret[@]})) 42 } 43 44 ## @fn bleopt/.read-arguments 45 ## @var[out] flags 46 ## H --help 47 ## c --color=always 48 ## n --color=never 49 ## r --reset 50 ## u --changed 51 ## I --initialize 52 ## @var[out] pvars 53 ## @var[out] specs 54 function bleopt/.read-arguments { 55 flags= pvars=() specs=() 56 while (($#)); do 57 local arg=$1; shift 58 case $arg in 59 (--) 60 ble/array#push specs "$@" 61 break ;; 62 (-) 63 ble/util/print "bleopt: unrecognized argument '$arg'." >&2 64 flags=E$flags ;; 65 (--*) 66 bleopt/.read-arguments/process-option "${arg:2}" ;; 67 (-*) 68 local i c 69 for ((i=1;i<${#arg};i++)); do 70 c=${arg:i:1} 71 case $c in 72 (r) bleopt/.read-arguments/process-option reset ;; 73 (u) bleopt/.read-arguments/process-option changed ;; 74 (I) bleopt/.read-arguments/process-option initialize ;; 75 (*) 76 ble/util/print "bleopt: unrecognized option '-$c'." >&2 77 flags=E$flags ;; 78 esac 79 done ;; 80 (*) 81 if local rex='^([_a-zA-Z0-9@]+)(:?=|$)(.*)'; [[ $arg =~ $rex ]]; then 82 local name=${BASH_REMATCH[1]#bleopt_} 83 local var=bleopt_$name 84 local op=${BASH_REMATCH[2]} 85 local value=${BASH_REMATCH[3]} 86 87 # check/expand variable names 88 if [[ $op == ':=' ]]; then 89 if [[ $var == *@* ]]; then 90 ble/util/print "bleopt: \`${var#bleopt_}': wildcard cannot be used in the definition." >&2 91 flags=E$flags 92 continue 93 fi 94 else 95 local ret; bleopt/expand-variable-pattern "$var" 96 97 # obsolete な物は除外 98 var=() 99 local v i=0 100 for v in "${ret[@]}"; do 101 ble/is-function "bleopt/obsolete:${v#bleopt_}" && continue 102 var[i++]=$v 103 done 104 105 # 表示目的で obsolete しかない時は obsolete でも表示 106 [[ ${#var[@]} == 0 ]] && var=("${ret[@]}") 107 108 # 適した物が見つからない場合は失敗 109 if ((${#var[@]}==0)); then 110 ble/util/print "bleopt: option \`$name' not found" >&2 111 flags=E$flags 112 continue 113 fi 114 fi 115 116 if [[ $op ]]; then 117 var=("${var[@]}") # #D1570: WA bash-3.0 ${scal[@]/x} bug 118 if ((_ble_bash>=40300)) && ! shopt -q compat42; then 119 ble/array#push specs "${var[@]/%/"=$value"}" # WA #D1570 #D1751 checked 120 else 121 ble/array#push specs "${var[@]/%/=$value}" # WA #D1570 #D1738 checked 122 fi 123 else 124 ble/array#push pvars "${var[@]}" 125 fi 126 else 127 ble/util/print "bleopt: unrecognized argument '$arg'" >&2 128 flags=E$flags 129 fi ;; 130 esac 131 done 132 } 133 134 function bleopt/changed.predicate { 135 local cur=$1 def=_ble_opt_def_${1#bleopt_} 136 [[ ! ${!def+set} || ${!cur} != "${!def}" ]] 137 } 138 139 ## @fn bleopt args... 140 ## @params[in] args 141 ## args は以下の内の何れかの形式を持つ。 142 ## 143 ## var=value 144 ## 既存の設定変数に値を設定する。 145 ## 設定変数が存在しないときはエラー。 146 ## var:=value 147 ## 設定変数に値を設定する。 148 ## 設定変数が存在しないときは新しく作成する。 149 ## var 150 ## 変数の設定内容を表示する 151 ## 152 function bleopt { 153 local flags pvars specs 154 bleopt/.read-arguments "$@" 155 if [[ $flags == *E* ]]; then 156 return 2 157 elif [[ $flags == *H* ]]; then 158 ble/util/print-lines \ 159 'usage: bleopt [OPTION] [NAME|NAME=VALUE|NAME:=VALUE]...' \ 160 ' Set ble.sh options. Without arguments, this prints all the settings.' \ 161 '' \ 162 ' Options' \ 163 ' --help Print this help.' \ 164 ' -r, --reset Reset options to the default values' \ 165 ' -I, --initialize Re-initialize settings' \ 166 ' -u, --changed Only select changed options' \ 167 ' --color[=always|never|auto]' \ 168 ' Change color settings.' \ 169 '' \ 170 ' Arguments' \ 171 ' NAME Print the value of the option.' \ 172 ' NAME=VALUE Set the value to the option.' \ 173 ' NAME:=VALUE Set or create the value to the option.' \ 174 '' \ 175 ' NAME can contain "@" as a wildcard.' \ 176 '' 177 return 0 178 fi 179 180 if ((${#pvars[@]}==0&&${#specs[@]}==0)); then 181 local var ip=0 182 for var in "${!bleopt_@}"; do 183 ble/is-function "bleopt/obsolete:${var#bleopt_}" && continue 184 pvars[ip++]=$var 185 done 186 fi 187 188 [[ $flags == *u* ]] && 189 ble/array#filter pvars bleopt/changed.predicate 190 191 # --reset: pvars を全て既定値の設定に読み替える 192 if [[ $flags == *r* ]]; then 193 local var 194 for var in "${pvars[@]}"; do 195 local name=${var#bleopt_} 196 ble/is-function bleopt/obsolete:"$name" && continue 197 local def=_ble_opt_def_$name 198 [[ ${!def+set} && ${!var-} != "${!def}" ]] && 199 ble/array#push specs "$var=${!def}" 200 done 201 pvars=() 202 elif [[ $flags == *I* ]]; then 203 local var 204 for var in "${pvars[@]}"; do 205 bleopt/reinitialize "${var#bleopt_}" 206 done 207 pvars=() 208 fi 209 210 if ((${#specs[@]})); then 211 local spec 212 for spec in "${specs[@]}"; do 213 local var=${spec%%=*} value=${spec#*=} 214 [[ ${!var+set} && ${!var} == "$value" ]] && continue 215 if ble/is-function bleopt/check:"${var#bleopt_}"; then 216 local bleopt_source=${BASH_SOURCE[1]} 217 local bleopt_lineno=${BASH_LINENO[0]} 218 if ! bleopt/check:"${var#bleopt_}"; then 219 flags=E$flags 220 continue 221 fi 222 fi 223 builtin eval -- "$var=\"\$value\"" 224 done 225 fi 226 227 if ((${#pvars[@]})); then 228 # 着色 229 local sgr0= sgr1= sgr2= sgr3= 230 if [[ $flags == *c* || $flags != *n* && -t 1 ]]; then 231 local ret 232 ble/color/face2sgr command_function; sgr1=$ret 233 ble/color/face2sgr syntax_varname; sgr2=$ret 234 ble/color/face2sgr syntax_quoted; sgr3=$ret 235 sgr0=$_ble_term_sgr0 236 fi 237 238 local var 239 for var in "${pvars[@]}"; do 240 local ret 241 ble/string#quote-word "${!var}" sgrq="$sgr3":sgr0="$sgr0" 242 ble/util/print "${sgr1}bleopt$sgr0 ${sgr2}${var#bleopt_}$sgr0=$ret" 243 done 244 fi 245 246 [[ $flags != *E* ]] 247 } 248 249 function bleopt/declare/.check-renamed-option { 250 var=bleopt_$2 251 252 local sgr0= sgr1= sgr2= sgr3= 253 if [[ -t 2 ]]; then 254 sgr0=$_ble_term_sgr0 255 sgr1=${_ble_term_setaf[2]} 256 sgr2=${_ble_term_setaf[1]}$_ble_term_bold 257 sgr3=${_ble_term_setaf[4]}$_ble_term_bold 258 fi 259 260 local locate=$sgr1${BASH_SOURCE[3]-'(stdin)'}:${BASH_LINENO[2]}$sgr0 261 ble/util/print "$locate (bleopt): The option '$sgr2$1$sgr0' has been renamed. Please use '$sgr3$2$sgr0' instead." >&2 262 if ble/is-function bleopt/check:"$2"; then 263 bleopt/check:"$2" 264 return "$?" 265 fi 266 return 0 267 } 268 function bleopt/declare { 269 local type=$1 name=bleopt_$2 default_value=$3 270 # local set=${!name+set} value=${!name-} 271 case $type in 272 (-o) 273 builtin eval -- "$name='[obsolete: renamed to $3]'" 274 builtin eval -- "function bleopt/check:$2 { bleopt/declare/.check-renamed-option $2 $3; }" 275 builtin eval -- "function bleopt/obsolete:$2 { :; }" ;; 276 (-n) 277 builtin eval -- "_ble_opt_def_$2=\$3" 278 builtin eval -- ": \"\${$name:=\$default_value}\"" ;; 279 (*) 280 builtin eval -- "_ble_opt_def_$2=\$3" 281 builtin eval -- ": \"\${$name=\$default_value}\"" ;; 282 esac 283 return 0 284 } 285 function bleopt/reinitialize { 286 local name=$1 287 local defname=_ble_opt_def_$name 288 local varname=bleopt_$name 289 [[ ${!defname+set} ]] || return 1 290 [[ ${!varname} == "${!defname}" ]] && return 0 291 ble/is-function bleopt/obsolete:"$name" && return 0 292 ble/is-function bleopt/check:"$name" || return 0 293 294 # 一旦値を既定値に戻して改めてチェックを行う。 295 local value=${!varname} 296 builtin eval -- "$varname=\$$defname" 297 bleopt/check:"$name" && 298 builtin eval "$varname=\$value" 299 } 300 301 ## @bleopt input_encoding 302 bleopt/declare -n input_encoding UTF-8 303 function bleopt/check:input_encoding { 304 if ! ble/is-function "ble/encoding:$value/decode"; then 305 ble/util/print "bleopt: Invalid value input_encoding='$value'." \ 306 "A function 'ble/encoding:$value/decode' is not defined." >&2 307 return 1 308 elif ! ble/is-function "ble/encoding:$value/b2c"; then 309 ble/util/print "bleopt: Invalid value input_encoding='$value'." \ 310 "A function 'ble/encoding:$value/b2c' is not defined." >&2 311 return 1 312 elif ! ble/is-function "ble/encoding:$value/c2bc"; then 313 ble/util/print "bleopt: Invalid value input_encoding='$value'." \ 314 "A function 'ble/encoding:$value/c2bc' is not defined." >&2 315 return 1 316 elif ! ble/is-function "ble/encoding:$value/generate-binder"; then 317 ble/util/print "bleopt: Invalid value input_encoding='$value'." \ 318 "A function 'ble/encoding:$value/generate-binder' is not defined." >&2 319 return 1 320 elif ! ble/is-function "ble/encoding:$value/is-intermediate"; then 321 ble/util/print "bleopt: Invalid value input_encoding='$value'." \ 322 "A function 'ble/encoding:$value/is-intermediate' is not defined." >&2 323 return 1 324 fi 325 326 # Note: ble/encoding:$value/clear は optional な設定である。 327 328 if [[ $bleopt_input_encoding != "$value" ]]; then 329 local bleopt_input_encoding=$value 330 ble/decode/rebind 331 fi 332 return 0 333 } 334 335 ## @bleopt internal_stackdump_enabled 336 ## エラーが起こった時に関数呼出の構造を標準エラー出力に出力するかどうかを制御する。 337 ## 算術式評価によって非零の値になる場合にエラーを出力する。 338 ## それ以外の場合にはエラーを出力しない。 339 bleopt/declare -v internal_stackdump_enabled 0 340 341 ## @bleopt openat_base 342 ## bash-4.1 未満で exec {var}>foo が使えない時に ble.sh で内部的に fd を割り当てる。 343 ## この時の fd の base を指定する。bleopt_openat_base, bleopt_openat_base+1, ... 344 ## という具合に順番に使用される。既定値は 30 である。 345 bleopt/declare -n openat_base 30 346 347 ## @bleopt pager 348 bleopt/declare -v pager '' 349 350 ## @bleopt editor 351 bleopt/declare -v editor '' 352 353 shopt -s checkwinsize 354 355 #------------------------------------------------------------------------------ 356 # util 357 358 function ble/util/setexit { return "$1"; } 359 360 ## @var _ble_util_upvar_setup 361 ## @var _ble_util_upvar 362 ## 363 ## これらの変数は関数を定義する時に [-v varname] の引数を認識させ、 364 ## 関数の結果を格納する変数名を外部から指定できるようにするのに用いる。 365 ## 使用する際は関数を以下の様に記述する。既定の格納先変数は ret となる。 366 ## 367 ## function MyFunction { 368 ## eval "$_ble_util_upvar_setup" 369 ## 370 ## ret=... # 処理を行い、変数 ret に結果を格納するコード 371 ## # (途中で return などすると正しく動かない事に注意) 372 ## 373 ## eval "$_ble_util_upvar" 374 ## } 375 ## 376 ## 既定の格納先変数を別の名前 (以下の例では arg) にする場合は次の様にする。 377 ## 378 ## function MyFunction { 379 ## eval "${_ble_util_upvar_setup//ret/arg}" 380 ## 381 ## arg=... # 処理を行い、変数 arg に結果を格納するコード 382 ## 383 ## eval "${_ble_util_upvar//ret/arg}" 384 ## } 385 ## 386 _ble_util_upvar_setup='local var=ret ret; [[ $1 == -v ]] && var=$2 && shift 2' 387 _ble_util_upvar='local "${var%%\[*\]}" && ble/util/upvar "$var" "$ret"' 388 if ((_ble_bash>=50000)); then 389 function ble/util/unlocal { 390 if shopt -q localvar_unset; then 391 shopt -u localvar_unset 392 builtin unset -v "$@" 393 shopt -s localvar_unset 394 else 395 builtin unset -v "$@" 396 fi 397 } 398 function ble/util/upvar { ble/util/unlocal "${1%%\[*\]}" && builtin eval "$1=\"\$2\""; } 399 function ble/util/uparr { ble/util/unlocal "$1" && builtin eval "$1=(\"\${@:2}\")"; } 400 else 401 function ble/util/unlocal { builtin unset -v "$@"; } 402 function ble/util/upvar { builtin unset -v "${1%%\[*\]}" && builtin eval "$1=\"\$2\""; } 403 function ble/util/uparr { builtin unset -v "$1" && builtin eval "$1=(\"\${@:2}\")"; } 404 fi 405 406 function ble/util/save-vars { 407 local __ble_name __ble_prefix=$1; shift 408 for __ble_name; do 409 if ble/is-array "$__ble_name"; then 410 builtin eval "$__ble_prefix$__ble_name=(\"\${$__ble_name[@]}\")" 411 else 412 builtin eval "$__ble_prefix$__ble_name=\"\$$__ble_name\"" 413 fi 414 done 415 } 416 function ble/util/restore-vars { 417 local __ble_name __ble_prefix=$1; shift 418 for __ble_name; do 419 if ble/is-array "$__ble_prefix$__ble_name"; then 420 # Note: bash-4.2 以下では set -u で空配列に対する "${arr[@]}" が失敗す 421 # るので ${arr[@]+"${arr[@]}"} とする。 422 builtin eval "$__ble_name=(\${$__ble_prefix$__ble_name[@]+\"\${$__ble_prefix$__ble_name[@]}\"})" 423 else 424 builtin eval "$__ble_name=\"\${$__ble_prefix$__ble_name-}\"" 425 fi 426 done 427 } 428 429 # 430 # variable, array and strings 431 # 432 433 ## @fn ble/variable#get-attr varname 434 ## 指定した変数の属性を取得します。 435 ## @var[out] attr 436 if ((_ble_bash>=40400)); then 437 function ble/variable#get-attr { 438 if [[ $1 == -v ]]; then 439 builtin eval -- "$2=\${!3@a}" 440 else 441 attr=${!1@a} 442 fi 443 } 444 function ble/variable#has-attr { [[ ${!1@a} == *["$2"]* ]]; } 445 else 446 function ble/variable#get-attr { 447 if [[ $1 == -v ]]; then 448 local __ble_var=$2 __ble_tmp=$3 449 else 450 local __ble_var=attr __ble_tmp=$1 451 fi 452 ble/util/assign __ble_tmp 'declare -p "$__ble_tmp" 2>/dev/null' 453 local rex='^declare -([a-zA-Z]*)'; [[ $__ble_tmp =~ $rex ]] 454 builtin eval -- "$__ble_var=\${BASH_REMATCH[1]-}" 455 return 0 456 } 457 function ble/variable#has-attr { 458 local __ble_tmp=$1 459 ble/util/assign __ble_tmp 'declare -p "$__ble_tmp" 2>/dev/null' 460 local rex='^declare -([a-zA-Z]*)' 461 [[ $__ble_tmp =~ $rex && ${BASH_REMATCH[1]} == *["$2"]* ]] 462 } 463 fi 464 function ble/is-inttype { ble/variable#has-attr "$1" i; } 465 function ble/is-readonly { ble/variable#has-attr "$1" r; } 466 function ble/is-transformed { ble/variable#has-attr "$1" luc; } 467 468 function ble/variable#is-declared { [[ ${!1+set} ]] || declare -p "$1" &>/dev/null; } 469 function ble/variable#is-global/.test { ! local "$1"; } 470 function ble/variable#is-global { 471 (builtin readonly "$1"; ble/variable#is-global/.test "$1") 2>/dev/null 472 } 473 function ble/variable#copy-state { 474 local src=$1 dst=$2 475 if [[ ${!src+set} ]]; then 476 builtin eval -- "$dst=\${$src}" 477 else 478 builtin unset -v "$dst[0]" 2>/dev/null || builtin unset -v "$dst" 479 fi 480 } 481 482 _ble_array_prototype=() 483 function ble/array#reserve-prototype { 484 local n=$1 i 485 for ((i=${#_ble_array_prototype[@]};i<n;i++)); do 486 _ble_array_prototype[i]= 487 done 488 } 489 490 ## @fn ble/is-array arr 491 ## 492 ## Note: これに関しては様々な実現方法が考えられるが大体余りうまく動かない。 493 ## 494 ## * ! declare +a arr だと現在の関数のローカル変数の判定になってしまう。 495 ## * bash-4.2 以降では ! declare -g +a arr を使えるが、 496 ## これだと呼び出し元の関数で定義されている配列が見えない。 497 ## というか現在のスコープの配列も見えない。 498 ## * 今の所は compgen -A arrayvar を用いているが、 499 ## この方法だと bash-4.3 以降では連想配列も配列と判定され、 500 ## bash-4.2 以下では連想配列は配列とはならない。 501 if ((_ble_bash>=40400)); then 502 function ble/is-array { [[ ${!1@a} == *a* ]]; } 503 function ble/is-assoc { [[ ${!1@a} == *A* ]]; } 504 else 505 function ble/is-array { 506 local "decl$1" 507 ble/util/assign "decl$1" "declare -p $1" 2>/dev/null || return 1 508 local rex='^declare -[b-zA-Z]*a' 509 builtin eval "[[ \$decl$1 =~ \$rex ]]" 510 } 511 function ble/is-assoc { 512 local "decl$1" 513 ble/util/assign "decl$1" "declare -p $1" 2>/dev/null || return 1 514 local rex='^declare -[a-zB-Z]*A' 515 builtin eval "[[ \$decl$1 =~ \$rex ]]" 516 } 517 ((_ble_bash>=40000)) || 518 function ble/is-assoc { false; } 519 fi 520 521 ## @fn ble/array#set arr value... 522 ## 配列に値を設定します。 523 ## Bash 4.4 で arr2=("${arr1[@]}") が遅い問題を回避する為の関数です。 524 function ble/array#set { builtin eval "$1=(\"\${@:2}\")"; } 525 526 ## @fn ble/array#push arr value... 527 if ((_ble_bash>=40000)); then 528 function ble/array#push { 529 builtin eval "$1+=(\"\${@:2}\")" 530 } 531 elif ((_ble_bash>=30100)); then 532 function ble/array#push { 533 # Note (workaround Bash 3.1/3.2 bug): #D1198 534 # 何故か a=("${@:2}") は IFS に特別な物が設定されていると 535 # "${*:2}" と同じ振る舞いになってしまう。 536 IFS=$_ble_term_IFS builtin eval "$1+=(\"\${@:2}\")" 537 } 538 else 539 function ble/array#push { 540 while (($#>=2)); do 541 builtin eval -- "$1[\${#$1[@]}]=\"\$2\"" 542 set -- "$1" "${@:3}" 543 done 544 } 545 fi 546 ## @fn ble/array#pop arr 547 ## @var[out] ret 548 function ble/array#pop { 549 builtin eval "local i$1=\$((\${#$1[@]}-1))" 550 if ((i$1>=0)); then 551 builtin eval "ret=\${$1[i$1]}" 552 builtin unset -v "$1[i$1]" 553 return 0 554 else 555 ret= 556 return 1 557 fi 558 } 559 ## @fn ble/array#unshift arr value... 560 function ble/array#unshift { 561 builtin eval -- "$1=(\"\${@:2}\" \"\${$1[@]}\")" 562 } 563 ## @fn ble/array#shift arr count 564 function ble/array#shift { 565 # Note: Bash 4.3 以下では ${arr[@]:${2:-1}} が offset='${2' 566 # length='-1' に解釈されるので、先に算術式展開させる。 567 builtin eval -- "$1=(\"\${$1[@]:$((${2:-1}))}\")" 568 } 569 ## @fn ble/array#reverse arr 570 function ble/array#reverse { 571 builtin eval " 572 set -- \"\${$1[@]}\"; $1=() 573 local e$1 i$1=\$# 574 for e$1; do $1[--i$1]=\"\$e$1\"; done" 575 } 576 577 ## @fn ble/array#insert-at arr index elements... 578 function ble/array#insert-at { 579 builtin eval "$1=(\"\${$1[@]::$2}\" \"\${@:3}\" \"\${$1[@]:$2}\")" 580 } 581 ## @fn ble/array#insert-after arr needle elements... 582 function ble/array#insert-after { 583 local _ble_local_script=' 584 local iARR=0 eARR aARR= 585 for eARR in "${ARR[@]}"; do 586 ((iARR++)) 587 [[ $eARR == "$2" ]] && aARR=iARR && break 588 done 589 [[ $aARR ]] && ble/array#insert-at "$1" "$aARR" "${@:3}" 590 '; builtin eval -- "${_ble_local_script//ARR/$1}" 591 } 592 ## @fn ble/array#insert-before arr needle elements... 593 function ble/array#insert-before { 594 local _ble_local_script=' 595 local iARR=0 eARR aARR= 596 for eARR in "${ARR[@]}"; do 597 [[ $eARR == "$2" ]] && aARR=iARR && break 598 ((iARR++)) 599 done 600 [[ $aARR ]] && ble/array#insert-at "$1" "$aARR" "${@:3}" 601 '; builtin eval -- "${_ble_local_script//ARR/$1}" 602 } 603 ## @fn ble/array#filter arr predicate 604 ## @param[in] predicate 605 ## When a function name is specified, the target string is passed 606 ## to the function as the first argument. Otherwise, the value of 607 ## PREDICATE is treated as a command string where the argument can 608 ## be referenced as $1. 609 function ble/array#filter/.eval { 610 builtin eval -- "$_ble_local_predicate_cmd" 611 } 612 function ble/array#filter { 613 local _ble_local_predicate=$2 614 if [[ $2 == *'$'* ]] || ! ble/is-function "$2"; then 615 _ble_local_predicate=ble/array#filter/.eval 616 _ble_local_predicate_cmd=$2 617 fi 618 619 local _ble_local_script=' 620 local -a aARR=() eARR 621 for eARR in "${ARR[@]}"; do 622 "$_ble_local_predicate" "$eARR" && ble/array#push "aARR" "$eARR" 623 done 624 ARR=("${aARR[@]}") 625 '; builtin eval -- "${_ble_local_script//ARR/$1}" 626 } 627 function ble/array#filter/not.predicate { ! "$_ble_local_pred" "$1"; } 628 function ble/array#remove-if { 629 local _ble_local_pred=$2 630 ble/array#filter "$1" ble/array#filter/not.predicate 631 } 632 ## @fn ble/array#filter-by-regex arr regex 633 function ble/array#filter/regex.predicate { [[ $1 =~ $_ble_local_rex ]]; } 634 function ble/array#filter-by-regex { 635 local _ble_local_rex=$2 636 local LC_ALL= LC_COLLATE=C 2>/dev/null 637 ble/array#filter "$1" ble/array#filter/regex.predicate 638 ble/util/unlocal LC_COLLATE LC_ALL 2>/dev/null 639 } 640 function ble/array#remove-by-regex { 641 local _ble_local_rex=$2 642 local LC_ALL= LC_COLLATE=C 2>/dev/null 643 ble/array#remove-if "$1" ble/array#filter/regex.predicate 644 ble/util/unlocal LC_COLLATE LC_ALL 2>/dev/null 645 } 646 function ble/array#filter/glob.predicate { [[ $1 == $_ble_local_glob ]]; } 647 function ble/array#filter-by-glob { 648 local _ble_local_glob=$2 649 local LC_ALL= LC_COLLATE=C 2>/dev/null 650 ble/array#filter "$1" ble/array#filter/glob.predicate 651 ble/util/unlocal LC_COLLATE LC_ALL 2>/dev/null 652 } 653 function ble/array#remove-by-glob { 654 local _ble_local_glob=$2 655 local LC_ALL= LC_COLLATE=C 2>/dev/null 656 ble/array#remove-if "$1" ble/array#filter/glob.predicate 657 ble/util/unlocal LC_COLLATE LC_ALL 2>/dev/null 658 } 659 ## @fn ble/array#remove arr element 660 function ble/array#remove/.predicate { [[ $1 != "$_ble_local_value" ]]; } 661 function ble/array#remove { 662 local _ble_local_value=$2 663 ble/array#filter "$1" ble/array#remove/.predicate 664 } 665 ## @fn ble/array#index arr needle 666 ## @var[out] ret 667 function ble/array#index { 668 local _ble_local_script=' 669 local eARR iARR=0 670 for eARR in "${ARR[@]}"; do 671 if [[ $eARR == "$2" ]]; then ret=$iARR; return 0; fi 672 ((++iARR)) 673 done 674 ret=-1; return 1 675 '; builtin eval -- "${_ble_local_script//ARR/$1}" 676 } 677 ## @fn ble/array#last-index arr needle 678 ## @var[out] ret 679 function ble/array#last-index { 680 local _ble_local_script=' 681 local eARR iARR=${#ARR[@]} 682 while ((iARR--)); do 683 [[ ${ARR[iARR]} == "$2" ]] && { ret=$iARR; return 0; } 684 done 685 ret=-1; return 1 686 '; builtin eval -- "${_ble_local_script//ARR/$1}" 687 } 688 ## @fn ble/array#remove-at arr index 689 function ble/array#remove-at { 690 local _ble_local_script=' 691 builtin unset -v "ARR[$2]" 692 ARR=("${ARR[@]}") 693 '; builtin eval -- "${_ble_local_script//ARR/$1}" 694 } 695 function ble/array#fill-range { 696 ble/array#reserve-prototype "$(($3-$2))" 697 local _ble_script=' 698 local -a sARR; sARR=("${_ble_array_prototype[@]::$3-$2}") 699 ARR=("${ARR[@]::$2}" "${sARR[@]/#/$4}" "${ARR[@]:$3}")' # WA #D1570 #D1738 checked 700 ((_ble_bash>=40300)) && ! shopt -q compat42 && 701 _ble_script=${_ble_script//'$4'/'"$4"'} 702 builtin eval -- "${_ble_script//ARR/$1}" 703 } 704 ## @fn ble/idict#replace arr needle [replacement] 705 ## needle に一致する要素を全て replacement に置換します。 706 ## replacement が指定されていない時は該当要素を unset します。 707 ## @var[in] arr 708 ## @var[in] needle 709 ## @var[in,opt] replacement 710 function ble/idict#replace { 711 local _ble_local_script=' 712 local iARR=0 extARR=1 713 for iARR in "${!ARR[@]}"; do 714 [[ ${ARR[iARR]} == "$2" ]] || continue 715 extARR=0 716 if (($#>=3)); then 717 ARR[iARR]=$3 718 else 719 builtin unset -v '\''ARR[iARR]'\'' 720 fi 721 done 722 return "$extARR" 723 '; builtin eval -- "${_ble_local_script//ARR/$1}" 724 } 725 726 function ble/idict#copy { 727 local _ble_script=' 728 '$1'=() 729 local i'$1$2' 730 for i'$1$2' in "${!'$2'[@]}"; do 731 '$1'[i'$1$2']=${'$2'[i'$1$2']} 732 done' 733 builtin eval -- "$_ble_script" 734 } 735 736 _ble_string_prototype=' ' 737 function ble/string#reserve-prototype { 738 local n=$1 c 739 for ((c=${#_ble_string_prototype};c<n;c*=2)); do 740 _ble_string_prototype=$_ble_string_prototype$_ble_string_prototype 741 done 742 } 743 744 ## @fn ble/string#repeat str count 745 ## @param[in] str 746 ## @param[in] count 747 ## @var[out] ret 748 function ble/string#repeat { 749 ble/string#reserve-prototype "$2" 750 ret=${_ble_string_prototype::$2} 751 ret=${ret// /"$1"} 752 } 753 754 ## @fn ble/string#common-prefix a b 755 ## @param[in] a b 756 ## @var[out] ret 757 function ble/string#common-prefix { 758 local a=$1 b=$2 759 ((${#a}>${#b})) && local a=$b b=$a 760 b=${b::${#a}} 761 if [[ $a == "$b" ]]; then 762 ret=$a 763 return 0 764 fi 765 766 # l <= 解 < u, (${a:u}: 一致しない, ${a:l} 一致する) 767 local l=0 u=${#a} m 768 while ((l+1<u)); do 769 ((m=(l+u)/2)) 770 if [[ ${a::m} == "${b::m}" ]]; then 771 ((l=m)) 772 else 773 ((u=m)) 774 fi 775 done 776 777 ret=${a::l} 778 } 779 780 ## @fn ble/string#common-suffix a b 781 ## @param[in] a b 782 ## @var[out] ret 783 function ble/string#common-suffix { 784 local a=$1 b=$2 785 ((${#a}>${#b})) && local a=$b b=$a 786 b=${b:${#b}-${#a}} 787 if [[ $a == "$b" ]]; then 788 ret=$a 789 return 0 790 fi 791 792 # l < 解 <= u, (${a:l}: 一致しない, ${a:u} 一致する) 793 local l=0 u=${#a} m 794 while ((l+1<u)); do 795 ((m=(l+u+1)/2)) 796 if [[ ${a:m} == "${b:m}" ]]; then 797 ((u=m)) 798 else 799 ((l=m)) 800 fi 801 done 802 803 ret=${a:u} 804 } 805 806 ## @fn ble/string#split arr sep str... 807 ## 文字列を分割します。 808 ## 空白類を分割に用いた場合は、空要素は削除されます。 809 ## 810 ## @param[out] arr 分割した文字列を格納する配列名を指定します。 811 ## @param[in] sep 分割に使用する文字を指定します。 812 ## @param[in] str 分割する文字列を指定します。 813 ## 814 function ble/string#split { 815 local IFS=$2 816 if [[ -o noglob ]]; then 817 # Note: 末尾の sep が無視されない様に、末尾に手で sep を 1 個追加している。 818 builtin eval "$1=(\$3\$2)" 819 else 820 set -f 821 builtin eval "$1=(\$3\$2)" 822 set +f 823 fi 824 } 825 function ble/string#split-words { 826 local IFS=$_ble_term_IFS 827 if [[ -o noglob ]]; then 828 builtin eval "$1=(\$2)" 829 else 830 set -f 831 builtin eval "$1=(\$2)" 832 set +f 833 fi 834 } 835 ## @fn ble/string#split-lines arr text 836 ## 文字列を行に分割します。空行も省略されません。 837 ## 838 ## @param[out] arr 分割した文字列を格納する配列名を指定します。 839 ## @param[in] text 分割する文字列を指定します。 840 ## @var[out] ret 841 ## 842 if ((_ble_bash>=40000)); then 843 function ble/string#split-lines { 844 mapfile -t "$1" <<< "$2" 845 } 846 else 847 function ble/string#split-lines { 848 ble/util/mapfile "$1" <<< "$2" 849 } 850 fi 851 ## @fn ble/string#count-char text chars 852 ## @param[in] text 853 ## @param[in] chars 854 ## 検索対象の文字の集合を指定します。 855 ## @var[out] ret 856 function ble/string#count-char { 857 local text=$1 char=$2 858 text=${text//[!"$char"]} 859 ret=${#text} 860 } 861 862 ## @fn ble/string#count-string text string 863 ## @var[out] ret 864 function ble/string#count-string { 865 local text=${1//"$2"} 866 ((ret=(${#1}-${#text})/${#2})) 867 } 868 869 ## @fn ble/string#index-of text needle [n] 870 ## @param[in] text 871 ## @param[in] needle 872 ## @param[in] n 873 ## この引数を指定したとき n 番目の一致を検索します。 874 ## @var[out] ret 875 ## 一致した場合に見つかった位置を返します。 876 ## 見つからなかった場合に -1 を返します。 877 ## @exit 878 ## 一致した場合に成功し、見つからなかった場合に失敗します。 879 function ble/string#index-of { 880 local haystack=$1 needle=$2 count=${3:-1} 881 ble/string#repeat '*"$needle"' "$count"; local pattern=$ret 882 builtin eval "local transformed=\${haystack#$pattern}" 883 ((ret=${#haystack}-${#transformed}-${#needle}, 884 ret<0&&(ret=-1),ret>=0)) 885 } 886 887 ## @fn ble/string#last-index-of text needle [n] 888 ## @param[in] text 889 ## @param[in] needle 890 ## @param[in] n 891 ## この引数を指定したとき n 番目の一致を検索します。 892 ## @var[out] ret 893 function ble/string#last-index-of { 894 local haystack=$1 needle=$2 count=${3:-1} 895 ble/string#repeat '"$needle"*' "$count"; local pattern=$ret 896 builtin eval "local transformed=\${haystack%$pattern}" 897 if [[ $transformed == "$haystack" ]]; then 898 ret=-1 899 else 900 ret=${#transformed} 901 fi 902 ((ret>=0)) 903 } 904 905 ## @fn ble/string#toggle-case text 906 ## @fn ble/string#toupper text 907 ## @fn ble/string#tolower text 908 ## @param[in] text 909 ## @var[out] ret 910 _ble_util_string_lower_list=abcdefghijklmnopqrstuvwxyz 911 _ble_util_string_upper_list=ABCDEFGHIJKLMNOPQRSTUVWXYZ 912 function ble/string#toggle-case.impl { 913 local LC_ALL= LC_COLLATE=C 914 local text=$1 ch i 915 local -a buff 916 for ((i=0;i<${#text};i++)); do 917 ch=${text:i:1} 918 if [[ $ch == [A-Z] ]]; then 919 ch=${_ble_util_string_upper_list%%"$ch"*} 920 ch=${_ble_util_string_lower_list:${#ch}:1} 921 elif [[ $ch == [a-z] ]]; then 922 ch=${_ble_util_string_lower_list%%"$ch"*} 923 ch=${_ble_util_string_upper_list:${#ch}:1} 924 fi 925 ble/array#push buff "$ch" 926 done 927 IFS= builtin eval 'ret="${buff[*]-}"' 928 } 929 function ble/string#toggle-case { 930 ble/string#toggle-case.impl "$1" 2>/dev/null # suppress locale error #D1440 931 } 932 ## @fn ble/string#tolower text 933 ## @fn ble/string#toupper text 934 ## @var[out] ret 935 if ((_ble_bash>=40000)); then 936 function ble/string#tolower { ret=${1,,}; } 937 function ble/string#toupper { ret=${1^^}; } 938 else 939 function ble/string#tolower.impl { 940 local LC_ALL= LC_COLLATE=C 941 local i text=$1 ch 942 local -a buff=() 943 for ((i=0;i<${#text};i++)); do 944 ch=${text:i:1} 945 if [[ $ch == [A-Z] ]]; then 946 ch=${_ble_util_string_upper_list%%"$ch"*} 947 ch=${_ble_util_string_lower_list:${#ch}:1} 948 fi 949 ble/array#push buff "$ch" 950 done 951 IFS= builtin eval 'ret="${buff[*]-}"' 952 } 953 function ble/string#toupper.impl { 954 local LC_ALL= LC_COLLATE=C 955 local i text=$1 ch 956 local -a buff=() 957 for ((i=0;i<${#text};i++)); do 958 ch=${text:i:1} 959 if [[ $ch == [a-z] ]]; then 960 ch=${_ble_util_string_lower_list%%"$ch"*} 961 ch=${_ble_util_string_upper_list:${#ch}:1} 962 fi 963 ble/array#push buff "$ch" 964 done 965 IFS= builtin eval 'ret="${buff[*]-}"' 966 } 967 function ble/string#tolower { 968 ble/string#tolower.impl "$1" 2>/dev/null # suppress locale error #D1440 969 } 970 function ble/string#toupper { 971 ble/string#toupper.impl "$1" 2>/dev/null # suppress locale error #D1440 972 } 973 fi 974 975 function ble/string#capitalize { 976 local tail=$1 977 978 # prefix 979 local rex='^[^a-zA-Z0-9]*' 980 [[ $tail =~ $rex ]] 981 local out=$BASH_REMATCH 982 tail=${tail:${#BASH_REMATCH}} 983 984 # words 985 rex='^[a-zA-Z0-9]+[^a-zA-Z0-9]*' 986 while [[ $tail =~ $rex ]]; do 987 local rematch=$BASH_REMATCH 988 ble/string#toupper "${rematch::1}"; out=$out$ret 989 ble/string#tolower "${rematch:1}" ; out=$out$ret 990 tail=${tail:${#rematch}} 991 done 992 ret=$out$tail 993 } 994 995 ## @fn ble/string#trim text 996 ## @var[out] ret 997 function ble/string#trim { 998 ret=$1 999 local rex=$'^[ \t\n]+' 1000 [[ $ret =~ $rex ]] && ret=${ret:${#BASH_REMATCH}} 1001 local rex=$'[ \t\n]+$' 1002 [[ $ret =~ $rex ]] && ret=${ret::${#ret}-${#BASH_REMATCH}} 1003 } 1004 ## @fn ble/string#ltrim text 1005 ## @var[out] ret 1006 function ble/string#ltrim { 1007 ret=$1 1008 local rex=$'^[ \t\n]+' 1009 [[ $ret =~ $rex ]] && ret=${ret:${#BASH_REMATCH}} 1010 } 1011 ## @fn ble/string#rtrim text 1012 ## @var[out] ret 1013 function ble/string#rtrim { 1014 ret=$1 1015 local rex=$'[ \t\n]+$' 1016 [[ $ret =~ $rex ]] && ret=${ret::${#ret}-${#BASH_REMATCH}} 1017 } 1018 1019 ## @fn ble/string#escape-characters text chars1 [chars2] 1020 ## @param[in] text 1021 ## @param[in] chars1 1022 ## @param[in,opt] chars2 1023 ## @var[out] ret 1024 if ((_ble_bash>=50200)); then 1025 function ble/string#escape-characters { 1026 ret=$1 1027 if [[ $ret == *["$2"]* ]]; then 1028 if [[ ! $3 ]]; then 1029 local patsub_replacement= 1030 shopt -q patsub_replacement && patsub_replacement=1 1031 shopt -s patsub_replacement 1032 ret=${ret//["$2"]/\\&} # #D1738 patsub_replacement 1033 [[ $patsub_replacement ]] || shopt -u patsub_replacement 1034 else 1035 local chars1=$2 chars2=${3:-$2} 1036 local i n=${#chars1} a b 1037 for ((i=0;i<n;i++)); do 1038 a=${chars1:i:1} b=\\${chars2:i:1} ret=${ret//"$a"/"$b"} 1039 done 1040 fi 1041 fi 1042 } 1043 else 1044 function ble/string#escape-characters { 1045 ret=$1 1046 if [[ $ret == *["$2"]* ]]; then 1047 local chars1=$2 chars2=${3:-$2} 1048 local i n=${#chars1} a b 1049 for ((i=0;i<n;i++)); do 1050 a=${chars1:i:1} b=\\${chars2:i:1} ret=${ret//"$a"/"$b"} 1051 done 1052 fi 1053 } 1054 fi 1055 1056 1057 ## @fn ble/string#escape-for-sed-regex text 1058 ## @fn ble/string#escape-for-awk-regex text 1059 ## @fn ble/string#escape-for-extended-regex text 1060 ## @fn ble/string#escape-for-bash-glob text 1061 ## @fn ble/string#escape-for-bash-single-quote text 1062 ## @fn ble/string#escape-for-bash-double-quote text 1063 ## @fn ble/string#escape-for-bash-escape-string text 1064 ## @param[in] text 1065 ## @var[out] ret 1066 function ble/string#escape-for-sed-regex { 1067 ble/string#escape-characters "$1" '\.[*^$/' 1068 } 1069 function ble/string#escape-for-awk-regex { 1070 ble/string#escape-characters "$1" '\.[*?+|^$(){}/' 1071 } 1072 function ble/string#escape-for-extended-regex { 1073 ble/string#escape-characters "$1" '\.[*?+|^$(){}' 1074 } 1075 function ble/string#escape-for-bash-glob { 1076 ble/string#escape-characters "$1" '\*?[(' 1077 } 1078 function ble/string#escape-for-bash-single-quote { 1079 local q="'" Q="'\''" 1080 ret=${1//$q/$Q} 1081 } 1082 function ble/string#escape-for-bash-double-quote { 1083 ble/string#escape-characters "$1" '\"$`' 1084 local a b 1085 a='!' b='"\!"' ret=${ret//"$a"/"$b"} # WA #D1751 checked 1086 } 1087 function ble/string#escape-for-bash-escape-string { 1088 ble/string#escape-characters "$1" $'\\\a\b\e\f\n\r\t\v'\' '\abefnrtv'\' 1089 } 1090 ## @fn ble/string#escape-for-bash-specialchars text flags 1091 ## @param[in] text 1092 ## @param[in] flags 1093 ## c 単語中でチルダ展開を誘導する文字をエスケープします。 1094 ## b ブレース展開の文字もエスケープします。 1095 ## H 語頭の #, ~ のエスケープをしません。 1096 ## T 語頭のチルダのエスケープをしません。 1097 ## G グロブ文字をエスケープしません。 1098 ## @var[out] ret 1099 function ble/string#escape-for-bash-specialchars { 1100 local chars='\ "'\''`$|&;<>()!^' 1101 # Note: = と : は文法的にはエスケープは不要だが 1102 # 補完の際の COMP_WORDBREAKS を避ける為に必要である。 1103 [[ $2 != *G* ]] && chars=$chars'*?[' 1104 [[ $2 == *c* ]] && chars=$chars'=:' 1105 [[ $2 == *b* ]] && chars=$chars'{,}' 1106 ble/string#escape-characters "$1" "$chars" 1107 [[ $2 != *[HT]* && $ret == '~'* ]] && ret=\\$ret 1108 [[ $2 != *H* && $ret == '#'* ]] && ret=\\$ret 1109 if [[ $ret == *[$']\n\t']* ]]; then 1110 local a b 1111 a=']' b=\\$a ret=${ret//"$a"/"$b"} 1112 a=$'\n' b="\$'\n'" ret=${ret//"$a"/"$b"} # WA #D1751 checked 1113 a=$'\t' b=$'\\\t' ret=${ret//"$a"/"$b"} 1114 fi 1115 1116 # 上の処理で extglob の ( も quote されてしまうので G の時には戻す。 1117 if [[ $2 == *G* ]] && shopt -q extglob; then 1118 local a b 1119 a='!\(' b='!(' ret=${ret//"$a"/"$b"} 1120 a='@\(' b='@(' ret=${ret//"$a"/"$b"} 1121 a='?\(' b='?(' ret=${ret//"$a"/"$b"} 1122 a='*\(' b='*(' ret=${ret//"$a"/"$b"} 1123 a='+\(' b='+(' ret=${ret//"$a"/"$b"} 1124 fi 1125 } 1126 1127 ## @fn ble/string#escape-for-display str [opts] 1128 ## str に含まれる制御文字を ^A などのキャレット表記に置き換えます。 1129 ## 1130 ## @param[in] str 1131 ## @param[in] opts 1132 ## revert 1133 ## キャレット表記を反転表示します。 1134 ## sgr1=* 1135 ## キャレット表記に用いる SGR シーケンスを指定します。 1136 ## キャレット表記の開始に挿入されます。 1137 ## sgr0=* 1138 ## キャレット表記以外の部分に用いる地の SGR シーケンスを指定します。 1139 ## キャレット表記の終端に挿入されます。 1140 ## 1141 function ble/string#escape-for-display { 1142 local head= tail=$1 opts=$2 1143 1144 local sgr0= sgr1= 1145 local rex_csi=$'\e\\[[ -?]*[@-~]' 1146 if [[ :$opts: == *:revert:* ]]; then 1147 ble/color/g2sgr "$_ble_color_gflags_Revert" 1148 sgr1=$ret sgr0=$_ble_term_sgr0 1149 else 1150 if local rex=':sgr1=(('$rex_csi'|[^:])*):'; [[ :$opts: =~ $rex ]]; then 1151 sgr1=${BASH_REMATCH[1]} sgr0=$_ble_term_sgr0 1152 fi 1153 if local rex=':sgr0=(('$rex_csi'|[^:])*):'; [[ :$opts: =~ $rex ]]; then 1154 sgr0=${BASH_REMATCH[1]} 1155 fi 1156 fi 1157 1158 while [[ $tail ]]; do 1159 if ble/util/isprint+ "$tail"; then 1160 head=$head${BASH_REMATCH} 1161 tail=${tail:${#BASH_REMATCH}} 1162 else 1163 ble/util/s2c "${tail::1}" 1164 local code=$ret 1165 if ((code<32)); then 1166 ble/util/c2s "$((code+64))" 1167 ret=$sgr1^$ret$sgr0 1168 elif ((code==127)); then 1169 ret=$sgr1^?$sgr0 1170 elif ((128<=code&&code<160)); then 1171 ble/util/c2s "$((code-64))" 1172 ret=${sgr1}M-^$ret$sgr0 1173 else 1174 ret=${tail::1} 1175 fi 1176 head=$head$ret 1177 tail=${tail:1} 1178 fi 1179 done 1180 ret=$head 1181 } 1182 1183 if ((_ble_bash>=40400)); then 1184 function ble/string#quote-words { 1185 local IFS=$_ble_term_IFS 1186 ret="${*@Q}" 1187 } 1188 function ble/string#quote-command { 1189 local IFS=$_ble_term_IFS 1190 ret=$1; shift 1191 (($#)) && ret="$ret ${*@Q}" 1192 } 1193 else 1194 function ble/string#quote-words { 1195 local q=\' Q="'\''" IFS=$_ble_term_IFS 1196 ret=("${@//$q/$Q}") 1197 ret=("${ret[@]/%/$q}") # WA #D1570 #D1738 checked 1198 ret="${ret[*]/#/$q}" # WA #D1570 #D1738 checked 1199 } 1200 function ble/string#quote-command { 1201 if (($#<=1)); then 1202 ret=$1 1203 return 0 1204 fi 1205 local q=\' Q="'\''" IFS=$_ble_term_IFS 1206 ret=("${@:2}") 1207 ret=("${ret[@]//$q/$Q}") # WA #D1570 #D1738 checked 1208 ret=("${ret[@]/%/$q}") # WA #D1570 #D1738 checked 1209 ret="$1 ${ret[*]/#/$q}" # WA #D1570 #D1738 checked 1210 } 1211 fi 1212 ## @fn ble/string#quote-word text opts 1213 function ble/string#quote-word { 1214 ret=${1-} 1215 1216 local rex_csi=$'\e\\[[ -?]*[@-~]' 1217 local opts=${2-} sgrq= sgr0= 1218 if [[ $opts ]]; then 1219 local rex=':sgrq=(('$rex_csi'|[^:])*):' 1220 if [[ :$opts: =~ $rex ]]; then 1221 sgrq=${BASH_REMATCH[1]} sgr0=$_ble_term_sgr0 1222 fi 1223 rex=':sgr0=(('$rex_csi'|[^:])*):' 1224 if [[ :$opts: =~ $rex ]]; then 1225 sgr0=${BASH_REMATCH[1]} 1226 elif [[ :$opts: == *:ansi:* ]]; then 1227 sgr0=$'\e[m' 1228 fi 1229 fi 1230 1231 if [[ ! $ret ]]; then 1232 if [[ :$opts: == *:quote-empty:* ]]; then 1233 ret=$sgrq\'\'$sgr0 1234 fi 1235 return 0 1236 fi 1237 1238 local chars=$'\a\b\e\f\n\r\t\v' 1239 if [[ $ret == *["$chars"]* ]]; then 1240 ble/string#escape-for-bash-escape-string "$ret" 1241 ret=$sgrq\$\'$ret\'$sgr0 1242 return 0 1243 fi 1244 1245 local chars=$_ble_term_IFS'"`$\<>()|&;*?[]!^=:{,}#~' q=\' 1246 if [[ $ret == *["$chars"]* ]]; then 1247 local Q="'$sgr0\'$sgrq'" 1248 ret=$sgrq$q${ret//$q/$Q}$q$sgr0 1249 ret=${ret#"$sgrq$q$q$sgr0"} ret=${ret%"$sgrq$q$q$sgr0"} 1250 elif [[ $ret == *["$q"]* ]]; then 1251 local Q="\'" 1252 ret=${ret//$q/$Q} 1253 fi 1254 } 1255 1256 function ble/string#match { [[ $1 =~ $2 ]]; } 1257 1258 ## @fn ble/string#create-unicode-progress-bar/.block value 1259 ## @var[out] ret 1260 function ble/string#create-unicode-progress-bar/.block { 1261 local block=$1 1262 if ((block<=0)); then 1263 ble/util/c2w "$((0x2588))" 1264 ble/string#repeat ' ' "$ret" 1265 elif ((block>=8)); then 1266 ble/util/c2s "$((0x2588))" 1267 ((${#ret}==1)) || ret='*' # LC_CTYPE が非対応の文字の時 1268 else 1269 ble/util/c2s "$((0x2590-block))" 1270 if ((${#ret}!=1)); then 1271 # LC_CTYPE が非対応の文字の時 1272 ble/util/c2w "$((0x2588))" 1273 ble/string#repeat ' ' "$((ret-1))" 1274 ret=$block$ret 1275 fi 1276 fi 1277 } 1278 1279 ## @fn ble/string#create-unicode-progress-bar value max width opts 1280 ## @param[in] opts 1281 ## unlimited ... 上限が不明である事を示します。 1282 ## @var[out] ret 1283 function ble/string#create-unicode-progress-bar { 1284 local value=$1 max=$2 width=$3 opts=:$4: 1285 1286 local opt_unlimited= 1287 if [[ $opts == *:unlimited:* ]]; then 1288 opt_unlimited=1 1289 ((value%=max,width--)) 1290 fi 1291 1292 local progress=$((value*8*width/max)) 1293 local progress_fraction=$((progress%8)) progress_integral=$((progress/8)) 1294 1295 local out= 1296 if ((progress_integral)); then 1297 if [[ $opt_unlimited ]]; then 1298 # unlimited の時は左は空白 1299 ble/string#create-unicode-progress-bar/.block 0 1300 else 1301 ble/string#create-unicode-progress-bar/.block 8 1302 fi 1303 ble/string#repeat "$ret" "$progress_integral" 1304 out=$ret 1305 fi 1306 1307 if ((progress_fraction)); then 1308 if [[ $opt_unlimited ]]; then 1309 # unlimited の時は2升を使って位置を表す 1310 ble/string#create-unicode-progress-bar/.block "$progress_fraction" 1311 out=$out$'\e[7m'$ret$'\e[27m' 1312 fi 1313 1314 ble/string#create-unicode-progress-bar/.block "$progress_fraction" 1315 out=$out$ret 1316 ((progress_integral++)) 1317 else 1318 if [[ $opt_unlimited ]]; then 1319 ble/string#create-unicode-progress-bar/.block 8 1320 out=$out$ret 1321 fi 1322 fi 1323 1324 if ((progress_integral<width)); then 1325 ble/string#create-unicode-progress-bar/.block 0 1326 ble/string#repeat "$ret" "$((width-progress_integral))" 1327 out=$out$ret 1328 fi 1329 1330 ret=$out 1331 } 1332 # Note: Bash-4.1 以下では "LC_CTYPE=C 組み込みコマンド" の形式だと 1333 # locale がその場で適用されないバグがある。 1334 function ble/util/strlen.impl { 1335 local LC_ALL= LC_CTYPE=C 1336 ret=${#1} 1337 } 1338 function ble/util/strlen { 1339 ble/util/strlen.impl "$@" 2>/dev/null # suppress locale error #D1440 1340 } 1341 function ble/util/substr.impl { 1342 local LC_ALL= LC_CTYPE=C 1343 ret=${1:$2:$3} 1344 } 1345 function ble/util/substr { 1346 ble/util/substr.impl "$@" 2>/dev/null # suppress locale error #D1440 1347 } 1348 1349 function ble/path#append { 1350 local _ble_local_script='opts=$opts${opts:+:}$2' 1351 _ble_local_script=${_ble_local_script//opts/"$1"} 1352 builtin eval -- "$_ble_local_script" 1353 } 1354 function ble/path#prepend { 1355 local _ble_local_script='opts=$2${opts:+:}$opts' 1356 _ble_local_script=${_ble_local_script//opts/"$1"} 1357 builtin eval -- "$_ble_local_script" 1358 } 1359 function ble/path#remove { 1360 [[ $2 ]] || return 1 1361 local _ble_local_script=' 1362 opts=:${opts//:/::}: 1363 opts=${opts//:"$2":} 1364 opts=${opts//::/:} opts=${opts#:} opts=${opts%:}' 1365 _ble_local_script=${_ble_local_script//opts/"$1"} 1366 builtin eval -- "$_ble_local_script" 1367 } 1368 function ble/path#remove-glob { 1369 [[ $2 ]] || return 1 1370 local _ble_local_script=' 1371 opts=:${opts//:/::}: 1372 opts=${opts//:$2:} 1373 opts=${opts//::/:} opts=${opts#:} opts=${opts%:}' 1374 _ble_local_script=${_ble_local_script//opts/"$1"} 1375 builtin eval -- "$_ble_local_script" 1376 } 1377 function ble/path#contains { 1378 builtin eval "[[ :\${$1}: == *:\"\$2\":* ]]" 1379 } 1380 1381 ## @fn ble/opts#has opts key 1382 function ble/opts#has { 1383 local rex=':'$2'[=:]' 1384 [[ :$1: =~ $rex ]] 1385 } 1386 ## @fn ble/opts#remove opts value 1387 function ble/opts#remove { 1388 ble/path#remove "$@" 1389 } 1390 1391 ## @fn ble/opts#extract-first-optarg opts key [default_value] 1392 function ble/opts#extract-first-optarg { 1393 ret= 1394 local rex=':'$2'(=[^:]*)?:' 1395 [[ :$1: =~ $rex ]] || return 1 1396 if [[ ${BASH_REMATCH[1]} ]]; then 1397 ret=${BASH_REMATCH[1]:1} 1398 elif [[ ${3+set} ]]; then 1399 ret=$3 1400 fi 1401 return 0 1402 } 1403 ## @fn ble/opts#extract-last-optarg opts key [default_value] 1404 function ble/opts#extract-last-optarg { 1405 ret= 1406 local rex='.*:'$2'(=[^:]*)?:' 1407 [[ :$1: =~ $rex ]] || return 1 1408 if [[ ${BASH_REMATCH[1]} ]]; then 1409 ret=${BASH_REMATCH[1]:1} 1410 elif [[ ${3+set} ]]; then 1411 ret=$3 1412 fi 1413 return 0 1414 } 1415 ## @fn ble/opts#extract-all-optargs opts key [default_value] 1416 ## extract all values from the string OPTS of the form 1417 ## "...:key=value1:...:key=value2:...:key:...". 1418 ## 1419 ## @param[in] key 1420 ## This should not include any special characters of regular 1421 ## expressions---preferably composed of [-_[:alnum:]]. 1422 ## 1423 ## @arr[out] ret 1424 function ble/opts#extract-all-optargs { 1425 ret=() 1426 local value=:$1: rex=':'$2'(=[^:]*)?(:.*)$' count=0 1427 while [[ $value =~ $rex ]]; do 1428 ((count++)) 1429 if [[ ${BASH_REMATCH[1]} ]]; then 1430 ble/array#push ret "${BASH_REMATCH[1]:1}" 1431 elif [[ ${3+set} ]]; then 1432 ble/array#push ret "$3" 1433 fi 1434 value=${BASH_REMATCH[2]} 1435 done 1436 ((count)) 1437 } 1438 1439 if ((_ble_bash>=40000)); then 1440 _ble_util_set_declare=(declare -A NAME) 1441 function ble/set#add { builtin eval -- "$1[x\$2]=1"; } 1442 function ble/set#remove { builtin unset -v "$1[x\$2]"; } 1443 function ble/set#contains { builtin eval "[[ \${$1[x\$2]+set} ]]"; } 1444 else 1445 _ble_util_set_declare=(declare NAME) 1446 function ble/set#.escape { 1447 _ble_local_value=${_ble_local_value//$_ble_term_FS/"$_ble_term_FS$_ble_term_FS"} 1448 _ble_local_value=${_ble_local_value//:/"$_ble_term_FS."} 1449 } 1450 function ble/set#add { 1451 local _ble_local_value=$2; ble/set#.escape 1452 ble/path#append "$1" "$_ble_local_value" 1453 } 1454 function ble/set#remove { 1455 local _ble_local_value=$2; ble/set#.escape 1456 ble/path#remove "$1" "$_ble_local_value" 1457 } 1458 function ble/set#contains { 1459 local _ble_local_value=$2; ble/set#.escape 1460 builtin eval "[[ :\$$1: == *:\"\$_ble_local_value\":* ]]" 1461 } 1462 fi 1463 1464 1465 #-------------------------------------- 1466 # dict 1467 1468 _ble_util_adict_declare='declare NAME NAME_keylist' 1469 ## @fn ble/dict#.resolve dict key 1470 function ble/adict#.resolve { 1471 # _ble_local_key 1472 _ble_local_key=$2 1473 _ble_local_key=${_ble_local_key//$_ble_term_FS/"$_ble_term_FS,"} 1474 _ble_local_key=${_ble_local_key//:/"$_ble_term_FS."} 1475 1476 local keylist=${1}_keylist; keylist=:${!keylist} 1477 local vec=${keylist%%:"$_ble_local_key":*} 1478 if [[ $vec != "$keylist" ]]; then 1479 vec=${vec//[!:]} 1480 _ble_local_index=${#vec} 1481 else 1482 _ble_local_index=-1 1483 fi 1484 } 1485 function ble/adict#set { 1486 local _ble_local_key _ble_local_index 1487 ble/adict#.resolve "$1" "$2" 1488 if ((_ble_local_index>=0)); then 1489 builtin eval -- "$1[_ble_local_index]=\$3" 1490 else 1491 local _ble_local_script=' 1492 local _ble_local_vec=${NAME_keylist//[!:]} 1493 NAME[${#_ble_local_vec}]=$3 1494 NAME_keylist=$NAME_keylist$_ble_local_key: 1495 ' 1496 builtin eval -- "${_ble_local_script//NAME/$1}" 1497 fi 1498 return 0 1499 } 1500 function ble/adict#get { 1501 local _ble_local_key _ble_local_index 1502 ble/adict#.resolve "$1" "$2" 1503 if ((_ble_local_index>=0)); then 1504 builtin eval -- "ret=\${$1[_ble_local_index]}; [[ \${$1[_ble_local_index]+set} ]]" 1505 else 1506 builtin eval -- ret= 1507 return 1 1508 fi 1509 } 1510 function ble/adict#unset { 1511 local _ble_local_key _ble_local_index 1512 ble/adict#.resolve "$1" "$2" 1513 ((_ble_local_index>=0)) && 1514 builtin eval -- "builtin unset -v '$1[_ble_local_index]'" 1515 return 0 1516 } 1517 function ble/adict#has { 1518 local _ble_local_key _ble_local_index 1519 ble/adict#.resolve "$1" "$2" 1520 ((_ble_local_index>=0)) && 1521 builtin eval -- "[[ \${$1[_ble_local_index]+set} ]]" 1522 } 1523 function ble/adict#clear { 1524 builtin eval -- "${1}_keylist= $1=()" 1525 } 1526 function ble/adict#keys { 1527 local _ble_local_keylist=${1}_keylist 1528 _ble_local_keylist=${!_ble_local_keylist%:} 1529 ble/string#split ret : "$_ble_local_keylist" 1530 if [[ $_ble_local_keylist == *"$_ble_term_FS"* ]]; then 1531 ret=("${ret[@]//$_ble_term_FS./:}") # WA #D1570 checked 1532 ret=("${ret[@]//$_ble_term_FS,/$_ble_term_FS}") # WA #D1570 #D1738 checked 1533 fi 1534 1535 # filter out unset elements 1536 local _ble_local_keys _ble_local_i _ble_local_ref=$1[_ble_local_i] 1537 _ble_local_keys=("${ret[@]}") ret=() 1538 for _ble_local_i in "${!_ble_local_keys[@]}"; do 1539 [[ ${_ble_local_ref+set} ]] && 1540 ble/array#push ret "${_ble_local_keys[_ble_local_i]}" 1541 done 1542 } 1543 1544 if ((_ble_bash>=40000)); then 1545 _ble_util_dict_declare='declare -A NAME' 1546 function ble/dict#set { builtin eval -- "$1[x\$2]=\$3"; } 1547 function ble/dict#get { builtin eval -- "ret=\${$1[x\$2]-}; [[ \${$1[x\$2]+set} ]]"; } 1548 function ble/dict#unset { builtin eval -- "builtin unset -v '$1[x\$2]'"; } 1549 function ble/dict#has { builtin eval -- "[[ \${$1[x\$2]+set} ]]"; } 1550 function ble/dict#clear { builtin eval -- "$1=()"; } 1551 function ble/dict#keys { builtin eval -- 'ret=("${!'"$1"'[@]}"); ret=("${ret[@]#x}")'; } 1552 else 1553 _ble_util_dict_declare='declare NAME NAME_keylist=' 1554 function ble/dict#set { ble/adict#set "$@"; } 1555 function ble/dict#get { ble/adict#get "$@"; } 1556 function ble/dict#unset { ble/adict#unset "$@"; } 1557 function ble/dict#has { ble/adict#has "$@"; } 1558 function ble/dict#clear { ble/adict#clear "$@"; } 1559 function ble/dict#keys { ble/adict#keys "$@"; } 1560 fi 1561 1562 if ((_ble_bash>=40200)); then 1563 _ble_util_gdict_declare='{ builtin unset -v NAME; declare -gA NAME; NAME=(); }' 1564 function ble/gdict#set { ble/dict#set "$@"; } 1565 function ble/gdict#get { ble/dict#get "$@"; } 1566 function ble/gdict#unset { ble/dict#unset "$@"; } 1567 function ble/gdict#has { ble/dict#has "$@"; } 1568 function ble/gdict#clear { ble/dict#clear "$@"; } 1569 function ble/gdict#keys { ble/dict#keys "$@"; } 1570 elif ((_ble_bash>=40000)); then 1571 _ble_util_gdict_declare='{ if ! ble/is-assoc NAME; then if local _ble_local_test 2>/dev/null; then NAME_keylist=; else builtin unset -v NAME NAME_keylist; declare -A NAME; fi fi; NAME=(); }' 1572 function ble/gdict#.is-adict { 1573 local keylist=${1}_keylist 1574 [[ ${!keylist+set} ]] 1575 } 1576 function ble/gdict#set { if ble/gdict#.is-adict "$1"; then ble/adict#set "$@"; else ble/dict#set "$@"; fi; } 1577 function ble/gdict#get { if ble/gdict#.is-adict "$1"; then ble/adict#get "$@"; else ble/dict#get "$@"; fi; } 1578 function ble/gdict#unset { if ble/gdict#.is-adict "$1"; then ble/adict#unset "$@"; else ble/dict#unset "$@"; fi; } 1579 function ble/gdict#has { if ble/gdict#.is-adict "$1"; then ble/adict#has "$@"; else ble/dict#has "$@"; fi; } 1580 function ble/gdict#clear { if ble/gdict#.is-adict "$1"; then ble/adict#clear "$@"; else ble/dict#clear "$@"; fi; } 1581 function ble/gdict#keys { if ble/gdict#.is-adict "$1"; then ble/adict#keys "$@"; else ble/dict#keys "$@"; fi; } 1582 else 1583 _ble_util_gdict_declare='{ builtin unset -v NAME NAME_keylist; NAME_keylist= NAME=(); }' 1584 function ble/gdict#set { ble/adict#set "$@"; } 1585 function ble/gdict#get { ble/adict#get "$@"; } 1586 function ble/gdict#unset { ble/adict#unset "$@"; } 1587 function ble/gdict#has { ble/adict#has "$@"; } 1588 function ble/gdict#clear { ble/adict#clear "$@"; } 1589 function ble/gdict#keys { ble/adict#keys "$@"; } 1590 fi 1591 1592 1593 function ble/dict/.print { 1594 declare -p "$2" &>/dev/null || return 1 1595 local ret _ble_local_key _ble_local_value 1596 1597 ble/util/print "builtin eval -- \"\${_ble_util_${1}_declare//NAME/$2}\"" 1598 ble/"$1"#keys "$2" 1599 for _ble_local_key in "${ret[@]}"; do 1600 ble/"$1"#get "$2" "$_ble_local_key" 1601 ble/string#quote-word "$ret" quote-empty 1602 _ble_local_value=$ret 1603 1604 ble/string#quote-word "$_ble_local_key" quote-empty 1605 _ble_local_key=$ret 1606 1607 ble/util/print "ble/$1#set $2 $_ble_local_key $_ble_local_value" 1608 done 1609 } 1610 function ble/dict#print { ble/dict/.print dict "$1"; } 1611 function ble/adict#print { ble/dict/.print adict "$1"; } 1612 function ble/gdict#print { ble/dict/.print gdict "$1"; } 1613 1614 function ble/dict/.copy { 1615 local ret 1616 ble/"$1"#keys "$2" 1617 ble/"$1"#clear "$3" 1618 local _ble_local_key 1619 for _ble_local_key in "${ret[@]}"; do 1620 ble/"$1"#get "$2" "$_ble_local_key" 1621 ble/"$1"#set "$3" "$_ble_local_key" "$ret" 1622 done 1623 } 1624 function ble/dict#cp { ble/dict/.copy dict "$1" "$2"; } 1625 function ble/adict#cp { ble/dict/.copy adict "$1" "$2"; } 1626 function ble/gdict#cp { ble/dict/.copy gdict "$1" "$2"; } 1627 1628 #------------------------------------------------------------------------------ 1629 # assign: reading files/streams into variables 1630 # 1631 1632 ## @fn ble/util/readfile var filename 1633 ## @fn ble/util/mapfile arr < filename 1634 ## ファイルの内容を変数または配列に読み取ります。 1635 ## 1636 ## @param[in] var 1637 ## 読み取った内容の格納先の変数名を指定します。 1638 ## @param[in] arr 1639 ## 読み取った内容を行毎に格納する配列の名前を指定します。 1640 ## @param[in] filename 1641 ## 読み取るファイルの場所を指定します。 1642 ## 1643 ## Note: bash-5.2 以上で $(< file) を使う可能性も考えたが、末尾改行が 1644 ## 消えてしまう事、末尾改行を未定義にしてまで使う程の速度差もない事、 1645 ## などから採用は見送る事にした。 1646 if ((_ble_bash>=40000)); then 1647 function ble/util/readfile { # 155ms for man bash 1648 local -a _ble_local_buffer=() 1649 mapfile _ble_local_buffer < "$2"; local _ble_local_ext=$? 1650 IFS= builtin eval "$1=\"\${_ble_local_buffer[*]-}\"" 1651 return "$_ble_local_ext" 1652 } 1653 function ble/util/mapfile { 1654 mapfile -t "$1" 1655 } 1656 else 1657 function ble/util/readfile { # 465ms for man bash 1658 [[ -r $2 && ! -d $2 ]] || return 1 1659 local IFS= 1660 ble/bash/read -d '' "$1" < "$2" 1661 return 0 1662 } 1663 function ble/util/mapfile { 1664 local IFS= 1665 local _ble_local_i=0 _ble_local_val _ble_local_arr; _ble_local_arr=() 1666 while ble/bash/read _ble_local_val || [[ $_ble_local_val ]]; do 1667 _ble_local_arr[_ble_local_i++]=$_ble_local_val 1668 done 1669 builtin eval "$1=(\"\${_ble_local_arr[@]}\")" 1670 } 1671 fi 1672 1673 function ble/util/copyfile { 1674 local src=$1 dst=$2 content 1675 ble/util/readfile content "$1" || return "$?" 1676 ble/util/put "$content" >| "$dst" 1677 } 1678 1679 ## @fn ble/util/writearray [OPTIONS] arr 1680 ## 配列の内容を読み出し可能な形式で出力します。 1681 ## 1682 ## OPTIONS 1683 ## -- 以降の引数は通常引数 1684 ## -d delim 配列要素を区切るのに使う文字を設定します。 1685 ## 既定値は改行 "\n" です。 1686 ## --nlfix 改行区切りで出力します。要素に改行が含まれる時は $'' を用 1687 ## いて内容をエスケープします。改行が含まれる要素番号の一覧 1688 ## を一番最後の要素に追加します。 1689 ## 1690 function ble/util/writearray/.read-arguments { 1691 _ble_local_array= 1692 _ble_local_nlfix= 1693 _ble_local_delim=$'\n' 1694 local flags= 1695 while (($#)); do 1696 local arg=$1; shift 1697 if [[ $flags != *-* && $arg == -* ]]; then 1698 case $arg in 1699 (--nlfix) _ble_local_nlfix=1 ;; 1700 (-d) 1701 if (($#)); then 1702 _ble_local_delim=$1; shift 1703 else 1704 ble/util/print "${FUNCNAME[1]}: '$arg': missing option argument." >&2 1705 flags=E$flags 1706 fi ;; 1707 (--) flags=-$flags ;; 1708 (*) 1709 ble/util/print "${FUNCNAME[1]}: '$arg': unrecognized option." >&2 1710 flags=E$flags ;; 1711 esac 1712 else 1713 if local rex='^[_a-zA-Z][_a-zA-Z0-9]*$'; ! [[ $arg =~ $rex ]]; then 1714 ble/util/print "${FUNCNAME[1]}: '$arg': invalid array name." >&2 1715 flags=E$flags 1716 elif [[ $flags == *A* ]]; then 1717 ble/util/print "${FUNCNAME[1]}: '$arg': an array name has been already specified." >&2 1718 flags=E$flags 1719 else 1720 _ble_local_array=$arg 1721 flags=A$flags 1722 fi 1723 fi 1724 done 1725 [[ $_ble_local_nlfix ]] && _ble_local_delim=$'\n' 1726 [[ $flags != *E* ]] 1727 } 1728 1729 _ble_bin_awk_libES=' 1730 function s2i_initialize(_, i) { 1731 for (i = 0; i < 16; i++) 1732 xdigit2int[sprintf("%x", i)] = i; 1733 for (i = 10; i < 16; i++) 1734 xdigit2int[sprintf("%X", i)] = i; 1735 } 1736 function s2i(s, base, _, i, n, r) { 1737 if (!base) base = 10; 1738 r = 0; 1739 n = length(s); 1740 for (i = 1; i <= n; i++) 1741 r = r * base + xdigit2int[substr(s, i, 1)]; 1742 return r; 1743 } 1744 1745 # ENCODING: UTF-8 1746 function c2s_initialize(_, i, n, buff) { 1747 if (sprintf("%c", 945) == "α") { 1748 C2S_UNICODE_PRINTF_C = 1; 1749 n = split(ENVIRON["__ble_rawbytes"], buff); 1750 for (i = 1; i <= n; i++) 1751 c2s_byte2raw[127 + i] = buff[i]; 1752 } else { 1753 C2S_UNICODE_PRINTF_C = 0; 1754 for (i = 1; i <= 255; i++) 1755 c2s_byte2char[i] = sprintf("%c", i); 1756 } 1757 } 1758 function c2s(code, _, leadbyte_mark, leadbyte_sup, tail) { 1759 if (C2S_UNICODE_PRINTF_C) 1760 return sprintf("%c", code); 1761 1762 leadbyte_sup = 128; # 0x80 1763 leadbyte_mark = 0; 1764 tail = ""; 1765 while (leadbyte_sup && code >= leadbyte_sup) { 1766 leadbyte_sup /= 2; 1767 leadbyte_mark = leadbyte_mark ? leadbyte_mark / 2 : 65472; # 0xFFC0 1768 tail = c2s_byte2char[128 + int(code % 64)] tail; 1769 code = int(code / 64); 1770 } 1771 return c2s_byte2char[(leadbyte_mark + code) % 256] tail; 1772 } 1773 function c2s_raw(code, _, ret) { 1774 if (code >= 128 && C2S_UNICODE_PRINTF_C) { 1775 ret = c2s_byte2raw[code]; 1776 if (ret != "") return ret; 1777 } 1778 return sprintf("%c", code); 1779 } 1780 1781 function es_initialize(_, c) { 1782 s2i_initialize(); 1783 c2s_initialize(); 1784 es_control_chars["a"] = "\a"; 1785 es_control_chars["b"] = "\b"; 1786 es_control_chars["t"] = "\t"; 1787 es_control_chars["n"] = "\n"; 1788 es_control_chars["v"] = "\v"; 1789 es_control_chars["f"] = "\f"; 1790 es_control_chars["r"] = "\r"; 1791 es_control_chars["e"] = "\033"; 1792 es_control_chars["E"] = "\033"; 1793 es_control_chars["?"] = "?"; 1794 es_control_chars["'\''"] = "'\''"; 1795 es_control_chars["\""] = "\""; 1796 es_control_chars["\\"] = "\\"; 1797 1798 for (c = 32; c < 127; c++) 1799 es_s2c[sprintf("%c", c)] = c; 1800 } 1801 function es_unescape(s, _, head, c) { 1802 head = ""; 1803 while (match(s, /^[^\\]*\\/)) { 1804 head = head substr(s, 1, RLENGTH - 1); 1805 s = substr(s, RLENGTH + 1); 1806 if ((c = es_control_chars[substr(s, 1, 1)])) { 1807 head = head c; 1808 s = substr(s, 2); 1809 } else if (match(s, /^[0-9]([0-9][0-9]?)?/)) { 1810 head = head c2s_raw(s2i(substr(s, 1, RLENGTH), 8) % 256); 1811 s = substr(s, RLENGTH + 1); 1812 } else if (match(s, /^x[0-9a-fA-F][0-9a-fA-F]?/)) { 1813 head = head c2s_raw(s2i(substr(s, 2, RLENGTH - 1), 16)); 1814 s = substr(s, RLENGTH + 1); 1815 } else if (match(s, /^U[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]([0-9a-fA-F]([0-9a-fA-F][0-9a-fA-F]?)?)?/)) { 1816 # \\U[0-9]{5,8} 1817 head = head c2s(s2i(substr(s, 2, RLENGTH - 1), 16)); 1818 s = substr(s, RLENGTH + 1); 1819 } else if (match(s, /^[uU][0-9a-fA-F]([0-9a-fA-F]([0-9a-fA-F][0-9a-fA-F]?)?)?/)) { 1820 # \\[uU][0-9]{1,4} 1821 head = head c2s(s2i(substr(s, 2, RLENGTH - 1), 16)); 1822 s = substr(s, RLENGTH + 1); 1823 } else if (match(s, /^c[ -~]/)) { 1824 # \\c[ -~] (non-ascii characters are unsupported) 1825 c = es_s2c[substr(s, 2, 1)]; 1826 head = head c2s(_ble_bash >= 40400 && c == 63 ? 127 : c % 32); 1827 s = substr(s, 3); 1828 } else { 1829 head = head "\\"; 1830 } 1831 } 1832 return head s; 1833 } 1834 ' 1835 _ble_bin_awk_libNLFIX=' 1836 ## @var nlfix_index 1837 ## @var nlfix_indices 1838 ## @var nlfix_rep_slash 1839 ## @var nlfix_rep_double_slash 1840 ## @fn nlfix_begin() 1841 ## @fn nlfix_push(elem) 1842 ## @fn nlfix_end() 1843 function nlfix_begin(_, tmp) { 1844 nlfix_rep_slash = "\\"; 1845 if (AWKTYPE == "xpg4") nlfix_rep_slash = "\\\\"; 1846 1847 nlfix_rep_double_slash = "\\\\"; 1848 sub(/.*/, nlfix_rep_double_slash, tmp); 1849 if (tmp == "\\") nlfix_rep_double_slash = "\\\\\\\\"; 1850 1851 nlfix_indices = ""; 1852 nlfix_index = 0; 1853 } 1854 function nlfix_push(elem, file) { 1855 if (elem ~ /\n/) { 1856 gsub(/\\/, nlfix_rep_double_slash, elem); 1857 gsub(/'\''/, nlfix_rep_slash "'\''", elem); 1858 gsub(/\007/, nlfix_rep_slash "a", elem); 1859 gsub(/\010/, nlfix_rep_slash "b", elem); 1860 gsub(/\011/, nlfix_rep_slash "t", elem); 1861 gsub(/\012/, nlfix_rep_slash "n", elem); 1862 gsub(/\013/, nlfix_rep_slash "v", elem); 1863 gsub(/\014/, nlfix_rep_slash "f", elem); 1864 gsub(/\015/, nlfix_rep_slash "r", elem); 1865 if (file) 1866 printf("$'\''%s'\''\n", elem) > file; 1867 else 1868 printf("$'\''%s'\''\n", elem); 1869 nlfix_indices = nlfix_indices != "" ? nlfix_indices " " nlfix_index : nlfix_index; 1870 } else { 1871 if (file) 1872 printf("%s\n", elem) > file; 1873 else 1874 printf("%s\n", elem); 1875 } 1876 nlfix_index++; 1877 } 1878 function nlfix_end(file) { 1879 if (file) 1880 printf("%s\n", nlfix_indices) > file; 1881 else 1882 printf("%s\n", nlfix_indices); 1883 } 1884 ' 1885 _ble_util_writearray_rawbytes= 1886 function ble/util/writearray { 1887 local _ble_local_array 1888 local -x _ble_local_nlfix _ble_local_delim 1889 ble/util/writearray/.read-arguments "$@" || return 2 1890 1891 # select the fastest awk implementation 1892 local __ble_awk=ble/bin/awk __ble_awktype=$_ble_bin_awk_type 1893 if ble/is-function ble/bin/mawk; then 1894 __ble_awk=ble/bin/mawk __ble_awktype=mawk 1895 elif ble/is-function ble/bin/nawk; then 1896 __ble_awk=ble/bin/nawk __ble_awktype=nawk 1897 fi 1898 1899 # Note: printf も遅いが awk による parse の方が遅いので nlfix でない限りは直 1900 # 接 printf を使う。但し、bash-5.2 以降では printf が格段に遅くなるので避ける。 1901 if ((!_ble_local_nlfix)) && ! [[ _ble_bash -ge 50200 && $__ble_awktype == [mn]awk ]]; then 1902 if [[ $_ble_local_delim ]]; then 1903 if [[ $_ble_local_delim == *["%\'"]* ]]; then 1904 local __ble_q=\' __ble_Q="'\''" 1905 _ble_local_delim=${_ble_local_delim//'%'/'%%'} 1906 _ble_local_delim=${_ble_local_delim//'\'/'\\'} 1907 _ble_local_delim=${_ble_local_delim//$__ble_q/$__ble_Q} 1908 fi 1909 builtin eval "printf '%s$_ble_local_delim' \"\${$_ble_local_array[@]}\"" 1910 else 1911 builtin eval "printf '%s\0' \"\${$_ble_local_array[@]}\"" 1912 fi 1913 return "$?" 1914 fi 1915 1916 # Note: mawk は定義していない関数を使おうとすると、それが実際に決し 1917 # て実行されないとしてもコンパイルに失敗して動かない。 1918 local __ble_function_gensub_dummy= 1919 [[ $__ble_awktype == gawk ]] || 1920 __ble_function_gensub_dummy='function gensub(rex, rep, n, str) { exit 3; }' 1921 1922 # Note: gawk の内部では $'\302' 等から現在のコードにないバイトを生成できない 1923 # ので外部から与える。 1924 if [[ ! $_ble_util_writearray_rawbytes ]]; then 1925 local IFS=$_ble_term_IFS __ble_tmp; __ble_tmp=('\'{2,3}{0..7}{0..7}) 1926 builtin eval "local _ble_util_writearray_rawbytes=\$'${__ble_tmp[*]}'" 1927 fi 1928 local -x __ble_rawbytes=$_ble_util_writearray_rawbytes 1929 1930 local __ble_rex_dq='^"([^\\"]|\\.)*"' 1931 local __ble_rex_es='^\$'\''([^\\'\'']|\\.)*'\''' 1932 local __ble_rex_sq='^'\''([^'\'']|'\'\\\\\'\'')*'\''' 1933 local __ble_rex_normal=$'^[^'$_ble_term_space'$`"'\''()|&;<>\\]' # Note: []{}?*#!~^, @(), +() は quote されていなくても OK とする 1934 declare -p "$_ble_local_array" | "$__ble_awk" -v _ble_bash="$_ble_bash" ' 1935 '"$__ble_function_gensub_dummy"' 1936 BEGIN { 1937 DELIM = ENVIRON["_ble_local_delim"]; 1938 FLAG_NLFIX = ENVIRON["_ble_local_nlfix"]; 1939 if (FLAG_NLFIX) DELIM = "\n"; 1940 1941 IS_GAWK = AWKTYPE == "gawk"; 1942 IS_XPG4 = AWKTYPE == "xpg4"; 1943 1944 REP_SL = "\\"; 1945 if (IS_XPG4) REP_SL = "\\\\"; 1946 1947 es_initialize(); 1948 1949 decl = ""; 1950 } 1951 1952 '"$_ble_bin_awk_libES"' 1953 '"$_ble_bin_awk_libNLFIX"' 1954 1955 # Note: "str" must not contain "&" or "\\\\". When "&" is 1956 # present, the escaping rule for "\\" changes in some awk. 1957 # Now there is no problem because only DELIM (one character) is 1958 # currently passed. 1959 function str2rep(str) { 1960 if (IS_XPG4) sub(/\\/, "\\\\\\\\", str); 1961 return str; 1962 } 1963 1964 1965 function unquote_dq(s, _, head) { 1966 if (IS_GAWK) { 1967 return gensub(/\\([$`"\\])/, "\\1", "g", s); 1968 } else { 1969 if (s ~ /\\[$`"\\]/) { 1970 gsub(/\\\$/, "$" , s); 1971 gsub(/\\`/ , "`" , s); 1972 gsub(/\\"/ , "\"", s); 1973 gsub(/\\\\/, "\\", s); 1974 } 1975 return s; 1976 } 1977 } 1978 function unquote_sq(s) { 1979 gsub(/'\'\\\\\'\''/, "'\''", s); 1980 return s; 1981 } 1982 function unquote_dqes(s) { 1983 if (s ~ /^"/) 1984 return unquote_dq(substr(s, 2, length(s) - 2)); 1985 else 1986 return es_unescape(substr(s, 3, length(s) - 3)); 1987 } 1988 function unquote(s) { 1989 if (s ~ /^"/) 1990 return unquote_dq(substr(s, 2, length(s) - 2)); 1991 else if (s ~ /^\$/) 1992 return es_unescape(substr(s, 3, length(s) - 3)); 1993 else if (s ~ /^'\''/) 1994 return unquote_sq(substr(s, 2, length(s) - 2)); 1995 else if (s ~ /^\\/) 1996 return substr(s, 2, 1); 1997 else 1998 return s; 1999 } 2000 2001 #% # 制御文字が要素に含まれていない場合は全て [1]="..." の形式になっている筈。 2002 function analyze_elements_dq(decl, _, arr, i, n) { 2003 if (decl ~ /^\[[0-9]+\]="([^'$'\1\2''"\n\\]|\\.)*"( \[[0-9]+\]="([^\1\2"\\]|\\.)*")*$/) { 2004 if (IS_GAWK) { 2005 decl = gensub(/\[[0-9]+\]="(([^"\\]|\\.)*)" ?/, "\\1\001", "g", decl); 2006 sub(/\001$/, "", decl); 2007 decl = gensub(/\\([\\$"`])/, "\\1", decl); 2008 } else { 2009 # Convert to a ^A-separated list 2010 gsub(/\[[0-9]+\]="([^"\\]|\\.)*" /, "&\001", decl); 2011 gsub(/" \001\[[0-9]+\]="/, "\001", decl); 2012 sub(/^\[[0-9]+\]="/, "", decl); 2013 sub(/"$/, "", decl); 2014 2015 # Unescape 2016 gsub(/\\\\/, "\002", decl); 2017 gsub(/\\\$/, "$", decl); 2018 gsub(/\\"/, "\"", decl); 2019 gsub(/\\`/, "`", decl); 2020 gsub(/\002/, REP_SL, decl); 2021 } 2022 2023 # Output 2024 if (DELIM != "") { 2025 gsub(/\001/, str2rep(DELIM), decl); 2026 printf("%s", decl DELIM); 2027 } else { 2028 n = split(decl, arr, /\001/); 2029 for (i = 1; i <= n; i++) 2030 printf("%s%c", arr[i], 0); 2031 } 2032 2033 #% # [N]="" の形式の時は要素内改行はないと想定 2034 if (FLAG_NLFIX) printf("\n"); 2035 2036 return 1; 2037 } 2038 return 0; 2039 } 2040 2041 function _process_elem(elem) { 2042 if (FLAG_NLFIX) { 2043 nlfix_push(elem); 2044 } else if (DELIM != "") { 2045 printf("%s", elem DELIM); 2046 } else { 2047 printf("%s%c", elem, 0); 2048 } 2049 } 2050 2051 #% # 任意の場合は多少遅くなるがこちらの関数で処理する。 2052 function analyze_elements_general(decl, _, arr, i, n, str, elem, m) { 2053 if (FLAG_NLFIX) 2054 nlfix_begin(); 2055 2056 # Note: We here assume that all the elements have the form [N]=... 2057 # Note: We here assume that the original array has at least one element 2058 n = split(decl, arr, /\]=/); 2059 str = " " arr[1]; 2060 elem = ""; 2061 first = 1; 2062 for (i = 2; i <= n; i++) { 2063 str = str "]=" arr[i]; 2064 if (sub(/^ \[[0-9]+\]=/, "", str)) { 2065 if (first) 2066 first = 0; 2067 else 2068 _process_elem(elem); 2069 elem = ""; 2070 } 2071 2072 if (match(str, /('"$__ble_rex_dq"'|'"$__ble_rex_es"') /)) { 2073 mlen = RLENGTH; 2074 elem = elem unquote_dqes(substr(str, 1, mlen - 1)); 2075 str = substr(str, mlen); 2076 continue; 2077 } else if (i == n || str !~ /^[\$"]/) { 2078 # Fallback: As far as all the values have the form "" or $'', the 2079 # control would only enter this branch for the last element. 2080 while (match(str, /'"$__ble_rex_dq"'|'"$__ble_rex_es"'|'"$__ble_rex_sq"'|'"$__ble_rex_normal"'|^\\./)) { 2081 mlen = RLENGTH; 2082 elem = elem unquote(substr(str, 1, mlen)); 2083 str = substr(str, mlen + 1); 2084 } 2085 } 2086 } 2087 _process_elem(elem); 2088 2089 if (FLAG_NLFIX) 2090 nlfix_end(); 2091 return 1; 2092 } 2093 2094 function process_declaration(decl) { 2095 #% # declare 除去 2096 sub(/^declare +(-[-aAilucnrtxfFgGI]+ +)?(-- +)?/, "", decl); 2097 2098 #% # 全体 quote の除去 2099 if (decl ~ /^([_a-zA-Z][_a-zA-Z0-9]*)='\''\(.*\)'\''$/) { 2100 sub(/='\''\(/, "=(", decl); 2101 sub(/\)'\''$/, ")", decl); 2102 gsub(/'\'\\\\\'\''/, "'\''", decl); 2103 } 2104 2105 #% # bash-3.0 の declare -p は改行について誤った出力をする。 2106 if (_ble_bash < 30100) gsub(/\\\n/, "\n", decl); 2107 2108 #% # #D1238 bash-4.3 以前の declare -p は ^A, ^? を 2109 #% # ^A^A, ^A^? と出力してしまうので補正する。 2110 #% # #D1325 更に Bash-3.0 では "x${_ble_term_DEL}y" とすると 2111 #% # _ble_term_DEL の中身が消えてしまうので 2112 #% # "x""${_ble_term_DEL}""y" とする必要がある。 2113 if (_ble_bash < 40400) { 2114 gsub(/\001\001/, "\001", decl); 2115 gsub(/\001\177/, "\177", decl); 2116 } 2117 2118 sub(/^([_a-zA-Z][_a-zA-Z0-9]*)=\(['"$_ble_term_space"']*/, "", decl); 2119 sub(/['"$_ble_term_space"']*\)['"$_ble_term_space"']*$/, "", decl); 2120 2121 #% # 空配列 2122 if (decl == "") return 1; 2123 2124 #% # [N]="value" だけの時の高速実装。mawk だと却って遅くなる様だ 2125 if (AWKTYPE != "mawk" && analyze_elements_dq(decl)) return 1; 2126 2127 return analyze_elements_general(decl); 2128 } 2129 { decl = decl ? decl "\n" $0: $0; } 2130 END { process_declaration(decl); } 2131 ' 2132 } 2133 function ble/util/readarray { 2134 local _ble_local_array 2135 local -x _ble_local_nlfix _ble_local_delim 2136 ble/util/writearray/.read-arguments "$@" || return 2 2137 2138 if ((_ble_bash>=40400)); then 2139 local _ble_local_script=' 2140 mapfile -t -d "$_ble_local_delim" ARR' 2141 elif ((_ble_bash>=40000)) && [[ $_ble_local_delim == $'\n' ]]; then 2142 local _ble_local_script=' 2143 mapfile -t ARR' 2144 else 2145 local _ble_local_script=' 2146 local IFS= ARRI=0; ARR=() 2147 while ble/bash/read -d "$_ble_local_delim" "ARR[ARRI++]"; do :; done' 2148 fi 2149 2150 if [[ $_ble_local_nlfix ]]; then 2151 _ble_local_script=$_ble_local_script' 2152 local ARRN=${#ARR[@]} ARRF ARRI 2153 if ((ARRN--)); then 2154 ble/string#split-words ARRF "${ARR[ARRN]}" 2155 builtin unset -v "ARR[ARRN]" 2156 for ARRI in "${ARRF[@]}"; do 2157 builtin eval -- "ARR[ARRI]=${ARR[ARRI]}" 2158 done 2159 fi' 2160 fi 2161 builtin eval -- "${_ble_local_script//ARR/$_ble_local_array}" 2162 } 2163 2164 ## @fn ble/util/assign var command 2165 ## var=$(command) の高速な代替です。command はサブシェルではなく現在のシェル 2166 ## で実行されます。Bash 5.3 の var=${ command; } に等価です。 2167 ## 2168 ## @param[in] var 2169 ## 代入先の変数名を指定します。 2170 ## @param[in] command... 2171 ## 実行するコマンドを指定します。 2172 ## 2173 ## @remarks util.bgproc.sh では « ble/util/assign bgpid '(set -m; command & 2174 ## bgpid=$!; ble/util/print "$bgpid")' » でプロセスグループが作られる事を想定 2175 ## している。例えば bgpid=$(...) はプロセスグループが作られないので使えない。 2176 ## 2177 _ble_util_assign_base=$_ble_base_run/$$.util.assign.tmp 2178 _ble_util_assign_level=0 2179 if ((_ble_bash>=40000)); then 2180 function ble/util/assign/mktmp { 2181 _ble_local_tmpfile=$_ble_util_assign_base.$((_ble_util_assign_level++)) 2182 ((BASH_SUBSHELL)) && _ble_local_tmpfile=$_ble_local_tmpfile.$BASHPID 2183 } 2184 else 2185 function ble/util/assign/mktmp { 2186 _ble_local_tmpfile=$_ble_util_assign_base.$((_ble_util_assign_level++)) 2187 ((BASH_SUBSHELL)) && _ble_local_tmpfile=$_ble_local_tmpfile.$RANDOM 2188 } 2189 fi 2190 function ble/util/assign/rmtmp { 2191 ((_ble_util_assign_level--)) 2192 #%if !release 2193 if ((BASH_SUBSHELL)); then 2194 printf 'caller %s\n' "${FUNCNAME[@]}" >| "$_ble_local_tmpfile" 2195 else 2196 >| "$_ble_local_tmpfile" 2197 fi 2198 #%else 2199 >| "$_ble_local_tmpfile" 2200 #%end 2201 } 2202 if ((_ble_bash>=50300)); then 2203 function ble/util/assign { 2204 builtin eval "$1=\${ builtin eval -- \"\$2\"; }" 2205 } 2206 elif ((_ble_bash>=40000)); then 2207 # mapfile の方が read より高速 2208 function ble/util/assign { 2209 local _ble_local_tmpfile; ble/util/assign/mktmp 2210 builtin eval -- "$2" >| "$_ble_local_tmpfile" 2211 local _ble_local_ret=$? _ble_local_arr= 2212 mapfile -t _ble_local_arr < "$_ble_local_tmpfile" 2213 ble/util/assign/rmtmp 2214 IFS=$'\n' builtin eval "$1=\"\${_ble_local_arr[*]}\"" 2215 return "$_ble_local_ret" 2216 } 2217 else 2218 function ble/util/assign { 2219 local _ble_local_tmpfile; ble/util/assign/mktmp 2220 builtin eval -- "$2" >| "$_ble_local_tmpfile" 2221 local _ble_local_ret=$? IFS= 2222 ble/bash/read -d '' "$1" < "$_ble_local_tmpfile" 2223 ble/util/assign/rmtmp 2224 builtin eval "$1=\${$1%\$_ble_term_nl}" 2225 return "$_ble_local_ret" 2226 } 2227 fi 2228 ## @fn ble/util/assign-array arr command args... 2229 ## mapfile -t arr < <(command ...) の高速な代替です。 2230 ## command はサブシェルではなく現在のシェルで実行されます。 2231 ## 2232 ## @param[in] arr 2233 ## 代入先の配列名を指定します。 2234 ## @param[in] command 2235 ## 実行するコマンドを指定します。 2236 ## @param[in] args... 2237 ## command から参照する引数 ($3 $4 ...) を指定します。 2238 ## 2239 if ((_ble_bash>=40000)); then 2240 function ble/util/assign-array { 2241 local _ble_local_tmpfile; ble/util/assign/mktmp 2242 builtin eval -- "$2" >| "$_ble_local_tmpfile" 2243 local _ble_local_ret=$? 2244 mapfile -t "$1" < "$_ble_local_tmpfile" 2245 ble/util/assign/rmtmp 2246 return "$_ble_local_ret" 2247 } 2248 else 2249 function ble/util/assign-array { 2250 local _ble_local_tmpfile; ble/util/assign/mktmp 2251 builtin eval -- "$2" >| "$_ble_local_tmpfile" 2252 local _ble_local_ret=$? 2253 ble/util/mapfile "$1" < "$_ble_local_tmpfile" 2254 ble/util/assign/rmtmp 2255 return "$_ble_local_ret" 2256 } 2257 fi 2258 2259 if ! ((_ble_bash>=40400)); then 2260 function ble/util/assign-array0 { 2261 local _ble_local_tmpfile; ble/util/assign/mktmp 2262 builtin eval -- "$2" >| "$_ble_local_tmpfile" 2263 local _ble_local_ret=$? 2264 mapfile -d '' -t "$1" < "$_ble_local_tmpfile" 2265 ble/util/assign/rmtmp 2266 return "$_ble_local_ret" 2267 } 2268 else 2269 function ble/util/assign-array0 { 2270 local _ble_local_tmpfile; ble/util/assign/mktmp 2271 builtin eval -- "$2" >| "$_ble_local_tmpfile" 2272 local _ble_local_ret=$? 2273 local IFS= i=0 _ble_local_arr 2274 while ble/bash/read -d '' "_ble_local_arr[i++]"; do :; done < "$_ble_local_tmpfile" 2275 ble/util/assign/rmtmp 2276 [[ ${_ble_local_arr[--i]} ]] || builtin unset -v "_ble_local_arr[i]" 2277 ble/util/unlocal i IFS 2278 builtin eval "$1=(\"\${_ble_local_arr[@]}\")" 2279 return "$_ble_local_ret" 2280 } 2281 fi 2282 2283 ## @fn ble/util/assign.has-output command 2284 function ble/util/assign.has-output { 2285 local _ble_local_tmpfile; ble/util/assign/mktmp 2286 builtin eval -- "$1" >| "$_ble_local_tmpfile" 2287 [[ -s $_ble_local_tmpfile ]] 2288 local _ble_local_ret=$? 2289 ble/util/assign/rmtmp 2290 return "$_ble_local_ret" 2291 } 2292 2293 function ble/util/assign-words { 2294 ble/util/assign "$1" "$2" 2295 ble/string#split-words "$1" "${!1}" 2296 } 2297 2298 2299 # ble/bin/awk の初期化に ble/util/assign を使うので 2300 ble/bin/awk/.instantiate 2301 2302 # 2303 # functions 2304 # 2305 2306 ## @fn ble/is-function function 2307 ## 関数 function が存在するかどうかを検査します。 2308 ## 2309 ## @param[in] function 2310 ## 存在を検査する関数の名前を指定します。 2311 ## 2312 if ((_ble_bash>=30200)); then 2313 function ble/is-function { 2314 declare -F "$1" &>/dev/null 2315 } 2316 else 2317 # bash-3.1 has bug in declare -f. 2318 # it does not accept a function name containing non-alnum chars. 2319 function ble/is-function { 2320 local type 2321 ble/util/type type "$1" 2322 [[ $type == function ]] 2323 } 2324 fi 2325 2326 ## @fn ble/function#getdef function 2327 ## @var[out] def 2328 ## 2329 ## Note: declare -pf "$name" が -o posix に依存しない関数定義の取得方 2330 ## 法であるかに思えたが、declare -pf "$name" を使うと -t 属性が付い 2331 ## ていた時に末尾に declare -ft name という余分な属性付加のコマンド 2332 ## が入ってしまう。或いはこの属性も一緒に保存できた方が良いのかもし 2333 ## れないが、取り敢えず今は属性が入らない様に declare -pf name は使 2334 ## わない。 2335 if ((_ble_bash>=30200)); then 2336 function ble/function#getdef { 2337 local name=$1 2338 ble/is-function "$name" || return 1 2339 if [[ -o posix ]]; then 2340 ble/util/assign def 'type "$name"' 2341 def=${def#*$'\n'} 2342 else 2343 ble/util/assign def 'declare -f "$name"' 2344 fi 2345 } 2346 else 2347 function ble/function#getdef { 2348 local name=$1 2349 ble/is-function "$name" || return 1 2350 ble/util/assign def 'type "$name"' 2351 def=${def#*$'\n'} 2352 } 2353 fi 2354 2355 ## @fn ble/function#evaldef def 2356 ## 関数を定義します。基本的に eval に等価ですが評価時に extglob を保 2357 ## 証します。 2358 function ble/function#evaldef { 2359 local reset_extglob= 2360 if ! shopt -q extglob; then 2361 reset_extglob=1 2362 shopt -s extglob 2363 fi 2364 builtin eval -- "$1"; local ext=$? 2365 [[ ! $reset_extglob ]] || shopt -u extglob 2366 return "$ext" 2367 } 2368 2369 builtin eval -- "${_ble_util_gdict_declare//NAME/_ble_util_function_traced}" 2370 function ble/function#trace { 2371 local func 2372 for func; do 2373 declare -ft "$func" &>/dev/null || continue 2374 ble/gdict#set _ble_util_function_traced "$func" 1 2375 done 2376 } 2377 function ble/function#has-trace { 2378 ble/gdict#has _ble_util_function_traced "$1" 2379 } 2380 function ble/function#has-attr { 2381 local __ble_tmp=$1 2382 ble/util/assign-array __ble_tmp 'declare -pf "$__ble_tmp" 2>/dev/null' 2383 local nline=${#__ble_tmp[@]} 2384 ((nline)) && 2385 ble/string#match "${__ble_tmp[nline-1]}" '^declare -([a-zA-Z]*)' && 2386 [[ ${BASH_REMATCH[1]} == *["$2"]* ]] 2387 } 2388 2389 ## @fn ble/function/is-global-trace-context 2390 ## この関数の呼び出し元の文脈で確実に global の DEBUG が見えているかどうかを 2391 ## 判定します。 2392 function ble/function/is-global-trace-context { 2393 # Note: 例え set -T が設定されていたとしても、それが global で設定された物な 2394 # のか呼び出しの何処かの深さで設定された物なのか分からない。なので、set -T 2395 # が設定されていたからと言って無条件に global が見えているとは限らない。 2396 # Note: ble に属する関数は勝手に set -T を一時的に有効にしたりする事は基本的 2397 # にないので許可する。但し、内部で一時的に restore-bash-options している時 2398 # はあるが、その内部で ble-attach 乃至は ble/function/is-global-trace-context等 2399 # を実行する事はないと仮定する。 2400 local func depth=1 ndepth=${#FUNCNAME[*]} 2401 for func in "${FUNCNAME[@]:1}"; do 2402 local src=${BASH_SOURCE[depth]} 2403 [[ $- == *T* && ( $func == ble || $func == ble[-/]* || $func == source && $src == "$_ble_base_blesh_raw" ) ]] || 2404 [[ $func == source && depth -eq ndepth-1 && BASH_LINENO[depth] -eq 0 && ( ${src##*/} == .bashrc || ${src##*/} == .bash_profile || ${src##*/} == .profile ) ]] || 2405 ble/gdict#has _ble_util_function_traced "$func" || return 1 2406 ((depth++)) 2407 done 2408 return 0 2409 } 2410 2411 ## @fn ble/function#try function args... 2412 ## 関数 function が存在している時に限り関数を呼び出します。 2413 ## 2414 ## @param[in] function 2415 ## 存在を検査して実行する関数の名前を指定します。 2416 ## @param[in] args 2417 ## 関数に渡す引数を指定します。 2418 ## @exit 関数が呼び出された場合はその終了ステータスを返します。 2419 ## 関数が存在しなかった場合は 127 を返します。 2420 ## 2421 function ble/function#try { 2422 local lastexit=$? 2423 ble/is-function "$1" || return 127 2424 ble/util/setexit "$lastexit" 2425 "$@" 2426 } 2427 2428 function ble/function#get-source-and-lineno { 2429 local ret unset_extdebug= 2430 shopt -q extdebug || { unset_extdebug=1; shopt -s extdebug; } 2431 ble/util/assign ret "declare -F '$1' 2>/dev/null"; local ext=$? 2432 [[ ! $unset_extdebug ]] || shopt -u extdebug 2433 if ((ext==0)); then 2434 ret=${ret#*' '} 2435 lineno=${ret%%' '*} 2436 source=${ret#*' '} 2437 [[ $lineno && ! ${lineno//[0-9]} && $source ]] || return 1 2438 fi 2439 return "$ext" 2440 } 2441 2442 ## @fn ble/function#advice type function proc 2443 ## 既存の関数の振る舞いを変更します。 2444 ## 2445 ## @param[in] type 2446 ## before を指定した時、処理 proc を関数 function の前に挿入します。 2447 ## after を指定した時、処理 proc を関数 function の後に挿入します。 2448 ## around を指定した時、関数 function の呼び出し前後に処理 proc を行います。 2449 ## around proc の中では本来の関数を呼び出す為に ble/function#advice/do 2450 ## を実行する必要があります。 2451 ## 2452 ## @fn ble/function#advice/do 2453 ## around proc の中から呼び出せる関数です。 2454 ## 本来の関数を呼び出します。 2455 ## 2456 ## @arr[in,out] ADVICE_WORDS 2457 ## proc の中から参照できる変数です。関数の呼び出しに使うコマンドを提供しま 2458 ## す。例えば元の関数呼び出しが function arg1 arg2 だった場合、 2459 ## ADVICE_WORDS=(function arg1 arg2) が設定されます。before/around に於いて 2460 ## 本来の関数の呼び出し前にこの配列を書き換える事で呼び出す関数または関数の 2461 ## 引数を変更する事ができます。 2462 ## 2463 ## @var[in.out] ADVICE_EXIT 2464 ## proc の中から参照できる変数です。after/around に於いて関数実行後の戻り値 2465 ## を参照または変更するのに使います。 2466 ## 2467 ## @var[in.out] ADVICE_FUNCNAME 2468 ## proc の中から参照できる変数です。FUNCNAME から ble/function#advice の調 2469 ## 整による余分な関数呼び出しを取り除いたものを保持します。 2470 ## 2471 function ble/function#advice/do { 2472 ble/util/setexit "$advice_lastexit" "$advice_lastarg" 2473 ble/function#advice/original:"${ADVICE_WORDS[@]}" 2474 ADVICE_EXIT=$? 2475 } 2476 function ble/function#advice/.proc { 2477 local advice_lastexit=$? advice_lastarg=$_ 2478 2479 local ADVICE_WORDS ADVICE_EXIT=127 2480 ADVICE_WORDS=("$@") 2481 local -a ADVICE_FUNCNAME=() 2482 local func 2483 for func in "${FUNCNAME[@]}"; do 2484 [[ $func == ble/function#advice/* ]] || 2485 ble/array#push ADVICE_FUNCNAME "$func" 2486 done 2487 ble/util/unlocal func 2488 2489 ble/function#try "ble/function#advice/before:$1" 2490 if ble/is-function "ble/function#advice/around:$1"; then 2491 "ble/function#advice/around:$1" 2492 else 2493 ble/function#advice/do 2494 fi 2495 ble/function#try "ble/function#advice/after:$1" 2496 return "$ADVICE_EXIT" 2497 } 2498 function ble/function#advice { 2499 local type=$1 name=$2 proc=$3 2500 if ! ble/is-function "$name"; then 2501 local t=; ble/util/type t "$name" 2502 case $t in 2503 (builtin|file) builtin eval "function $name { : ZBe85Oe28nBdg; command $name \"\$@\"; }" ;; 2504 (*) 2505 ble/util/print "ble/function#advice: $name is not a function." >&2 2506 return 1 ;; 2507 esac 2508 fi 2509 2510 local def; ble/function#getdef "$name" 2511 case $type in 2512 (remove) 2513 if [[ $def == *'ble/function#advice/.proc'* ]]; then 2514 ble/function#getdef "ble/function#advice/original:$name" 2515 if [[ $def ]]; then 2516 if [[ $def == *ZBe85Oe28nBdg* ]]; then 2517 builtin unset -f "$name" 2518 else 2519 ble/function#evaldef "${def#*:}" 2520 fi 2521 fi 2522 fi 2523 builtin unset -f ble/function#advice/{before,after,around,original}:"$name" 2>/dev/null 2524 return 0 ;; 2525 (before|after|around) 2526 if [[ $def != *'ble/function#advice/.proc'* ]]; then 2527 ble/function#evaldef "ble/function#advice/original:$def" 2528 builtin eval "function $name { ble/function#advice/.proc \"\$FUNCNAME\" \"\$@\"; }" 2529 fi 2530 2531 local q=\' Q="'\''" 2532 builtin eval "ble/function#advice/$type:$name() { builtin eval '${proc//$q/$Q}'; }" 2533 return 0 ;; 2534 (*) 2535 ble/util/print "ble/function#advice unknown advice type '$type'" >&2 2536 return 2 ;; 2537 esac 2538 } 2539 2540 ## @fn ble/function#push name [proc] 2541 ## @fn ble/function#pop name 2542 ## 関数定義を保存・復元する関数です。 2543 ## 2544 function ble/function#push { 2545 local name=$1 proc=$2 2546 if ble/is-function "$name"; then 2547 local index=0 2548 while ble/is-function "ble/function#push/$index:$name"; do 2549 ((++index)) 2550 done 2551 2552 local def; ble/function#getdef "$name" 2553 ble/function#evaldef "ble/function#push/$index:$def" 2554 fi 2555 2556 if [[ $proc ]]; then 2557 local q=\' Q="'\''" 2558 builtin eval "function $name { builtin eval -- '${proc//$q/$Q}'; }" 2559 else 2560 builtin unset -f "$name" 2561 fi 2562 return 0 2563 } 2564 function ble/function#pop { 2565 local name=$1 proc=$2 2566 2567 local index=-1 2568 while ble/is-function "ble/function#push/$((index+1)):$name"; do 2569 ((++index)) 2570 done 2571 2572 if ((index<0)); then 2573 if ble/is-function "$name"; then 2574 builtin unset -f "$name" 2575 return 0 2576 elif ble/bin#has "$name"; then 2577 ble/util/print "ble/function#pop: $name is not a function." >&2 2578 return 1 2579 else 2580 return 0 2581 fi 2582 else 2583 local def; ble/function#getdef "ble/function#push/$index:$name" 2584 ble/function#evaldef "${def#*:}" 2585 builtin unset -f "ble/function#push/$index:$name" 2586 return 0 2587 fi 2588 } 2589 function ble/function#push/call-top { 2590 local func=${FUNCNAME[1]} 2591 if ! ble/is-function "$func"; then 2592 ble/util/print "ble/function#push/call-top: This function should be called from a function" >&2 2593 return 1 2594 fi 2595 local index=0 2596 if [[ $func == ble/function#push/?*:?* ]]; then 2597 index=${func#*/*/}; index=${index%%:*} 2598 func=${func#*:} 2599 else 2600 while ble/is-function "ble/function#push/$index:$func"; do ((index++)); done 2601 fi 2602 if ((index==0)); then 2603 command "$func" "$@" 2604 else 2605 "ble/function#push/$((index-1)):$func" "$@" 2606 fi 2607 } 2608 2609 : "${_ble_util_lambda_count:=0}" 2610 ## @fn ble/function#lambda var body 2611 ## 無名関数を定義しその実際の名前を変数 var に格納します。 2612 function ble/function#lambda { 2613 local _ble_local_q=\' _ble_local_Q="'\''" 2614 ble/util/set "$1" ble/function#lambda/$((_ble_util_lambda_count++)) 2615 builtin eval -- "function ${!1} { builtin eval -- '${2//$_ble_local_q/$_ble_local_Q}'; }" 2616 } 2617 2618 ## @fn ble/function#suppress-stderr function_name 2619 ## @param[in] function_name 2620 function ble/function#suppress-stderr { 2621 local name=$1 2622 if ! ble/is-function "$name"; then 2623 ble/util/print "$FUNCNAME: '$name' is not a function name" >&2 2624 return 2 2625 fi 2626 2627 # 重複して suppress-stderr した時の為、未定義の時のみ実装を待避 2628 local lambda=ble/function#suppress-stderr:$name 2629 if ! ble/is-function "$lambda"; then 2630 local def; ble/function#getdef "$name" 2631 ble/function#evaldef "ble/function#suppress-stderr:$def" 2632 fi 2633 2634 builtin eval "function $name { $lambda \"\$@\" 2>/dev/null; }" 2635 return 0 2636 } 2637 2638 # 2639 # miscallaneous utils 2640 # 2641 2642 # Note: "printf -v" for an array element is only allowed in bash-4.1 2643 # or later. 2644 if ((_ble_bash>=40100)); then 2645 function ble/util/set { 2646 builtin printf -v "$1" %s "$2" 2647 } 2648 else 2649 function ble/util/set { 2650 builtin eval -- "$1=\"\$2\"" 2651 } 2652 fi 2653 2654 if ((_ble_bash>=30100)); then 2655 function ble/util/sprintf { 2656 builtin printf -v "$@" 2657 } 2658 else 2659 function ble/util/sprintf { 2660 local -a args; args=("${@:2}") 2661 ble/util/assign "$1" 'builtin printf "${args[@]}"' 2662 } 2663 fi 2664 2665 ## @fn ble/util/type varname command 2666 ## @param[out] varname 2667 ## 結果を格納する変数名を指定します。 2668 ## @param[in] command 2669 ## 種類を判定するコマンド名を指定します。 2670 function ble/util/type { 2671 ble/util/assign-array "$1" 'builtin type -a -t -- "$3" 2>/dev/null' "$2" 2672 } 2673 2674 if ((_ble_bash>=40000)); then 2675 function ble/is-alias { 2676 [[ ${BASH_ALIASES[$1]+set} ]] 2677 } 2678 function ble/alias#active { 2679 shopt -q expand_aliases && 2680 [[ ${BASH_ALIASES[$1]+set} ]] 2681 } 2682 ## @fn ble/alias#expand word 2683 ## @var[out] ret 2684 ## @exit 2685 ## エイリアス展開が実際に行われた時に成功します。 2686 function ble/alias#expand { 2687 ret=$1 2688 shopt -q expand_aliases && 2689 ret=${BASH_ALIASES[$ret]-$ret} 2690 } 2691 function ble/alias/list { 2692 ret=("${!BASH_ALIASES[@]}") 2693 } 2694 else 2695 function ble/is-alias { 2696 [[ $1 != *=* ]] && alias "$1" &>/dev/null 2697 } 2698 function ble/alias#active { 2699 shopt -q expand_aliases && 2700 [[ $1 != *=* ]] && alias "$1" &>/dev/null 2701 } 2702 function ble/alias#expand { 2703 ret=$1 2704 local type; ble/util/type type "$ret" 2705 [[ $type != alias ]] && return 1 2706 local data; ble/util/assign data 'LC_ALL=C alias "$ret"' &>/dev/null 2707 [[ $data == 'alias '*=* ]] && builtin eval "ret=${data#alias *=}" 2708 } 2709 function ble/alias/list { 2710 ret=() 2711 local data iret=0 2712 ble/util/assign-array data 'alias -p' 2713 for data in "${data[@]}"; do 2714 [[ $data == 'alias '*=* ]] && 2715 data=${data%%=*} && 2716 builtin eval "ret[iret++]=${data#alias }" 2717 done 2718 } 2719 fi 2720 2721 if ((_ble_bash>=40000)); then 2722 # #D1341 対策 変数代入形式だと組み込みコマンドにロケールが適用されない。 2723 function ble/util/is-stdin-ready { 2724 local IFS= LC_ALL= LC_CTYPE=C 2725 builtin read -t 0 2726 } 2727 # suppress locale error #D1440 2728 ble/function#suppress-stderr ble/util/is-stdin-ready 2729 else 2730 function ble/util/is-stdin-ready { false; } 2731 fi 2732 2733 # Note: BASHPID は Bash-4.0 以上 2734 2735 if ((_ble_bash>=40000)); then 2736 function ble/util/getpid { :; } 2737 function ble/util/is-running-in-subshell { [[ $$ != $BASHPID ]]; } 2738 else 2739 ## @fn ble/util/getpid 2740 ## @var[out] BASHPID 2741 function ble/util/getpid { 2742 local command='echo $PPID' 2743 ble/util/assign BASHPID 'ble/bin/sh -c "$command"' 2744 } 2745 function ble/util/is-running-in-subshell { 2746 # Note: bash-4.3 以下では BASH_SUBSHELL はパイプやプロセス置換で増えないの 2747 # で信頼性が低いらしい。唯、関数内で実行している限りは大丈夫なのかもしれ 2748 # ない。 2749 ((BASH_SUBSHELL==0)) || return 0 2750 local BASHPID; ble/util/getpid 2751 [[ $$ != $BASHPID ]] 2752 } 2753 fi 2754 2755 ## @fn ble/fd#is-open fd 2756 ## 指定したファイルディスクリプタが開いているかどうか判定します。 2757 function ble/fd#is-open { builtin : >&"$1"; } 2>/dev/null 2758 2759 _ble_util_openat_nextfd= 2760 function ble/fd#alloc/.nextfd { 2761 [[ $_ble_util_openat_nextfd ]] || 2762 _ble_util_openat_nextfd=${bleopt_openat_base:-30} 2763 # Note: Bash 3.1 では exec fd>&- で明示的に閉じても駄目。 2764 # 開いた後に読み取りプロセスで読み取りに失敗する。 2765 # なので開いていない fd を探す必要がある。#D0992 2766 # Note: 指定された fd が開いているかどうかを 2767 # 可搬に高速に判定する方法を見つけたので 2768 # 常に開いていない fd を探索する。#D1318 2769 # Note: fd が枯渇すると探索が無限ループになるので fd 探索範囲の上限を 1024 に 2770 # 制限する。もし見つからない場合には初期値の fd を上書きする。 2771 local _ble_local_init=$_ble_util_openat_nextfd 2772 local _ble_local_limit=$((_ble_local_init+1024)) 2773 while ((_ble_util_openat_nextfd<_ble_local_limit)) && 2774 ble/fd#is-open "$_ble_util_openat_nextfd"; do 2775 ((_ble_util_openat_nextfd++)) 2776 done 2777 if ((_ble_util_openat_nextfd>=_ble_local_limit)); then 2778 _ble_util_openat_nextfd=$_ble_local_init 2779 builtin eval "exec $_ble_util_openat_nextfd>&-" 2780 fi 2781 (($1=_ble_util_openat_nextfd++)) 2782 } 2783 2784 ## @fn ble/fd#alloc fdvar redirect [opts] 2785 ## "exec {fdvar}>foo" に該当する操作を実行します。 2786 ## @param[out] fdvar 2787 ## 指定した変数に使用されたファイルディスクリプタを代入します。 2788 ## @param[in] redirect 2789 ## リダイレクトを指定します。 2790 ## @param[in,opt] opts 2791 ## export 2792 ## 指定した変数を export します。 2793 ## inherit 2794 ## 既に fdvar が存在して有効な fd であれば何もしません。新しく fd を確保 2795 ## した場合には終了処理を登録しません。また上記の export を含みます。 2796 ## share 2797 ## >&NUMBER の形式のリダイレクトの場合に fd を複製する代わりに単に NUMBER 2798 ## を fdvar に代入します。 2799 ## overwrite 2800 ## 既に fdvar が存在する場合その fd を上書きします。 2801 _ble_util_openat_fdlist=() 2802 function ble/fd#alloc { 2803 local _ble_local_preserve= 2804 if [[ :$3: == *:inherit:* ]]; then 2805 [[ ${!1-} ]] && 2806 ble/fd#is-open "${!1}" && 2807 return 0 2808 fi 2809 2810 if [[ :$3: == *:share:* ]]; then 2811 local _ble_local_ret='[<>]&['$_ble_term_IFS']*([0-9]+)['$_ble_term_IFS']*$' 2812 if [[ $2 =~ $rex ]]; then 2813 builtin eval -- "$1=${BASH_REMATCH[1]}" 2814 return 0 2815 fi 2816 fi 2817 2818 if [[ ${!1-} && :$3: == *:overwrite:* ]]; then 2819 _ble_local_preserve=1 2820 builtin eval "exec ${!1}$2" 2821 elif ((_ble_bash>=40100)) && [[ :$3: != *:base:* ]]; then 2822 builtin eval "exec {$1}$2" 2823 else 2824 ble/fd#alloc/.nextfd "$1" 2825 # Note: Bash 3.2/3.1 のバグを避けるため、 2826 # >&- を用いて一旦明示的に閉じる必要がある #D0857 2827 builtin eval "exec ${!1}>&- ${!1}$2" 2828 fi; local _ble_local_ext=$? 2829 2830 if [[ :$3: == *:inherit:* || :$3: == *:export:* ]]; then 2831 export "$1" 2832 elif [[ ! $_ble_local_preserve ]]; then 2833 ble/array#push _ble_util_openat_fdlist "${!1}" 2834 fi 2835 return "$_ble_local_ext" 2836 } 2837 function ble/fd#finalize { 2838 local fd 2839 for fd in "${_ble_util_openat_fdlist[@]}"; do 2840 builtin eval "exec $fd>&-" 2841 done 2842 _ble_util_openat_fdlist=() 2843 } 2844 ## @fn ble/fd#close fd 2845 ## 指定した fd を閉じます。 2846 function ble/fd#close { 2847 set -- "$(($1))" 2848 (($1>=3)) || return 1 2849 builtin eval "exec $1>&-" 2850 ble/array#remove _ble_util_openat_fdlist "$1" 2851 return 0 2852 } 2853 2854 ## @var _ble_util_fd_stdout 2855 ## @var _ble_util_fd_stderr 2856 ## @var _ble_util_fd_null 2857 ## @var _ble_util_fd_zero 2858 ## 既に定義されている場合は継承する 2859 if [[ $_ble_init_command ]]; then 2860 # コマンドモードで実行している時には標準ストリームはそのまま使う。 2861 _ble_util_fd_stdin=0 2862 _ble_util_fd_stdout=1 2863 _ble_util_fd_stderr=2 2864 else 2865 if [[ -t 0 ]]; then 2866 ble/fd#alloc _ble_util_fd_stdin '<&0' base:overwrite:export 2867 else 2868 ble/fd#alloc _ble_util_fd_stdin '< /dev/tty' base:inherit 2869 fi 2870 if [[ -t 1 ]]; then 2871 ble/fd#alloc _ble_util_fd_stdout '>&1' base:overwrite:export 2872 else 2873 ble/fd#alloc _ble_util_fd_stdout '> /dev/tty' base:inherit 2874 fi 2875 if [[ -t 2 ]]; then 2876 ble/fd#alloc _ble_util_fd_stderr '>&2' base:overwrite:export 2877 else 2878 ble/fd#alloc _ble_util_fd_stderr ">&$_ble_util_fd_stdout" base:inherit:share 2879 fi 2880 fi 2881 ble/fd#alloc _ble_util_fd_null '<> /dev/null' base:inherit 2882 [[ -c /dev/zero ]] && 2883 ble/fd#alloc _ble_util_fd_zero '< /dev/zero' base:inherit 2884 2885 function ble/fd#close-all-tty { 2886 local -a fds=() 2887 if [[ -d /proc/$$/fd ]]; then 2888 ble/util/getpid 2889 local fd 2890 for fd in /proc/"$BASHPID"/fd/[0-9]*; do 2891 ble/array#push fds "${fd##*/}" 2892 done 2893 else 2894 fd=({0..255}) 2895 fi 2896 2897 # Note: 0 1 2 及び _ble_util_fd_std{in,out,err} を閉じる事を考えたが、どうも 2898 # redirect によって待避されている物などたくさんある様なので全部チェックする事 2899 # にした。 2900 local fd 2901 for fd in "${fds[@]}"; do 2902 if ble/string#match "$fd" '^[0-9]+$' && [[ -t $fd ]]; then 2903 builtin eval "exec $fd>&- $fd>&$_ble_util_fd_null" 2904 ble/array#remove _ble_util_openat_fdlist "$fd" 2905 fi 2906 done 2907 } 2908 ## @fn ble/util/nohup command [opts] 2909 ## @param[in] command 2910 ## @param[in,opt] opts 2911 ## @opts print-bgpid 2912 function ble/util/nohup { 2913 if ((!BASH_SUBSHELL)); then 2914 (ble/util/nohup "$@") 2915 return "$?" 2916 fi 2917 2918 ble/fd#close-all-tty 2919 shopt -u huponexit 2920 builtin eval -- "$1" &>/dev/null </dev/null & { local pid=$!; disown; } 2921 if [[ :$2: == *:print-bgpid:* ]]; then 2922 ble/util/print "$pid" 2923 fi 2924 } 2925 2926 function ble/util/print-quoted-command { 2927 local ret; ble/string#quote-command "$@" 2928 ble/util/print "$ret" 2929 } 2930 function ble/util/declare-print-definitions { 2931 (($#==0)) && return 0 2932 2933 # Note (#D2055): mawk 1.3.3-20090705 bug の為に [:space:] を正規表現内部で使 2934 # 用する事ができない。Ubuntu 16.04 LTS 及び Ubuntu 18.04 LTS で mawk 2935 # 1.3.3-20090705 が使用されている。なので _ble_term_space という変数に 2936 # <SP><TAB> を入れて使う。 2937 2938 declare -p "$@" | ble/bin/awk -v _ble_bash="$_ble_bash" -v OSTYPE="$OSTYPE" ' 2939 BEGIN { 2940 decl = ""; 2941 2942 #% # 対策 #D1270: MSYS2 で ^M を代入すると消える 2943 flag_escape_cr = OSTYPE == "msys"; 2944 } 2945 2946 function fix_value(value) { 2947 #% # bash-3.0 の declare -p は改行について誤った出力をする。 2948 if (_ble_bash < 30100) gsub(/\\\n/, "\n", value); 2949 2950 #% # #D1238 bash-4.3 以前の declare -p は ^A, ^? を 2951 #% # ^A^A, ^A^? と出力してしまうので補正する。 2952 #% # #D1325 更に Bash-3.0 では "x${_ble_term_DEL}y" とすると 2953 #% # _ble_term_DEL の中身が消えてしまうので 2954 #% # "x""${_ble_term_DEL}""y" とする必要がある。 2955 if (_ble_bash < 30100) { 2956 gsub(/\001\001/, "\"\"${_ble_term_SOH}\"\"", value); 2957 gsub(/\001\177/, "\"\"${_ble_term_DEL}\"\"", value); 2958 } else if (_ble_bash < 40400) { 2959 gsub(/\001\001/, "${_ble_term_SOH}", value); 2960 gsub(/\001\177/, "${_ble_term_DEL}", value); 2961 } 2962 2963 if (flag_escape_cr) 2964 gsub(/\015/, "${_ble_term_CR}", value); 2965 return value; 2966 } 2967 2968 #% # #D1522 #D1614 Bash-3.2 未満で配列要素に ^A または ^? を含む場合は 2969 #% # arr=(...) の形式だと評価時に ^A, ^? が倍加するので、 2970 #% # 要素ごとに代入を行う必要がある。 2971 function print_array_elements(decl, _, name, out, key, value) { 2972 if (match(decl, /^[_a-zA-Z][_a-zA-Z0-9]*=\(/) == 0) return 0; 2973 name = substr(decl, 1, RLENGTH - 2); 2974 decl = substr(decl, RLENGTH + 1, length(decl) - RLENGTH - 1); 2975 sub(/^['"$_ble_term_space"']+/, decl); 2976 2977 out = name "=()\n"; 2978 2979 while (match(decl, /^\[[0-9]+\]=/)) { 2980 key = substr(decl, 2, RLENGTH - 3); 2981 decl = substr(decl, RLENGTH + 1); 2982 2983 value = ""; 2984 if (match(decl, /^('\''[^'\'']*'\''|\$'\''([^\\'\'']|\\.)*'\''|\$?"([^\\"]|\\.)*"|\\.|[^'"$_ble_term_space"'"'\''`;&|()])*/)) { 2985 value = substr(decl, 1, RLENGTH) 2986 decl = substr(decl, RLENGTH + 1) 2987 } 2988 2989 out = out name "[" key "]=" fix_value(value) "\n"; 2990 sub(/^['"$_ble_term_space"']+/, decl); 2991 } 2992 2993 if (decl != "") return 0; 2994 2995 print out; 2996 return 1; 2997 } 2998 2999 function declflush(_, isArray) { 3000 if (!decl) return 0; 3001 isArray = (decl ~ /^declare +-[ilucnrtxfFgGI]*[aA]/); 3002 3003 #% # declare 除去 3004 sub(/^declare +(-[-aAilucnrtxfFgGI]+ +)?(-- +)?/, "", decl); 3005 if (isArray) { 3006 if (decl ~ /^([_a-zA-Z][_a-zA-Z0-9]*)='\''\(.*\)'\''$/) { 3007 sub(/='\''\(/, "=(", decl); 3008 sub(/\)'\''$/, ")", decl); 3009 gsub(/'\'\\\\\'\''/, "'\''", decl); 3010 } 3011 3012 if (_ble_bash < 40000 && decl ~ /[\001\177]/) 3013 if (print_array_elements(decl)) 3014 return 1; 3015 } 3016 3017 print fix_value(decl); 3018 decl = ""; 3019 return 1; 3020 } 3021 /^declare / { 3022 declflush(); 3023 decl = $0; 3024 next; 3025 } 3026 { decl = decl "\n" $0; } 3027 END { declflush(); } 3028 ' 3029 } 3030 3031 ## @fn ble/util/print-global-definitions/.print-decl name opts 3032 ## 指定された変数の宣言を出力します。 3033 ## @param[in] name 3034 ## 処理対象の変数名を指定します。 3035 ## @param[in] opts 3036 ## 指定した名前の変数が見つからない時 unset が指定されます。 3037 ## @stdout 3038 ## 変数宣言を出力します。指定した名前の変数が見つからない時は unset 状態に 3039 ## するコマンドを出力します。 3040 function ble/util/print-global-definitions/.print-decl { 3041 local __ble_name=$1 __ble_decl= 3042 if [[ ! ${!__ble_name+set} || :$2: == *:unset:* ]]; then 3043 __ble_decl="declare $__ble_name; builtin unset -v $__ble_name" 3044 elif ble/variable#has-attr "$__ble_name" aA; then 3045 if ((_ble_bash>=40000)); then 3046 ble/util/assign __ble_decl "declare -p $__ble_name" 2>/dev/null 3047 __ble_decl=${__ble_decl#declare -* } 3048 else 3049 ble/util/assign __ble_decl "ble/util/declare-print-definitions $__ble_name" 2>/dev/null 3050 fi 3051 if ble/is-array "$__ble_name"; then 3052 __ble_decl="declare -a $__ble_decl" 3053 else 3054 __ble_decl="declare -A $__ble_decl" 3055 fi 3056 else 3057 __ble_decl=${!__ble_name} 3058 __ble_decl="declare $__ble_name='${__ble_decl//$__ble_q/$__ble_Q}'" 3059 fi 3060 ble/util/print "$__ble_decl" 3061 } 3062 3063 ## @fn ble/util/print-global-definitions varnames... 3064 ## @var[in] varnames 3065 ## 3066 ## 指定した変数のグローバル変数としての定義を出力します。 3067 ## 3068 ## 制限: 途中同名の readonly ローカル変数がある場合は、 3069 ## グローバル変数の値は取得できないので unset を返す。 3070 ## そもそも readonly な変数には問題が多いので ble.sh では使わない。 3071 ## 3072 ## 制限: __ble_* という変数名はこの関数の実装に使用するので、 3073 ## 対応しない。 3074 ## 3075 ## Note: bash-4.2 にはバグがあって、グローバル変数が存在しない時に 3076 ## declare -g -r var とすると、ローカルに新しく読み取り専用の var 変数が作られる。 3077 ## 現在の実装では問題にならない。 3078 ## 3079 function ble/util/print-global-definitions { 3080 local __ble_opts= 3081 [[ $1 == --hidden-only ]] && { __ble_opts=hidden-only; shift; } 3082 ble/util/for-global-variables ble/util/print-global-definitions/.print-decl "$__ble_opts" "$@" 3083 } 3084 3085 ## @fn ble/util/for-global-variables proc opts varnames... 3086 ## @fn proc name opts 3087 ## @param[in] name 3088 ## 処理対象の変数名を指定します。 3089 ## @param[in] opts 3090 ## 指定した名前の変数が見つからない時 unset が指定されます。 3091 ## @param[in] opts 3092 ## hidden-only が含まれている時、対応するグローバル変数が別のローカル変数で 3093 ## 被覆されている時にのみ proc を呼び出します。 3094 ## @param[in] varnames... 3095 ## 処理対象の変数名の集合を指定します。 3096 function ble/util/for-global-variables { 3097 local __ble_proc=$1 __ble_opts=$2; shift 2 3098 local __ble_hidden_only= 3099 [[ :$__ble_opts: == *:hidden-only:* ]] && __ble_hidden_only=1 3100 ( 3101 ((_ble_bash>=50000)) && shopt -u localvar_unset 3102 __ble_error= 3103 __ble_q="'" __ble_Q="'\''" 3104 # 補完で 20 階層も関数呼び出しが重なることはなかろう 3105 __ble_MaxLoop=20 3106 builtin unset -v "${!_ble_processed_@}" 3107 3108 for __ble_name; do 3109 [[ ${__ble_name//[_a-zA-Z0-9]} || $__ble_name == __ble_* ]] && continue 3110 ((__ble_processed_$__ble_name)) && continue 3111 ((__ble_processed_$__ble_name=1)) 3112 3113 __ble_found= 3114 if ((_ble_bash>=40200)); then 3115 declare -g -r "$__ble_name" 3116 for ((__ble_i=0;__ble_i<__ble_MaxLoop;__ble_i++)); do 3117 if ! builtin unset -v "$__ble_name"; then 3118 # Note: Even if the variable is declared readonly at the top level, 3119 # we can test if the visible variable is global or not by using 3120 # `(readonly var; ! local var)' because `local var' succeeds if the 3121 # visible variable is local readonly. 3122 if builtin readonly "$__ble_name"; ble/variable#is-global/.test "$__ble_name"; then 3123 __ble_found=1 3124 [[ $__ble_hidden_only && $__ble_i == 0 ]] || 3125 "$__ble_proc" "$__ble_name" 3126 fi 3127 break 3128 fi 3129 done 3130 else 3131 for ((__ble_i=0;__ble_i<__ble_MaxLoop;__ble_i++)); do 3132 if ble/variable#is-global "$__ble_name"; then 3133 __ble_found=1 3134 [[ $__ble_hidden_only && $__ble_i == 0 ]] || 3135 "$__ble_proc" "$__ble_name" 3136 break 3137 fi 3138 builtin unset -v "$__ble_name" || break 3139 done 3140 fi 3141 3142 if [[ ! $__ble_found ]]; then 3143 __ble_error=1 3144 [[ $__ble_hidden_only && $__ble_i == 0 ]] || 3145 "$__ble_proc" "$__ble_name" unset 3146 fi 3147 done 3148 3149 [[ ! $__ble_error ]] 3150 ) 2>/dev/null 3151 } 3152 3153 ## @fn ble/util/has-glob-pattern pattern 3154 ## 指定したパターンがグロブパターンを含むかどうかを判定します。 3155 ## 3156 ## Note: Bash 5.0 では変数に \ が含まれている時に echo $var を実行すると 3157 ## パス名展開と解釈されて failglob, nullglob などが有効になるが、 3158 ## echo \[a\] の様に明示的に書いている場合にはパス名展開と解釈されない。 3159 ## この判定では明示的に書いた時にグロブパターンと認識されるかどうかに基づく。 3160 function ble/util/has-glob-pattern { 3161 [[ $1 ]] || return 1 3162 3163 local restore=: 3164 if ! shopt -q nullglob 2>/dev/null; then 3165 restore="$restore;shopt -u nullglob" 3166 shopt -s nullglob 3167 fi 3168 if shopt -q failglob 2>/dev/null; then 3169 restore="$restore;shopt -s failglob" 3170 shopt -u failglob 3171 fi 3172 3173 local dummy=$_ble_base_run/$$.dummy ret 3174 builtin eval "ret=(\"\$dummy\"/${1#/})" 2>/dev/null 3175 builtin eval -- "$restore" 3176 [[ ! $ret ]] 3177 } 3178 3179 ## @fn ble/util/is-cygwin-slow-glob word 3180 ## Cygwin では // で始まるパスの展開は遅い (#D1168) のでその判定を行う。 3181 function ble/util/is-cygwin-slow-glob { 3182 # Note: core-complete.sh ではエスケープを行うので 3183 # "'//...'" 等の様な文字列が "$1" に渡される。 3184 [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && ${1#\'} == //* && ! -o noglob ]] && 3185 ble/util/has-glob-pattern "$1" 3186 } 3187 3188 ## @fn ble/util/eval-pathname-expansion pattern 3189 ## @var[out] ret 3190 function ble/util/eval-pathname-expansion { 3191 ret=() 3192 if ble/util/is-cygwin-slow-glob; then # Note: #D1168 3193 if shopt -q failglob &>/dev/null; then 3194 return 1 3195 elif shopt -q nullglob &>/dev/null; then 3196 return 0 3197 else 3198 set -f 3199 ble/util/eval-pathname-expansion "$1"; local ext=$1 3200 set +f 3201 return "$ext" 3202 fi 3203 fi 3204 3205 # adjust glob settings 3206 local canon= 3207 if [[ :$2: == *:canonical:* ]]; then 3208 canon=1 3209 local set=$- shopt gignore=$GLOBIGNORE 3210 if ((_ble_bash>=40100)); then 3211 shopt=$BASHOPTS 3212 else 3213 shopt= 3214 shopt -q failglob && shopt=$shopt:failglob 3215 shopt -q nullglob && shopt=$shopt:nullglob 3216 shopt -q extglob && shopt=$shopt:extglob 3217 shopt -q dotglob && shopt=$shopt:dotglob 3218 fi 3219 shopt -u failglob 3220 shopt -s nullglob 3221 shopt -s extglob 3222 set +f 3223 GLOBIGNORE= 3224 fi 3225 3226 # Note: eval で囲んでおかないと failglob 失敗時に続きが実行されない 3227 # Note: failglob で失敗した時のエラーメッセージは殺す 3228 builtin eval "ret=($1)" 2>/dev/null; local ext=$? 3229 3230 # restore glob settings 3231 if [[ $canon ]]; then 3232 # Note: dotglob is changed by GLOBIGNORE 3233 GLOBIGNORE=$gignore 3234 if [[ :$shopt: == *:dotglob:* ]]; then shopt -s dotglob; else shopt -u dotglob; fi 3235 [[ $set == *f* ]] && set -f 3236 [[ :$shopt: != *:extglob:* ]] && shopt -u extglob 3237 [[ :$shopt: != *:nullglob:* ]] && shopt -u nullglob 3238 [[ :$shopt: == *:failglob:* ]] && shopt -s failglob 3239 fi 3240 3241 return "$ext" 3242 } 3243 3244 3245 # 正規表現は _ble_bash>=30000 3246 _ble_util_rex_isprint='^[ -~]+' 3247 ## @fn ble/util/isprint+ str 3248 ## 3249 ## @var[out] BASH_REMATCH ble-exit/text/update/position で使用する。 3250 function ble/util/isprint+ { 3251 local LC_ALL= LC_COLLATE=C 3252 [[ $1 =~ $_ble_util_rex_isprint ]] 3253 } 3254 # suppress locale error #D1440 3255 ble/function#suppress-stderr ble/util/isprint+ 3256 3257 if ((_ble_bash>=40200)); then 3258 function ble/util/strftime { 3259 if [[ $1 = -v ]]; then 3260 builtin printf -v "$2" "%($3)T" "${4:--1}" 3261 else 3262 builtin printf "%($1)T" "${2:--1}" 3263 fi 3264 } 3265 else 3266 function ble/util/strftime { 3267 if [[ $1 = -v ]]; then 3268 local fmt=$3 time=$4 3269 ble/util/assign "$2" 'ble/bin/date +"$fmt" $time' 3270 else 3271 ble/bin/date +"$1" $2 3272 fi 3273 } 3274 fi 3275 3276 #%< util.hook.sh 3277 3278 #------------------------------------------------------------------------------ 3279 # ble/util/msleep 3280 3281 #%include benchmark.sh 3282 3283 function ble/util/msleep/.check-builtin-sleep { 3284 local ret; ble/util/readlink "$BASH" 3285 local bash_prefix=${ret%/*/*} 3286 if [[ -s $bash_prefix/lib/bash/sleep ]] && 3287 (enable -f "$bash_prefix/lib/bash/sleep" sleep && builtin sleep 0.0) &>/dev/null; then 3288 enable -f "$bash_prefix/lib/bash/sleep" sleep 3289 return 0 3290 else 3291 return 1 3292 fi 3293 } 3294 function ble/util/msleep/.check-sleep-decimal-support { 3295 local version; ble/util/assign version 'LC_ALL=C ble/bin/sleep --version 2>&1' 2>/dev/null # suppress locale error #D1440 3296 [[ $version == *'GNU coreutils'* || $OSTYPE == darwin* && $version == 'usage: sleep seconds' ]] 3297 } 3298 3299 _ble_util_msleep_delay=2000 # [usec] 3300 function ble/util/msleep/.core { 3301 local sec=${1%%.*} 3302 ((10#0${1##*.}&&sec++)) # 小数部分は切り上げ 3303 ble/bin/sleep "$sec" 3304 } 3305 function ble/util/msleep { 3306 local v=$((1000*$1-_ble_util_msleep_delay)) 3307 ((v<=0)) && v=0 3308 ble/util/sprintf v '%d.%06d' "$((v/1000000))" "$((v%1000000))" 3309 ble/util/msleep/.core "$v" 3310 } 3311 3312 _ble_util_msleep_calibrate_count=0 3313 function ble/util/msleep/.calibrate-loop { 3314 local _ble_measure_threshold=10000 3315 local ret nsec _ble_measure_count=1 v=0 3316 _ble_util_msleep_delay=0 ble-measure -q 'ble/util/msleep 1' 3317 local delay=$((nsec/1000-1000)) count=$_ble_util_msleep_calibrate_count 3318 ((count<=0||delay<_ble_util_msleep_delay)) && _ble_util_msleep_delay=$delay # 最小値 3319 # ((_ble_util_msleep_delay=(count*_ble_util_msleep_delay+delay)/(count+1))) # 平均値 3320 } 3321 function ble/util/msleep/calibrate { 3322 ble/util/msleep/.calibrate-loop &>/dev/null 3323 ((++_ble_util_msleep_calibrate_count<5)) && 3324 ble/util/idle.continue 3325 } 3326 3327 ## @fn ble/util/msleep/.use-read-timeout type 3328 ## @param[in] type 3329 ## FILE.OPEN 3330 ## FILE=fifo mkfifo によりファイルを作成します。 3331 ## FILE=zero /dev/zero を開きます。 3332 ## FILE=ptmx /dev/ptmx を開きます。 3333 ## OPEN=open 毎回ファイルを開きます。 3334 ## OPEN=exec1 ファイルを読み取り専用で開きます。 3335 ## OPEN=exec2 ファイルを読み書き両用で開きます。 3336 ## socket 3337 ## /dev/udp/0.0.0.0/80 を使います。 3338 ## procsub 3339 ## 9< <(sleep) を使います。 3340 function ble/util/msleep/.use-read-timeout { 3341 local msleep_type=$1 opts=${2-} 3342 _ble_util_msleep_fd= 3343 case $msleep_type in 3344 (socket) 3345 _ble_util_msleep_delay1=10000 # short msleep にかかる時間 [usec] 3346 _ble_util_msleep_delay2=50000 # /bin/sleep 0 にかかる時間 [usec] 3347 function ble/util/msleep/.core2 { 3348 ((v-=_ble_util_msleep_delay2)) 3349 ble/bin/sleep "$((v/1000000))" 3350 ((v%=1000000)) 3351 } 3352 function ble/util/msleep { 3353 local v=$((1000*$1-_ble_util_msleep_delay1)) 3354 ((v<=0)) && v=100 3355 ((v>1000000+_ble_util_msleep_delay2)) && 3356 ble/util/msleep/.core2 3357 ble/util/sprintf v '%d.%06d' "$((v/1000000))" "$((v%1000000))" 3358 ! ble/bash/read-timeout "$v" v < /dev/udp/0.0.0.0/80 3359 } 3360 function ble/util/msleep/.calibrate-loop { 3361 local _ble_measure_threshold=10000 3362 local ret nsec _ble_measure_count=1 v=0 3363 3364 _ble_util_msleep_delay1=0 ble-measure 'ble/util/msleep 1' 3365 local delay=$((nsec/1000-1000)) count=$_ble_util_msleep_calibrate_count 3366 ((count<=0||delay<_ble_util_msleep_delay1)) && _ble_util_msleep_delay1=$delay # 最小値 3367 3368 _ble_util_msleep_delay2=0 ble-measure 'ble/util/msleep/.core2' 3369 local delay=$((nsec/1000)) 3370 ((count<=0||delay<_ble_util_msleep_delay2)) && _ble_util_msleep_delay2=$delay # 最小値 3371 } ;; 3372 (procsub) 3373 _ble_util_msleep_delay=300 3374 ble/fd#alloc _ble_util_msleep_fd '< <( 3375 [[ $- == *i* ]] && builtin trap -- '' INT QUIT 3376 while kill -0 $$; do command sleep 300; done &>/dev/null 3377 )' 3378 function ble/util/msleep { 3379 local v=$((1000*$1-_ble_util_msleep_delay)) 3380 ((v<=0)) && v=100 3381 ble/util/sprintf v '%d.%06d' "$((v/1000000))" "$((v%1000000))" 3382 ! ble/bash/read-timeout "$v" -u "$_ble_util_msleep_fd" v 3383 } ;; 3384 (*.*) 3385 if local rex='^(fifo|zero|ptmx)\.(open|exec)([12])(-[a-z]+)?$'; [[ $msleep_type =~ $rex ]]; then 3386 local file=${BASH_REMATCH[1]} 3387 local open=${BASH_REMATCH[2]} 3388 local direction=${BASH_REMATCH[3]} 3389 local fall=${BASH_REMATCH[4]} 3390 3391 # tmpfile 3392 case $file in 3393 (fifo) 3394 _ble_util_msleep_tmp=$_ble_base_run/$$.util.msleep.pipe 3395 if [[ ! -p $_ble_util_msleep_tmp ]]; then 3396 [[ -e $_ble_util_msleep_tmp ]] && ble/bin/rm -rf "$_ble_util_msleep_tmp" 3397 ble/bin/mkfifo "$_ble_util_msleep_tmp" 3398 fi ;; 3399 (zero) 3400 open=dup 3401 _ble_util_msleep_tmp=$_ble_util_fd_zero ;; 3402 (ptmx) 3403 _ble_util_msleep_tmp=/dev/ptmx ;; 3404 esac 3405 3406 # redirection type 3407 local redir='<' 3408 ((direction==2)) && redir='<>' 3409 3410 # open type 3411 if [[ $open == dup ]]; then 3412 _ble_util_msleep_fd=$_ble_util_msleep_tmp 3413 _ble_util_msleep_read='! ble/bash/read-timeout "$v" -u "$_ble_util_msleep_fd" v' 3414 elif [[ $open == exec ]]; then 3415 ble/fd#alloc _ble_util_msleep_fd "$redir \"\$_ble_util_msleep_tmp\"" 3416 _ble_util_msleep_read='! ble/bash/read-timeout "$v" -u "$_ble_util_msleep_fd" v' 3417 else 3418 _ble_util_msleep_read='! ble/bash/read-timeout "$v" v '$redir' "$_ble_util_msleep_tmp"' 3419 fi 3420 3421 # fallback/switch 3422 if [[ $fall == '-coreutil' ]]; then 3423 _ble_util_msleep_switch=200 # [msec] 3424 _ble_util_msleep_delay1=2000 # short msleep にかかる時間 [usec] 3425 _ble_util_msleep_delay2=50000 # /bin/sleep 0 にかかる時間 [usec] 3426 function ble/util/msleep { 3427 if (($1<_ble_util_msleep_switch)); then 3428 local v=$((1000*$1-_ble_util_msleep_delay1)) 3429 ((v<=0)) && v=100 3430 ble/util/sprintf v '%d.%06d' "$((v/1000000))" "$((v%1000000))" 3431 builtin eval -- "$_ble_util_msleep_read" 3432 else 3433 local v=$((1000*$1-_ble_util_msleep_delay2)) 3434 ((v<=0)) && v=100 3435 ble/util/sprintf v '%d.%06d' "$((v/1000000))" "$((v%1000000))" 3436 ble/bin/sleep "$v" 3437 fi 3438 } 3439 function ble/util/msleep/.calibrate-loop { 3440 local _ble_measure_threshold=10000 3441 local ret nsec _ble_measure_count=1 3442 3443 _ble_util_msleep_switch=200 3444 _ble_util_msleep_delay1=0 ble-measure 'ble/util/msleep 1' 3445 local delay=$((nsec/1000-1000)) count=$_ble_util_msleep_calibrate_count 3446 ((count<=0||delay<_ble_util_msleep_delay1)) && _ble_util_msleep_delay1=$delay # 最小値を選択 3447 3448 _ble_util_msleep_delay2=0 ble-measure 'ble/bin/sleep 0' 3449 local delay=$((nsec/1000)) 3450 ((count<=0||delay<_ble_util_msleep_delay2)) && _ble_util_msleep_delay2=$delay # 最小値を選択 3451 ((_ble_util_msleep_switch=_ble_util_msleep_delay2/1000+10)) 3452 } 3453 else 3454 function ble/util/msleep { 3455 local v=$((1000*$1-_ble_util_msleep_delay)) 3456 ((v<=0)) && v=100 3457 ble/util/sprintf v '%d.%06d' "$((v/1000000))" "$((v%1000000))" 3458 builtin eval -- "$_ble_util_msleep_read" 3459 } 3460 fi 3461 fi ;; 3462 esac 3463 3464 # Note: 古い Cygwin では双方向パイプで "Communication error on send" というエラーになる。 3465 # 期待通りの振る舞いをしなかったらプロセス置換に置き換える。 #D1449 3466 # #D1467 Cygwin/Linux では timeout は 142 だが、これはシステム依存。 3467 # man bash にある様に 128 より大きいかどうかで判定 3468 if [[ :$opts: == *:check:* && $_ble_util_msleep_fd ]]; then 3469 if ble/bash/read-timeout 0.000001 -u "$_ble_util_msleep_fd" _ble_util_msleep_dummy 2>/dev/null; (($?<=128)); then 3470 ble/fd#close _ble_util_msleep_fd 3471 _ble_util_msleep_fd= 3472 return 1 3473 fi 3474 fi 3475 return 0 3476 } 3477 3478 _ble_util_msleep_builtin_available= 3479 if ((_ble_bash>=40400)) && ble/util/msleep/.check-builtin-sleep; then 3480 _ble_util_msleep_builtin_available=1 3481 _ble_util_msleep_delay=300 3482 function ble/util/msleep/.core { builtin sleep "$1"; } 3483 3484 ## @fn ble/builtin/sleep/.read-time time 3485 ## @var[out] a1 b1 3486 ## それぞれ整数部と小数部を返します。 3487 ## @var[in,out] flags 3488 function ble/builtin/sleep/.read-time { 3489 a1=0 b1=0 3490 local unit= exp= 3491 if local rex='^\+?([0-9]*)\.([0-9]*)([eE][-+]?[0-9]+)?([smhd]?)$'; [[ $1 =~ $rex ]]; then 3492 a1=${BASH_REMATCH[1]} 3493 b1=${BASH_REMATCH[2]}00000000000000 3494 b1=$((10#0${b1::14})) 3495 exp=${BASH_REMATCH[3]} 3496 unit=${BASH_REMATCH[4]} 3497 elif rex='^\+?([0-9]+)([eE][-+]?[0-9]+)?([smhd]?)$'; [[ $1 =~ $rex ]]; then 3498 a1=${BASH_REMATCH[1]} 3499 exp=${BASH_REMATCH[2]} 3500 unit=${BASH_REMATCH[3]} 3501 else 3502 ble/util/print "ble/builtin/sleep: invalid time spec '$1'" >&2 3503 flags=E$flags 3504 return 2 3505 fi 3506 3507 if [[ $exp ]]; then 3508 case $exp in 3509 ([eE]-*) 3510 ((exp=10#0${exp:2})) 3511 while ((exp--)); do 3512 ((b1=a1%10*frac_scale/10+b1/10,a1/=10)) 3513 done ;; 3514 ([eE]*) 3515 exp=${exp:1} 3516 ((exp=${exp#+})) 3517 while ((exp--)); do 3518 ((b1*=10,a1=a1*10+b1/frac_scale,b1%=frac_scale)) 3519 done ;; 3520 esac 3521 fi 3522 3523 local scale= 3524 case $unit in 3525 (d) ((scale=24*3600)) ;; 3526 (h) ((scale=3600)) ;; 3527 (m) ((scale=60)) ;; 3528 esac 3529 if [[ $scale ]]; then 3530 ((b1*=scale)) 3531 ((a1=a1*scale+b1/frac_scale)) 3532 ((b1%=frac_scale)) 3533 fi 3534 return 0 3535 } 3536 3537 function ble/builtin/sleep { 3538 local set shopt; ble/base/.adjust-bash-options set shopt 3539 local frac_scale=100000000000000 3540 local a=0 b=0 flags= 3541 if (($#==0)); then 3542 ble/util/print "ble/builtin/sleep: no argument" >&2 3543 flags=E$flags 3544 fi 3545 while (($#)); do 3546 case $1 in 3547 (--version) flags=v$flags ;; 3548 (--help) flags=h$flags ;; 3549 (-*) 3550 flags=E$flags 3551 ble/util/print "ble/builtin/sleep: unknown option '$1'" >&2 ;; 3552 (*) 3553 if local a1 b1; ble/builtin/sleep/.read-time "$1"; then 3554 ((b+=b1)) 3555 ((a=a+a1+b/frac_scale)) 3556 ((b%=frac_scale)) 3557 fi ;; 3558 esac 3559 shift 3560 done 3561 if [[ $flags == *h* ]]; then 3562 ble/util/print-lines \ 3563 'usage: sleep NUMBER[SUFFIX]...' \ 3564 'Pause for the time specified by the sum of the arguments. SUFFIX is one of "s"' \ 3565 '(seconds), "m" (minutes), "h" (hours) or "d" (days).' \ 3566 '' \ 3567 'OPTIONS' \ 3568 ' --help Show this help.' \ 3569 ' --version Show version.' 3570 fi 3571 if [[ $flags == *v* ]]; then 3572 ble/util/print "sleep (ble) $BLE_VERSION" 3573 fi 3574 if [[ $flags == *E* ]]; then 3575 ble/util/setexit 2 3576 elif [[ $flags == *[vh]* ]]; then 3577 ble/util/setexit 0 3578 else 3579 b=00000000000000$b 3580 b=${b:${#b}-14} 3581 builtin sleep "$a.$b" 3582 fi 3583 local ext=$? 3584 ble/base/.restore-bash-options set shopt 1 3585 return "$ext" 3586 } 3587 function sleep { ble/builtin/sleep "$@"; } 3588 elif [[ -f $_ble_base/lib/init-msleep.sh ]] && 3589 source "$_ble_base/lib/init-msleep.sh" && 3590 ble/util/msleep/.load-compiled-builtin 3591 then 3592 # 自前で sleep.so をコンパイルする。 3593 # 3594 # Note: #D1452 #D1468 #D1469 元々使っていた read -t による手法が 3595 # Bash のバグでブロックする事が分かった。bash 4.3..5.1 ならばどの OS 3596 # でも再現する。仕方が無いので自前で loadable builtin をコンパイルす 3597 # る事にした。と思ったがライセンスの問題でこれを有効にする訳には行か 3598 # ない。 3599 function ble/util/msleep { ble/builtin/msleep "$1"; } 3600 elif ((40000<=_ble_bash&&!(40300<=_ble_bash&&_ble_bash<50200))) && 3601 [[ $OSTYPE != cygwin* && $OSTYPE != mingw* && $OSTYPE != haiku* && $OSTYPE != minix* ]] 3602 then 3603 # FIFO (mkfifo) を予め読み書き両用で開いて置き read -t する方法。 3604 # 3605 # Note: #D1452 #D1468 #D1469 Bash 4.3 以降では一般に read -t が 3606 # SIGALRM との race condition で固まる可能性がある。socket 3607 # (/dev/udp) や fifo で特に問題が発生しやすい。特に Cygwin で顕著。 3608 # 但し、発生する頻度は環境や用法・手法によって異なる。Cygwin/MSYS, 3609 # Haiku 及び Minix では fifo は思う様に動かない。 3610 ble/util/msleep/.use-read-timeout fifo.exec2 3611 elif ((_ble_bash>=40000)) && ble/fd#is-open "$_ble_util_fd_zero"; then 3612 # /dev/zero に対して read -t する方法。 3613 # 3614 # Note: #D1452 #D1468 #D1469 元々使っていた FIFO に対する方法が安全 3615 # でない時は /dev/zero に対して read -t する。0 を読み続ける事になる 3616 # ので CPU を使う事になるが短時間の sleep の時のみに使う事にして我慢 3617 # する事にする。確認した全ての OS で /dev/zero は存在した (Linux, 3618 # Cygwin, FreeBSD, Solaris, Minix, Haiku, MSYS2)。 3619 ble/util/msleep/.use-read-timeout zero.exec1-coreutil 3620 elif ble/bin#freeze-utility-path sleepenh; then 3621 function ble/util/msleep/.core { ble/bin/sleepenh "$1" &>/dev/null; } 3622 elif ble/bin#freeze-utility-path usleep; then 3623 function ble/util/msleep { 3624 local v=$((1000*$1-_ble_util_msleep_delay)) 3625 ((v<=0)) && v=0 3626 ble/bin/usleep "$v" &>/dev/null 3627 } 3628 elif ble/util/msleep/.check-sleep-decimal-support; then 3629 function ble/util/msleep/.core { ble/bin/sleep "$1"; } 3630 fi 3631 3632 function ble/util/sleep { 3633 local msec=$((${1%%.*}*1000)) 3634 if [[ $1 == *.* ]]; then 3635 frac=${1##*.}000 3636 ((msec+=10#0${frac::3})) 3637 fi 3638 ble/util/msleep "$msec" 3639 } 3640 3641 #------------------------------------------------------------------------------ 3642 # ble/util/conditional-sync 3643 3644 function ble/util/conditional-sync/.collect-descendant-pids { 3645 local pid=$1 awk_script=' 3646 $1 ~ /^[0-9]+$/ && $2 ~ /^[0-9]+$/ { 3647 child[$2,child[$2]++]=$1; 3648 } 3649 function print_recursive(pid, _, n, i) { 3650 if (child[pid]) { 3651 n = child[pid]; 3652 child[pid] = 0; # avoid infinite loop 3653 for (i = 0; i < n; i++) { 3654 print_recursive(child[pid, i]); 3655 } 3656 } 3657 print pid; 3658 } 3659 END { print_recursive(pid); } 3660 ' 3661 ble/util/assign ret 'ble/bin/ps -A -o pid,ppid' 3662 ble/util/assign-array ret 'ble/bin/awk -v pid="$pid" "$awk_script" <<< "$ret"' 3663 } 3664 3665 ## @fn ble/util/conditional-sync/.kill 3666 ## @var[in] __ble_pid 3667 ## @var[in] __ble_opts 3668 function ble/util/conditional-sync/.kill { 3669 [[ $__ble_pid ]] || return 0 3670 local kill_pids 3671 if [[ :$__ble_opts: == *:killall:* ]]; then 3672 ble/util/conditional-sync/.collect-descendant-pids "$__ble_pid" 3673 kill_pids=("${ret[@]}") 3674 else 3675 kill_pids=("$__ble_pid") 3676 fi 3677 3678 # Note #D2031: In Cygwin/MSYS2 (Windows), we somehow need to fork at least 3679 # one process before `kill` to make sure it works. 3680 if [[ $OSTYPE == cygwin* || $OSTYPE == msys* ]]; then 3681 (ble/util/setexit 0) 3682 fi 3683 3684 if [[ :$__ble_opts: == *:SIGKILL:* ]]; then 3685 builtin kill -9 "${kill_pids[@]}" &>/dev/null 3686 else 3687 builtin kill -- "${kill_pids[@]}" &>/dev/null 3688 fi 3689 } &>/dev/null 3690 3691 ## @fn ble/util/conditional-sync command [condition weight opts] 3692 ## Evaluate COMMAND and kill it when CONDITION becomes unsatisfied before 3693 ## COMMAND ends. 3694 ## 3695 ## @param[in] command 3696 ## The command that is evaluated in a subshell. If an empty string is 3697 ## specified, only the CONDITION is checked for the synchronization and any 3698 ## background subshell is not started. 3699 ## 3700 ## @param[in,opt] condition 3701 ## The command to test the condition to continue to run the command. The 3702 ## default condition is "! ble/decode/has-input". The following local 3703 ## variables are available from the condition command: 3704 ## 3705 ## @var sync_elapsed 3706 ## Accumulated time of sleep in milliseconds. 3707 ## 3708 ## @param[in,opt] weight 3709 ## The interval of checking CONDITION in milliseconds. The default is 3710 ## "100". 3711 ## @param[in,opt] opts 3712 ## A colon-separated list of the following fields to control the detailed 3713 ## behavior: 3714 ## 3715 ## @opt progressive-weight 3716 ## The interval of checking CONDITION is gradually increased and stops 3717 ## at the value specified by WEIGHT. 3718 ## @opt timeout=TIMEOUT 3719 ## When this is specified, COMMAND is unconditionally terminated when 3720 ## it does not end until the time specified by TIMEOUT in milliseconds 3721 ## @opt killall 3722 ## Kill also all the children and descendant processes. When this is 3723 ## unspecified, only the subshell used to run COMMAND is killed. 3724 ## @opt SIGKILL 3725 ## The processes are killed by SIGKILL. When this is unspecified, the 3726 ## processes are killed by SIGTERM. 3727 ## 3728 ## @opt pid=PID 3729 ## When specified, COMMAND is not evaluate, and the function instead 3730 ## waits for the exit of the process specified by PID. If a negative 3731 ## integer is specified, it is treated as PGID. When the condition is 3732 ## unsatisfied or the timeout has been reached, the specified process 3733 ## will be killed. 3734 ## 3735 ## @opt no-wait-pid 3736 ## Do not wait for the exit status of the background process 3737 ## 3738 function ble/util/conditional-sync { 3739 local __ble_command=$1 3740 local __ble_continue=${2:-'! ble/decode/has-input'} 3741 local __ble_weight=$3; ((__ble_weight<=0&&(__ble_weight=100))) 3742 local __ble_opts=$4 3743 3744 local __ble_timeout= __ble_rex=':timeout=([^:]+):' 3745 [[ :$__ble_opts: =~ $__ble_rex ]] && ((__ble_timeout=BASH_REMATCH[1])) 3746 3747 [[ :$__ble_opts: == *:progressive-weight:* ]] && 3748 local __ble_weight_max=$__ble_weight __ble_weight=1 3749 3750 # read opt "pid=PID/-PGID" 3751 local ret 3752 ble/opts#extract-last-optarg "$__ble_opts" pid 3753 local __ble_pid=$ret 3754 ble/util/unlocal ret 3755 3756 local sync_elapsed=0 3757 if [[ $__ble_timeout ]] && ((__ble_timeout<=0)); then 3758 ble/util/conditional-sync/.kill 3759 return 142 3760 fi 3761 builtin eval -- "$__ble_continue" || return 148 3762 ( 3763 [[ $__ble_pid ]] || builtin eval -- "$__ble_command" & __ble_pid=$! 3764 while 3765 # check timeout 3766 if [[ $__ble_timeout ]]; then 3767 if ((__ble_timeout<=0)); then 3768 ble/util/conditional-sync/.kill 3769 return 142 3770 fi 3771 ((__ble_weight>__ble_timeout)) && __ble_weight=$__ble_timeout 3772 ((__ble_timeout-=__ble_weight)) 3773 fi 3774 3775 ble/util/msleep "$__ble_weight" 3776 ((sync_elapsed+=__ble_weight)) 3777 [[ :$__ble_opts: == *:progressive-weight:* ]] && 3778 ((__ble_weight<<=1,__ble_weight>__ble_weight_max&&(__ble_weight=__ble_weight_max))) 3779 [[ ! $__ble_pid ]] || builtin kill -0 "$__ble_pid" &>/dev/null 3780 do 3781 if ! builtin eval -- "$__ble_continue"; then 3782 ble/util/conditional-sync/.kill 3783 return 148 3784 fi 3785 done 3786 [[ ! $__ble_pid || :$__ble_opts: == *:no-wait-pid:* ]] || wait "$__ble_pid" 3787 ) 3788 } 3789 3790 #------------------------------------------------------------------------------ 3791 3792 ## @fn ble/util/cat [files..] 3793 ## cat の代替。直接扱えない NUL で区切って読み出す。 3794 function ble/util/cat/.impl { 3795 local content= IFS= 3796 while ble/bash/read -d '' content; do 3797 printf '%s\0' "$content" 3798 done 3799 [[ $content ]] && printf '%s' "$content" 3800 } 3801 function ble/util/cat { 3802 if (($#)); then 3803 local file 3804 for file; do ble/util/cat/.impl < "$1"; done 3805 else 3806 ble/util/cat/.impl 3807 fi 3808 } 3809 3810 _ble_util_less_fallback= 3811 function ble/util/get-pager { 3812 if [[ ! $_ble_util_less_fallback ]]; then 3813 if type -t less &>/dev/null; then 3814 _ble_util_less_fallback=less 3815 elif type -t pager &>/dev/null; then 3816 _ble_util_less_fallback=pager 3817 elif type -t more &>/dev/null; then 3818 _ble_util_less_fallback=more 3819 else 3820 _ble_util_less_fallback=cat 3821 fi 3822 fi 3823 3824 builtin eval "$1=\${bleopt_pager:-\${PAGER:-\$_ble_util_less_fallback}}" 3825 } 3826 function ble/util/pager { 3827 local pager; ble/util/get-pager pager 3828 builtin eval -- "$pager \"\$@\"" 3829 } 3830 3831 _ble_util_file_stat= 3832 function ble/file/has-stat { 3833 if [[ ! $_ble_util_file_stat ]]; then 3834 _ble_util_file_stat=- 3835 if ble/bin#freeze-utility-path -n stat; then 3836 # 参考: http://stackoverflow.com/questions/17878684/best-way-to-get-file-modified-time-in-seconds 3837 if ble/bin/stat -c %Y / &>/dev/null; then 3838 _ble_util_file_stat=c 3839 elif ble/bin/stat -f %m / &>/dev/null; then 3840 _ble_util_file_stat=f 3841 fi 3842 fi 3843 fi 3844 3845 function ble/file/has-stat { [[ $_ble_util_file_stat != - ]]; } || return 1 3846 ble/file/has-stat 3847 } 3848 3849 ## @fn ble/file#mtime filename 3850 ## ファイル filename の mtime を取得します。 3851 ## @param[in] filename ファイル名を指定します。 3852 ## 3853 ## @var[out] ret 3854 ## 時刻を Unix Epoch で取得します。 3855 ## 秒以下の少数も取得できる場合には ret[1] に小数部を格納します。 3856 ## 3857 function ble/file#mtime { 3858 # fallback: print current time 3859 function ble/file#mtime { ble/util/strftime -v ret '%s %N'; ble/string#split-words ret "$ret"; ((0)); } || return 1 3860 3861 if ble/bin/date -r / +%s &>/dev/null; then 3862 function ble/file#mtime { local file=$1; ble/util/assign-words ret 'ble/bin/date -r "$file" +"%s %N"' 2>/dev/null; } 3863 elif ble/file/has-stat; then 3864 # 参考: http://stackoverflow.com/questions/17878684/best-way-to-get-file-modified-time-in-seconds 3865 case $_ble_util_file_stat in 3866 (c) function ble/file#mtime { local file=$1; ble/util/assign ret 'ble/bin/stat -c %Y "$file"' 2>/dev/null; } ;; 3867 (f) function ble/file#mtime { local file=$1; ble/util/assign ret 'ble/bin/stat -f %m "$file"' 2>/dev/null; } ;; 3868 esac 3869 fi 3870 3871 ble/file#mtime "$@" 3872 } 3873 3874 function ble/file#inode { 3875 # fallback 3876 function ble/file#inode { ret=; ((0)); } || return 1 3877 3878 if ble/bin#freeze-utility-path -n ls && 3879 ble/util/assign-words ret 'ble/bin/ls -di /' 2>/dev/null && 3880 ((${#ret[@]}==2)) && ble/string#match "$ret" '^[0-9]+$' 3881 then 3882 function ble/file#inode { local file=$1; ble/util/assign-words ret 'ble/bin/ls -di "$file"' 2>/dev/null; } 3883 elif ble/file/has-stat; then 3884 case $_ble_util_file_stat in 3885 (c) function ble/file#inode { local file=$1; ble/util/assign-words ret 'ble/bin/stat -c %i "$file"' 2>/dev/null; } ;; 3886 (f) function ble/file#inode { local file=$1; ble/util/assign-words ret 'ble/bin/stat -f %i "$file"' 2>/dev/null; } ;; 3887 esac 3888 fi 3889 3890 ble/file#inode "$@" 3891 } 3892 3893 function ble/file#hash { 3894 local file=$1 size 3895 if ! ble/util/assign size 'ble/bin/wc -c "$file" 2>/dev/null'; then 3896 ret=error:$RANDOM 3897 return 1 3898 fi 3899 ble/string#split-words size "$size" 3900 ble/file#hash/.impl 3901 } 3902 if ble/bin#freeze-utility-path -n git; then 3903 function ble/file#hash/.impl { 3904 ble/util/assign ret 'ble/bin/git hash-object "$file"' 3905 ret="size:$size;hash:$ret" 3906 } 3907 elif ble/bin#freeze-utility-path -n openssl; then 3908 function ble/file#hash/.impl { 3909 ble/util/assign-words ret 'ble/bin/openssl sha1 -r "$file"' 3910 ret="size:$size;sha1:$ret" 3911 } 3912 elif ble/bin#freeze-utility-path -n sha1sum; then 3913 function ble/file#hash/.impl { 3914 ble/util/assign-words ret 'ble/bin/sha1sum "$file"' 3915 ret="size:$size;sha1:$ret" 3916 } 3917 elif ble/bin#freeze-utility-path -n sha1; then 3918 function ble/file#hash/.impl { 3919 ble/util/assign-words ret 'ble/bin/sha1 -r "$file"' 3920 ret="size:$size;sha1:$ret" 3921 } 3922 elif ble/bin#freeze-utility-path -n md5sum; then 3923 function ble/file#hash/.impl { 3924 ble/util/assign-words ret 'ble/bin/md5sum "$file"' 3925 ret="size:$size;md5:$ret" 3926 } 3927 elif ble/bin#freeze-utility-path -n md5; then 3928 function ble/file#hash/.impl { 3929 ble/util/assign-words ret 'ble/bin/md5 -r "$file"' 3930 ret="size:$size;md5:$ret" 3931 } 3932 elif ble/bin#freeze-utility-path -n cksum; then 3933 function ble/file#hash/.impl { 3934 ble/util/assign-words ret 'ble/bin/cksum "$file"' 3935 ret="size:$size;cksum:$ret" 3936 } 3937 else 3938 function ble/file#hash/.impl { 3939 ret="size:$size" 3940 } 3941 fi 3942 3943 #------------------------------------------------------------------------------ 3944 ## @fn ble/util/buffer text 3945 _ble_util_buffer=() 3946 function ble/util/buffer { 3947 _ble_util_buffer[${#_ble_util_buffer[@]}]=$1 3948 } 3949 function ble/util/buffer.print { 3950 ble/util/buffer "$1"$'\n' 3951 } 3952 function ble/util/buffer.flush { 3953 IFS= builtin eval 'local text="${_ble_util_buffer[*]-}"' 3954 _ble_util_buffer=() 3955 [[ $text ]] || return 0 3956 3957 # Note: 出力の瞬間だけカーソルを非表示にする。Windows terminal など途中 3958 # のカーソル移動も無理やり表示しようとする端末に対する対策。 3959 [[ $_ble_term_state == internal ]] && 3960 [[ $_ble_term_cursor_hidden_internal != hidden ]] && 3961 [[ $text != *"$_ble_term_civis"* && $text != *"$_ble_term_cvvis"* ]] && 3962 text=$_ble_term_civis$text$_ble_term_cvvis 3963 3964 ble/util/put "$text" 3965 } 3966 function ble/util/buffer.clear { 3967 _ble_util_buffer=() 3968 } 3969 3970 #------------------------------------------------------------------------------ 3971 # class dirty-range, urange 3972 3973 function ble/dirty-range#load { 3974 local prefix= 3975 if [[ $1 == --prefix=* ]]; then 3976 prefix=${1#--prefix=} 3977 ((beg=${prefix}beg, 3978 end=${prefix}end, 3979 end0=${prefix}end0)) 3980 fi 3981 } 3982 3983 function ble/dirty-range#clear { 3984 local prefix= 3985 if [[ $1 == --prefix=* ]]; then 3986 prefix=${1#--prefix=} 3987 shift 3988 fi 3989 3990 ((${prefix}beg=-1, 3991 ${prefix}end=-1, 3992 ${prefix}end0=-1)) 3993 } 3994 3995 ## @fn ble/dirty-range#update [--prefix=PREFIX] beg end end0 3996 ## @param[out] PREFIX 3997 ## @param[in] beg 変更開始点。beg<0 は変更がない事を表す 3998 ## @param[in] end 変更終了点。end<0 は変更が末端までである事を表す 3999 ## @param[in] end0 変更前の end に対応する位置。 4000 function ble/dirty-range#update { 4001 local prefix= 4002 if [[ $1 == --prefix=* ]]; then 4003 prefix=${1#--prefix=} 4004 shift 4005 [[ $prefix ]] && local beg end end0 4006 fi 4007 4008 local begB=$1 endB=$2 endB0=$3 4009 ((begB<0)) && return 1 4010 4011 local begA endA endA0 4012 ((begA=${prefix}beg,endA=${prefix}end,endA0=${prefix}end0)) 4013 4014 local delta 4015 if ((begA<0)); then 4016 ((beg=begB, 4017 end=endB, 4018 end0=endB0)) 4019 else 4020 ((beg=begA<begB?begA:begB)) 4021 if ((endA<0||endB<0)); then 4022 ((end=-1,end0=-1)) 4023 else 4024 ((end=endB,end0=endA0, 4025 (delta=endA-endB0)>0?(end+=delta):(end0-=delta))) 4026 fi 4027 fi 4028 4029 if [[ $prefix ]]; then 4030 ((${prefix}beg=beg, 4031 ${prefix}end=end, 4032 ${prefix}end0=end0)) 4033 fi 4034 } 4035 4036 ## @fn ble/urange#clear [--prefix=prefix] 4037 ## 4038 ## @param[in,opt] prefix= 4039 ## @var[in,out] {prefix}umin {prefix}umax 4040 ## 4041 function ble/urange#clear { 4042 local prefix= 4043 if [[ $1 == --prefix=* ]]; then 4044 prefix=${1#*=}; shift 4045 fi 4046 ((${prefix}umin=-1,${prefix}umax=-1)) 4047 } 4048 ## @fn ble/urange#update [--prefix=prefix] min max 4049 ## 4050 ## @param[in,opt] prefix= 4051 ## @param[in] min max 4052 ## @var[in,out] {prefix}umin {prefix}umax 4053 ## 4054 function ble/urange#update { 4055 local prefix= 4056 if [[ $1 == --prefix=* ]]; then 4057 prefix=${1#*=}; shift 4058 fi 4059 local min=$1 max=$2 4060 ((0<=min&&min<max)) || return 1 4061 (((${prefix}umin<0||min<${prefix}umin)&&(${prefix}umin=min), 4062 (${prefix}umax<0||${prefix}umax<max)&&(${prefix}umax=max))) 4063 } 4064 ## @fn ble/urange#shift [--prefix=prefix] dbeg dend dend0 4065 ## 4066 ## @param[in,opt] prefix= 4067 ## @param[in] dbeg dend dend0 4068 ## @var[in,out] {prefix}umin {prefix}umax 4069 ## 4070 function ble/urange#shift { 4071 local prefix= 4072 if [[ $1 == --prefix=* ]]; then 4073 prefix=${1#*=}; shift 4074 fi 4075 local dbeg=$1 dend=$2 dend0=$3 shift=$4 4076 ((dbeg>=0)) || return 1 4077 [[ $shift ]] || ((shift=dend-dend0)) 4078 ((${prefix}umin>=0&&( 4079 dbeg<=${prefix}umin&&(${prefix}umin<=dend0?(${prefix}umin=dend):(${prefix}umin+=shift)), 4080 dbeg<=${prefix}umax&&(${prefix}umax<=dend0?(${prefix}umax=dbeg):(${prefix}umax+=shift))), 4081 ${prefix}umin<${prefix}umax||( 4082 ${prefix}umin=-1, 4083 ${prefix}umax=-1))) 4084 } 4085 4086 #------------------------------------------------------------------------------ 4087 ## @fn ble/util/joblist opts 4088 ## 現在のジョブ一覧を取得すると共に、ジョブ状態の変化を調べる。 4089 ## 4090 ## @param[in] opts 4091 ## ignore-volatile-jobs 4092 ## 4093 ## @var[in,out] _ble_util_joblist_events 4094 ## @var[out] joblist ジョブ一覧を格納する配列 4095 ## @var[in,out] _ble_util_joblist_jobs 内部使用 4096 ## @var[in,out] _ble_util_joblist_list 内部使用 4097 ## 4098 ## @remark 実装方法について。 4099 ## 終了したジョブを確認するために内部で2回 jobs を呼び出す。 4100 ## 比較のために前回の jobs の呼び出し結果も _ble_util_joblist_{jobs,list} (#1) に記録する。 4101 ## 先ず jobs0,list (#2) に1回目の jobs 呼び出し結果を格納して #1 と #2 の比較を行いジョブ状態の変化を調べる。 4102 ## 次に #1 に2回目の jobs 呼び出し結果を上書きして #2 と #1 の比較を行い終了ジョブを調べる。 4103 ## 4104 _ble_util_joblist_jobs= 4105 _ble_util_joblist_list=() 4106 _ble_util_joblist_events=() 4107 function ble/util/joblist { 4108 local opts=$1 jobs0 4109 ble/util/assign jobs0 'jobs' 4110 if [[ $jobs0 == "$_ble_util_joblist_jobs" ]]; then 4111 # 前回の呼び出し結果と同じならば状態変化はないものとして良い。終了・強制終 4112 # 了したジョブがあるとしたら "終了" だとか "Terminated" だとかいう表示にな 4113 # っているはずだが、その様な表示は二回以上は為されないので必ず変化がある。 4114 joblist=("${_ble_util_joblist_list[@]}") 4115 return 0 4116 elif [[ ! $jobs0 ]]; then 4117 # 前回の呼び出しで存在したジョブが新しい呼び出しで無断で消滅することは恐ら 4118 # くない。今回の結果が空という事は本来は前回の結果も空のはずであり、だとす 4119 # ると上の分岐に入るはずなのでここには来ないはずだ。しかしここに入った時の 4120 # 為に念を入れて空に設定して戻るようにする。 4121 _ble_util_joblist_jobs= 4122 _ble_util_joblist_list=() 4123 joblist=() 4124 return 0 4125 fi 4126 4127 local lines list ijob 4128 ble/string#split lines $'\n' "$jobs0" 4129 if ((${#lines[@]})); then 4130 ble/util/joblist.split list "${lines[@]}" 4131 else 4132 list=() 4133 fi 4134 4135 # check changed jobs from _ble_util_joblist_list to list 4136 if [[ $jobs0 != "$_ble_util_joblist_jobs" ]]; then 4137 for ijob in "${!list[@]}"; do 4138 if [[ ${_ble_util_joblist_list[ijob]} && ${list[ijob]#'['*']'[-+ ]} != "${_ble_util_joblist_list[ijob]#'['*']'[-+ ]}" ]]; then 4139 if [[ ${list[ijob]} != *'__ble_suppress_joblist__'* ]]; then 4140 ble/array#push _ble_util_joblist_events "${list[ijob]}" 4141 fi 4142 list[ijob]= 4143 fi 4144 done 4145 fi 4146 4147 ble/util/assign _ble_util_joblist_jobs 'jobs' 4148 _ble_util_joblist_list=() 4149 if [[ $_ble_util_joblist_jobs != "$jobs0" ]]; then 4150 ble/string#split lines $'\n' "$_ble_util_joblist_jobs" 4151 ble/util/joblist.split _ble_util_joblist_list "${lines[@]}" 4152 4153 # check removed jobs through list -> _ble_util_joblist_list. 4154 if [[ :$opts: != *:ignore-volatile-jobs:* ]]; then 4155 for ijob in "${!list[@]}"; do 4156 local job0=${list[ijob]} 4157 if [[ $job0 && ! ${_ble_util_joblist_list[ijob]} ]]; then 4158 if [[ $job0 != *'__ble_suppress_joblist__'* ]]; then 4159 ble/array#push _ble_util_joblist_events "$job0" 4160 fi 4161 fi 4162 done 4163 fi 4164 else 4165 for ijob in "${!list[@]}"; do 4166 [[ ${list[ijob]} ]] && 4167 _ble_util_joblist_list[ijob]=${list[ijob]} 4168 done 4169 fi 4170 joblist=("${_ble_util_joblist_list[@]}") 4171 } 2>/dev/null 4172 4173 function ble/util/joblist.split { 4174 local arr=$1; shift 4175 local line ijob= rex_ijob='^\[([0-9]+)\]' 4176 for line; do 4177 [[ $line =~ $rex_ijob ]] && ijob=${BASH_REMATCH[1]} 4178 [[ $ijob ]] && builtin eval "$arr[ijob]=\${$arr[ijob]}\${$arr[ijob]:+\$_ble_term_nl}\$line" 4179 done 4180 } 4181 4182 ## @fn ble/util/joblist.check 4183 ## ジョブ状態変化の確認だけ行います。 4184 ## 内部的に jobs を呼び出す直前に、ジョブ状態変化を取り逃がさない為に明示的に呼び出します。 4185 function ble/util/joblist.check { 4186 local joblist 4187 ble/util/joblist "$@" 4188 } 4189 ## @fn ble/util/joblist.has-events 4190 ## 未出力のジョブ状態変化の記録があるかを確認します。 4191 function ble/util/joblist.has-events { 4192 local joblist 4193 ble/util/joblist 4194 ((${#_ble_util_joblist_events[@]})) 4195 } 4196 4197 ## @fn ble/util/joblist.flush 4198 ## ジョブ状態変化の確認とそれまでに検出した変化の出力を行います。 4199 function ble/util/joblist.flush { 4200 local joblist 4201 ble/util/joblist 4202 ((${#_ble_util_joblist_events[@]})) || return 1 4203 printf '%s\n' "${_ble_util_joblist_events[@]}" 4204 _ble_util_joblist_events=() 4205 } 4206 function ble/util/joblist.bflush { 4207 local joblist out 4208 ble/util/joblist 4209 ((${#_ble_util_joblist_events[@]})) || return 1 4210 ble/util/sprintf out '%s\n' "${_ble_util_joblist_events[@]}" 4211 ble/util/buffer "$out" 4212 _ble_util_joblist_events=() 4213 } 4214 4215 ## @fn ble/util/joblist.clear 4216 ## bash 自身によってジョブ状態変化が出力される場合には比較用のバッファを clear します。 4217 function ble/util/joblist.clear { 4218 _ble_util_joblist_jobs= 4219 _ble_util_joblist_list=() 4220 } 4221 4222 #------------------------------------------------------------------------------ 4223 ## @fn ble/util/save-editing-mode varname 4224 ## 現在の編集モード (emacs/vi/none) を変数に設定します。 4225 ## 4226 ## @param varname 設定する変数の変数名を指定します。 4227 ## 4228 function ble/util/save-editing-mode { 4229 if [[ -o emacs ]]; then 4230 builtin eval "$1=emacs" 4231 elif [[ -o vi ]]; then 4232 builtin eval "$1=vi" 4233 else 4234 builtin eval "$1=none" 4235 fi 4236 } 4237 ## @fn ble/util/restore-editing-mode varname 4238 ## 編集モードを復元します。 4239 ## 4240 ## @param varname 編集モードを記録した変数の変数名を指定します。 4241 ## 4242 function ble/util/restore-editing-mode { 4243 case ${!1} in 4244 (emacs) set -o emacs ;; 4245 (vi) set -o vi ;; 4246 (none) set +o emacs ;; 4247 esac 4248 } 4249 4250 ## @fn ble/util/reset-keymap-of-editing-mode 4251 ## 既定の keymap に戻す。bind 'set keymap vi-insert' 等で 4252 ## 既定の keymap 以外になっている事がある。 4253 ## set -o emacs/vi を実行すれば既定の keymap に戻る。#D1038 4254 function ble/util/reset-keymap-of-editing-mode { 4255 if [[ -o emacs ]]; then 4256 set -o emacs 4257 elif [[ -o vi ]]; then 4258 set -o vi 4259 fi 4260 } 4261 4262 ## @fn ble/util/rlvar#load 4263 ## @var[out] _ble_local_rlvars 4264 function ble/util/rlvar#load { 4265 # Note (#D1823): suppress warnings in a non-interactive session 4266 ble/util/assign _ble_local_rlvars 'builtin bind -v 2>/dev/null' 4267 _ble_local_rlvars=$'\n'$_ble_local_rlvars 4268 } 4269 4270 ## @fn ble/util/rlvar#has name 4271 ## 指定した readline 変数に bash が対応しているか確認します。 4272 function ble/util/rlvar#has { 4273 if [[ ! ${_ble_local_rlvars:-} ]]; then 4274 local _ble_local_rlvars 4275 ble/util/rlvar#load 4276 fi 4277 [[ $_ble_local_rlvars == *$'\n'"set $1 "* ]] 4278 } 4279 4280 ## @fn ble/util/rlvar#test name [default(0 or 1)] 4281 function ble/util/rlvar#test { 4282 if [[ ! ${_ble_local_rlvars:-} ]]; then 4283 local _ble_local_rlvars 4284 ble/util/rlvar#load 4285 fi 4286 if [[ $_ble_local_rlvars == *$'\n'"set $1 on"* ]]; then 4287 return 0 4288 elif [[ $_ble_local_rlvars == *$'\n'"set $1 off"* ]]; then 4289 return 1 4290 elif (($#>=2)); then 4291 (($2)) 4292 return "$?" 4293 else 4294 return 2 4295 fi 4296 } 4297 ## @fn ble/util/rlvar#read name [default_value] 4298 function ble/util/rlvar#read { 4299 [[ ${2+set} ]] && ret=$2 4300 if [[ ! ${_ble_local_rlvars:-} ]]; then 4301 local _ble_local_rlvars 4302 ble/util/rlvar#load 4303 fi 4304 local rhs=${_ble_local_rlvars#*$'\n'"set $1 "} 4305 [[ $rhs != "$_ble_local_rlvars" ]] && ret=${rhs%%$'\n'*} 4306 } 4307 4308 ## @fn ble/util/rlvar#bind-bleopt name bleopt [opts] 4309 function ble/util/rlvar#bind-bleopt { 4310 local name=$1 bleopt=$2 opts=$3 4311 if [[ ! ${_ble_local_rlvars:-} ]]; then 4312 local _ble_local_rlvars 4313 ble/util/rlvar#load 4314 fi 4315 4316 # Bash が readlie 変数に対応している場合、bleopt に対する代入と合わせて 4317 # readline 変数にも対応する値を設定する。 4318 if ble/util/rlvar#has "$name"; then 4319 # 値の同期 4320 # Note (#D1148): ble.sh の側で Bash と異なる既定値を持っている物については 4321 # (初期化時に --keep-rlvars を指定していない限りは) ble.sh の側に書き換えて 4322 # しまう。多くのユーザは自分で設定しないので便利な機能が off になっている。 4323 # 一方で設定するユーザは自分で off に戻すぐらいはできるだろう。 4324 if [[ :$_ble_base_arguments_opts: == *:keep-rlvars:* ]]; then 4325 local ret; ble/util/rlvar#read "$name" 4326 [[ :$opts: == *:bool:* && $ret == off ]] && ret= 4327 bleopt "$bleopt=$ret" 4328 else 4329 local var=bleopt_$bleopt val=off 4330 [[ ${!var:-} ]] && val=on 4331 # Note: #D1823 suppress stderr for non-interactive warning 4332 builtin bind "set $name $val" 2>/dev/null 4333 fi 4334 4335 local proc_original= 4336 if ble/is-function "bleopt/check:$bleopt"; then 4337 ble/function#push "bleopt/check:$bleopt" 4338 proc_original='ble/function#push/call-top "$@" || return "$?"' 4339 fi 4340 4341 # Note: #D1823 suppress stderr for non-interactive warning 4342 local proc_set='builtin bind "set '$name' $value" 2>/dev/null' 4343 if [[ :$opts: == *:bool:* ]]; then 4344 proc_set=' 4345 if [[ $value ]]; then 4346 builtin bind "set '$name' on" 2>/dev/null 4347 else 4348 builtin bind "set '$name' off" 2>/dev/null 4349 fi' 4350 fi 4351 4352 builtin eval -- " 4353 function bleopt/check:$bleopt { 4354 $proc_original 4355 $proc_set 4356 return 0 4357 }" 4358 fi 4359 4360 local proc_bleopt='bleopt '$bleopt'="$1"' 4361 if [[ :$opts: == *:bool:* ]]; then 4362 proc_bleopt=' 4363 local value; ble/string#split-words value "$1" 4364 if [[ ${value-} == 1 || ${value-} == [Oo][Nn] ]]; then 4365 bleopt '$bleopt'="$value" 4366 else 4367 bleopt '$bleopt'= 4368 fi' 4369 fi 4370 builtin eval -- " 4371 function ble/builtin/bind/set:$name { 4372 $proc_bleopt 4373 return 0 4374 }" 4375 } 4376 4377 #------------------------------------------------------------------------------ 4378 # Functions for modules 4379 4380 ## @fn ble/util/invoke-hook array 4381 ## array に登録されているコマンドを実行します。 4382 function ble/util/invoke-hook { 4383 local -a hooks; builtin eval "hooks=(\"\${$1[@]}\")" 4384 local hook ext=0 4385 for hook in "${hooks[@]}"; do builtin eval -- "$hook \"\${@:2}\"" || ext=$?; done 4386 return "$ext" 4387 } 4388 4389 ## @fn ble/util/.read-arguments-for-no-option-command commandname args... 4390 ## @var[out] flags args 4391 function ble/util/.read-arguments-for-no-option-command { 4392 local commandname=$1; shift 4393 flags= args=() 4394 4395 local flag_literal= 4396 while (($#)); do 4397 local arg=$1; shift 4398 if [[ ! $flag_literal ]]; then 4399 case $arg in 4400 (--) flag_literal=1 ;; 4401 (--help) flags=h$flags ;; 4402 (-*) 4403 ble/util/print "$commandname: unrecognized option '$arg'" >&2 4404 flags=e$flags ;; 4405 (*) 4406 ble/array#push args "$arg" ;; 4407 esac 4408 else 4409 ble/array#push args "$arg" 4410 fi 4411 done 4412 } 4413 4414 4415 ## @fn ble-autoload scriptfile functions... 4416 ## 関数が定義されたファイルを自動で読み取る設定を行います。 4417 ## scriptfile には functions の実体を定義します。 4418 ## functions に指定した関数が初めて呼び出された時に、 4419 ## scriptfile が自動的に source されます。 4420 ## 4421 ## @param[in] scriptfile 4422 ## functions が定義されているファイル 4423 ## 4424 ## 注意: このファイル内でグローバルに変数を定義する際は 4425 ## declare/typeset を用いないで下さい。 4426 ## autoload を行う関数内から source されるので、 4427 ## その関数のローカル変数として扱われてしまいます。 4428 ## 連想配列などの特殊変数を定義したい場合は ble-autoload 4429 ## の設定時に同時に行って下さい。 4430 ## ※declare -g は bash-4.3 以降です 4431 ## 4432 ## @param[in] functions... 4433 ## 定義する関数名のリスト 4434 ## 4435 ## scriptfile の source の起点となる関数です。 4436 ## scriptfile に定義される関数名を全て列挙する必要はなく、 4437 ## scriptfile 呼出の起点として使用する関数のみで充分です。 4438 ## 4439 function ble/util/autoload { 4440 local file=$1; shift 4441 ble/util/import/is-loaded "$file" && return 0 4442 4443 # ※$FUNCNAME は元から環境変数に設定されている場合、 4444 # 特別変数として定義されない。 4445 # この場合無闇にコマンドとして実行するのは危険である。 4446 4447 local q=\' Q="'\''" funcname 4448 for funcname; do 4449 builtin eval "function $funcname { 4450 builtin unset -f $funcname 4451 ble-import '${file//$q/$Q}' && 4452 $funcname \"\$@\" 4453 }" 4454 done 4455 } 4456 function ble/util/autoload/.print-usage { 4457 ble/util/print 'usage: ble-autoload SCRIPTFILE FUNCTION...' 4458 ble/util/print ' Setup delayed loading of functions defined in the specified script file.' 4459 } >&2 4460 ## @fn ble/util/autoload/.read-arguments args... 4461 ## @var[out] file functions flags 4462 function ble/util/autoload/.read-arguments { 4463 file= flags= functions=() 4464 4465 local args 4466 ble/util/.read-arguments-for-no-option-command ble-autoload "$@" 4467 4468 # check empty arguments 4469 local arg index=0 4470 for arg in "${args[@]}"; do 4471 if [[ ! $arg ]]; then 4472 if ((index==0)); then 4473 ble/util/print 'ble-autoload: the script filename should not be empty.' >&2 4474 else 4475 ble/util/print 'ble-autoload: function names should not be empty.' >&2 4476 fi 4477 flags=e$flags 4478 fi 4479 ((index++)) 4480 done 4481 4482 [[ $flags == *h* ]] && return 0 4483 4484 if ((${#args[*]}==0)); then 4485 ble/util/print 'ble-autoload: script filename is not specified.' >&2 4486 flags=e$flags 4487 elif ((${#args[*]}==1)); then 4488 ble/util/print 'ble-autoload: function names are not specified.' >&2 4489 flags=e$flags 4490 fi 4491 4492 file=${args[0]} functions=("${args[@]:1}") 4493 } 4494 function ble-autoload { 4495 local file flags 4496 local -a functions=() 4497 ble/util/autoload/.read-arguments "$@" 4498 if [[ $flags == *[eh]* ]]; then 4499 [[ $flags == *e* ]] && builtin printf '\n' 4500 ble/util/autoload/.print-usage 4501 [[ $flags == *e* ]] && return 2 4502 return 0 4503 fi 4504 4505 ble/util/autoload "$file" "${functions[@]}" 4506 } 4507 4508 ## @fn ble-import scriptfile... 4509 ## 指定したファイルを検索して source で読み込みます。 4510 ## 既に import 済みのファイルは読み込みません。 4511 ## 4512 ## @param[in] scriptfile 4513 ## 読み込むファイルを指定します。 4514 ## 絶対パスで指定した場合にはそのファイルを使用します。 4515 ## それ以外の場合には $_ble_base:$_ble_base/local:$_ble_base/share から検索します。 4516 ## 4517 _ble_util_import_files=() 4518 4519 bleopt/declare -n import_path "${XDG_DATA_HOME:-$HOME/.local/share}/blesh/local" 4520 4521 ## @fn ble/util/import/search/.check-directory name dir 4522 ## @var[out] ret 4523 function ble/util/import/search/.check-directory { 4524 local name=$1 dir=${2%/} 4525 [[ -d ${dir:=/} ]] || return 1 4526 4527 # {lib,contrib}/ で始まるパスの時は lib,contrib ディレクトリのみで探索 4528 if [[ $name == lib/* ]]; then 4529 [[ $dir == */lib ]] || return 1 4530 dir=${dir%/lib} 4531 elif [[ $name == contrib/* ]]; then 4532 [[ $dir == */contrib ]] || return 1 4533 dir=${dir%/contrib} 4534 fi 4535 4536 if [[ -f $dir/$name ]]; then 4537 ret=$dir/$name 4538 return 0 4539 elif [[ $name != *.bash && -f $dir/$name.bash ]]; then 4540 ret=$dir/$name.bash 4541 return 0 4542 elif [[ $name != *.sh && -f $dir/$name.sh ]]; then 4543 ret=$dir/$name.sh 4544 return 0 4545 fi 4546 return 1 4547 } 4548 function ble/util/import/search { 4549 ret=$1 4550 if [[ $ret != /* && $ret != ./* && $ret != ../* ]]; then 4551 local -a dirs=() 4552 if [[ $bleopt_import_path ]]; then 4553 local tmp; ble/string#split tmp : "$bleopt_import_path" 4554 ble/array#push dirs "${tmp[@]}" 4555 fi 4556 ble/array#push dirs "$_ble_base"{,/contrib,/lib} 4557 4558 "${_ble_util_set_declare[@]//NAME/checked}" # WA #D1570 checked 4559 local path 4560 for path in "${dirs[@]}"; do 4561 ble/set#contains checked "$path" && continue 4562 ble/set#add checked "$path" 4563 ble/util/import/search/.check-directory "$ret" "$path" && break 4564 done 4565 fi 4566 [[ -e $ret && ! -d $ret ]] 4567 } 4568 function ble/util/import/encode-filename { 4569 ret=$1 4570 local chars=%$'\t\n !"$&\'();<>\\^`|' # <emacs bug `> 4571 if [[ $ret == *["$chars"]* ]]; then 4572 local i n=${#chars} reps a b 4573 reps=(%{25,08,0A,2{0..2},24,2{6..9},3B,3C,3E,5C,5E,60,7C}) 4574 for ((i=0;i<n;i++)); do 4575 a=${chars:i:1} b=${reps[i]} ret=${ret//"$a"/"$b"} 4576 done 4577 fi 4578 return 0 4579 } 4580 function ble/util/import/is-loaded { 4581 local ret 4582 ble/util/import/search "$1" && 4583 ble/util/import/encode-filename "$ret" && 4584 ble/is-function ble/util/import/guard:"$ret" 4585 } 4586 # called by ble/base/unload (ble.pp) 4587 function ble/util/import/finalize { 4588 local file ret 4589 for file in "${_ble_util_import_files[@]}"; do 4590 ble/util/import/encode-filename "$file"; local enc=$ret 4591 local guard=ble/util/import/guard:$enc 4592 builtin unset -f "$guard" 4593 4594 local onload=ble/util/import/onload:$enc 4595 if ble/is-function "$onload"; then 4596 "$onload" ble/util/unlocal 4597 builtin unset -f "$onload" 4598 fi 4599 done 4600 _ble_util_import_files=() 4601 } 4602 ## @fn ble/util/import/.read-arguments args... 4603 ## @var[out] files not_found 4604 ## @var[out] flags 4605 ## E error 4606 ## h help 4607 ## d delay 4608 ## f force 4609 ## q query 4610 function ble/util/import/.read-arguments { 4611 flags= files=() not_found=() 4612 while (($#)); do 4613 local arg=$1; shift 4614 if [[ $flags != *-* ]]; then 4615 case $arg in 4616 (--) 4617 flags=-$flags 4618 continue ;; 4619 (--*) 4620 case $arg in 4621 (--delay) flags=d$flags ;; 4622 (--help) flags=h$flags ;; 4623 (--force) flags=f$flags ;; 4624 (--query) flags=q$flags ;; 4625 (*) 4626 ble/util/print "ble-import: unrecognized option '$arg'" >&2 4627 flags=E$flags ;; 4628 esac 4629 continue ;; 4630 (-?*) 4631 local i c 4632 for ((i=1;i<${#arg};i++)); do 4633 c=${arg:i:1} 4634 case $c in 4635 ([dfq]) flags=$c$flags ;; 4636 (*) 4637 ble/util/print "ble-import: unrecognized option '-$c'" >&2 4638 flags=E$flags ;; 4639 esac 4640 done 4641 continue ;; 4642 esac 4643 fi 4644 4645 local ret 4646 if ! ble/util/import/search "$arg"; then 4647 ble/array#push not_found "$arg" 4648 continue 4649 fi; local file=$ret 4650 ble/array#push files "$file" 4651 done 4652 4653 # 存在しないファイルがあった時 4654 if [[ $flags != *[fq]* ]] && ((${#not_found[@]})); then 4655 local file 4656 for file in "${not_found[@]}"; do 4657 ble/util/print "ble-import: file '$file' not found" >&2 4658 done 4659 flags=E$flags 4660 fi 4661 4662 return 0 4663 } 4664 function ble/util/import { 4665 local files file ext=0 ret enc 4666 files=("$@") 4667 set -- # Note #D: source によって引数が継承されるのを防ぐ 4668 for file in "${files[@]}"; do 4669 ble/util/import/encode-filename "$file"; enc=$ret 4670 local guard=ble/util/import/guard:$enc 4671 ble/is-function "$guard" && return 0 4672 [[ -e $file ]] || return 1 4673 source "$file" || { ext=$?; continue; } 4674 builtin eval "function $guard { :; }" 4675 ble/array#push _ble_util_import_files "$file" 4676 4677 local onload=ble/util/import/onload:$enc 4678 ble/function#try "$onload" ble/util/invoke-hook 4679 done 4680 return "$ext" 4681 } 4682 ## @fn ble/util/import/option:query 4683 ## @var[in] files not_found 4684 function ble/util/import/option:query { 4685 if ((${#not_found[@]})); then 4686 return 127 4687 elif ((${#files[@]})); then 4688 local file 4689 for file in "${files[@]}"; do 4690 ble/util/import/is-loaded "$file" || return 1 4691 done 4692 return 0 4693 else 4694 ble/util/print-lines "${_ble_util_import_files[@]}" 4695 return "$?" 4696 fi 4697 } 4698 4699 function ble-import { 4700 local files flags not_found 4701 ble/util/import/.read-arguments "$@" 4702 if [[ $flags == *[Eh]* ]]; then 4703 [[ $flags == *E* ]] && ble/util/print 4704 ble/util/print-lines \ 4705 'usage: ble-import [-dfq|--delay|--force|--query] [--] [SCRIPTFILE...]' \ 4706 'usage: ble-import --help' \ 4707 ' Search and source script files that have not yet been loaded.' \ 4708 '' \ 4709 ' OPTIONS' \ 4710 ' --help Show this help.' \ 4711 ' -d, --delay Delay actual loading of the files if possible.' \ 4712 ' -f, --force Ignore non-existent files without errors.' \ 4713 ' -q, --query When SCRIPTFILEs are specified, test if all of these files' \ 4714 ' are already loaded. Without SCRIPTFILEs, print the list of' \ 4715 ' already imported files.' \ 4716 '' \ 4717 >&2 4718 [[ $flags == *E* ]] && return 2 4719 return 0 4720 fi 4721 4722 if [[ $flags == *q* ]]; then 4723 ble/util/import/option:query 4724 return "$?" 4725 fi 4726 4727 if ((!${#files[@]})); then 4728 [[ $flags == *f* ]] && return 0 4729 ble/util/print 'ble-import: files are not specified.' >&2 4730 return 2 4731 fi 4732 4733 if [[ $flags == *d* ]] && ble/is-function ble/util/idle.push; then 4734 local ret 4735 ble/string#quote-command ble/util/import "${files[@]}" 4736 ble/util/idle.push "$ret" 4737 return 0 4738 fi 4739 4740 ble/util/import "${files[@]}" 4741 } 4742 4743 _ble_util_import_onload_count=0 4744 function ble/util/import/eval-after-load { 4745 local ret file 4746 if ! ble/util/import/search "$1"; then 4747 ble/util/print "ble-import: file '$1' not found." >&2 4748 return 2 4749 fi; file=$ret 4750 4751 ble/util/import/encode-filename "$file"; local enc=$ret 4752 local guard=ble/util/import/guard:$enc 4753 if ble/is-function "$guard"; then 4754 builtin eval -- "$2" 4755 else 4756 local onload=ble/util/import/onload:$enc 4757 if ! ble/is-function "$onload"; then 4758 local q=\' Q="'\''" list=_ble_util_import_onload_$((_ble_util_import_onload_count++)) 4759 builtin eval -- "$list=(); function $onload { \"\$1\" $list \"\${@:2}\"; }" 4760 fi 4761 "$onload" ble/array#push "$2" 4762 fi 4763 } 4764 4765 ## @fn ble-stackdump [message] 4766 ## 現在のコールスタックの状態を出力します。 4767 ## 4768 ## @param[in,opt] message 4769 ## スタック情報の前に表示するメッセージを指定します。 4770 ## @var[in] _ble_util_stackdump_title 4771 ## スタック情報の前に表示するタイトルを指定します。 4772 ## 4773 _ble_util_stackdump_title=stackdump 4774 _ble_util_stackdump_start= 4775 function ble/util/stackdump { 4776 ((bleopt_internal_stackdump_enabled)) || return 1 4777 local message=$1 nl=$'\n' IFS=$_ble_term_IFS 4778 message="$_ble_term_sgr0$_ble_util_stackdump_title: $message$nl" 4779 local extdebug= iarg=$BASH_ARGC args= 4780 shopt -q extdebug 2>/dev/null && extdebug=1 4781 local i i0=${_ble_util_stackdump_start:-1} iN=${#FUNCNAME[*]} 4782 for ((i=i0;i<iN;i++)); do 4783 if [[ $extdebug ]] && ((BASH_ARGC[i])); then 4784 args=("${BASH_ARGV[@]:iarg:BASH_ARGC[i]}") 4785 ble/array#reverse args 4786 args=" ${args[*]}" 4787 ((iarg+=BASH_ARGC[i])) 4788 else 4789 args= 4790 fi 4791 message="$message @ ${BASH_SOURCE[i]}:${BASH_LINENO[i-1]} (${FUNCNAME[i]}$args)$nl" 4792 done 4793 ble/util/put "$message" 4794 } 4795 function ble-stackdump { 4796 local flags args 4797 ble/util/.read-arguments-for-no-option-command ble-stackdump "$@" 4798 if [[ $flags == *[eh]* ]]; then 4799 [[ $flags == *e* ]] && ble/util/print 4800 { 4801 ble/util/print 'usage: ble-stackdump command [message]' 4802 ble/util/print ' Print stackdump.' 4803 } >&2 4804 [[ $flags == *e* ]] && return 2 4805 return 0 4806 fi 4807 4808 local _ble_util_stackdump_start=2 4809 local IFS=$_ble_term_IFS 4810 ble/util/stackdump "${args[*]}" 4811 } 4812 4813 ## @fn ble-assert command [message] 4814 ## コマンドを評価し失敗した時にメッセージを表示します。 4815 ## 4816 ## @param[in] command 4817 ## 評価するコマンドを指定します。eval で評価されます。 4818 ## @param[in,opt] message 4819 ## 失敗した時に表示するメッセージを指定します。 4820 ## 4821 function ble/util/assert { 4822 local expr=$1 message=$2 4823 if ! builtin eval -- "$expr"; then 4824 shift 4825 local _ble_util_stackdump_title='assertion failure' 4826 local _ble_util_stackdump_start=3 4827 ble/util/stackdump "$expr$_ble_term_nl$message" >&2 4828 return 1 4829 else 4830 return 0 4831 fi 4832 } 4833 function ble-assert { 4834 local flags args 4835 ble/util/.read-arguments-for-no-option-command ble-assert "$@" 4836 if [[ $flags != *h* ]]; then 4837 if ((${#args[@]}==0)); then 4838 ble/util/print 'ble-assert: command is not specified.' >&2 4839 flags=e$flags 4840 fi 4841 fi 4842 if [[ $flags == *[eh]* ]]; then 4843 [[ $flags == *e* ]] && ble/util/print 4844 { 4845 ble/util/print 'usage: ble-assert command [message]' 4846 ble/util/print ' Evaluate command and print stackdump on fail.' 4847 } >&2 4848 [[ $flags == *e* ]] && return 2 4849 return 0 4850 fi 4851 4852 local IFS=$_ble_term_IFS 4853 ble/util/assert "${args[0]}" "${args[*]:1}" 4854 } 4855 4856 #------------------------------------------------------------------------------ 4857 # Event loop 4858 4859 bleopt/declare -v debug_idle '' 4860 4861 ## @fn ble/util/clock 4862 ## 時間を計測するのに使うことができるミリ秒単位の軽量な時計です。 4863 ## 計測の起点は ble.sh のロード時です。 4864 ## @var[out] ret 4865 _ble_util_clock_base= 4866 _ble_util_clock_reso= 4867 _ble_util_clock_type= 4868 function ble/util/clock/.initialize { 4869 local LC_ALL= LC_NUMERIC=C 4870 if ((_ble_bash>=50000)) && { 4871 local now=$EPOCHREALTIME 4872 [[ $now == *.???* && $now != $EPOCHREALTIME ]]; }; then 4873 # implementation with EPOCHREALTIME 4874 builtin readonly EPOCHREALTIME 4875 _ble_util_clock_base=$((10#0${now%.*})) 4876 _ble_util_clock_reso=1 4877 _ble_util_clock_type=EPOCHREALTIME 4878 function ble/util/clock { 4879 local LC_ALL= LC_NUMERIC=C 4880 local now=$EPOCHREALTIME 4881 local integral=$((10#0${now%%.*}-_ble_util_clock_base)) 4882 local mantissa=${now#*.}000; mantissa=${mantissa::3} 4883 ((ret=integral*1000+10#0$mantissa)) 4884 } 4885 ble/function#suppress-stderr ble/util/clock # locale 4886 elif [[ -r /proc/uptime ]] && { 4887 local uptime 4888 ble/util/readfile uptime /proc/uptime 4889 ble/string#split-words uptime "$uptime" 4890 [[ $uptime == *.* ]]; }; then 4891 # implementation with /proc/uptime 4892 _ble_util_clock_base=$((10#0${uptime%.*})) 4893 _ble_util_clock_reso=10 4894 _ble_util_clock_type=uptime 4895 function ble/util/clock { 4896 local now 4897 ble/util/readfile now /proc/uptime 4898 ble/string#split-words now "$now" 4899 local integral=$((10#0${now%%.*}-_ble_util_clock_base)) 4900 local fraction=${now#*.}000; fraction=${fraction::3} 4901 ((ret=integral*1000+10#0$fraction)) 4902 } 4903 elif ((_ble_bash>=40200)); then 4904 printf -v _ble_util_clock_base '%(%s)T' -1 4905 _ble_util_clock_reso=1000 4906 _ble_util_clock_type=printf 4907 function ble/util/clock { 4908 local now; printf -v now '%(%s)T' -1 4909 ((ret=(now-_ble_util_clock_base)*1000)) 4910 } 4911 elif [[ $SECONDS && ! ${SECONDS//[0-9]} ]]; then 4912 builtin readonly SECONDS 4913 _ble_util_clock_base=$SECONDS 4914 _ble_util_clock_reso=1000 4915 _ble_util_clock_type=SECONDS 4916 function ble/util/clock { 4917 local now=$SECONDS 4918 ((ret=(now-_ble_util_clock_base)*1000)) 4919 } 4920 else 4921 ble/util/strftime -v _ble_util_clock_base '%s' 4922 _ble_util_clock_reso=1000 4923 _ble_util_clock_type=date 4924 function ble/util/clock { 4925 ble/util/strftime -v ret '%s' 4926 ((ret=(ret-_ble_util_clock_base)*1000)) 4927 } 4928 fi 4929 } 4930 ble/util/clock/.initialize 2>/dev/null 4931 4932 if ((_ble_bash>=40000)); then 4933 ## @fn[custom] ble/util/idle/IS_IDLE 4934 ## 他にするべき処理がない時 (アイドル時) に終了ステータス 0 を返します。 4935 ## Note: この設定関数は ble-decode.sh で上書きされます。 4936 function ble/util/idle/IS_IDLE { ! ble/util/is-stdin-ready; } 4937 4938 _ble_util_idle_sclock=0 4939 function ble/util/idle/.sleep { 4940 local msec=$1 4941 ((msec<=0)) && return 0 4942 ble/util/msleep "$msec" 4943 ((_ble_util_idle_sclock+=msec)) 4944 } 4945 4946 function ble/util/idle.clock/.initialize { 4947 function ble/util/idle.clock/.initialize { :; } 4948 4949 ## @fn ble/util/idle.clock 4950 ## タスクスケジューリングに使用する時計 4951 ## @var[out] ret 4952 function ble/util/idle.clock/.restart { :; } 4953 if [[ ! $_ble_util_clock_type || $_ble_util_clock_type == date ]]; then 4954 function ble/util/idle.clock { 4955 ret=$_ble_util_idle_sclock 4956 } 4957 elif ((_ble_util_clock_reso<=100)); then 4958 function ble/util/idle.clock { 4959 ble/util/clock 4960 } 4961 else 4962 ## @fn ble/util/idle/.adjusted-clock 4963 ## 参照時計 (rclock) と sleep 累積時間 (sclock) を元にして、 4964 ## 参照時計を秒以下に解像度を上げた時計 (aclock) を提供します。 4965 ## 4966 ## @var[in,out] _ble_util_idle_aclock_tick_rclock 4967 ## @var[in,out] _ble_util_idle_aclock_tick_sclock 4968 ## 最後に参照時計が切り替わった時の rclock と sclock の値を保持します。 4969 ## 4970 ## @var[in,out] _ble_util_idle_aclock_shift 4971 ## 時刻のシフト量を表します。 4972 ## 4973 ## 初期化時の秒以下の時刻が分からないため、 4974 ## 取り敢えず 0.000 になっていると想定して時刻を測り始めます。 4975 ## 最初の秒の切り替わりの時点でずれの量が判明するので、それを記録します。 4976 ## 一様時計を提供する為に、以降もこのずれを適用する為に使用します。 4977 ## 4978 _ble_util_idle_aclock_shift= 4979 _ble_util_idle_aclock_tick_rclock= 4980 _ble_util_idle_aclock_tick_sclock= 4981 function ble/util/idle.clock/.restart { 4982 _ble_util_idle_aclock_shift= 4983 _ble_util_idle_aclock_tick_rclock= 4984 _ble_util_idle_aclock_tick_sclock= 4985 } 4986 function ble/util/idle/.adjusted-clock { 4987 local resolution=$_ble_util_clock_reso 4988 local sclock=$_ble_util_idle_sclock 4989 local ret; ble/util/clock; local rclock=$((ret/resolution*resolution)) 4990 4991 if [[ $_ble_util_idle_aclock_tick_rclock != "$rclock" ]]; then 4992 if [[ $_ble_util_idle_aclock_tick_rclock && ! $_ble_util_idle_aclock_shift ]]; then 4993 local delta=$((sclock-_ble_util_idle_aclock_tick_sclock)) 4994 ((_ble_util_idle_aclock_shift=delta<resolution?resolution-delta:0)) 4995 fi 4996 _ble_util_idle_aclock_tick_rclock=$rclock 4997 _ble_util_idle_aclock_tick_sclock=$sclock 4998 fi 4999 5000 ((ret=rclock+(sclock-_ble_util_idle_aclock_tick_sclock)-_ble_util_idle_aclock_shift)) 5001 } 5002 function ble/util/idle.clock { 5003 ble/util/idle/.adjusted-clock 5004 } 5005 fi 5006 } 5007 5008 function ble/util/idle/.initialize-options { 5009 local interval='ble_util_idle_elapsed>600000?500:(ble_util_idle_elapsed>60000?200:(ble_util_idle_elapsed>5000?100:20))' 5010 ((_ble_bash>50000)) && [[ $_ble_util_msleep_builtin_available ]] && interval=20 5011 bleopt/declare -v idle_interval "$interval" 5012 } 5013 ble/util/idle/.initialize-options 5014 5015 ## @arr _ble_util_idle_task 5016 ## タスク一覧を保持します。各要素は一つのタスクを表し、 5017 ## status|command の形式の文字列です。 5018 ## command にはタスクを実行する coroutine を指定します。 5019 ## status は以下の何れかの値を持ちます。 5020 ## 5021 ## R 5022 ## 現在実行中のタスクである事を表します。 5023 ## ble/util/idle.push で設定されます。 5024 ## I 5025 ## 次のユーザの入力を待っているタスクです。 5026 ## タスク内から ble/util/idle.wait-user-input で設定します。 5027 ## S<rtime> 5028 ## 時刻 <rtime> になるのを待っているタスクです。 5029 ## タスク内から ble/util/idle.sleep で設定します。 5030 ## W<stime> 5031 ## sleep 累積時間 <stime> になるのを待っているタスクです。 5032 ## タスク内から ble/util/idle.isleep で設定します。 5033 ## E<filename> 5034 ## ファイルまたはディレクトリ <filename> が現れるのを待っているタスクです。 5035 ## タスク内から ble/util/idle.wait-filename で設定します。 5036 ## F<filename> 5037 ## ファイル <filename> が有限のサイズになるのを待っているタスクです。 5038 ## タスク内から ble/util/idle.wait-file-content で設定します。 5039 ## P<pid> 5040 ## プロセス <pid> (ユーザからアクセス可能) が終了するのを待っているタスクです。 5041 ## タスク内から ble/util/idle.wait-process で設定します。 5042 ## C<command> 5043 ## コマンド <command> の実行結果が真になるのを待っているタスクです。 5044 ## タスク内から ble/util/idle.wait-condition で設定します。 5045 ## Z 5046 ## 停止中のタスクです。外部から状態を設定する事によって再開します。 5047 ## 5048 _ble_util_idle_task=() 5049 _ble_util_idle_lasttask= 5050 _ble_util_idle_SEP=$_ble_term_FS 5051 5052 ## @fn ble/util/idle.do 5053 ## 待機状態の処理を開始します。 5054 ## 5055 ## @exit 5056 ## 待機処理を何かしら実行した時に成功 (0) を返します。 5057 ## 何も実行しなかった時に失敗 (1) を返します。 5058 ## 5059 function ble/util/idle.do { 5060 local IFS=$_ble_term_IFS 5061 ble/util/idle/IS_IDLE || return 1 5062 ((${#_ble_util_idle_task[@]}==0)) && return 1 5063 ble/util/buffer.flush >&2 5064 5065 local ret 5066 ble/util/idle.clock/.initialize 5067 ble/util/idle.clock/.restart 5068 ble/util/idle.clock 5069 local _ble_idle_clock_start=$ret 5070 local _ble_idle_sclock_start=$_ble_util_idle_sclock 5071 local _ble_idle_is_first=1 5072 local _ble_idle_processed= 5073 local _ble_idle_info_shown= 5074 local _ble_idle_after_task=0 5075 while :; do 5076 local _ble_idle_key 5077 local _ble_idle_next_time= _ble_idle_next_itime= _ble_idle_running= _ble_idle_waiting= 5078 for _ble_idle_key in "${!_ble_util_idle_task[@]}"; do 5079 ble/util/idle/IS_IDLE || break 2 5080 local _ble_idle_to_process= 5081 local _ble_idle_status=${_ble_util_idle_task[_ble_idle_key]%%"$_ble_util_idle_SEP"*} 5082 case ${_ble_idle_status::1} in 5083 (R) _ble_idle_to_process=1 ;; 5084 (I) [[ $_ble_idle_is_first ]] && _ble_idle_to_process=1 ;; 5085 (S) ble/util/idle/.check-clock "$_ble_idle_status" && _ble_idle_to_process=1 ;; 5086 (W) ble/util/idle/.check-clock "$_ble_idle_status" && _ble_idle_to_process=1 ;; 5087 (F) [[ -s ${_ble_idle_status:1} ]] && _ble_idle_to_process=1 ;; 5088 (E) [[ -e ${_ble_idle_status:1} ]] && _ble_idle_to_process=1 ;; 5089 (P) ! builtin kill -0 ${_ble_idle_status:1} &>/dev/null && _ble_idle_to_process=1 ;; 5090 (C) builtin eval -- "${_ble_idle_status:1}" && _ble_idle_to_process=1 ;; 5091 (Z) ;; 5092 (*) builtin unset -v '_ble_util_idle_task[_ble_idle_key]' 5093 esac 5094 5095 if [[ $_ble_idle_to_process ]]; then 5096 local _ble_idle_command=${_ble_util_idle_task[_ble_idle_key]#*"$_ble_util_idle_SEP"} 5097 _ble_idle_processed=1 5098 ble/util/idle.do/.call-task "$_ble_idle_command" 5099 5100 # Note: #D1450 _ble_idle_command が 148 を返したとしても idle.do は中 5101 # 断しない事にした。IS_IDLE と条件が同じとは限らないので。 5102 # ((ext==148)) && return 0 5103 5104 ((_ble_idle_after_task++)) 5105 elif [[ $_ble_idle_status == [FEPC]* ]]; then 5106 _ble_idle_waiting=1 5107 fi 5108 done 5109 5110 _ble_idle_is_first= 5111 ble/util/idle.do/.sleep-until-next; local ext=$? 5112 ((ext==148)) && break 5113 5114 [[ $_ble_idle_next_itime$_ble_idle_next_time$_ble_idle_running$_ble_idle_waiting ]] || break 5115 done 5116 5117 [[ $_ble_idle_info_shown ]] && 5118 ble/edit/info/immediate-default 5119 ble/util/idle.do/.do-after-task 5120 [[ $_ble_idle_processed ]] 5121 } 5122 ## @fn ble/util/idle.do/.do-after-task 5123 ## @var[ref] _ble_idle_after_task 5124 function ble/util/idle.do/.do-after-task { 5125 if ((_ble_idle_after_task)); then 5126 # 50ms 以上の待機時間があれば再描画などの処理を試行する。 5127 blehook/invoke idle_after_task 5128 _ble_idle_after_task=0 5129 fi 5130 } 5131 ## @fn ble/util/idle.do/.call-task command 5132 ## @var[in,out] _ble_idle_next_time 5133 ## @var[in,out] _ble_idle_next_itime 5134 ## @var[in,out] _ble_idle_running 5135 ## @var[in,out] _ble_idle_waiting 5136 function ble/util/idle.do/.call-task { 5137 local _ble_local_command=$1 5138 local ble_util_idle_status= 5139 local ble_util_idle_elapsed=$((_ble_util_idle_sclock-_ble_idle_sclock_start)) 5140 if [[ $bleopt_debug_idle && ( $_ble_edit_info_scene == default || $_ble_idle_info_shown ) ]]; then 5141 _ble_idle_info_shown=1 5142 ble/edit/info/immediate-show text "${EPOCHREALTIME:+[$EPOCHREALTIME] }idle: $_ble_local_command" 5143 fi 5144 builtin eval -- "$_ble_local_command"; local ext=$? 5145 if ((ext==148)); then 5146 _ble_util_idle_task[_ble_idle_key]=R$_ble_util_idle_SEP$_ble_local_command 5147 elif [[ $ble_util_idle_status ]]; then 5148 _ble_util_idle_task[_ble_idle_key]=$ble_util_idle_status$_ble_util_idle_SEP$_ble_local_command 5149 if [[ $ble_util_idle_status == [WS]* ]]; then 5150 local scheduled_time=${ble_util_idle_status:1} 5151 if [[ $ble_util_idle_status == W* ]]; then 5152 local next=_ble_idle_next_itime 5153 else 5154 local next=_ble_idle_next_time 5155 fi 5156 if [[ ! ${!next} ]] || ((scheduled_time<next)); then 5157 builtin eval "$next=\$scheduled_time" 5158 fi 5159 elif [[ $ble_util_idle_status == R ]]; then 5160 _ble_idle_running=1 5161 elif [[ $ble_util_idle_status == [FEPC]* ]]; then 5162 _ble_idle_waiting=1 5163 fi 5164 else 5165 builtin unset -v '_ble_util_idle_task[_ble_idle_key]' 5166 fi 5167 return "$ext" 5168 } 5169 ## @fn ble/util/idle/.check-clock status 5170 ## @var[in,out] _ble_idle_next_itime 5171 ## @var[in,out] _ble_idle_next_time 5172 function ble/util/idle/.check-clock { 5173 local status=$1 5174 if [[ $status == W* ]]; then 5175 local next=_ble_idle_next_itime 5176 local current_time=$_ble_util_idle_sclock 5177 elif [[ $status == S* ]]; then 5178 local ret 5179 local next=_ble_idle_next_time 5180 ble/util/idle.clock; local current_time=$ret 5181 else 5182 return 1 5183 fi 5184 5185 local scheduled_time=${status:1} 5186 if ((scheduled_time<=current_time)); then 5187 return 0 5188 elif [[ ! ${!next} ]] || ((scheduled_time<next)); then 5189 builtin eval "$next=\$scheduled_time" 5190 fi 5191 return 1 5192 } 5193 ## @fn ble/util/idle.do/.sleep-until-next 5194 ## @var[in] _ble_idle_next_time 5195 ## @var[in] _ble_idle_next_itime 5196 ## @var[in] _ble_idle_running 5197 ## @var[in] _ble_idle_waiting 5198 function ble/util/idle.do/.sleep-until-next { 5199 ble/util/idle/IS_IDLE || return 148 5200 [[ $_ble_idle_running ]] && return 0 5201 local isfirst=1 5202 while 5203 # ファイル等他の条件を待っている時は一回だけで外に戻り状態確認する 5204 [[ $_ble_idle_waiting && ! $isfirst ]] && break 5205 5206 local sleep_amount= 5207 if [[ $_ble_idle_next_itime ]]; then 5208 local clock=$_ble_util_idle_sclock 5209 local sleep1=$((_ble_idle_next_itime-clock)) 5210 if [[ ! $sleep_amount ]] || ((sleep1<sleep_amount)); then 5211 sleep_amount=$sleep1 5212 fi 5213 fi 5214 if [[ $_ble_idle_next_time ]]; then 5215 local ret; ble/util/idle.clock; local clock=$ret 5216 local sleep1=$((_ble_idle_next_time-clock)) 5217 if [[ ! $sleep_amount ]] || ((sleep1<sleep_amount)); then 5218 sleep_amount=$sleep1 5219 fi 5220 fi 5221 [[ $_ble_idle_waiting ]] || ((sleep_amount>0)) 5222 do 5223 # Note: 変数 ble_util_idle_elapsed は 5224 # $((bleopt_idle_interval)) の評価時に参照される。 5225 local ble_util_idle_elapsed=$((_ble_util_idle_sclock-_ble_idle_sclock_start)) 5226 5227 # sleep_amount が十分に長い場合に idle_after_task が必要あれば実行する 5228 ((sleep_amount>50)) && ble/util/idle.do/.do-after-task 5229 5230 local interval=$((bleopt_idle_interval)) 5231 5232 if [[ ! $sleep_amount ]] || ((interval<sleep_amount)); then 5233 sleep_amount=$interval 5234 fi 5235 ble/util/idle/.sleep "$sleep_amount" 5236 ble/util/idle/IS_IDLE || return 148 5237 isfirst= 5238 done 5239 } 5240 5241 function ble/util/idle.push/.impl { 5242 local base=$1 entry=$2 5243 local i=$base 5244 while [[ ${_ble_util_idle_task[i]-} ]]; do ((i++)); done 5245 _ble_util_idle_task[i]=$entry 5246 _ble_util_idle_lasttask=$i 5247 } 5248 function ble/util/idle.push { 5249 local status=R nice=0 5250 while [[ $1 == -* ]]; do 5251 local sleep= isleep= 5252 case $1 in 5253 (-[SWPFEC]) status=${1:1}$2; shift 2 ;; 5254 (-[SWPFECIRZ]*) status=${1:1}; shift ;; 5255 (-n) nice=$2; shift 2 ;; 5256 (-n*) nice=${1#-n}; shift ;; 5257 (--sleep) sleep=$2; shift 2 ;; 5258 (--sleep=*) sleep=${1#*=}; shift ;; 5259 (--isleep) isleep=$2; shift 2 ;; 5260 (--isleep=*) isleep=${1#*=}; shift ;; 5261 (*) break ;; 5262 esac 5263 5264 if [[ $sleep ]]; then 5265 local ret; ble/util/idle.clock 5266 status=S$((ret+sleep)) 5267 elif [[ $isleep ]]; then 5268 status=W$((_ble_util_idle_sclock+isleep)) 5269 fi 5270 done 5271 ble/util/idle.push/.impl "$nice" "$status$_ble_util_idle_SEP$1" 5272 } 5273 function ble/util/idle.push-background { 5274 ble/util/idle.push -n 10000 "$@" 5275 } 5276 function ble/util/idle.cancel { 5277 local command=$1 i removed= 5278 for i in "${!_ble_util_idle_task[@]}"; do 5279 [[ ${_ble_util_idle_task[i]} == *"$_ble_util_idle_SEP$command" ]] && 5280 builtin unset -v '_ble_util_idle_task[i]' && 5281 removed=1 5282 done 5283 [[ $removed ]] 5284 } 5285 5286 function ble/util/is-running-in-idle { 5287 [[ ${ble_util_idle_status+set} ]] 5288 } 5289 function ble/util/idle.suspend { 5290 [[ ${ble_util_idle_status+set} ]] || return 2 5291 ble_util_idle_status=Z 5292 } 5293 function ble/util/idle.sleep { 5294 [[ ${ble_util_idle_status+set} ]] || return 2 5295 local ret; ble/util/idle.clock 5296 ble_util_idle_status=S$((ret+$1)) 5297 } 5298 function ble/util/idle.isleep { 5299 [[ ${ble_util_idle_status+set} ]] || return 2 5300 ble_util_idle_status=W$((_ble_util_idle_sclock+$1)) 5301 } 5302 ## @fn ble/util/idle.sleep-until clock opts 5303 function ble/util/idle.sleep-until { 5304 [[ ${ble_util_idle_status+set} ]] || return 2 5305 if [[ :$2: == *:checked:* ]]; then 5306 local ret; ble/util/idle.clock 5307 (($1>ret)) || return 1 5308 fi 5309 ble_util_idle_status=S$1 5310 } 5311 ## @fn ble/util/idle.isleep-until sclock opts 5312 function ble/util/idle.isleep-until { 5313 [[ ${ble_util_idle_status+set} ]] || return 2 5314 if [[ :$2: == *:checked:* ]]; then 5315 (($1>_ble_util_idle_sclock)) || return 1 5316 fi 5317 ble_util_idle_status=W$1 5318 } 5319 function ble/util/idle.wait-user-input { 5320 [[ ${ble_util_idle_status+set} ]] || return 2 5321 ble_util_idle_status=I 5322 } 5323 function ble/util/idle.wait-process { 5324 [[ ${ble_util_idle_status+set} ]] || return 2 5325 ble_util_idle_status=P$1 5326 } 5327 function ble/util/idle.wait-file-content { 5328 [[ ${ble_util_idle_status+set} ]] || return 2 5329 ble_util_idle_status=F$1 5330 } 5331 function ble/util/idle.wait-filename { 5332 [[ ${ble_util_idle_status+set} ]] || return 2 5333 ble_util_idle_status=E$1 5334 } 5335 function ble/util/idle.wait-condition { 5336 [[ ${ble_util_idle_status+set} ]] || return 2 5337 ble_util_idle_status=C$1 5338 } 5339 function ble/util/idle.continue { 5340 [[ ${ble_util_idle_status+set} ]] || return 2 5341 ble_util_idle_status=R 5342 } 5343 5344 function ble/util/idle/.delare-external-modifier { 5345 local name=$1 5346 builtin eval -- 'function ble/util/idle#'$name' { 5347 local index=$1 5348 [[ ${_ble_util_idle_task[index]+set} ]] || return 2 5349 local ble_util_idle_status=${_ble_util_idle_task[index]%%"$_ble_util_idle_SEP"*} 5350 local ble_util_idle_command=${_ble_util_idle_task[index]#*"$_ble_util_idle_SEP"} 5351 ble/util/idle.'$name' "${@:2}" 5352 _ble_util_idle_task[index]=$ble_util_idle_status$_ble_util_idle_SEP$ble_util_idle_command 5353 }' 5354 } 5355 # @fn ble/util/idle#suspend 5356 # @fn ble/util/idle#sleep time 5357 # @fn ble/util/idle#isleep time 5358 ble/util/idle/.delare-external-modifier suspend 5359 ble/util/idle/.delare-external-modifier sleep 5360 ble/util/idle/.delare-external-modifier isleep 5361 5362 ble/util/idle.push-background 'ble/util/msleep/calibrate' 5363 else 5364 function ble/util/idle.do { false; } 5365 fi 5366 5367 #------------------------------------------------------------------------------ 5368 # ble/util/fiberchain 5369 5370 _ble_util_fiberchain=() 5371 _ble_util_fiberchain_prefix= 5372 function ble/util/fiberchain#initialize { 5373 _ble_util_fiberchain=() 5374 _ble_util_fiberchain_prefix=$1 5375 } 5376 function ble/util/fiberchain#resume/.core { 5377 _ble_util_fiberchain=() 5378 local fib_clock=0 5379 local fib_ntask=$# 5380 while (($#)); do 5381 ((fib_ntask--)) 5382 local fiber=${1%%:*} fib_suspend= fib_kill= 5383 local argv; ble/string#split-words argv "$fiber" 5384 [[ $1 == *:* ]] && fib_suspend=${1#*:} 5385 "$_ble_util_fiberchain_prefix/$argv.fib" "${argv[@]:1}" 5386 5387 if [[ $fib_kill ]]; then 5388 break 5389 elif [[ $fib_suspend ]]; then 5390 _ble_util_fiberchain=("$fiber:$fib_suspend" "${@:2}") 5391 return 148 5392 fi 5393 shift 5394 done 5395 } 5396 function ble/util/fiberchain#resume { 5397 ble/util/fiberchain#resume/.core "${_ble_util_fiberchain[@]}" 5398 } 5399 ## @fn ble/util/fiberchain#push fiber... 5400 ## @param[in] fiber 5401 ## 複数指定することができます。 5402 ## 一つ一つは空白区切りの単語を並べた文字列です。 5403 ## コロン ":" を含むことはできません。 5404 ## 一番最初の単語にファイバー名 name を指定します。 5405 ## 引数 args... があれば二つ目以降の単語として指定します。 5406 ## 5407 ## @remarks 5408 ## 実際に実行されるファイバーは以下のコマンドになります。 5409 ## "$_ble_util_fiber_chain_prefix/$name.fib" "${args[@]}" 5410 ## 5411 function ble/util/fiberchain#push { 5412 ble/array#push _ble_util_fiberchain "$@" 5413 } 5414 function ble/util/fiberchain#clear { 5415 _ble_util_fiberchain=() 5416 } 5417 5418 #------------------------------------------------------------------------------ 5419 # **** terminal controls **** 5420 5421 bleopt/declare -v vbell_default_message ' Wuff, -- Wuff!! ' 5422 bleopt/declare -v vbell_duration 2000 5423 bleopt/declare -n vbell_align left 5424 5425 function ble/term:cygwin/initialize.hook { 5426 # RIの修正 5427 # Note: Cygwin console では何故か RI (ESC M) が 5428 # 1行スクロールアップとして実装されている。 5429 # 一方で CUU (CSI A) で上にスクロールできる。 5430 printf '\eM\e[B' >&"$_ble_util_fd_stderr" 5431 _ble_term_ri=$'\e[A' 5432 5433 # DLの修正 5434 function ble/canvas/put-dl.draw { 5435 local value=${1-1} i 5436 ((value)) || return 1 5437 5438 # Note: DL が最終行まで消去する時、何も消去されない…。 5439 DRAW_BUFF[${#DRAW_BUFF[*]}]=$'\e[2K' 5440 if ((value>1)); then 5441 local ret 5442 ble/string#repeat $'\e[B\e[2K' "$((value-1))"; local a=$ret 5443 DRAW_BUFF[${#DRAW_BUFF[*]}]=$ret$'\e['$((value-1))'A' 5444 fi 5445 5446 DRAW_BUFF[${#DRAW_BUFF[*]}]=${_ble_term_dl//'%d'/$value} 5447 } 5448 } 5449 5450 function ble/term/DA2R.hook { 5451 blehook term_DA2R-=ble/term/DA2R.hook 5452 case $_ble_term_TERM in 5453 (contra:*) 5454 _ble_term_cuu=$'\e[%dk' 5455 _ble_term_cud=$'\e[%de' 5456 _ble_term_cuf=$'\e[%da' 5457 _ble_term_cub=$'\e[%dj' 5458 _ble_term_cup=$'\e[%l;%cf' ;; 5459 (cygwin:*) 5460 ble/term:cygwin/initialize.hook ;; 5461 esac 5462 } 5463 function ble/term/.initialize { 5464 if [[ -s $_ble_base_cache/term.$TERM && $_ble_base_cache/term.$TERM -nt $_ble_base/lib/init-term.sh ]]; then 5465 source "$_ble_base_cache/term.$TERM" 5466 else 5467 source "$_ble_base/lib/init-term.sh" 5468 fi 5469 5470 ble/string#reserve-prototype "$_ble_term_it" 5471 blehook term_DA2R!=ble/term/DA2R.hook 5472 } 5473 ble/term/.initialize 5474 5475 function ble/term/put { 5476 BUFF[${#BUFF[@]}]=$1 5477 } 5478 function ble/term/cup { 5479 local x=$1 y=$2 esc=$_ble_term_cup 5480 esc=${esc//'%x'/$x} 5481 esc=${esc//'%y'/$y} 5482 esc=${esc//'%c'/$((x+1))} 5483 esc=${esc//'%l'/$((y+1))} 5484 BUFF[${#BUFF[@]}]=$esc 5485 } 5486 function ble/term/flush { 5487 IFS= builtin eval 'ble/util/put "${BUFF[*]}"' 5488 BUFF=() 5489 } 5490 5491 # **** vbell/abell **** 5492 5493 function ble/term/audible-bell { 5494 ble/util/put '' 1>&2 5495 } 5496 5497 # visible-bell の表示の管理について。 5498 # 5499 # vbell の表示の削除には worker サブシェルを使用する。 5500 # 現在の表示内容及び消去に関しては二つのファイルを使う。 5501 # 5502 # workerfile=$_ble_base_run/$$.visible-bell.$i 5503 # 1つの worker に対して1つ割り当てられ、 5504 # その worker が生きている間は非空である。 5505 # またそのタイムスタンプは worker 起動時刻を表す。 5506 # 5507 # _ble_term_visible_bell_ftime=$_ble_base_run/$$.visible-bell.time 5508 # 最後に表示の更新を行った時刻を記録するのに使う。 5509 # 5510 # 前回の表示内容は以下の配列に格納する。 5511 # 5512 # @arr _ble_term_visible_bell_prev=(vbell_type message [x0 y0 x y]) 5513 5514 _ble_term_visible_bell_prev=() 5515 _ble_term_visible_bell_ftime=$_ble_base_run/$$.visible-bell.time 5516 5517 _ble_term_visible_bell_show='%message%' 5518 _ble_term_visible_bell_clear= 5519 function ble/term/visible-bell:term/init { 5520 if [[ ! $_ble_term_visible_bell_clear ]]; then 5521 local -a BUFF=() 5522 ble/term/put "$_ble_term_ri_or_cuu1$_ble_term_sc$_ble_term_sgr0" 5523 ble/term/cup 0 0 5524 ble/term/put "$_ble_term_el%message%$_ble_term_sgr0$_ble_term_rc${_ble_term_cud//'%d'/1}" 5525 IFS= builtin eval '_ble_term_visible_bell_show="${BUFF[*]}"' 5526 5527 BUFF=() 5528 ble/term/put "$_ble_term_sc$_ble_term_sgr0" 5529 ble/term/cup 0 0 5530 ble/term/put "$_ble_term_el2$_ble_term_rc" 5531 IFS= builtin eval '_ble_term_visible_bell_clear="${BUFF[*]}"' 5532 fi 5533 5534 # 一行に収まる様に切り詰める 5535 local cols=${COLUMNS:-80} 5536 ((_ble_term_xenl||cols--)) 5537 local message=${1::cols} 5538 _ble_term_visible_bell_prev=(term "$message") 5539 } 5540 function ble/term/visible-bell:term/show { 5541 local sgr=$1 message=${_ble_term_visible_bell_prev[1]} 5542 message=${_ble_term_visible_bell_show//'%message%'/"$sgr$message"} 5543 ble/util/put "$message" >&2 5544 } 5545 function ble/term/visible-bell:term/update { 5546 ble/term/visible-bell:term/show "$@" 5547 } 5548 function ble/term/visible-bell:term/clear { 5549 local sgr=$1 5550 ble/util/put "$_ble_term_visible_bell_clear" >&2 5551 } 5552 5553 function ble/term/visible-bell:canvas/init { 5554 local message=$1 5555 5556 local lines=1 cols=${COLUMNS:-80} 5557 ((_ble_term_xenl||cols--)) 5558 local x= y= 5559 local ret sgr0= sgr1= 5560 ble/canvas/trace-text "$message" nonewline:external-sgr 5561 message=$ret 5562 5563 local x0=0 y0=0 5564 if [[ $bleopt_vbell_align == right ]]; then 5565 ((x0=COLUMNS-1-x,x0<0&&(x0=0))) 5566 elif [[ $bleopt_vbell_align == center ]]; then 5567 ((x0=(COLUMNS-1-x)/2,x0<0&&(x0=0))) 5568 fi 5569 5570 _ble_term_visible_bell_prev=(canvas "$message" "$x0" "$y0" "$x" "$y") 5571 } 5572 function ble/term/visible-bell:canvas/show { 5573 local sgr=$1 opts=$2 5574 local message=${_ble_term_visible_bell_prev[1]} 5575 local x0=${_ble_term_visible_bell_prev[2]} 5576 local y0=${_ble_term_visible_bell_prev[3]} 5577 local x=${_ble_term_visible_bell_prev[4]} 5578 local y=${_ble_term_visible_bell_prev[5]} 5579 5580 local -a DRAW_BUFF=() 5581 [[ :$opts: != *:update:* && $_ble_attached ]] && # WA #D1495 5582 [[ $_ble_term_ri || :$opts: != *:erased:* && :$opts: != *:update:* ]] && 5583 ble/canvas/panel/ensure-tmargin.draw 5584 if [[ $_ble_term_rc ]]; then 5585 local ret= 5586 [[ :$opts: != *:update:* && $_ble_attached ]] && ble/canvas/panel/save-position goto-top-dock # WA #D1495 5587 ble/canvas/put.draw "$_ble_term_ri_or_cuu1$_ble_term_sc$_ble_term_sgr0" 5588 ble/canvas/put-cup.draw "$((y0+1))" "$((x0+1))" 5589 ble/canvas/put.draw "$sgr$message$_ble_term_sgr0" 5590 ble/canvas/put.draw "$_ble_term_rc" 5591 ble/canvas/put-cud.draw 1 5592 [[ :$opts: != *:update:* && $_ble_attached ]] && ble/canvas/panel/load-position.draw "$ret" # WA #D1495 5593 else 5594 ble/canvas/put.draw "$_ble_term_ri_or_cuu1$_ble_term_sgr0" 5595 ble/canvas/put-hpa.draw "$((1+x0))" 5596 ble/canvas/put.draw "$sgr$message$_ble_term_sgr0" 5597 ble/canvas/put-cud.draw 1 5598 ble/canvas/put-hpa.draw "$((1+_ble_canvas_x))" 5599 fi 5600 ble/canvas/bflush.draw 5601 ble/util/buffer.flush >&2 5602 } 5603 function ble/term/visible-bell:canvas/update { 5604 ble/term/visible-bell:canvas/show "$@" 5605 } 5606 function ble/term/visible-bell:canvas/clear { 5607 local sgr=$1 5608 local x0=${_ble_term_visible_bell_prev[2]} 5609 local y0=${_ble_term_visible_bell_prev[3]} 5610 local x=${_ble_term_visible_bell_prev[4]} 5611 local y=${_ble_term_visible_bell_prev[5]} 5612 5613 local -a DRAW_BUFF=() 5614 if [[ $_ble_term_rc ]]; then 5615 local ret= 5616 #[[ $_ble_attached ]] && ble/canvas/panel/save-position goto-top-dock # WA #D1495 5617 ble/canvas/put.draw "$_ble_term_sc$_ble_term_sgr0" 5618 ble/canvas/put-cup.draw "$((y0+1))" "$((x0+1))" 5619 ble/canvas/put.draw "$sgr" 5620 ble/canvas/put-spaces.draw "$x" 5621 #ble/canvas/put-ech.draw "$x" 5622 #ble/canvas/put.draw "$_ble_term_el" 5623 ble/canvas/put.draw "$_ble_term_sgr0$_ble_term_rc" 5624 #[[ $_ble_attached ]] && ble/canvas/panel/load-position.draw "$ret" # WA #D1495 5625 else 5626 : # 親プロセスの _ble_canvas_x が分からないので座標がずれる 5627 # ble/util/buffer.flush >&2 5628 # ble/canvas/put.draw "$_ble_term_ri_or_cuu1$_ble_term_sgr0" 5629 # ble/canvas/put-hpa.draw "$((1+x0))" 5630 # ble/canvas/put.draw "$sgr" 5631 # ble/canvas/put-spaces.draw "$x" 5632 # ble/canvas/put.draw "$_ble_term_sgr0" 5633 # ble/canvas/put-cud.draw 1 5634 # ble/canvas/put-hpa.draw "$((1+_ble_canvas_x))" # 親プロセスの _ble_canvas_x? 5635 fi 5636 ble/canvas/flush.draw >&2 5637 } 5638 5639 function ble/term/visible-bell/defface.hook { 5640 ble/color/defface vbell reverse 5641 ble/color/defface vbell_flash reverse,fg=green 5642 ble/color/defface vbell_erase bg=252 5643 } 5644 blehook color_defface_load+=ble/term/visible-bell/defface.hook 5645 5646 function ble/term/visible-bell/.show { 5647 local bell_type=${_ble_term_visible_bell_prev[0]} 5648 ble/term/visible-bell:"$bell_type"/show "$@" 5649 } 5650 function ble/term/visible-bell/.update { 5651 local bell_type=${_ble_term_visible_bell_prev[0]} 5652 ble/term/visible-bell:"$bell_type"/update "$1" "$2:update" 5653 } 5654 function ble/term/visible-bell/.clear { 5655 local bell_type=${_ble_term_visible_bell_prev[0]} 5656 ble/term/visible-bell:"$bell_type"/clear "$@" 5657 >| "$_ble_term_visible_bell_ftime" 5658 } 5659 5660 function ble/term/visible-bell/.erase-previous-visible-bell { 5661 local ret workers 5662 ble/util/eval-pathname-expansion '"$_ble_base_run/$$.visible-bell."*' canonical 5663 workers=("${ret[@]}") 5664 5665 local workerfile 5666 for workerfile in "${workers[@]}"; do 5667 if [[ -s $workerfile && ! ( $workerfile -ot $_ble_term_visible_bell_ftime ) ]]; then 5668 ble/term/visible-bell/.clear "$sgr0" 5669 return 0 5670 fi 5671 done 5672 return 1 5673 } 5674 5675 function ble/term/visible-bell/.create-workerfile { 5676 local i=0 5677 while 5678 workerfile=$_ble_base_run/$$.visible-bell.$i 5679 [[ -s $workerfile ]] 5680 do ((i++)); done 5681 ble/util/print 1 >| "$workerfile" 5682 } 5683 ## @fn ble/term/visible-bell/.worker 5684 ## @var[in] workerfile 5685 function ble/term/visible-bell/.worker { 5686 # Note: ble/util/assign は使えない。本体の ble/util/assign と一時ファイルが衝突する可能性がある。 5687 ble/util/msleep 50 5688 [[ $workerfile -ot $_ble_term_visible_bell_ftime ]] && return 0 >| "$workerfile" 5689 ble/term/visible-bell/.update "$sgr2" 5690 5691 if [[ :$opts: == *:persistent:* ]]; then 5692 local dead_workerfile=$_ble_base_run/$$.visible-bell.Z 5693 ble/util/print 1 >| "$dead_workerfile" 5694 return 0 >| "$workerfile" 5695 fi 5696 5697 # load time duration settings 5698 local msec=$bleopt_vbell_duration 5699 5700 # wait 5701 ble/util/msleep "$msec" 5702 [[ $workerfile -ot $_ble_term_visible_bell_ftime ]] && return 0 >| "$workerfile" 5703 5704 # check and clear 5705 ble/term/visible-bell/.clear "$sgr0" 5706 5707 >| "$workerfile" 5708 } 5709 5710 ## @fn ble/term/visible-bell message [opts] 5711 function ble/term/visible-bell { 5712 local message=$1 opts=$2 5713 message=${message:-$bleopt_vbell_default_message} 5714 5715 # Note: 1行しかない時は表示しない。0行の時は全てログに行くので出力する。空文 5716 # 字列の時は設定されていないだけなので表示する。 5717 ((LINES==1)) && return 0 5718 5719 if ble/is-function ble/canvas/trace-text; then 5720 ble/term/visible-bell:canvas/init "$message" 5721 else 5722 ble/term/visible-bell:term/init "$message" 5723 fi 5724 5725 local sgr0=$_ble_term_sgr0 5726 local sgr1=${_ble_term_setaf[2]}$_ble_term_rev 5727 local sgr2=$_ble_term_rev 5728 if ble/is-function ble/color/face2sgr; then 5729 local ret 5730 ble/color/face2sgr vbell_flash; sgr1=$ret 5731 ble/color/face2sgr vbell; sgr2=$ret 5732 ble/color/face2sgr vbell_erase; sgr0=$ret 5733 fi 5734 5735 local show_opts= 5736 ble/term/visible-bell/.erase-previous-visible-bell && show_opts=erased 5737 ble/term/visible-bell/.show "$sgr1" "$show_opts" 5738 5739 local workerfile; ble/term/visible-bell/.create-workerfile 5740 # Note: __ble_suppress_joblist__ を指定する事によって、 5741 # 終了したジョブの一覧に現れない様にする。 5742 # 対策しないと read の置き換え実装でジョブ一覧が表示されてしまう。 5743 # Note: 標準出力を閉じて置かないと $() の中で 5744 # read を呼び出した時に visible-bell worker がブロックしてしまう。 5745 # ref #D1000, #D1087 5746 ( ble/term/visible-bell/.worker __ble_suppress_joblist__ 1>/dev/null & ) 5747 } 5748 function ble/term/visible-bell/cancel-erasure { 5749 >| "$_ble_term_visible_bell_ftime" 5750 } 5751 function ble/term/visible-bell/erase { 5752 local sgr0=$_ble_term_sgr0 5753 if ble/is-function ble/color/face2sgr; then 5754 local ret 5755 ble/color/face2sgr vbell_erase; sgr0=$ret 5756 fi 5757 ble/term/visible-bell/.erase-previous-visible-bell 5758 } 5759 5760 #---- stty -------------------------------------------------------------------- 5761 5762 # 改行 (C-m, C-j) の取り扱いについて 5763 # 入力の C-m が C-j に勝手に変換されない様に -icrnl を指定する必要がある。 5764 # (-nl の設定の中に icrnl が含まれているので、これを取り消さなければならない) 5765 # 一方で、出力の LF は CR LF に変換されて欲しいので onlcr は保持する。 5766 # (これは -nl の設定に含まれている) 5767 # 5768 # -icanon について 5769 # stty icanon を設定するプログラムがある。これを設定すると入力が buffering され 5770 # その場で入力を受信する事ができない。結果として hang した様に見える。 5771 # 従って、enter で -icanon を設定する事にする。 5772 5773 ## @var _ble_term_stty_state 5774 ## 現在 stty で制御文字の効果が解除されているかどうかを保持します。 5775 ## 5776 ## Note #D1238: arr=(...) の形式を用いると Bash 3.2 では勝手に ^? が ^A^? に化けてしまう 5777 ## 仕方がないので此処では ble/array#push を使って以下の配列を初期化する事にする。 5778 _ble_term_stty_state= 5779 _ble_term_stty_flags_enter=() 5780 _ble_term_stty_flags_leave=() 5781 ble/array#push _ble_term_stty_flags_enter intr undef quit undef susp undef 5782 ble/array#push _ble_term_stty_flags_leave intr '' quit '' susp '' 5783 function ble/term/stty/.initialize-flags { 5784 # # ^U, ^V, ^W, ^? 5785 # # Note: lnext, werase は POSIX にはないので stty の項目に存在する 5786 # # かチェックする。 5787 # # Note (#D1683): ble/decode/bind/adjust-uvw が正しい対策。以下の対 5788 # # 策の効果は不明。寧ろ vim :term 内部で ^? が効かなくなるなど問 5789 # # 題を起こす様なので取り敢えず無効化する。 5790 # ble/array#push _ble_term_stty_flags_enter kill undef erase undef 5791 # ble/array#push _ble_term_stty_flags_leave kill '' erase '' 5792 # local stty; ble/util/assign stty 'stty -a' 5793 # if [[ $stty == *' lnext '* ]]; then 5794 # ble/array#push _ble_term_stty_flags_enter lnext undef 5795 # ble/array#push _ble_term_stty_flags_leave lnext '' 5796 # fi 5797 # if [[ $stty == *' werase '* ]]; then 5798 # ble/array#push _ble_term_stty_flags_enter werase undef 5799 # ble/array#push _ble_term_stty_flags_leave werase '' 5800 # fi 5801 5802 if [[ $TERM == minix ]]; then 5803 local stty; ble/util/assign stty 'stty -a' 5804 if [[ $stty == *' rprnt '* ]]; then 5805 ble/array#push _ble_term_stty_flags_enter rprnt undef 5806 ble/array#push _ble_term_stty_flags_leave rprnt '' 5807 elif [[ $stty == *' reprint '* ]]; then 5808 ble/array#push _ble_term_stty_flags_enter reprint undef 5809 ble/array#push _ble_term_stty_flags_leave reprint '' 5810 fi 5811 fi 5812 } 5813 ble/term/stty/.initialize-flags 5814 5815 function ble/term/stty/initialize { 5816 ble/bin/stty -ixon -echo -nl -icrnl -icanon \ 5817 "${_ble_term_stty_flags_enter[@]}" 5818 _ble_term_stty_state=1 5819 } 5820 function ble/term/stty/leave { 5821 [[ ! $_ble_term_stty_state ]] && return 0 5822 ble/bin/stty echo -nl icanon \ 5823 "${_ble_term_stty_flags_leave[@]}" 5824 _ble_term_stty_state= 5825 } 5826 function ble/term/stty/enter { 5827 [[ $_ble_term_stty_state ]] && return 0 5828 ble/bin/stty -echo -nl -icrnl -icanon \ 5829 "${_ble_term_stty_flags_enter[@]}" 5830 _ble_term_stty_state=1 5831 } 5832 function ble/term/stty/finalize { 5833 ble/term/stty/leave 5834 } 5835 function ble/term/stty/TRAPEXIT { 5836 # exit の場合は echo 5837 ble/bin/stty echo -nl \ 5838 "${_ble_term_stty_flags_leave[@]}" 5839 5840 # Note (#D): WA for bash-5.2 stty: bash-5.2 以降では EXIT trap よりも後に readline 5841 # が stty を復元しようとするので、セッション終了後に制御端末が壊れた状態にな 5842 # る。親プロセスが同じ端末に属していてかつ ble.sh セッションでない場合には、 5843 # 入力に支障を来すので制御端末の状態を手動で復元する様に表示を行う。 5844 if ((_ble_bash>=50200)) && [[ :$1: == *:EXIT:* && ! -e $_ble_base_run/$PPID.load ]]; then 5845 local lines 5846 ble/util/assign-array lines 'ble/bin/ps -o tty "$$" "$PPID"' 5847 ((${#lines[@]}>=3)) && lines=("${lines[@]:${#lines[@]}-2}") 5848 if [[ ${lines[0]} == ${lines[1]} ]]; then 5849 local sgr=$_ble_term_bold${_ble_term_setaf[4]} sgr0=$_ble_term_sgr0 5850 ble/util/print "ble: Please run \`${sgr}stty sane$sgr0' to recover the correct TTY state." >&"${_ble_util_fd_stderr:-2}" 5851 fi 5852 fi 5853 } 5854 5855 function ble/term/update-winsize { 5856 # (0) checkwinsize による実装 (2167.054 usec/eval) 5857 if ((_ble_bash<50200||50300<=_ble_bash)); then 5858 function ble/term/update-winsize { 5859 if shopt -q checkwinsize; then 5860 (:) 5861 else 5862 shopt -s checkwinsize 5863 (:) 5864 shopt -u checkwinsize 5865 fi 2>&"$_ble_util_fd_stderr" 5866 } 5867 ble/term/update-winsize 5868 return 0 5869 fi 5870 5871 local ret 5872 5873 # (a) "tput lines cols" または "tput li co" による実装 (2909.052 usec/eval) 5874 if ble/bin#freeze-utility-path tput; then 5875 if ble/util/assign-words ret 'ble/bin/tput lines cols' 2>/dev/null && 5876 [[ ${#ret[@]} -eq 2 && ${ret[0]} =~ ^[0-9]+$ && ${ret[1]} =~ ^[0-9]+$ ]] 5877 then 5878 LINES=${ret[0]} COLUMNS=${ret[1]} 5879 function ble/term/update-winsize { 5880 local -x ret LINES= COLUMNS= 5881 ble/util/assign-words ret 'ble/bin/tput lines cols' 2>/dev/null 5882 ble/util/unlocal LINES COLUMNS 5883 [[ ${ret[0]} ]] && LINES=${ret[0]} 5884 [[ ${ret[1]} ]] && COLUMNS=${ret[1]} 5885 } 5886 return 0 5887 elif ble/util/assign-words ret 'ble/bin/tput li co' 2>/dev/null && 5888 [[ ${#ret[@]} -eq 2 && ${ret[0]} =~ ^[0-9]+$ && ${ret[1]} =~ ^[0-9]+$ ]] 5889 then 5890 LINES=${ret[0]} COLUMNS=${ret[1]} 5891 function ble/term/update-winsize { 5892 local -x ret LINES= COLUMNS= 5893 ble/util/assign-words ret 'ble/bin/tput li co' 2>/dev/null 5894 ble/util/unlocal LINES COLUMNS 5895 [[ ${ret[0]} ]] && LINES=${ret[0]} 5896 [[ ${ret[1]} ]] && COLUMNS=${ret[1]} 5897 } 5898 return 0 5899 fi 5900 fi 5901 5902 # (b) "stty size" による実装 (2976.172 usec/eval) 5903 if ble/util/assign-words ret 'ble/bin/stty size' 2>/dev/null && 5904 [[ ${#ret[@]} -eq 2 && ${ret[0]} =~ ^[0-9]+$ && ${ret[1]} =~ ^[0-9]+$ ]] 5905 then 5906 LINES=${ret[0]} COLUMNS=${ret[1]} 5907 function ble/term/update-winsize { 5908 local ret 5909 ble/util/assign-words ret 'ble/bin/stty size' 2>/dev/null 5910 [[ ${ret[0]} ]] && LINES=${ret[0]} 5911 [[ ${ret[1]} ]] && COLUMNS=${ret[1]} 5912 } 5913 return 0 5914 fi 5915 5916 # (c) "resize" による実装 (3108.696 usec/eval) 5917 if ble/bin#freeze-utility-path resize && 5918 ble/util/assign ret 'ble/bin/resize' && 5919 ble/string#match "$ret" 'COLUMNS=([0-9]+).*LINES=([0-9]+)' 5920 then 5921 LINES=${BASH_REMATCH[2]} COLUMNS=${BASH_REMATCH[1]} 5922 function ble/term/update-winsize { 5923 local ret 5924 ble/util/assign ret 'ble/bin/resize' 2>/dev/null 5925 ble/string#match ret 'COLUMNS=([0-9]+).*LINES=([0-9]+)' 5926 [[ ${BASH_REMATCH[2]} ]] && LINES=${BASH_REMATCH[2]} 5927 [[ ${BASH_REMATCH[1]} ]] && COLUMNS=${BASH_REMATCH[1]} 5928 } 5929 return 0 5930 fi 5931 5932 # (d) "bash -O checkwinsize -c ..." による実装 (bash-4.3 以上) (9094.595 usec/eval) 5933 function ble/term/update-winsize { 5934 local ret script='LINES= COLUMNS=; (:); [[ $COLUMNS && $LINES ]] && builtin echo "$LINES $COLUMNS"' 5935 ble/util/assign-words ret '"$BASH" -O checkwinsize -c "$script"' 2>&"$_ble_util_fd_stderr" 5936 [[ ${ret[0]} ]] && LINES=${ret[0]} 5937 [[ ${ret[1]} ]] && COLUMNS=${ret[1]} 5938 } 5939 ble/term/update-winsize 5940 return 0 5941 } 5942 5943 # bash-5.2 では "bind -x" 内部で checkwinsize が動作しないので 5944 # ble/term/stty/enter に於いて自前で端末サイズを取得して LINES COLUMNS を更新す 5945 # る。 5946 if ((50200<=_ble_bash&&_ble_bash<50300)); then 5947 ## @fn ble/term/update-winsize/.stty-enter.advice 5948 ## ble/term/stty/enter の実装を "stty size" を用いて調節します。 5949 ## 5950 ## 最初の ble/term/stty/enter の呼び出し時に'stty "${enter_options[@]}" 5951 ## size' が動くか検査し、使えそうならば今後はstty に size を追加して呼び出 5952 ## してそれを元にして LINES, COLUMNS を再設定する様にする。 5953 ## 5954 ## テスト自体が stty の設定を変更するので、初回の ble/term/stty/enter の呼 5955 ## び出しの時にテストも含めて調整を実行することにしている。 5956 function ble/term/update-winsize/.stty-enter.advice { 5957 local ret stderr test_command='ble/bin/stty -echo -nl -icrnl -icanon "${_ble_term_stty_flags_enter[@]}" size' 5958 if ble/util/assign stderr 'ble/util/assign-words ret "$test_command" 2>&1' && 5959 [[ ! $stderr ]] && 5960 ((${#ret[@]}==2)) && 5961 [[ ${ret[0]} =~ ^[0-9]+$ && ${ret[1]} =~ ^[0-9]+$ ]] 5962 then 5963 LINES=${ret[0]} COLUMNS=${ret[1]} 5964 function ble/term/stty/enter { 5965 [[ $_ble_term_stty_state ]] && return 0 5966 local ret 5967 ble/util/assign-words ret 'ble/bin/stty -echo -nl -icrnl -icanon "${_ble_term_stty_flags_enter[@]}" size' 5968 [[ ${ret[0]} =~ ^[0-9]+$ ]] && LINES=${ret[0]} 5969 [[ ${ret[1]} =~ ^[0-9]+$ ]] && COLUMNS=${ret[1]} 5970 _ble_term_stty_state=1 5971 } 5972 else 5973 ble/term/update-winsize 5974 ble/function#advice before ble/term/stty/enter ble/term/update-winsize 5975 fi 5976 builtin unset -f "$FUNCNAME" 5977 } 5978 ble/function#advice before ble/term/stty/enter ble/term/update-winsize/.stty-enter.advice 5979 fi 5980 5981 5982 #---- cursor state ------------------------------------------------------------ 5983 5984 bleopt/declare -v term_cursor_external 0 5985 5986 _ble_term_cursor_current=unknown 5987 _ble_term_cursor_internal=0 5988 _ble_term_cursor_hidden_current=unknown 5989 _ble_term_cursor_hidden_internal=reveal 5990 5991 # #D1516 今迄にカーソル変更がなく、且つ既定値に戻そうとしている時は何 5992 # もしない為、初めから 0 にしておく事にする。xterm.js で DECSCUSR(0) 5993 # がユーザー既定値でない事への対策。外部コマンドがカーソル形状を復元 5994 # するという事を前提にしている。 5995 # #D1873 単に 0 を指定しているだけだと cursor をユーザー設定していなく 5996 # ても、コマンド実行後の term/enter の時に結局 unknown が設定されて、 5997 # DECSCUSR(0) が送信されて問題になる。未だ一度も ble.sh として変更し 5998 # ていない事を表す値として default という物を導入する事にした。 5999 # default の時には term/enter 時のクリアをしない。 6000 _ble_term_cursor_current=default 6001 6002 function ble/term/cursor-state/.update { 6003 local state=$(($1)) 6004 [[ ${_ble_term_cursor_current/default/0} == "$state" ]] && return 0 6005 6006 if [[ ! $_ble_term_Ss ]]; then 6007 case $_ble_term_TERM in 6008 (mintty:*|xterm:*|RLogin:*|kitty:*|screen:*|tmux:*|contra:*|cygwin:*|wezterm:*|wt:*) 6009 local _ble_term_Ss=$'\e[@1 q' ;; 6010 esac 6011 fi 6012 local ret=${_ble_term_Ss//@1/"$state"} 6013 6014 # Note: 既に pass-through seq が含まれている時はスキップする。 6015 [[ $ret && $ret != $'\eP'*$'\e\\' ]] && 6016 ble/term/quote-passthrough "$ret" '' all 6017 6018 ble/util/buffer "$ret" 6019 6020 _ble_term_cursor_current=$state 6021 } 6022 function ble/term/cursor-state/set-internal { 6023 _ble_term_cursor_internal=$1 6024 [[ $_ble_term_state == internal ]] && 6025 ble/term/cursor-state/.update "$1" 6026 } 6027 6028 function ble/term/cursor-state/.update-hidden { 6029 local state=$1 6030 [[ $state != hidden ]] && state=reveal 6031 [[ $_ble_term_cursor_hidden_current == "$state" ]] && return 0 6032 6033 if [[ $state == hidden ]]; then 6034 ble/util/buffer "$_ble_term_civis" 6035 else 6036 ble/util/buffer "$_ble_term_cvvis" 6037 fi 6038 6039 _ble_term_cursor_hidden_current=$state 6040 } 6041 function ble/term/cursor-state/hide { 6042 _ble_term_cursor_hidden_internal=hidden 6043 [[ $_ble_term_state == internal ]] && 6044 ble/term/cursor-state/.update-hidden hidden 6045 } 6046 function ble/term/cursor-state/reveal { 6047 _ble_term_cursor_hidden_internal=reveal 6048 [[ $_ble_term_state == internal ]] && 6049 ble/term/cursor-state/.update-hidden reveal 6050 } 6051 6052 #---- DECSET(2004): bracketed paste mode -------------------------------------- 6053 6054 function ble/term/bracketed-paste-mode/.init { 6055 local _ble_local_rlvars; ble/util/rlvar#load 6056 6057 bleopt/declare -v term_bracketed_paste_mode on 6058 if ((_ble_bash>=50100)) && ! ble/util/rlvar#test enable-bracketed-paste; then 6059 # Bash 5.1 以降では既定で on なのでもし無効になっていたら意図的にユーザーが 6060 # off にしたという事。 6061 bleopt term_bracketed_paste_mode= 6062 fi 6063 function bleopt/check:term_bracketed_paste_mode { 6064 if [[ $_ble_term_bracketedPasteMode_internal ]]; then 6065 if [[ $value ]]; then 6066 [[ $bleopt_term_bracketed_paste_mode ]] || ble/util/buffer $'\e[?2004h' 6067 else 6068 [[ ! $bleopt_term_bracketed_paste_mode ]] || ble/util/buffer $'\e[?2004l' 6069 fi 6070 fi 6071 } 6072 ble/util/rlvar#bind-bleopt enable-bracketed-paste term_bracketed_paste_mode bool 6073 6074 builtin unset -f "$FUNCNAME" 6075 } 6076 ble/term/bracketed-paste-mode/.init 6077 6078 _ble_term_bracketedPasteMode_internal= 6079 function ble/term/bracketed-paste-mode/enter { 6080 _ble_term_bracketedPasteMode_internal=1 6081 [[ ${bleopt_term_bracketed_paste_mode-} ]] && 6082 ble/util/buffer $'\e[?2004h' 6083 } 6084 function ble/term/bracketed-paste-mode/leave { 6085 _ble_term_bracketedPasteMode_internal= 6086 [[ ${bleopt_term_bracketed_paste_mode-} ]] && 6087 ble/util/buffer $'\e[?2004l' 6088 } 6089 if [[ $TERM == minix ]]; then 6090 # Minix console は DECSET も使えない 6091 function ble/term/bracketed-paste-mode/enter { :; } 6092 function ble/term/bracketed-paste-mode/leave { :; } 6093 fi 6094 6095 #---- DA2 --------------------------------------------------------------------- 6096 6097 _ble_term_TERM=() 6098 _ble_term_DA1R=() 6099 _ble_term_DA2R=() 6100 _ble_term_TERM_done= 6101 6102 ## @fn ble/term/DA2/initialize-term [depth] 6103 ## @var[out] _ble_term_TERM 6104 function ble/term/DA2/initialize-term { 6105 local depth=$1 6106 local da2r=${_ble_term_DA2R[depth]} 6107 local rex='^[0-9]*(;[0-9]*)*$'; [[ $da2r =~ $rex ]] || return 1 6108 local da2r_vec 6109 ble/string#split da2r_vec ';' "$da2r" 6110 da2r_vec=("${da2r_vec[@]/#/10#0}") # 0で始まっていても10進数で解釈; WA #D1570 checked (is-array) 6111 6112 case $da2r in 6113 # Note #D1946: Terminology は xterm と区別が付かないが決め打ちの様なので、丁 6114 # 度 xterm の該当 version を使っている可能性は低いと見て、取り敢えず 6115 # terminology と判断する事にする。 6116 ('0;271;0') _ble_term_TERM[depth]=terminology:200 ;; # 2012-10-05 https://github.com/borisfaure/terminology/commit/500e7be8b2b876462ed567ef6c90527f37482adb 6117 ('41;285;0') _ble_term_TERM[depth]=terminology:300 ;; # 2013-01-22 https://github.com/borisfaure/terminology/commit/526cc2aeacc0ae54825cbc3a3e2ab64f612f83c9 6118 ('61;337;0') _ble_term_TERM[depth]=terminology:10400 ;; # 2019-01-20 https://github.com/borisfaure/terminology/commit/96bbfd054b271f7ad7f31e699b13c12cb8fbb2e2 6119 6120 # Note #D1909: wezterm が 2022-04-07 に DA2 を変更している。xterm-277 と区別 6121 # が付かないが、ちょうど該当 xterm version (2012-01-08) を使っている可能性は 6122 # 低いと見て取り敢えず wezterm とする。更に mlterm-3.4.2..3.7.1 6123 # (201412..201608) も 1;277;0 を使っていた。 6124 ('0;0;0') _ble_term_TERM[depth]=wezterm:0 ;; 6125 ('1;277;0') _ble_term_TERM[depth]=wezterm:20220408 ;; # 2022-04-07 https://github.com/wez/wezterm/commit/ad91e3776808507cbef9e6d758b89d7ca92a4c7e 6126 6127 # Konsole も大体決め打ちにしている。最近変更した様だ。 6128 ('0;115;0') _ble_term_TERM[depth]=konsole:30000 ;; # 2001-09-16 https://github.com/KDE/konsole/commit/2d93fed82aa27e89c9d7301d09d2e24e4fa4416d 6129 ('1;115;0') _ble_term_TERM[depth]=konsole:220380 ;; # 2022-02-24 https://github.com/KDE/konsole/commit/0cc64dcf7b90075bd17e46653df3069208d6a590 6130 6131 # mlterm (#D1999) 6132 # - 0;96;0 v3.1.0 (2012-03-24) https://github.com/arakiken/mlterm/commit/6ca37d7f99337194d8c893cc48285c0614762535 6133 # * 1;96;0 v3.1.2 (2012-05-20) https://github.com/arakiken/mlterm/commit/6293d0af9cf1e78fd6c35620824b62ff6c87370b 6134 # * 1;277;0 v3.4.2 (2014-12-27) https://github.com/arakiken/mlterm/commit/c4fb36291ec67daf73c48c5b60d1af88ad0487e6 6135 # - 1;279;0 v3.7.2 (2016-08-06) https://github.com/arakiken/mlterm/commit/24a2a4886b70f747fba4ea7c07d6e50a6a49039d 6136 # * 24;279;0 v3.7.2 (2016-08-11) https://github.com/arakiken/mlterm/commit/d094f0f4a31224e1b8d2fa15c6ab37bd1c4c4713 6137 ('1;96;0') _ble_term_TERM[depth]=mlterm:30102 ;; 6138 ('1;277;0') _ble_term_TERM[depth]=mlterm:30402 ;; # Note: wezterm:20220408 と同じ。wezterm の方を優先 6139 ('24;279;0') _ble_term_TERM[depth]=mlterm:30702 ;; 6140 6141 ('0;10;1') # Windows Terminal 6142 # 現状ハードコードされている。 6143 # https://github.com/microsoft/terminal/blob/bcc38d04/src/terminal/adapter/adaptDispatch.cpp#L779-L782 6144 _ble_term_TERM[depth]=wt:0 ;; 6145 ('0;'*';1') 6146 if ((da2r_vec[1]>=1001)); then 6147 # Alacritty 6148 # https://github.com/alacritty/alacritty/blob/4734b2b8/alacritty_terminal/src/term/mod.rs#L1315 6149 # https://github.com/alacritty/alacritty/blob/4734b2b8/alacritty_terminal/src/term/mod.rs#L3104 6150 _ble_term_TERM[depth]=alacritty:$((da2r_vec[1])) 6151 fi ;; 6152 ('1;0'?????';0') 6153 _ble_term_TERM[depth]=foot:${da2r:3:5} ;; 6154 ('1;'*) 6155 if ((4000<=da2r_vec[1]&&da2r_vec[1]<=4009&&3<=da2r_vec[2])); then 6156 _ble_term_TERM[depth]=kitty:$((da2r_vec[1]-4000)) 6157 elif ((2000<=da2r_vec[1]&&da2r_vec[1]<5400&&da2r_vec[2]==0)); then 6158 local version=$((da2r_vec[1])) 6159 _ble_term_TERM[depth]=vte:$version 6160 if ((version<4000)); then 6161 # Note #D1785: vte 0.40.0 未満では DECSCUSR に対応していない。更に未知のシーケ 6162 # ンスを無視する事もできない。それにも拘らず vte-based な端末は 6163 # TERM=xterm を設定するので DECSCUSR が出力されて表示が乱れる原因になる。 6164 # vte の version を見て強制的に DECSCUSR を off にする。 6165 _ble_term_Ss= 6166 fi 6167 fi ;; 6168 ('65;'*) 6169 if ((5300<=da2r_vec[1]&&da2r_vec[2]==1)); then 6170 _ble_term_TERM[depth]=vte:$((da2r_vec[1])) 6171 elif ((da2r_vec[1]>=100)); then 6172 _ble_term_TERM[depth]=RLogin:$((da2r_vec[1])) 6173 fi ;; 6174 ('67;'*) 6175 local rex='^67;[0-9]{3,};0$' 6176 if [[ $TERM == cygwin && $da2r =~ $rex ]]; then 6177 _ble_term_TERM[depth]=cygwin:$((da2r_vec[1])) 6178 fi ;; 6179 ('77;'*';0') 6180 _ble_term_TERM[depth]=mintty:$((da2r_vec[1])) ;; 6181 ('83;'*) 6182 local rex='^83;[0-9]+;0$' 6183 [[ $da2r =~ $rex ]] && _ble_term_TERM[depth]=screen:$((da2r_vec[1])) ;; 6184 ('84;0;0') 6185 _ble_term_TERM[depth]=tmux:0 ;; 6186 ('99;'*) 6187 _ble_term_TERM[depth]=contra:$((da2r_vec[1])) ;; 6188 esac 6189 [[ ${_ble_term_TERM[depth]} ]] && return 0 6190 6191 # xterm 6192 if rex='^xterm(-|$)'; [[ $TERM =~ $rex ]]; then 6193 local version=$((da2r_vec[1])) 6194 if rex='^1;[0-9]+;0$'; [[ $da2r =~ $rex ]]; then 6195 # Note: vte (2000以上), kitty (4000以上) は処理済み 6196 true 6197 elif rex='^0;[0-9]+;0$'; [[ $da2r =~ $rex ]]; then 6198 ((95<=version)) 6199 elif rex='^(2|24|1[89]|41|6[145]);[0-9]+;0$'; [[ $da2r =~ $rex ]]; then 6200 ((280<=version)) 6201 elif rex='^32;[0-9]+;0$'; [[ $da2r =~ $rex ]]; then 6202 ((354<=version&&version<2000)) 6203 else 6204 false 6205 fi && { _ble_term_TERM[depth]=xterm:$version; return 0; } 6206 fi 6207 6208 _ble_term_TERM[depth]=unknown:- 6209 return 0 6210 } 6211 6212 function ble/term/DA1/notify { _ble_term_DA1R=$1; blehook/invoke term_DA1R; } 6213 function ble/term/DA2/notify { 6214 # Note #D1485: screen で attach した時に外側の端末の DA2R が混入する 6215 # 事がある。2回目以降に受信した内容は ble.sh の内部では使用しない事 6216 # にする。 6217 local depth=${#_ble_term_DA2R[@]} 6218 if ((depth==0)) || ble/string#match "${_ble_term_TERM[depth-1]}" '^(screen|tmux):'; then 6219 _ble_term_DA2R[depth]=$1 6220 ble/term/DA2/initialize-term "$depth" 6221 6222 local is_outermost=1 6223 case ${_ble_term_TERM[depth]} in 6224 (screen:*|tmux:*) 6225 # 外側の端末にも DA2 要求を出す。[ Note: 最初の DA2 要求は 6226 # ble/decode/attach (decode.sh) から送信されている。 ] 6227 local ret is_outermost= 6228 ble/term/quote-passthrough $'\e[>c' "$((depth+1))" 6229 ble/util/buffer "$ret" ;; 6230 (contra:*) 6231 if [[ ! ${_ble_term_Ss-} ]]; then 6232 _ble_term_Ss=$'\e[@1 q' 6233 fi ;; 6234 (terminology:*) 6235 # Note #D1946: Terminology にはカーソル位置を戻した時に xenl 状態 6236 # (ty->termstate.wrapnext) が残ってしまうバグがある。これを避ける為に一旦 6237 # CR で行頭に戻ってから DECRC する。 6238 _ble_term_sc=$'\e7' _ble_term_rc=$'\r\e8' ;; 6239 esac 6240 6241 if [[ $is_outermost ]]; then 6242 _ble_term_TERM_done=1 6243 ble/term/modifyOtherKeys/reset 6244 fi 6245 6246 # 外側の端末情報は以降では処理しない 6247 ((depth)) && return 0 6248 fi 6249 6250 blehook/invoke term_DA2R 6251 } 6252 6253 ## @fn ble/term/quote-passthrough seq [level] [opts] 6254 ## 指定したシーケンスを、端末マルチプレクサを通過する様に加工します。 6255 ## 6256 ## @param[in] seq 6257 ## 送信するシーケンスを指定します。 6258 ## 6259 ## @param[in,opt] level 6260 ## シーケンスを届ける階層。0 が一番内側の Bash が動作している端末マルチプレ 6261 ## クサ。省略した場合は一番外側の端末にシーケンスを届ける。 6262 ## 6263 ## @param[in,opt] opts 6264 ## コロン区切りの設定。 6265 ## 6266 ## all 6267 ## 指定した階層以下の全ての端末・端末マルチプレクサに同じシーケンスを送信 6268 ## する。[ Note: terminal multiplexer 自体が処理して外側に作用するかもし 6269 ## れないので、先に pass-through で外側に送った後に terminal multiplexer 6270 ## 自体にも送る。 ] 6271 ## 6272 ## @var[out] ret 6273 ## 加工されたシーケンスを格納します。 6274 ## 6275 function ble/term/quote-passthrough { 6276 local seq=$1 level=${2:-$((${#_ble_term_DA2R[@]}-1))} opts=$3 6277 local all=; [[ :$opts: == *:all:* ]] && all=1 6278 ret=$seq 6279 [[ $seq ]] || return 0 6280 local i 6281 for ((i=level;--i>=0;)); do 6282 if [[ ${_ble_term_TERM[i]} == tmux:* ]]; then 6283 # Note: tmux では pass-through seq の中に含まれる \e は \e\e の様に 6284 # escape する。 6285 ret=$'\ePtmux;'${ret//$'\e'/$'\e\e'}$'\e\\'${all:+$seq} 6286 else 6287 # Note: screen は、最初に現れる \e\\ で pass-through sequence が終わって 6288 # しまうので単純に pass-through sequence を入れ子にはできない。なので、例 6289 # えば "\ePXXX\e\\YYY" を pass-through する時には、\e と \\ の間で 6290 # [\ePXXX\e][\\YYY] の様に分割して、それぞれ pass-through する。 6291 ret=$'\eP'${ret//$'\e\\'/$'\e\e\\\eP\\'}$'\e\\'${all:+$seq} 6292 fi 6293 done 6294 } 6295 6296 _ble_term_DECSTBM= 6297 _ble_term_DECSTBM_reset= 6298 function ble/term/test-DECSTBM.hook1 { 6299 (($1==2)) && _ble_term_DECSTBM=$'\e[%s;%sr' 6300 } 6301 function ble/term/test-DECSTBM.hook2 { 6302 if [[ $_ble_term_DECSTBM ]]; then 6303 if (($1==2)); then 6304 # Failed to reset DECSTBM with \e[;r 6305 _ble_term_DECSTBM_reset=$'\e[r' 6306 else 6307 _ble_term_DECSTBM_reset=$'\e[;r' 6308 fi 6309 fi 6310 } 6311 function ble/term/test-DECSTBM { 6312 # Note: kitty 及び wezterm では SCORC と区別できる形の \e[;r では復 6313 # 帰できない。 6314 local -a DRAW_BUFF=() 6315 ble/canvas/panel/goto-top-dock.draw 6316 ble/canvas/put.draw "$_ble_term_sc"$'\e[1;2r' 6317 ble/canvas/put-cup.draw 2 1 6318 ble/canvas/put-cud.draw 1 6319 ble/term/CPR/request.draw ble/term/test-DECSTBM.hook1 6320 ble/canvas/put.draw $'\e[;r' 6321 ble/canvas/put-cup.draw 2 1 6322 ble/canvas/put-cud.draw 1 6323 ble/term/CPR/request.draw ble/term/test-DECSTBM.hook2 6324 ble/canvas/put.draw $'\e[r'"$_ble_term_rc" 6325 ble/canvas/bflush.draw 6326 } 6327 6328 #---- DSR(6) ------------------------------------------------------------------ 6329 # CPR (CURSOR POSITION REPORT) 6330 6331 _ble_term_CPR_timeout=60 6332 _ble_term_CPR_last_seconds=$SECONDS 6333 _ble_term_CPR_hook=() 6334 function ble/term/CPR/request.buff { 6335 ((SECONDS>_ble_term_CPR_last_seconds+_ble_term_CPR_timeout)) && 6336 _ble_term_CPR_hook=() 6337 _ble_term_CPR_last_seconds=$SECONDS 6338 ble/array#push _ble_term_CPR_hook "$1" 6339 ble/util/buffer $'\e[6n' 6340 return 147 6341 } 6342 function ble/term/CPR/request.draw { 6343 ((SECONDS>_ble_term_CPR_last_seconds+_ble_term_CPR_timeout)) && 6344 _ble_term_CPR_hook=() 6345 _ble_term_CPR_last_seconds=$SECONDS 6346 ble/array#push _ble_term_CPR_hook "$1" 6347 ble/canvas/put.draw $'\e[6n' 6348 return 147 6349 } 6350 function ble/term/CPR/notify { 6351 local hook=${_ble_term_CPR_hook[0]} 6352 ble/array#shift _ble_term_CPR_hook 6353 [[ ! $hook ]] || builtin eval -- "$hook $1 $2" 6354 } 6355 6356 #---- SGR(>4): modifyOtherKeys ------------------------------------------------ 6357 6358 bleopt/declare -v term_modifyOtherKeys_external auto 6359 bleopt/declare -v term_modifyOtherKeys_internal auto 6360 bleopt/declare -v term_modifyOtherKeys_passthrough_kitty_protocol '' 6361 6362 _ble_term_modifyOtherKeys_current= 6363 _ble_term_modifyOtherKeys_current_method= 6364 _ble_term_modifyOtherKeys_current_TERM= 6365 function ble/term/modifyOtherKeys/.update { 6366 local IFS=$_ble_term_IFS state=${1%%:*} 6367 [[ $1 == "$_ble_term_modifyOtherKeys_current" ]] && 6368 [[ $state != 2 || "${_ble_term_TERM[*]}" == "$_ble_term_modifyOtherKeys_current_TERM" ]] && 6369 return 0 6370 6371 # Note: RLogin では modifyStringKeys (\e[>5m) も指定しないと駄目。 6372 # また、RLogin は modifyStringKeys にすると S-数字 を 6373 # 記号に翻訳してくれないので注意。 6374 local previous=${_ble_term_modifyOtherKeys_current%%:*} method 6375 if [[ $state == 2 ]]; then 6376 case $_ble_term_TERM in 6377 (RLogin:*) method=RLogin_modifyStringKeys ;; 6378 (kitty:*) 6379 local da2r_vec 6380 ble/string#split da2r_vec ';' "$_ble_term_DA2R" 6381 if ((da2r_vec[2]>=23)); then 6382 method=kitty_keyboard_protocol 6383 else 6384 method=kitty_modifyOtherKeys 6385 fi ;; 6386 (screen:*|tmux:*) 6387 method=modifyOtherKeys 6388 6389 if [[ $bleopt_term_modifyOtherKeys_passthrough_kitty_protocol ]]; then 6390 # Note (#D1843): if the outermost terminal is kitty-0.23+, we directly 6391 # send keyboard-protocol sequences to the outermost kitty. 6392 local index=$((${#_ble_term_TERM[*]}-1)) 6393 if [[ ${_ble_term_TERM[index]} == kitty:* ]]; then 6394 local da2r_vec 6395 ble/string#split da2r_vec ';' "${_ble_term_DA2R[index]}" 6396 ((da2r_vec[2]>=23)) && method=kitty_keyboard_protocol 6397 fi 6398 fi ;; 6399 (*) 6400 method=modifyOtherKeys 6401 if [[ $1 == *:auto ]]; then 6402 # 問題を起こす端末で無効化。 6403 ble/term/modifyOtherKeys/.supported || method=disabled 6404 fi ;; 6405 esac 6406 6407 # Note #D2062: mc の内部にいる時は外側の端末に関係なく modifyOtherKeys は無 6408 # 効化する。C-o が効かなくなるし、その他の mc に対するコマンドも効かなくな 6409 # る可能性がある。 6410 [[ $MC_SID ]] && method=disabled 6411 6412 # 別の方式で有効化されている時は先に解除しておく。 6413 if ((previous>=2)) && 6414 [[ $method != "$_ble_term_modifyOtherKeys_current_method" ]] 6415 then 6416 ble/term/modifyOtherKeys/.update 1 6417 previous=1 6418 fi 6419 else 6420 method=$_ble_term_modifyOtherKeys_current_method 6421 fi 6422 _ble_term_modifyOtherKeys_current=$1 6423 _ble_term_modifyOtherKeys_current_method=$method 6424 _ble_term_modifyOtherKeys_current_TERM="${_ble_term_TERM[*]}" 6425 6426 case $method in 6427 (RLogin_modifyStringKeys) 6428 case $state in 6429 (0) ble/util/buffer $'\e[>5;0m' ;; 6430 (1) ble/util/buffer $'\e[>5;1m' ;; 6431 (2) ble/util/buffer $'\e[>5;1m\e[>5;2m' ;; 6432 esac 6433 ;; # fallback to modifyOtherKeys 6434 (kitty_modifyOtherKeys) 6435 # Note: kitty has quirks in its implementation of modifyOtherKeys. 6436 # Note #D1549: 1 では無効にならない。変な振る舞い。 6437 # Note #D1626: 更に最近の kitty では \e[>4;0m でも駄目で \e[>4m としなければならない様だ。 6438 case $state in 6439 (0|1) ble/util/buffer $'\e[>4;0m\e[>4m' ;; 6440 (2) ble/util/buffer $'\e[>4;1m\e[>4;2m\e[m' ;; 6441 esac 6442 return 0 ;; 6443 (kitty_keyboard_protocol) 6444 # Note: Kovid removed the support for modifyOtherKeys in kitty 0.24 after 6445 # vim has pointed out the quirk of kitty. The kitty keyboard mode only 6446 # has push/pop operations so that their numbers need to be balanced. 6447 local seq= 6448 case $state in 6449 (0|1) # pop keyboard mode 6450 # When this is empty, ble.sh has not yet pushed any keyboard modes, so 6451 # we just ignore the keyboard mode change. 6452 [[ $previous ]] || return 0 6453 6454 ((previous>=2)) && seq=$'\e[<u' ;; 6455 (2) # push keyboard mode 6456 ((previous>=2)) || seq=$'\e[>1u' ;; 6457 esac 6458 if [[ $seq ]]; then 6459 # Note (#D1843): we directly send kitty-keyboard-protocol sequences to 6460 # the outermost terminal. 6461 local ret 6462 ble/term/quote-passthrough "$seq" 6463 ble/util/buffer "$ret" 6464 6465 # find innermost tmux and adjust its modifyOtherKeys state (do not care 6466 # about screen which is transparent for the user input keys) 6467 local level 6468 for ((level=1;level<${#_ble_term_TERM[@]}-1;level++)); do 6469 [[ ${_ble_term_TERM[level]} == tmux:* ]] || continue 6470 case $state in 6471 (0) seq=$'\e[>4;0m\e[m' ;; 6472 (1) seq=$'\e[>4;1m\e[m' ;; 6473 (2) seq=$'\e[>4;1m\e[>4;2m\e[m' ;; 6474 esac 6475 ble/term/quote-passthrough "$seq" "$level" 6476 ble/util/buffer "$ret" 6477 break 6478 done 6479 fi 6480 return 0 ;; 6481 (disabled) 6482 return 0 ;; 6483 esac 6484 6485 # Note: 対応していない端末が SGR と勘違いしても 6486 # 大丈夫な様に SGR を最後にクリアしておく。 6487 # Note: \e[>4;2m の時は、対応していない端末のため 6488 # 一端 \e[>4;1m にしてから \e[>4;2m にする。 6489 case $state in 6490 (0) ble/util/buffer $'\e[>4;0m\e[m' ;; 6491 (1) ble/util/buffer $'\e[>4;1m\e[m' ;; 6492 (2) ble/util/buffer $'\e[>4;1m\e[>4;2m\e[m' ;; 6493 esac 6494 } 6495 function ble/term/modifyOtherKeys/.supported { 6496 [[ $_ble_term_TERM_done ]] || return 1 6497 6498 # libvte は SGR(>4) を直接画面に表示してしまう。 6499 [[ $_ble_term_TERM == vte:* ]] && return 1 6500 6501 # 改造版 Poderosa は通知でウィンドウサイズを毎回変更するので表示が乱れてしまう 6502 [[ $MWG_LOGINTERM == rosaterm ]] && return 1 6503 6504 case $TERM in 6505 (linux) 6506 # Note #D1213: linux (kernel 5.0.0) は "\e[>" でエスケープシーケンスを閉じ 6507 # てしまう。5.4.8 は大丈夫だがそれでも modifyOtherKeys に対応していない。 6508 return 1 ;; 6509 (minix|sun*) 6510 # minix, Solaris のコンソールもそのまま出力してしまう。 6511 return 1 ;; 6512 (st|st-*) 6513 # Note #D1631: st のエラーログに unknown csi が出るとの文句が出たので無効化。 6514 # 恐らく将来に亘って st は modifyOtherKeys には対応しないだろう。 6515 return 1 ;; 6516 esac 6517 6518 return 0 6519 } 6520 function ble/term/modifyOtherKeys/enter { 6521 local value=$bleopt_term_modifyOtherKeys_internal 6522 if [[ $value == auto ]]; then 6523 value=2:auto 6524 fi 6525 ble/term/modifyOtherKeys/.update "$value" 6526 } 6527 function ble/term/modifyOtherKeys/leave { 6528 local value=$bleopt_term_modifyOtherKeys_external 6529 if [[ $value == auto ]]; then 6530 value=1:auto 6531 fi 6532 ble/term/modifyOtherKeys/.update "$value" 6533 } 6534 function ble/term/modifyOtherKeys/reset { 6535 ble/term/modifyOtherKeys/.update "$_ble_term_modifyOtherKeys_current" 6536 } 6537 6538 #---- Alternate Screen Buffer mode -------------------------------------------- 6539 6540 _ble_term_altscr_state= 6541 function ble/term/enter-altscr { 6542 [[ $_ble_term_altscr_state ]] && return 0 6543 _ble_term_altscr_state=("$_ble_canvas_x" "$_ble_canvas_y") 6544 if [[ $_ble_term_rmcup ]]; then 6545 ble/util/buffer "$_ble_term_smcup" 6546 else 6547 local -a DRAW_BUFF=() 6548 ble/canvas/put.draw $'\e[?1049h' 6549 ble/canvas/put-cup.draw "$LINES" 0 6550 ble/canvas/put-ind.draw "$LINES" 6551 ble/canvas/bflush.draw 6552 fi 6553 } 6554 function ble/term/leave-altscr { 6555 [[ $_ble_term_altscr_state ]] || return 0 6556 if [[ $_ble_term_rmcup ]]; then 6557 ble/util/buffer "$_ble_term_rmcup" 6558 else 6559 local -a DRAW_BUFF=() 6560 ble/canvas/put-cup.draw "$LINES" 0 6561 ble/canvas/put-ind.draw 6562 ble/canvas/put.draw $'\e[?1049l' 6563 ble/canvas/bflush.draw 6564 fi 6565 _ble_canvas_x=${_ble_term_altscr_state[0]} 6566 _ble_canvas_y=${_ble_term_altscr_state[1]} 6567 _ble_term_altscr_state=() 6568 } 6569 6570 #---- rl variable: convert-meta ----------------------------------------------- 6571 6572 _ble_term_rl_convert_meta_adjusted= 6573 _ble_term_rl_convert_meta_external= 6574 function ble/term/rl-convert-meta/enter { 6575 [[ $_ble_term_rl_convert_meta_adjusted ]] && return 0 6576 _ble_term_rl_convert_meta_adjusted=1 6577 6578 if ble/util/rlvar#test convert-meta; then 6579 _ble_term_rl_convert_meta_external=on 6580 builtin bind 'set convert-meta off' 6581 else 6582 _ble_term_rl_convert_meta_external=off 6583 fi 6584 } 6585 function ble/term/rl-convert-meta/leave { 6586 [[ $_ble_term_rl_convert_meta_adjusted ]] || return 1 6587 _ble_term_rl_convert_meta_adjusted= 6588 6589 [[ $_ble_term_rl_convert_meta_external == on ]] && 6590 builtin bind 'set convert-meta on' 6591 } 6592 6593 #---- terminal enter/leave ---------------------------------------------------- 6594 6595 function ble/term/enter-for-widget { 6596 ble/term/bracketed-paste-mode/enter 6597 ble/term/modifyOtherKeys/enter 6598 ble/term/cursor-state/.update "$_ble_term_cursor_internal" 6599 ble/term/cursor-state/.update-hidden "$_ble_term_cursor_hidden_internal" 6600 ble/util/buffer.flush >&2 6601 } 6602 function ble/term/leave-for-widget { 6603 ble/term/visible-bell/erase 6604 ble/term/bracketed-paste-mode/leave 6605 ble/term/modifyOtherKeys/leave 6606 ble/term/cursor-state/.update "$bleopt_term_cursor_external" 6607 ble/term/cursor-state/.update-hidden reveal 6608 ble/util/buffer.flush >&2 6609 } 6610 6611 _ble_term_state=external 6612 function ble/term/enter { 6613 [[ $_ble_term_state == internal ]] && return 0 6614 ble/term/stty/enter 6615 ble/term/rl-convert-meta/enter 6616 ble/term/enter-for-widget 6617 _ble_term_state=internal 6618 } 6619 function ble/term/leave { 6620 [[ $_ble_term_state == external ]] && return 0 6621 ble/term/stty/leave 6622 ble/term/rl-convert-meta/leave 6623 ble/term/leave-for-widget 6624 [[ $_ble_term_cursor_current == default ]] || 6625 _ble_term_cursor_current=unknown # vim は復元してくれない 6626 _ble_term_cursor_hidden_current=unknown 6627 _ble_term_state=external 6628 } 6629 6630 function ble/term/finalize { 6631 ble/term/stty/finalize 6632 ble/term/leave 6633 ble/util/buffer.flush >&2 6634 } 6635 function ble/term/initialize { 6636 ble/term/stty/initialize 6637 ble/term/test-DECSTBM 6638 ble/term/enter 6639 } 6640 6641 #------------------------------------------------------------------------------ 6642 # String manipulations 6643 6644 _ble_util_s2c_table_enabled= 6645 ## @fn ble/util/s2c text [index] 6646 ## @param[in] text 6647 ## @param[in,opt] index 6648 ## @var[out] ret 6649 if ((_ble_bash>=50300)); then 6650 # printf "'c" で Unicode が読める (どの LC_CTYPE でも Unicode になる) 6651 function ble/util/s2c { 6652 builtin printf -v ret %d "'$1" 6653 } 6654 elif ((_ble_bash>=40100)); then 6655 function ble/util/s2c { 6656 # Note #D1881: bash-5.2 以前では printf %d "'x" に対して mbstate_t 状態が 6657 # 残ってしまう。なので一旦 clear を試みる。 6658 if ble/util/is-unicode-output; then 6659 builtin printf -v ret %d "'μ" 6660 else 6661 builtin printf -v ret %d "'x" 6662 fi 6663 builtin printf -v ret %d "'$1" 6664 } 6665 elif ((_ble_bash>=40000&&!_ble_bash_loaded_in_function)); then 6666 # - 連想配列にキャッシュできる 6667 # - printf "'c" で unicode が読める 6668 declare -A _ble_util_s2c_table 6669 _ble_util_s2c_table_enabled=1 6670 function ble/util/s2c { 6671 [[ $_ble_util_locale_triple != "$LC_ALL:$LC_CTYPE:$LANG" ]] && 6672 ble/util/.cache/update-locale 6673 6674 local s=${1::1} 6675 ret=${_ble_util_s2c_table[x$s]} 6676 [[ $ret ]] && return 0 6677 6678 ble/util/sprintf ret %d "'$s" 6679 _ble_util_s2c_table[x$s]=$ret 6680 } 6681 elif ((_ble_bash>=40000)); then 6682 function ble/util/s2c { 6683 ble/util/sprintf ret %d "'${1::1}" 6684 } 6685 else 6686 # bash-3 では printf %d "'あ" 等としても 6687 # "あ" を構成する先頭バイトの値が表示されるだけである。 6688 # 何とかして unicode 値に変換するコマンドを見つけるか、 6689 # 各バイトを取り出して unicode に変換するかする必要がある。 6690 # bash-3 では read -n 1 を用いてバイト単位で読み取れる。これを利用する。 6691 function ble/util/s2c { 6692 local s=${1::1} 6693 if [[ $s == [$'\x01'-$'\x7F'] ]]; then 6694 if [[ $s == $'\x7F' ]]; then 6695 # Note: bash-3.0 では printf %d "'^?" とすると 0 になってしまう。 6696 # printf %d \'^? であれば問題なく 127 になる。 6697 ret=127 6698 else 6699 ble/util/sprintf ret %d "'$s" 6700 fi 6701 return 0 6702 fi 6703 6704 local bytes byte 6705 ble/util/assign bytes ' 6706 local IFS= 6707 while ble/bash/read -n 1 byte; do 6708 builtin printf "%d " "'\''$byte" 6709 done <<< "$s" 6710 IFS=$_ble_term_IFS 6711 ' 6712 "ble/encoding:$bleopt_input_encoding/b2c" $bytes 6713 } 6714 fi 6715 6716 # ble/util/c2s 6717 6718 ## @fn ble/util/c2s.impl char 6719 ## @var[out] ret 6720 if ((_ble_bash>=40200)); then 6721 # $'...' in bash-4.2 supports \uXXXX and \UXXXXXXXX sequences. 6722 6723 # workarounds of bashbug that printf '\uFFFF' results in a broken surrogate 6724 # pair in systems where sizeof(wchar_t) == 2. 6725 function ble/util/.has-bashbug-printf-uffff { 6726 ((40200<=_ble_bash&&_ble_bash<50000)) || return 1 6727 local LC_ALL=C.UTF-8 2>/dev/null # Workaround: CentOS 7 に C.UTF-8 がなかった 6728 local ret 6729 builtin printf -v ret '\uFFFF' 6730 ((${#ret}==2)) 6731 } 6732 # suppress locale error #D1440 6733 ble/function#suppress-stderr ble/util/.has-bashbug-printf-uffff 6734 6735 if ble/util/.has-bashbug-printf-uffff; then 6736 function ble/util/c2s.impl { 6737 if ((0xE000<=$1&&$1<=0xFFFF)) && [[ $_ble_util_locale_encoding == UTF-8 ]]; then 6738 builtin printf -v ret '\\x%02x' "$((0xE0|$1>>12&0x0F))" "$((0x80|$1>>6&0x3F))" "$((0x80|$1&0x3F))" 6739 else 6740 builtin printf -v ret '\\U%08x' "$1" 6741 fi 6742 builtin eval "ret=\$'$ret'" 6743 } 6744 function ble/util/chars2s.impl { 6745 if [[ $_ble_util_locale_encoding == UTF-8 ]]; then 6746 local -a buff=() 6747 local c i=0 6748 for c; do 6749 ble/util/c2s.cached "$c" 6750 buff[i++]=$ret 6751 done 6752 IFS= builtin eval 'ret="${buff[*]}"' 6753 else 6754 builtin printf -v ret '\\U%08x' "$@" 6755 builtin eval "ret=\$'$ret'" 6756 fi 6757 } 6758 else 6759 function ble/util/c2s.impl { 6760 builtin printf -v ret '\\U%08x' "$1" 6761 builtin eval "ret=\$'$ret'" 6762 } 6763 function ble/util/chars2s.impl { 6764 builtin printf -v ret '\\U%08x' "$@" 6765 builtin eval "ret=\$'$ret'" 6766 } 6767 fi 6768 else 6769 _ble_text_xdigit=(0 1 2 3 4 5 6 7 8 9 A B C D E F) 6770 _ble_text_hexmap=() 6771 for ((i=0;i<256;i++)); do 6772 _ble_text_hexmap[i]=${_ble_text_xdigit[i>>4&0xF]}${_ble_text_xdigit[i&0xF]} 6773 done 6774 6775 # 動作確認済 3.1, 3.2, 4.0, 4.2, 4.3 6776 function ble/util/c2s.impl { 6777 if (($1<0x80)); then 6778 builtin eval "ret=\$'\\x${_ble_text_hexmap[$1]}'" 6779 return 0 6780 fi 6781 6782 local bytes i iN seq= 6783 ble/encoding:"$_ble_util_locale_encoding"/c2b "$1" 6784 for ((i=0,iN=${#bytes[@]};i<iN;i++)); do 6785 seq="$seq\\x${_ble_text_hexmap[bytes[i]&0xFF]}" 6786 done 6787 builtin eval "ret=\$'$seq'" 6788 } 6789 6790 function ble/util/chars2s.loop { 6791 for c; do 6792 ble/util/c2s.cached "$c" 6793 buff[i++]=$ret 6794 done 6795 } 6796 function ble/util/chars2s.impl { 6797 # Note: 大量の引数を抱えた関数からの関数呼び出しは重いので 6798 # B=160 毎に小分けにして関数を呼び出す事にする。 6799 local -a buff=() 6800 local c i=0 b N=$# B=160 6801 for ((b=0;b+B<N;b+=B)); do 6802 ble/util/chars2s.loop "${@:b+1:B}" 6803 done 6804 ble/util/chars2s.loop "${@:b+1:N-b}" 6805 IFS= builtin eval 'ret="${buff[*]}"' 6806 } 6807 fi 6808 6809 # どうもキャッシュするのが一番速い様だ 6810 _ble_util_c2s_table=() 6811 ## @fn ble/util/c2s char 6812 ## @var[out] ret 6813 function ble/util/c2s { 6814 [[ $_ble_util_locale_triple != "$LC_ALL:$LC_CTYPE:$LANG" ]] && 6815 ble/util/.cache/update-locale 6816 6817 ret=${_ble_util_c2s_table[$1]-} 6818 if [[ ! $ret ]]; then 6819 ble/util/c2s.impl "$1" 6820 _ble_util_c2s_table[$1]=$ret 6821 fi 6822 } 6823 function ble/util/c2s.cached { 6824 # locale check のない版 6825 ret=${_ble_util_c2s_table[$1]-} 6826 if [[ ! $ret ]]; then 6827 ble/util/c2s.impl "$1" 6828 _ble_util_c2s_table[$1]=$ret 6829 fi 6830 } 6831 function ble/util/chars2s { 6832 [[ $_ble_util_locale_triple != "$LC_ALL:$LC_CTYPE:$LANG" ]] && 6833 ble/util/.cache/update-locale 6834 ble/util/chars2s.impl "$@" 6835 } 6836 6837 ## @fn ble/util/c2bc 6838 ## gets a byte count of the encoded data of the char 6839 ## 指定した文字を現在の符号化方式で符号化した時のバイト数を取得します。 6840 ## @param[in] $1 = code 6841 ## @param[out] ret 6842 function ble/util/c2bc { 6843 "ble/encoding:$bleopt_input_encoding/c2bc" "$1" 6844 } 6845 6846 ## @fn ble/util/.cache/update-locale 6847 ## 6848 ## 使い方 6849 ## 6850 ## [[ $_ble_util_locale_triple != "$LC_ALL:$LC_CTYPE:$LANG" ]] && 6851 ## ble/util/.cache/update-locale 6852 ## 6853 _ble_util_locale_triple= 6854 _ble_util_locale_ctype= 6855 _ble_util_locale_encoding=UTF-8 6856 function ble/util/.cache/update-locale { 6857 _ble_util_locale_triple=$LC_ALL:$LC_CTYPE:$LANG 6858 6859 # clear cache if LC_CTYPE is changed 6860 local ret; ble/string#tolower "${LC_ALL:-${LC_CTYPE:-$LANG}}" 6861 if [[ $_ble_util_locale_ctype != "$ret" ]]; then 6862 _ble_util_locale_ctype=$ret 6863 _ble_util_c2s_table=() 6864 [[ $_ble_util_s2c_table_enabled ]] && 6865 _ble_util_s2c_table=() 6866 6867 _ble_util_locale_encoding=C 6868 if local rex='\.([^@]+)'; [[ $_ble_util_locale_ctype =~ $rex ]]; then 6869 local enc=${BASH_REMATCH[1]} 6870 if [[ $enc == utf-8 || $enc == utf8 ]]; then 6871 enc=UTF-8 6872 fi 6873 6874 ble/is-function "ble/encoding:$enc/b2c" && 6875 _ble_util_locale_encoding=$enc 6876 fi 6877 fi 6878 } 6879 6880 function ble/util/is-unicode-output { 6881 [[ $_ble_util_locale_triple != "$LC_ALL:$LC_CTYPE:$LANG" ]] && 6882 ble/util/.cache/update-locale 6883 [[ $_ble_util_locale_encoding == UTF-8 ]] 6884 } 6885 6886 #------------------------------------------------------------------------------ 6887 6888 ## @fn ble/util/s2chars text 6889 ## @var[out] ret 6890 function ble/util/s2chars { 6891 local text=$1 n=${#1} i chars 6892 chars=() 6893 for ((i=0;i<n;i++)); do 6894 ble/util/s2c "${text:i:1}" 6895 ble/array#push chars "$ret" 6896 done 6897 ret=("${chars[@]}") 6898 } 6899 function ble/util/s2bytes { 6900 local LC_ALL= LC_CTYPE=C 6901 ble/util/s2chars "$1"; local ext=$? 6902 ble/util/unlocal LC_ALL LC_CTYPE 6903 ble/util/.cache/update-locale 6904 return "$?" 6905 } &>/dev/null 6906 6907 # bind で使用される keyseq の形式 6908 6909 ## @fn ble/util/c2keyseq char 6910 ## @var[out] ret 6911 function ble/util/c2keyseq { 6912 local char=$(($1)) 6913 case $char in 6914 (7) ret='\a' ;; 6915 (8) ret='\b' ;; 6916 (9) ret='\t' ;; 6917 (10) ret='\n' ;; 6918 (11) ret='\v' ;; 6919 (12) ret='\f' ;; 6920 (13) ret='\r' ;; 6921 (27) ret='\e' ;; 6922 (92) ret='\\' ;; 6923 (127) ret='\d' ;; 6924 (28) ret='\x1c' ;; # workaround \C-\, \C-\\ 6925 (156) ret='\x9c' ;; # workaround \M-\C-\, \M-\C-\\ 6926 (*) 6927 if ((char<32||128<=char&&char<160)); then 6928 local char7=$((char&0x7F)) 6929 if ((1<=char7&&char7<=26)); then 6930 ble/util/c2s "$((char7+96))" 6931 else 6932 ble/util/c2s "$((char7+64))" 6933 fi 6934 ret='\C-'$ret 6935 ((char&0x80)) && ret='\M-'$ret 6936 else 6937 ble/util/c2s "$char" 6938 fi ;; 6939 esac 6940 } 6941 ## @fn ble/util/chars2keyseq char... 6942 ## @var[out] ret 6943 function ble/util/chars2keyseq { 6944 local char str= 6945 for char; do 6946 ble/util/c2keyseq "$char" 6947 str=$str$ret 6948 done 6949 ret=$str 6950 } 6951 ## @fn ble/util/keyseq2chars keyseq 6952 ## @arr[out] ret 6953 function ble/util/keyseq2chars { 6954 local keyseq=$1 6955 local -a chars=() 6956 local mods= 6957 local rex='^([^\]+)|^\\([CM]-|[0-7]{1,3}|x[0-9a-fA-F]{1,2}|.)?' 6958 while [[ $keyseq ]]; do 6959 local text=${keyseq::1} esc 6960 [[ $keyseq =~ $rex ]] && 6961 text=${BASH_REMATCH[1]} esc=${BASH_REMATCH[2]} 6962 6963 if [[ $text ]]; then 6964 keyseq=${keyseq:${#text}} 6965 ble/util/s2chars "$text" 6966 else 6967 keyseq=${keyseq:1+${#esc}} 6968 ret=() 6969 case $esc in 6970 ([CM]-) mods=$mods${esc::1}; continue ;; 6971 (x?*) ret=$((16#${esc#x})) ;; 6972 ([0-7]*) ret=$((8#$esc)) ;; 6973 (a) ret=7 ;; 6974 (b) ret=8 ;; 6975 (t) ret=9 ;; 6976 (n) ret=10 ;; 6977 (v) ret=11 ;; 6978 (f) ret=12 ;; 6979 (r) ret=13 ;; 6980 (e) ret=27 ;; 6981 (d) ret=127 ;; 6982 (*) ble/util/s2c "$esc" ;; 6983 esac 6984 fi 6985 6986 [[ $mods == *C* ]] && ((ret=ret==63?127:(ret&0x1F))) 6987 [[ $mods == *M* ]] && ble/array#push chars 27 6988 #[[ $mods == *M* ]] && ((ret|=0x80)) 6989 mods= 6990 ble/array#push chars "${ret[@]}" 6991 done 6992 6993 if [[ $mods ]]; then 6994 [[ $mods == *M* ]] && ble/array#push chars 27 6995 ble/array#push chars 0 6996 fi 6997 6998 ret=("${chars[@]}") 6999 } 7000 7001 #------------------------------------------------------------------------------ 7002 7003 ## @fn ble/encoding:UTF-8/b2c byte... 7004 ## @var[out] ret 7005 function ble/encoding:UTF-8/b2c { 7006 local bytes b0 n i 7007 bytes=("$@") 7008 ret=0 7009 ((b0=bytes[0]&0xFF)) 7010 ((n=b0>=0xF0 7011 ?(b0>=0xFC?5:(b0>=0xF8?4:3)) 7012 :(b0>=0xE0?2:(b0>=0xC0?1:0)), 7013 ret=n?b0&0x7F>>n:b0)) 7014 for ((i=1;i<=n;i++)); do 7015 ((ret=ret<<6|0x3F&bytes[i])) 7016 done 7017 } 7018 7019 ## @fn ble/encoding:UTF-8/c2b char 7020 ## @arr[out] bytes 7021 function ble/encoding:UTF-8/c2b { 7022 local code=$1 n i 7023 ((code=code&0x7FFFFFFF, 7024 n=code<0x80?0:( 7025 code<0x800?1:( 7026 code<0x10000?2:( 7027 code<0x200000?3:( 7028 code<0x4000000?4:5)))))) 7029 if ((n==0)); then 7030 bytes=("$code") 7031 else 7032 bytes=() 7033 for ((i=n;i;i--)); do 7034 ((bytes[i]=0x80|code&0x3F, 7035 code>>=6)) 7036 done 7037 ((bytes[0]=code&0x3F>>n|0xFF80>>n&0xFF)) 7038 fi 7039 } 7040 7041 ## @fn ble/encoding:C/b2c byte 7042 ## @var[out] ret 7043 function ble/encoding:C/b2c { 7044 local byte=$1 7045 ((ret=byte&0xFF)) 7046 } 7047 ## @fn ble/encoding:C/c2b char 7048 ## @arr[out] bytes 7049 function ble/encoding:C/c2b { 7050 local code=$1 7051 bytes=("$((code&0xFF))") 7052 } 7053 7054 #------------------------------------------------------------------------------ 7055 # builtin readonly 7056 7057 builtin eval -- "${_ble_util_gdict_declare//NAME/_ble_builtin_readonly_blacklist}" 7058 7059 function ble/builtin/readonly/.initialize-blacklist { 7060 function ble/builtin/readonly/.initialize-blacklist { return 0; } 7061 7062 local -a list=() 7063 7064 # Bash variables ble.sh uses 7065 ble/array#push list FUNCNEST IFS IGNOREEOF POSIXLY_CORRECT TMOUT # adjust 7066 ble/array#push list PWD OLDPWD CDPATH # readlink 7067 ble/array#push list BASHPID GLOBIGNORE MAPFILE REPLY # util 7068 ble/array#push list INPUTRC # decode 7069 ble/array#push list LINES COLUMNS # canvas 7070 ble/array#push list HIST{CONTROL,IGNORE,SIZE,TIMEFORMAT} # history 7071 ble/array#push list PROMPT_COMMAND PS1 # prompt 7072 ble/array#push list BASH_COMMAND BASH_REMATCH HISTCMD LINENO PIPESTATUS TIMEFORMAT # exec 7073 ble/array#push list BASH_XTRACEFD PS4 # debug 7074 7075 # Other common variables that ble.sh uses 7076 ble/array#push list CC LESS MANOPT MANPAGER PAGER PATH MANPATH 7077 7078 # Other uppercase variables that ble.sh internally uses in each module 7079 ble/array#push list BUFF # util 7080 ble/array#push list KEYS KEYMAP WIDGET LASTWIDGET # decode 7081 ble/array#push list DRAW_BUFF # canvas 7082 ble/array#push list D{MIN,MAX,MAX0} {HIGHLIGHT,PREV}_{BUFF,UMAX,UMIN} LEVEL LAYER_{UMAX,UMIN} # color 7083 ble/array#push list HISTINDEX_NEXT FILE LINE INDEX INDEX_FILE # history 7084 ble/array#push list ARG FLAG REG # vi 7085 ble/array#push list COMP{1,2,S,V} ACTION CAND DATA INSERT PREFIX_LEN # core-complete 7086 ble/array#push list PRETTY_NAME NAME VERSION # edit (/etc/os-release) 7087 7088 local v 7089 for v in "${list[@]}"; do ble/gdict#set _ble_builtin_readonly_blacklist "$v" 1; done 7090 } 7091 function ble/builtin/readonly/.check-variable-name { 7092 # Local variables are always allowed to make readonly. Note: this 7093 # implementation does not block propagating tempenv variables being 7094 # propagated to the global context. There is no way to reliably detect the 7095 # tempenv variables. 7096 ble/variable#is-global "$1" || return 0 7097 7098 # If the variable starts with "_" but does not start with "_ble", it could be 7099 # a global variable used by another framework. We allow such namespaced 7100 # variables being readonly. 7101 if [[ $1 == _* && $1 != _ble* && $1 != __ble* ]]; then 7102 return 0 7103 fi 7104 7105 # These special variables should not be made readonly. 7106 case $1 in 7107 (?) return 1;; # single character variables 7108 (BLE_*|ADVICE_*) return 1;; # ble.sh variables 7109 (COMP_*|COMPREPLY) return 1;; # completion variables 7110 (READLINE_*) return 1;; # readline variables 7111 (LC_*|LANG) return 1;; # locale variables 7112 esac 7113 7114 # If the variable name is in the black list, the variable cannot be readonly. 7115 ble/builtin/readonly/.initialize-blacklist 7116 if ble/gdict#get _ble_builtin_readonly_blacklist; then 7117 return 1 7118 fi 7119 7120 # Otherwise, the variables that do not contain lowercase characters are 7121 # allowed to become readonly. 7122 if [[ $1 != *[a-z]* ]]; then 7123 return 0 7124 fi 7125 7126 return 1 7127 } 7128 7129 builtin eval -- "${_ble_util_gdict_declare//NAME/_ble_builtin_readonly_mark}" 7130 _ble_builtin_readonly_message_count=0 7131 blehook internal_PREEXEC!='_ble_builtin_readonly_message_count=0' 7132 7133 ## @fn ble/builtin/readonly/.print-warning 7134 ## @var[out] _ble_local_caller 7135 function ble/builtin/readonly/.print-warning { 7136 [[ -t 2 ]] || return 0 7137 7138 # If the caller iformation has not been initialized: 7139 if [[ ! $_ble_local_caller ]]; then 7140 _ble_local_caller=- 7141 local i n=${#FUNCNAME[@]} 7142 for ((i=1;i<n;i++)); do 7143 [[ ${FUNCNAME[i]} == *readonly ]] && continue 7144 [[ ${FUNCNAME[i]} == ble/function#advice/* ]] && continue 7145 _ble_local_caller="${BASH_SOURCE[i]}:${BASH_LINENO[i-1]}" 7146 break 7147 done 7148 fi 7149 7150 local s_caller= 7151 if [[ $_ble_local_caller != - ]]; then 7152 ! ble/gdict#has _ble_builtin_readonly_mark "$_ble_local_caller:$1" || return 0 7153 ble/gdict#set _ble_builtin_readonly_mark "$_ble_local_caller:$1" yes 7154 s_caller=" ($_ble_local_caller)" 7155 else 7156 # We show messages only up to ten times 7157 ((_ble_builtin_readonly_message_count++<10)) || return 0 7158 fi 7159 7160 _ble_local_flags=w$_ble_local_flags 7161 ble/util/print "ble.sh$s_caller: An attempt to make variable \`$1' readonly was blocked." >&2 7162 7163 return 0 7164 } 7165 function ble/builtin/readonly { 7166 local _ble_local_set _ble_local_shopt 7167 ble/base/.adjust-bash-options _ble_local_set _ble_local_shopt 7168 7169 local _ble_local_flags= 7170 local -a _ble_local_options=() 7171 local _ble_local_caller= # used by print-warning 7172 while (($#)); do 7173 if ble/string#match "$1" '^([_a-zA-Z][_a-zA-Z0-9]*)($|=)'; then 7174 _ble_local_flags=v$_ble_local_flags 7175 local _ble_local_var=${BASH_REMATCH[1]} 7176 if [[ ${BASH_REMATCH[2]} == = ]]; then 7177 ble/util/sprintf "$_ble_local_var" "${1#*=}" 7178 fi 7179 7180 if ble/builtin/readonly/.check-variable-name "$_ble_local_var"; then 7181 _ble_local_flags=r$_ble_local_flags 7182 ble/array#push _ble_local_options "$_ble_local_var" 7183 else 7184 ble/builtin/readonly/.print-warning "$1" 7185 fi 7186 else 7187 ble/array#push _ble_local_options "$1" 7188 fi 7189 shift 7190 done 7191 7192 if [[ $_ble_local_flags == *w* ]]; then 7193 ble/util/print 'ble.sh: The global variables with unprefixed lowercase names or special names should not be made readonly. It can break arbitrary Bash configurations.' >&2 7194 fi 7195 local _ble_local_ext=0 7196 if [[ $_ble_local_flags != *v* || $_ble_local_flags == *r* ]]; then 7197 # We call `builtin readonly' only when no variables are specified 7198 # (e.g. readonly, readonly --help), or at least one variable are allowed to 7199 # become readonly. 7200 builtin readonly "${_ble_local_options[@]}" 7201 _ble_local_ext=$? 7202 fi 7203 ble/base/.restore-bash-options _ble_local_set _ble_local_shopt 7204 return "$?" 7205 } 7206 7207 function readonly { ble/builtin/readonly "$@"; } 7208 7209 #------------------------------------------------------------------------------ 7210 # ble/util/message 7211 7212 >| "$_ble_base_run/$$.util.message-listening" 7213 >> "$_ble_base_run/$$.util.message" 7214 _ble_util_message_precmd=() 7215 7216 ## @fn ble/util/message/.encode-data target data 7217 ## @param[in] target 7218 ## 送信対象のプロセスの PID を指定します 7219 ## @param[in] data 7220 ## 送るデータを指定します 7221 ## @var[out] ret 7222 function ble/util/message/.encode-data { 7223 local target=$1 data=$2 7224 if ((${#data}<256)); then 7225 ble/string#quote-word "$data" 7226 ret=eval:$ret 7227 else 7228 ble/util/getpid 7229 7230 local index=0 file 7231 while 7232 file=$_ble_base_run/$target.util.message.data-$BASHPID-$index 7233 [[ -e $file ]] 7234 do ((++index)); done 7235 7236 ble/util/put "$data" >| "$file" 7237 7238 ret=file:${file##*.} 7239 fi 7240 } 7241 ## @fn ble/util/message/.decode-data data 7242 ## @var[out] ret 7243 function ble/util/message/.decode-data { 7244 ret=$1 7245 case $ret in 7246 (eval:*) 7247 local value=${ret#eval:} 7248 ble/syntax:bash/simple-word/is-simple "$value" && 7249 builtin eval -- "ret=($value)" ;; 7250 (file:*) 7251 local file=$_ble_base_run/$$.util.message.${ret#file:} 7252 ble/util/readfile ret "$file" 7253 ble/array#push _ble_local_remove "$file" 7254 esac 7255 } 7256 7257 function ble/util/message.post { 7258 local target=${1:-$$} event=${2-} type=${3-} data=${4-} 7259 7260 if ! [[ $type && $type != *["$_ble_term_IFS"]* ]]; then 7261 ble/util/print "ble/util/message: invalid message type format '$type'" >&2 7262 return 2 7263 elif [[ $target == $$ ]] && ! ble/is-function ble/util/message/handler:"$type"; then 7264 ble/util/print "ble/util/message: unknown message type name '$type'" >&2 7265 return 2 7266 fi 7267 7268 case $event in 7269 (precmd) ;; 7270 (*) 7271 ble/util/print "ble/util/message: unknown event type '$event'" >&2 7272 return 2 ;; 7273 esac 7274 7275 if [[ $target == broadcast ]]; then 7276 local ret file 7277 ble/util/eval-pathname-expansion '"$_ble_base_run"/+([0-9]).util.message-listening' canonical 7278 for file in "${ret[@]}"; do 7279 file=${file%-listening} 7280 local pid=${file##*/}; pid=${pid%%.*} 7281 if builtin kill -0 "$pid"; then 7282 ble/util/message/.encode-data "$pid" "$data" 7283 ble/util/print "$event $type $ret" >> "$file" 7284 fi 7285 done 7286 7287 elif ble/string#match "$target" '^[0-9]+$'; then 7288 if ! builtin kill -0 "$target"; then 7289 ble/util/print "ble/util/message: target process $target is not found" >&2 7290 return 2 7291 elif [[ ! -f $_ble_base_run/$target.util.message-listening ]]; then 7292 ble/util/print "ble/util/message: target process $target is not listening ble-messages" >&2 7293 return 2 7294 fi 7295 7296 local ret 7297 ble/util/message/.encode-data "$target" "$data" 7298 ble/util/print "$event $type $ret" >> "$_ble_base_run/$target.util.message" 7299 else 7300 ble/util/print "ble/util/message: unknown target '$target'" >&2 7301 return 2 7302 fi 7303 } 7304 function ble/util/message.check { 7305 local file=$_ble_base_run/$$.util.message 7306 while [[ -f $file && -s $file ]]; do 7307 local fread=$file 7308 ble/bin/mv -f "$file" "$file-reading" && fread=$file-reading 7309 7310 local IFS=$_ble_term_IFS event type data 7311 while ble/bash/read event type data || [[ $event ]]; do 7312 # check message handler 7313 [[ $type ]] && ble/is-function ble/util/message/handler:"$type" || continue 7314 7315 case $event in 7316 (precmd) ble/array#push _ble_util_message_precmd "$type $data" ;; 7317 esac 7318 done < "$fread" 7319 7320 >| "$fread" 7321 done 7322 } 7323 function ble/util/message.process { 7324 ble/util/message.check 7325 7326 local event=$1 7327 case $event in 7328 (precmd) 7329 local _ble_local_messages 7330 _ble_local_messages=("${_ble_util_message_precmd[@]}") 7331 _ble_util_message_precmd=() 7332 7333 local _ble_local_message 7334 local -a _ble_local_remove=() 7335 for _ble_local_message in "${_ble_local_messages[@]}"; do 7336 local _ble_local_event=${_ble_local_message%%' '*} 7337 local ret; ble/util/message/.decode-data "${_ble_local_message#* }" 7338 local _ble_local_data=$ret 7339 ble/util/unlocal ret 7340 ble/util/message/handler:"$_ble_local_event" "$_ble_local_data" 7341 done 7342 7343 ((${#_ble_local_remove[@]})) &&ble/bin/rm -f "${_ble_local_remove[@]}" ;; 7344 (*) 7345 ble/util/print "ble/util/message: unknown event type '$event'" >&2 7346 return 2 ;; 7347 esac 7348 } 7349 7350 function ble/util/message/handler:print { 7351 ble/edit/enter-command-layout # #D1800 pair=leave-command-layout 7352 ble/util/print "$1" >&2 7353 ble/edit/leave-command-layout # #D1800 pair=enter-command-layout 7354 } 7355 7356 blehook internal_PRECMD!='ble/util/message.process precmd'