sistema_progs

Programas para customizar o meu entorno de traballo nos meus equipos persoais
Log | Files | Refs

decode.sh (144886B)


      1 #! /bin/bash
      2 
      3 bleopt/declare -v decode_error_char_abell ''
      4 bleopt/declare -v decode_error_char_vbell 1
      5 bleopt/declare -v decode_error_char_discard ''
      6 bleopt/declare -v decode_error_cseq_abell ''
      7 bleopt/declare -v decode_error_cseq_vbell ''
      8 bleopt/declare -v decode_error_cseq_discard 1
      9 bleopt/declare -v decode_error_kseq_abell 1
     10 bleopt/declare -v decode_error_kseq_vbell 1
     11 bleopt/declare -v decode_error_kseq_discard 1
     12 
     13 ## @bleopt default_keymap
     14 ##   既定の編集モードに使われるキーマップを指定します。
     15 ## bleopt_default_keymap=auto
     16 ##   [[ -o emacs/vi ]] の状態に応じて emacs/vi を切り替えます。
     17 ## bleopt_default_keymap=emacs
     18 ##   emacs と同様の編集モードを使用します。
     19 ## bleopt_default_keymap=vi
     20 ##   vi と同様の編集モードを使用します。
     21 bleopt/declare -n default_keymap auto
     22 
     23 function bleopt/check:default_keymap {
     24   case $value in
     25   (auto|emacs|vi|safe)
     26     if [[ $_ble_decode_bind_state != none ]]; then
     27       local bleopt_default_keymap=$value
     28       ble/decode/reset-default-keymap
     29     fi
     30     return 0 ;;
     31   (*)
     32     ble/util/print "bleopt: Invalid value default_keymap='value'. The value should be one of \`auto', \`emacs', \`vi'." >&2
     33     return 1 ;;
     34   esac
     35 }
     36 
     37 ## @fn bleopt/get:default_keymap
     38 ##   @var[out] ret
     39 function bleopt/get:default_keymap {
     40   ret=$bleopt_default_keymap
     41   if [[ $ret == auto ]]; then
     42     if [[ -o vi ]]; then
     43       ret=vi
     44     else
     45       ret=emacs
     46     fi
     47   fi
     48 }
     49 
     50 ## @bleopt decode_isolated_esc
     51 ##   bleopt decode_isolated_esc=meta
     52 ##     単体で受信した ESC を、前置詞として受信した ESC と同様に、
     53 ##     Meta 修飾または特殊キーのエスケープシーケンスとして扱います。
     54 ##   bleopt decode_isolated_esc=esc
     55 ##     単体で受信した ESC を、C-[ として扱います。
     56 bleopt/declare -n decode_isolated_esc auto
     57 
     58 function bleopt/check:decode_isolated_esc {
     59   case $value in
     60   (meta|esc|auto) ;;
     61   (*)
     62     ble/util/print "bleopt: Invalid value decode_isolated_esc='$value'. One of the values 'auto', 'meta' or 'esc' is expected." >&2
     63     return 1 ;;
     64   esac
     65 }
     66 function ble/decode/uses-isolated-esc {
     67   if [[ $bleopt_decode_isolated_esc == esc ]]; then
     68     return 0
     69   elif [[ $bleopt_decode_isolated_esc == auto ]]; then
     70     if local ret; bleopt/get:default_keymap; [[ $ret == vi ]]; then
     71       return 0
     72     elif [[ ! $_ble_decode_key__seq ]]; then
     73       local dicthead=_ble_decode_${_ble_decode_keymap}_kmap_ key=$((_ble_decode_Ctrl|91))
     74       builtin eval "local ent=\${$dicthead$_ble_decode_key__seq[key]-}"
     75       [[ ${ent:2} ]] && return 0
     76     fi
     77   fi
     78 
     79   return 1
     80 }
     81 
     82 ## @bleopt decode_abort_char
     83 bleopt/declare -n decode_abort_char 28
     84 
     85 ## @bleopt decode_macro_limit
     86 bleopt/declare -n decode_macro_limit 1024
     87 
     88 # **** key names ****
     89 
     90 _ble_decode_Meta=0x08000000
     91 _ble_decode_Ctrl=0x04000000
     92 _ble_decode_Shft=0x02000000
     93 _ble_decode_Hypr=0x01000000
     94 _ble_decode_Supr=0x00800000
     95 _ble_decode_Altr=0x00400000
     96 _ble_decode_MaskChar=0x001FFFFF
     97 _ble_decode_MaskFlag=0x7FC00000
     98 
     99 ## @var _ble_decode_Erro
    100 ##   文字復号に異常があった事を表します。
    101 ## @var _ble_decode_Macr
    102 ##   マクロ再生で生成された文字である事を表します。
    103 _ble_decode_Erro=0x40000000
    104 _ble_decode_Macr=0x20000000
    105 
    106 _ble_decode_Flag3=0x10000000 # unused
    107 _ble_decode_FlagA=0x00200000 # unused
    108 
    109 _ble_decode_IsolatedESC=$((0x07FF))
    110 _ble_decode_EscapedNUL=$((0x07FE)) # charlog#encode で用いる
    111 _ble_decode_FunctionKeyBase=0x110000
    112 
    113 ## @fn ble-decode-kbd/.set-keycode keyname key
    114 ##
    115 ## @fn ble-decode-kbd/.get-keycode keyname
    116 ##   @var[out] ret
    117 _ble_decode_kbd_ver=gdict
    118 _ble_decode_kbd__n=0
    119 _ble_decode_kbd__c2k=()
    120 builtin eval -- "${_ble_util_gdict_declare//NAME/_ble_decode_kbd__k2c}"
    121 ble/is-assoc _ble_decode_kbd__k2c || _ble_decode_kbd_ver=adict
    122 
    123 function ble-decode-kbd/.set-keycode {
    124   local keyname=$1
    125   local code=$2
    126   : "${_ble_decode_kbd__c2k[code]:=$keyname}"
    127   ble/gdict#set _ble_decode_kbd__k2c "$keyname" "$code"
    128 }
    129 function ble-decode-kbd/.get-keycode {
    130   ble/gdict#get _ble_decode_kbd__k2c "$1"
    131 }
    132 
    133 ## @fn ble-decode-kbd/.get-keyname keycode
    134 ##
    135 ##   keycode に対応するキーの名前を求めます。
    136 ##   対応するキーが存在しない場合には空文字列を返します。
    137 ##
    138 ##   @param[in] keycode keycode
    139 ##   @var[out]  ret     keyname
    140 ##
    141 function ble-decode-kbd/.get-keyname {
    142   local keycode=$1
    143   ret=${_ble_decode_kbd__c2k[keycode]}
    144   if [[ ! $ret ]] && ((keycode<_ble_decode_FunctionKeyBase)); then
    145     ble/util/c2s "$keycode"
    146   fi
    147 }
    148 ## @fn ble-decode-kbd/generate-keycode keyname
    149 ##   指定した名前に対応する keycode を取得します。
    150 ##   指定した名前の key が登録されていない場合は、
    151 ##   新しく kecode を割り当てて返します。
    152 ##   @param[in]  keyname keyname
    153 ##   @var  [out] ret     keycode
    154 function ble-decode-kbd/generate-keycode {
    155   local keyname=$1
    156   if ((${#keyname}==1)); then
    157     ble/util/s2c "$1"
    158   elif [[ $keyname && ! ${keyname//[_a-zA-Z0-9]} ]]; then
    159     ble-decode-kbd/.get-keycode "$keyname"
    160     if [[ ! $ret ]]; then
    161       ((ret=_ble_decode_FunctionKeyBase+_ble_decode_kbd__n++))
    162       ble-decode-kbd/.set-keycode "$keyname" "$ret"
    163     fi
    164   else
    165     ret=-1
    166     return 1
    167   fi
    168 }
    169 
    170 function ble-decode-kbd/.initialize {
    171   ble-decode-kbd/.set-keycode TAB  9
    172   ble-decode-kbd/.set-keycode RET  13
    173 
    174   ble-decode-kbd/.set-keycode NUL  0
    175   ble-decode-kbd/.set-keycode SOH  1
    176   ble-decode-kbd/.set-keycode STX  2
    177   ble-decode-kbd/.set-keycode ETX  3
    178   ble-decode-kbd/.set-keycode EOT  4
    179   ble-decode-kbd/.set-keycode ENQ  5
    180   ble-decode-kbd/.set-keycode ACK  6
    181   ble-decode-kbd/.set-keycode BEL  7
    182   ble-decode-kbd/.set-keycode BS   8
    183   ble-decode-kbd/.set-keycode HT   9  # aka TAB
    184   ble-decode-kbd/.set-keycode LF   10
    185   ble-decode-kbd/.set-keycode VT   11
    186   ble-decode-kbd/.set-keycode FF   12
    187   ble-decode-kbd/.set-keycode CR   13 # aka RET
    188   ble-decode-kbd/.set-keycode SO   14
    189   ble-decode-kbd/.set-keycode SI   15
    190 
    191   ble-decode-kbd/.set-keycode DLE  16
    192   ble-decode-kbd/.set-keycode DC1  17
    193   ble-decode-kbd/.set-keycode DC2  18
    194   ble-decode-kbd/.set-keycode DC3  19
    195   ble-decode-kbd/.set-keycode DC4  20
    196   ble-decode-kbd/.set-keycode NAK  21
    197   ble-decode-kbd/.set-keycode SYN  22
    198   ble-decode-kbd/.set-keycode ETB  23
    199   ble-decode-kbd/.set-keycode CAN  24
    200   ble-decode-kbd/.set-keycode EM   25
    201   ble-decode-kbd/.set-keycode SUB  26
    202   ble-decode-kbd/.set-keycode ESC  27
    203   ble-decode-kbd/.set-keycode FS   28
    204   ble-decode-kbd/.set-keycode GS   29
    205   ble-decode-kbd/.set-keycode RS   30
    206   ble-decode-kbd/.set-keycode US   31
    207 
    208   ble-decode-kbd/.set-keycode SP   32
    209   ble-decode-kbd/.set-keycode DEL  127
    210 
    211   ble-decode-kbd/.set-keycode PAD  128
    212   ble-decode-kbd/.set-keycode HOP  129
    213   ble-decode-kbd/.set-keycode BPH  130
    214   ble-decode-kbd/.set-keycode NBH  131
    215   ble-decode-kbd/.set-keycode IND  132
    216   ble-decode-kbd/.set-keycode NEL  133
    217   ble-decode-kbd/.set-keycode SSA  134
    218   ble-decode-kbd/.set-keycode ESA  135
    219   ble-decode-kbd/.set-keycode HTS  136
    220   ble-decode-kbd/.set-keycode HTJ  137
    221   ble-decode-kbd/.set-keycode VTS  138
    222   ble-decode-kbd/.set-keycode PLD  139
    223   ble-decode-kbd/.set-keycode PLU  140
    224   ble-decode-kbd/.set-keycode RI   141
    225   ble-decode-kbd/.set-keycode SS2  142
    226   ble-decode-kbd/.set-keycode SS3  143
    227 
    228   ble-decode-kbd/.set-keycode DCS  144
    229   ble-decode-kbd/.set-keycode PU1  145
    230   ble-decode-kbd/.set-keycode PU2  146
    231   ble-decode-kbd/.set-keycode STS  147
    232   ble-decode-kbd/.set-keycode CCH  148
    233   ble-decode-kbd/.set-keycode MW   149
    234   ble-decode-kbd/.set-keycode SPA  150
    235   ble-decode-kbd/.set-keycode EPA  151
    236   ble-decode-kbd/.set-keycode SOS  152
    237   ble-decode-kbd/.set-keycode SGCI 153
    238   ble-decode-kbd/.set-keycode SCI  154
    239   ble-decode-kbd/.set-keycode CSI  155
    240   ble-decode-kbd/.set-keycode ST   156
    241   ble-decode-kbd/.set-keycode OSC  157
    242   ble-decode-kbd/.set-keycode PM   158
    243   ble-decode-kbd/.set-keycode APC  159
    244 
    245   ble-decode-kbd/.set-keycode @ESC "$_ble_decode_IsolatedESC"
    246   ble-decode-kbd/.set-keycode @NUL "$_ble_decode_EscapedNUL"
    247 
    248   local ret
    249   ble-decode-kbd/generate-keycode __batch_char__
    250   _ble_decode_KCODE_BATCH_CHAR=$ret
    251   ble-decode-kbd/generate-keycode __defchar__
    252   _ble_decode_KCODE_DEFCHAR=$ret
    253   ble-decode-kbd/generate-keycode __default__
    254   _ble_decode_KCODE_DEFAULT=$ret
    255   ble-decode-kbd/generate-keycode __before_widget__
    256   _ble_decode_KCODE_BEFORE_WIDGET=$ret
    257   ble-decode-kbd/generate-keycode __after_widget__
    258   _ble_decode_KCODE_AFTER_WIDGET=$ret
    259   ble-decode-kbd/generate-keycode __attach__
    260   _ble_decode_KCODE_ATTACH=$ret
    261   ble-decode-kbd/generate-keycode __detach__
    262   _ble_decode_KCODE_DETACH=$ret
    263 
    264   ble-decode-kbd/generate-keycode shift
    265   _ble_decode_KCODE_SHIFT=$ret
    266   ble-decode-kbd/generate-keycode alter
    267   _ble_decode_KCODE_ALTER=$ret
    268   ble-decode-kbd/generate-keycode control
    269   _ble_decode_KCODE_CONTROL=$ret
    270   ble-decode-kbd/generate-keycode meta
    271   _ble_decode_KCODE_META=$ret
    272   ble-decode-kbd/generate-keycode super
    273   _ble_decode_KCODE_SUPER=$ret
    274   ble-decode-kbd/generate-keycode hyper
    275   _ble_decode_KCODE_HYPER=$ret
    276 
    277   # Note: 無視するキー。ble-decode-char に於いて
    278   #   端末からの通知などを処理した時に使う。
    279   ble-decode-kbd/generate-keycode __ignore__
    280   _ble_decode_KCODE_IGNORE=$ret
    281 
    282   # Note: bleopt decode_error_cseq_discard
    283   ble-decode-kbd/generate-keycode __error__
    284   _ble_decode_KCODE_ERROR=$ret
    285 
    286   # Note: line_limit による制限超過時のイベント
    287   ble-decode-kbd/generate-keycode __line_limit__
    288   _ble_decode_KCODE_LINE_LIMIT=$ret
    289 
    290   # Note: 暫定的な対応なので後で変更するかもしれない
    291   ble-decode-kbd/generate-keycode mouse
    292   _ble_decode_KCODE_MOUSE=$ret
    293   ble-decode-kbd/generate-keycode mouse_move
    294   _ble_decode_KCODE_MOUSE_MOVE=$ret
    295 
    296   # Note: 以下は改めてそれぞれのファイルで参照される
    297   #   コードを固定する為にここで定義しておく。
    298   ble-decode-kbd/generate-keycode auto_complete_enter
    299 
    300   # Note: ここに新しく kcode を追加した時には init-cmap.sh に何か変更をして、
    301   # cmap 及び keymap が更新される様にする必要がある。emacs.sh, vi.sh については
    302   # init-cmap.sh を見て自動的に更新されるので特別な処置は必要ない筈。
    303 }
    304 
    305 ble-decode-kbd/.initialize
    306 
    307 ## @fn ble-decode-kbd [TYPE:]VALUE...
    308 ##   @param[in] TYPE VALUE
    309 ##     キー列を指定します。TYPE はキー列の解釈方法を指定します。
    310 ##     TYPE の値に応じて VALUE には以下の物を指定します。TYPE の既定値は kbd です。
    311 ##     kspecs ... kspecs を指定します。
    312 ##     keys   ... キーコードの整数列を指定します。
    313 ##     chars  ... 文字コードの整数列を指定します。
    314 ##     keyseq ... bash bind の keyseq を指定します。
    315 ##     raw    ... バイト列を直接文字列として指定します。
    316 ##
    317 ##   @var[out] ret
    318 ##     キー列を空白区切りの整数列として返します。
    319 function ble-decode-kbd {
    320   local IFS=$_ble_term_IFS
    321   local spec="$*"
    322   case $spec in
    323   (keys:*)
    324     ret="${spec#*:}"
    325     return 0 ;;
    326   (chars:*)
    327     local chars
    328     ble/string#split-words chars "${spec#*:}"
    329     ble/decode/cmap/decode-chars "${ret[@]}"
    330     ret="${keys[*]}"
    331     return 0 ;;
    332   (keyseq:*) # i.e. untranslated keyseq
    333     local keys
    334     ble/util/keyseq2chars "${spec#*:}"
    335     ble/decode/cmap/decode-chars "${ret[@]}"
    336     ret="${keys[*]}"
    337     return 0 ;;
    338   (raw:*) # i.e. translated keyseq
    339     ble/util/s2chars "${spec#*:}"
    340     ble/decode/cmap/decode-chars "${ret[@]}"
    341     ret="${keys[*]}"
    342     return 0 ;;
    343   (kspecs:*)
    344     spec=${spec#*:} ;;
    345   esac
    346 
    347   local kspecs; ble/string#split-words kspecs "$spec"
    348   local kspec code codes
    349   codes=()
    350   for kspec in "${kspecs[@]}"; do
    351     code=0
    352     while [[ $kspec == ?-* ]]; do
    353       case ${kspec::1} in
    354       (S) ((code|=_ble_decode_Shft)) ;;
    355       (C) ((code|=_ble_decode_Ctrl)) ;;
    356       (M) ((code|=_ble_decode_Meta)) ;;
    357       (A) ((code|=_ble_decode_Altr)) ;;
    358       (s) ((code|=_ble_decode_Supr)) ;;
    359       (H) ((code|=_ble_decode_Hypr)) ;;
    360       (*) ((code|=_ble_decode_Erro)) ;;
    361       esac
    362       kspec=${kspec:2}
    363     done
    364 
    365     if [[ $kspec == ? ]]; then
    366       ble/util/s2c "$kspec"
    367       ((code|=ret))
    368     elif [[ $kspec && ! ${kspec//[@_a-zA-Z0-9]} ]]; then
    369       ble-decode-kbd/.get-keycode "$kspec"
    370       [[ $ret ]] || ble-decode-kbd/generate-keycode "$kspec"
    371       ((code|=ret))
    372     elif [[ $kspec == ^? ]]; then
    373       if [[ $kspec == '^?' ]]; then
    374         ((code|=0x7F))
    375       elif [[ $kspec == '^`' ]]; then
    376         ((code|=0x20))
    377       else
    378         ble/util/s2c "${kspec:1}"
    379         ((code|=ret&0x1F))
    380       fi
    381     elif local rex='^U\+([0-9a-fA-F]+)$'; [[ $kspec =~ $rex ]]; then
    382       ((code|=0x${BASH_REMATCH[1]}))
    383     else
    384       ((code|=_ble_decode_Erro))
    385     fi
    386 
    387     codes[${#codes[@]}]=$code
    388   done
    389 
    390   ret="${codes[*]}"
    391 }
    392 
    393 ## @fn ble-decode-unkbd/.single-key key
    394 ##   @var[in] key
    395 ##     キーを表す整数値
    396 ##   @var[out] ret
    397 ##     key の文字列表現を返します。
    398 function ble-decode-unkbd/.single-key {
    399   local key=$1
    400 
    401   local f_unknown=
    402   local char=$((key&_ble_decode_MaskChar))
    403   ble-decode-kbd/.get-keyname "$char"
    404   if [[ ! $ret ]]; then
    405     f_unknown=1
    406     ret=__UNKNOWN__
    407   fi
    408 
    409   ((key&_ble_decode_Shft)) && ret=S-$ret
    410   ((key&_ble_decode_Meta)) && ret=M-$ret
    411   ((key&_ble_decode_Ctrl)) && ret=C-$ret
    412   ((key&_ble_decode_Altr)) && ret=A-$ret
    413   ((key&_ble_decode_Supr)) && ret=s-$ret
    414   ((key&_ble_decode_Hypr)) && ret=H-$ret
    415 
    416   [[ ! $f_unknown ]]
    417 }
    418 
    419 ## @fn ble-decode-unkbd keys...
    420 ##   @param[in] keys
    421 ##     キーを表す整数値の列を指定します。
    422 ##   @var[out] ret
    423 function ble-decode-unkbd {
    424   local IFS=$_ble_term_IFS
    425   local -a kspecs
    426   local key
    427   for key in $*; do
    428     ble-decode-unkbd/.single-key "$key"
    429     kspecs[${#kspecs[@]}]=$ret
    430   done
    431   ret="${kspecs[*]}"
    432 }
    433 
    434 # **** ble-decode-byte ****
    435 
    436 ## @fn[custom] ble-decode/PROLOGUE
    437 ## @fn[custom] ble-decode/EPILOGUE
    438 function ble-decode/PROLOGUE { :; }
    439 function ble-decode/EPILOGUE { :; }
    440 
    441 _ble_decode_input_buffer=()
    442 _ble_decode_input_count=0
    443 _ble_decode_input_original_info=()
    444 
    445 _ble_decode_show_progress_hook=ble-decode/.hook/show-progress
    446 _ble_decode_erase_progress_hook=ble-decode/.hook/erase-progress
    447 function ble-decode/.hook/show-progress {
    448   if [[ $_ble_edit_info_scene == store ]]; then
    449     _ble_decode_input_original_info=("${_ble_edit_info[@]}")
    450     return 0
    451   elif [[ $_ble_edit_info_scene == default ]]; then
    452     _ble_decode_input_original_info=()
    453   elif [[ $_ble_edit_info_scene != decode_input_progress ]]; then
    454     return 0
    455   fi
    456 
    457   local progress_opts= opt_percentage=1
    458   if [[ $ble_batch_insert_count ]]; then
    459     local total=$ble_batch_insert_count
    460     local value=$ble_batch_insert_index
    461     local label='constructing text...'
    462     local sgr=$'\e[1;38;5;204;48;5;253m'
    463   elif ((${#_ble_decode_input_buffer[@]})); then
    464     local total=10000
    465     local value=$((${#_ble_decode_input_buffer[@]}%10000))
    466     local label="${#_ble_decode_input_buffer[@]} bytes received..."
    467     local sgr=$'\e[1;38;5;135;48;5;253m'
    468     progress_opts=unlimited
    469     opt_percentage=
    470   elif ((_ble_decode_input_count)); then
    471     local total=${#chars[@]}
    472     local value=$((total-_ble_decode_input_count-1))
    473     local label='decoding input...'
    474     local sgr=$'\e[1;38;5;69;48;5;253m'
    475   elif ((ble_decode_char_total)); then
    476     local total=$ble_decode_char_total
    477     local value=$((total-ble_decode_char_rest-1))
    478     local label='processing input...'
    479     local sgr=$'\e[1;38;5;71;48;5;253m'
    480   else
    481     return 0
    482   fi
    483 
    484   if [[ $opt_percentage ]]; then
    485     local mill=$((value*1000/total))
    486     local cent=${mill::${#mill}-1} frac=${mill:${#mill}-1}
    487     label="${cent:-0}.$frac% $label"
    488   fi
    489 
    490   local text="($label)"
    491   if ble/util/is-unicode-output; then
    492     local ret
    493     ble/string#create-unicode-progress-bar "$value" "$total" 10 "$progress_opts"
    494     text=$sgr$ret$'\e[m '$text
    495   fi
    496 
    497   ble/edit/info/show ansi "$text"
    498 
    499   _ble_edit_info_scene=decode_input_progress
    500 }
    501 function ble-decode/.hook/erase-progress {
    502   [[ $_ble_edit_info_scene == decode_input_progress ]] || return 1
    503   if ((${#_ble_decode_input_original_info[@]})); then
    504     ble/edit/info/show store "${_ble_decode_input_original_info[@]}"
    505   else
    506     ble/edit/info/default
    507   fi
    508 }
    509 
    510 ## @fn ble-decode/.check-abort byte
    511 ##   bleopt_decode_abort_char による decode abort を検出します。
    512 ##
    513 ##   @remarks modifyOtherKeys も考慮に入れると実は C-x の形式のキーは
    514 ##   "CSI 27;5; code ~" や "CSI code ;5u" の形式で送られてくる。
    515 ##   _ble_decode_input_buffer に記録されている受信済みバイトも検査して
    516 ##   これらのシーケンスを構成していないか確認する必要がある。
    517 ##
    518 function ble-decode/.check-abort {
    519   if (($1==bleopt_decode_abort_char)); then
    520     local nbytes=${#_ble_decode_input_buffer[@]}
    521     local nchars=${#_ble_decode_char_buffer[@]}
    522     ((nbytes||nchars)); return "$?"
    523   fi
    524 
    525   (($1==0x7e||$1==0x75)) || return 1
    526 
    527   local i=$((${#_ble_decode_input_buffer[@]}-1))
    528   local n
    529   ((n=bleopt_decode_abort_char,
    530     n+=(1<=n&&n<=26?96:64)))
    531 
    532   if (($1==0x7e)); then
    533     # Check "CSI >? 27 ; 5 ; XXX ~"
    534 
    535     # Check code
    536     for ((;n;n/=10)); do
    537       ((i>=0)) && ((_ble_decode_input_buffer[i--]==n%10+48)) || return 1
    538     done
    539 
    540     # Check "27;5;"
    541     ((i>=4)) || return 1
    542     ((_ble_decode_input_buffer[i--]==59)) || return 1
    543     ((_ble_decode_input_buffer[i--]==53)) || return 1
    544     ((_ble_decode_input_buffer[i--]==59)) || return 1
    545     ((_ble_decode_input_buffer[i--]==55)) || return 1
    546     ((_ble_decode_input_buffer[i--]==50)) || return 1
    547 
    548   elif (($1==0x75)); then
    549     # Check "CSI >? XXX ; 5 u"
    550 
    551     # Check ";5"
    552     ((i>=1)) || return 1
    553     ((_ble_decode_input_buffer[i--]==53)) || return 1
    554     ((_ble_decode_input_buffer[i--]==59)) || return 1
    555 
    556     # Check code
    557     for ((;n;n/=10)); do
    558       ((i>=0)) && ((_ble_decode_input_buffer[i--]==n%10+48)) || return 1
    559     done
    560   fi
    561 
    562   # Skip ">"
    563   ((i>=0)) && ((_ble_decode_input_buffer[i]==62&&i--))
    564 
    565   # Check CSI ("\e[", "\xC0\x9B[" or "\xC2\x9B")
    566   # ENCODING: UTF-8 (\xC0\x9B)
    567   ((i>=0)) || return 1
    568   if ((_ble_decode_input_buffer[i]==0x5B)); then
    569     if ((i>=1&&_ble_decode_input_buffer[i-1]==0x1B)); then
    570       ((i-=2))
    571     elif ((i>=2&&_ble_decode_input_buffer[i-1]==0x9B&&_ble_decode_input_buffer[i-2]==0xC0)); then
    572       ((i-=3))
    573     else
    574       return 1
    575     fi
    576   elif ((_ble_decode_input_buffer[i]==0x9B)); then
    577     ((--i>=0)) && ((_ble_decode_input_buffer[i--]==0xC2)) || return 1
    578   else
    579     return 1
    580   fi
    581   (((i>=0||${#_ble_decode_char_buffer[@]}))); return "$?"
    582   return 0
    583 }
    584 
    585 if ((_ble_bash>=40400)); then
    586   function ble/decode/nonblocking-read {
    587     local timeout=${1:-0.01} ntimeout=${2:-1} loop=${3:-100}
    588     local LC_ALL= LC_CTYPE=C IFS=
    589     local -a data=()
    590     local line buff ext
    591     while ((loop--)); do
    592       ble/bash/read-timeout "$timeout" -r -d '' buff; ext=$?
    593       [[ $buff ]] && line=$line$buff
    594       if ((ext==0)); then
    595         ble/array#push data "$line"
    596         line=
    597       elif ((ext>128)); then
    598         # timeout
    599         ((--ntimeout)) || break
    600         [[ $buff ]] || break
    601       else
    602         break
    603       fi
    604     done
    605 
    606     ble/util/assign ret '{
    607       ((${#data[@]})) && printf %s\\0 "${data[@]}"
    608       [[ $line ]] && printf %s "$line"
    609     } | ble/bin/od -A n -t u1 -v'
    610     ble/string#split-words ret "$ret"
    611   }
    612   # suppress locale error #D1440
    613   ble/function#suppress-stderr ble/decode/nonblocking-read
    614 elif ((_ble_bash>=40000)); then
    615   function ble/decode/nonblocking-read {
    616     local timeout=${1:-0.01} ntimeout=${2:-1} loop=${3:-100}
    617     local LC_ALL= LC_CTYPE=C IFS= 2>/dev/null
    618     local -a data=()
    619     local line buff
    620     while ((loop--)); do
    621       builtin read -t 0 || break
    622       ble/bash/read -d '' -n 1 buff || break
    623       if [[ $buff ]]; then
    624         line=$line$buff
    625       else
    626         ble/array#push data "$line"
    627         line=
    628       fi
    629     done
    630 
    631     ble/util/assign ret '{
    632       ((${#data[@]})) && printf %s\\0 "${data[@]}"
    633       [[ $line ]] && printf %s "$line"
    634     } | ble/bin/od -A n -t u1 -v'
    635     ble/string#split-words ret "$ret"
    636   }
    637   # suppress locale error #D1440
    638   ble/function#suppress-stderr ble/decode/nonblocking-read
    639 fi
    640 
    641 function ble-decode/.hook/adjust-volatile-options {
    642   # Note: bind -x 内の set +v は揮発性なのでできるだけ先頭で set +v しておく。
    643   # (PROLOGUE 内から呼ばれる) stdout.on より前であれば大丈夫 #D0930
    644   if [[ $_ble_bash_options_adjusted ]]; then
    645     set +ev
    646   fi
    647   if [[ $_ble_bash_POSIXLY_CORRECT_adjusted && ${POSIXLY_CORRECT+set} ]]; then
    648     set +o posix
    649     ble/base/workaround-POSIXLY_CORRECT
    650   fi
    651 }
    652 
    653 ## @var _ble_decode_hook_count
    654 ##   これまでに呼び出された ble-decode/.hook の回数を記録する。(同じ
    655 ##   bash プロセス内の前の ble.sh session も含めて) 今までに一度も呼び
    656 ##   出された事がない場合には空文字列を設定する。
    657 _ble_decode_hook_count=${_ble_decode_hook_count:+0}
    658 _ble_decode_hook_Processing=
    659 function ble-decode/.hook {
    660 #%if leakvar
    661 ble/debug/leakvar#check $"leakvar" H0-begin
    662 #%end.i
    663   ((_ble_decode_hook_count++))
    664   if ble/util/is-stdin-ready; then
    665     ble/array#push _ble_decode_input_buffer "$@"
    666 
    667     local buflen=${#_ble_decode_input_buffer[@]}
    668     if ((buflen%257==0&&buflen>=2000)); then
    669       ble-decode/.hook/adjust-volatile-options
    670 
    671       local IFS=$_ble_term_IFS
    672       local _ble_decode_hook_Processing=prologue
    673       ble-decode/PROLOGUE
    674       _ble_decode_hook_Processing=body
    675 
    676       # その場で標準入力を読み切る
    677       local char=${_ble_decode_input_buffer[buflen-1]}
    678       if ((_ble_bash<40000||char==0xC0||char==0xDF)); then
    679         # Note: これらの文字は bind -s マクロの非終端文字。
    680         # 現在マクロの処理中である可能性があるので標準入力から
    681         # 読み取るとバイトの順序が変わる可能性がある。
    682         # 従って読み取りは行わない。
    683         builtin eval -- "$_ble_decode_show_progress_hook"
    684       else
    685         while ble/util/is-stdin-ready; do
    686           builtin eval -- "$_ble_decode_show_progress_hook"
    687           local ret; ble/decode/nonblocking-read 0.02 1 527
    688           ble/array#push _ble_decode_input_buffer "${ret[@]}"
    689         done
    690       fi
    691       _ble_decode_hook_Processing=epilogue
    692       ble-decode/EPILOGUE
    693       ble/util/unlocal _ble_decode_hook_Processing
    694 #%if leakvar
    695 ble/debug/leakvar#check $"leakvar" H0b1-1
    696 #%end.i
    697 
    698       local ret
    699       ble/array#pop _ble_decode_input_buffer
    700       ble-decode/.hook "$ret"
    701 #%if leakvar
    702 ble/debug/leakvar#check $"leakvar" H0b1-2
    703 #%end.i
    704     fi
    705 #%if leakvar
    706 ble/debug/leakvar#check $"leakvar" H0b2
    707 #%end.i
    708 
    709     return 0
    710   fi
    711 
    712   ble-decode/.hook/adjust-volatile-options
    713 
    714   local IFS=$_ble_term_IFS
    715   local _ble_decode_hook_Processing=prologue
    716   ble-decode/PROLOGUE
    717   _ble_decode_hook_Processing=body
    718 #%if leakvar
    719 ble/debug/leakvar#check $"leakvar" H1-PROLOGUE
    720 #%end.i
    721 
    722   # abort #D0998
    723   if ble-decode/.check-abort "$1"; then
    724     _ble_decode_char__hook=
    725     _ble_decode_input_buffer=()
    726     _ble_decode_char_buffer=()
    727     ble/term/visible-bell "Abort by 'bleopt decode_abort_char=$bleopt_decode_abort_char'"
    728     shift
    729     # 何れにしても EPILOGUE を実行する必要があるので下に流れる。
    730     # ble/term/visible-bell を表示する為には PROLOGUE の後でなければならない事にも注意する。
    731   fi
    732 #%if leakvar
    733 ble/debug/leakvar#check $"leakvar" H2-abort
    734 #%end.i
    735 
    736   local chars
    737   # Note: Bash-4.4 で遅いので ble/array#set 経由で設定する
    738   ble/array#set chars "${_ble_decode_input_buffer[@]}" "$@"
    739   _ble_decode_input_buffer=()
    740   _ble_decode_input_count=${#chars[@]}
    741 
    742   if ((_ble_decode_input_count>=200)); then
    743     local i N=${#chars[@]}
    744     local B=$((N/100))
    745     ((B<100)) && B=100 || ((B>1000)) && B=1000
    746     for ((i=0;i<N;i+=B)); do
    747       ((_ble_decode_input_count=N-i-B))
    748       ((_ble_decode_input_count<0)) && _ble_decode_input_count=0
    749       builtin eval -- "$_ble_decode_show_progress_hook"
    750 #%if debug_keylogger
    751       ((_ble_debug_keylog_enabled)) && ble/array#push _ble_debug_keylog_bytes "${chars[@]:i:B}"
    752 #%end
    753 #%if leakvar
    754 ble/debug/leakvar#check $"leakvar" "[H3b1: before decode $chars...]"
    755 #%end.i
    756       "ble/encoding:$bleopt_input_encoding/decode" "${chars[@]:i:B}"
    757 #%if leakvar
    758 ble/debug/leakvar#check $"leakvar" "[H3b1: after decode $chars...]"
    759 #%end.i
    760     done
    761   else
    762     local c
    763     for c in "${chars[@]}"; do
    764       ((--_ble_decode_input_count))
    765 #%if debug_keylogger
    766       ((_ble_debug_keylog_enabled)) && ble/array#push _ble_debug_keylog_bytes "$c"
    767 #%end
    768 #%if leakvar
    769 ble/debug/leakvar#check $"leakvar" "[H3b2: before decode $c]"
    770 #%end.i
    771       "ble/encoding:$bleopt_input_encoding/decode" "$c"
    772 #%if leakvar
    773 ble/debug/leakvar#check $"leakvar" "[H3b2: after decode $c]"
    774 #%end.i
    775     done
    776   fi
    777 
    778 #%if leakvar
    779 ble/debug/leakvar#check $"leakvar" H4
    780 #%end.i
    781   ble/decode/has-input || ble-decode-key/batch/flush
    782 #%if leakvar
    783 ble/debug/leakvar#check $"leakvar" H4-batch
    784 #%end.i
    785 
    786   builtin eval -- "$_ble_decode_erase_progress_hook"
    787   _ble_decode_hook_Processing=epilogue
    788   ble-decode/EPILOGUE
    789 #%if leakvar
    790 ble/debug/leakvar#check $"leakvar" H4-EPILOGUE
    791 #%end.i
    792 }
    793 
    794 ## @fn ble-decode-byte bytes...
    795 ##   バイト値を整数で受け取って、現在の文字符号化方式に従ってデコードをします。
    796 ##   デコードした結果得られた文字は ble-decode-char を呼び出す事によって処理します。
    797 ##
    798 ##   Note: 現在 ble.sh 内部では使用されていません。
    799 ##     この関数はユーザが呼び出す事を想定した関数です。
    800 function ble-decode-byte {
    801   while (($#)); do
    802     "ble/encoding:$bleopt_input_encoding/decode" "$1"
    803     shift
    804   done
    805 }
    806 
    807 # **** ble-decode-char/csi ****
    808 
    809 _ble_decode_csi_mode=0
    810 _ble_decode_csi_args=
    811 _ble_decode_csimap_tilde=()
    812 _ble_decode_csimap_alpha=()
    813 function ble-decode-char/csi/print/.print-csidef {
    814   local qalpha qkey ret q=\' Q="'\''"
    815   if [[ $sgrq ]]; then
    816     ble/string#quote-word "$1" quote-empty:sgrq="$sgrq":sgr0="$sgr0"; qalpha=$ret
    817     ble/string#quote-word "$2" quote-empty:sgrq="$sgrq":sgr0="$sgr0"; qkey=$ret
    818   else
    819     qalpha="'${1//$q/$Q}'"
    820     qkey="'${2//$q/$Q}'"
    821   fi
    822   ble/util/print "${sgrf}ble-bind$sgr0 $sgro--csi$sgr0 $qalpha $qkey"
    823 
    824 }
    825 ## @fn ble-decode-char/csi/print
    826 ##   @var[in] ble_bind_print sgr0 sgrf sgrq sgrc sgro
    827 function ble-decode-char/csi/print {
    828   [[ $ble_bind_print ]] || local sgr0= sgrf= sgrq= sgrc= sgro=
    829   local num ret
    830   for num in "${!_ble_decode_csimap_tilde[@]}"; do
    831     ble-decode-unkbd "${_ble_decode_csimap_tilde[num]}"
    832     ble-decode-char/csi/print/.print-csidef "$num~" "$ret"
    833   done
    834 
    835   for num in "${!_ble_decode_csimap_alpha[@]}"; do
    836     local s; ble/util/c2s "$num"; s=$ret
    837     ble-decode-unkbd "${_ble_decode_csimap_alpha[num]}"
    838     ble-decode-char/csi/print/.print-csidef "$s" "$ret"
    839   done
    840 }
    841 
    842 function ble-decode-char/csi/clear {
    843   _ble_decode_csi_mode=0
    844 }
    845 
    846 # Initialized in lib/init-cmap.sh
    847 _ble_decode_csimap_kitty_u=()
    848 
    849 ## @fn ble-decode/char/csi/.translate-kitty-csi-u
    850 ##   @var[in,out] key
    851 function ble-decode/char/csi/.translate-kitty-csi-u {
    852   local name=${_ble_decode_csimap_kitty_u[key]}
    853   if [[ $name ]]; then
    854     local ret
    855     ble-decode-kbd/generate-keycode "$name"
    856     key=$ret
    857   fi
    858 }
    859 function ble-decode-char/csi/.modify-key {
    860   local mod=$(($1-1))
    861   if ((mod>=0)); then
    862     # Note: xterm, mintty では modifyOtherKeys で通常文字に対するシフトは
    863     #   文字自体もそれに応じて変化させ、更に修飾フラグも設定する。
    864     # Note: RLogin は修飾がある場合は常に英大文字に統一する。
    865     if ((33<=key&&key<_ble_decode_FunctionKeyBase)); then
    866       local term=${_ble_term_TERM[0]+${_ble_term_TERM[${#_ble_term_TERM[@]}-1]}}
    867       if (((mod&0x01)&&0x31<=key&&key<=0x39)) && [[ $term == RLogin:* ]]; then
    868         # RLogin は数字に対する S- 修飾の解決はしてくれない。
    869         ((key-=16,mod&=~0x01))
    870       elif ((mod==0x01)); then
    871         if [[ $term != contra:* ]]; then
    872           # S- だけの時には単に S- を外す
    873           ((mod&=~0x01))
    874         fi
    875       elif ((65<=key&&key<=90)); then
    876         # 他の修飾がある時は英大文字は小文字に統一する
    877         ((key|=0x20))
    878       fi
    879     fi
    880 
    881     # Note: Supr 0x08 以降は独自
    882     ((mod&0x01&&(key|=_ble_decode_Shft),
    883       mod&0x02&&(key|=_ble_decode_Meta),
    884       mod&0x04&&(key|=_ble_decode_Ctrl),
    885       mod&0x08&&(key|=_ble_decode_Supr),
    886       mod&0x10&&(key|=_ble_decode_Hypr),
    887       mod&0x20&&(key|=_ble_decode_Altr)))
    888   fi
    889 }
    890 function ble-decode-char/csi/.decode {
    891   local char=$1 rex key
    892   if ((char==126)); then # ~
    893     if rex='^>?27;([0-9]+);?([0-9]+)$' && [[ $_ble_decode_csi_args =~ $rex ]]; then
    894       # xterm "CSI 2 7 ; <mod> ; <char> ~" sequences
    895       local param1=$((10#0${BASH_REMATCH[1]}))
    896       local param2=$((10#0${BASH_REMATCH[2]}))
    897       local key=$((param2&_ble_decode_MaskChar))
    898       ble-decode-char/csi/.modify-key "$param1"
    899       csistat=$key
    900       return 0
    901     fi
    902 
    903     if rex='^>?([0-9]+)(;([0-9]+))?$' && [[ $_ble_decode_csi_args =~ $rex ]]; then
    904       # "CSI <key> ; <mod> ~" sequences
    905       local param1=$((10#0${BASH_REMATCH[1]}))
    906       local param3=$((10#0${BASH_REMATCH[3]}))
    907       key=${_ble_decode_csimap_tilde[param1]}
    908       if [[ $key ]]; then
    909         ble-decode-char/csi/.modify-key "$param3"
    910         csistat=$key
    911         return 0
    912       fi
    913     fi
    914   elif ((char==117)); then # u
    915     if rex='^([0-9]*)(;[0-9]*)?$'; [[ $_ble_decode_csi_args =~ $rex ]]; then
    916       # xterm/mlterm "CSI <char> ; <mode> u" sequences
    917       # Note: 実は "CSI 1 ; mod u" が kp5 とする端末がある事に注意する。
    918       local rematch1=${BASH_REMATCH[1]}
    919       if [[ $rematch1 != 1 ]]; then
    920         local key=$((10#0$rematch1)) mods=$((10#0${BASH_REMATCH:${#rematch1}+1}))
    921         [[ $_ble_term_TERM == kitty:* ]] && ble-decode/char/csi/.translate-kitty-csi-u
    922         ble-decode-char/csi/.modify-key "$mods"
    923         csistat=$key
    924       fi
    925       return 0
    926     fi
    927   elif ((char==94||char==64)); then # ^, @
    928     if rex='^[0-9]+$' && [[ $_ble_decode_csi_args =~ $rex ]]; then
    929       # rxvt "CSI <key> ^", "CSI <key> @" sequences
    930       local param1=$((10#0${BASH_REMATCH[1]}))
    931       local param3=$((10#0${BASH_REMATCH[3]}))
    932       key=${_ble_decode_csimap_tilde[param1]}
    933       if [[ $key ]]; then
    934         ((key|=_ble_decode_Ctrl,
    935           char==64&&(key|=_ble_decode_Shft)))
    936         ble-decode-char/csi/.modify-key "$param3"
    937         csistat=$key
    938         return 0
    939       fi
    940     fi
    941   elif ((char==99)); then # c
    942     if rex='^[?>]'; [[ $_ble_decode_csi_args =~ $rex ]]; then
    943       # DA1 応答 "CSI ? Pm c" (何故か DA2 要求に対して DA1 で返す端末がある?)
    944       # DA2 応答 "CSI > Pm c"
    945       if [[ $_ble_decode_csi_args == '?'* ]]; then
    946         ble/term/DA1/notify "${_ble_decode_csi_args:1}"
    947       else
    948         ble/term/DA2/notify "${_ble_decode_csi_args:1}"
    949       fi
    950       csistat=$_ble_decode_KCODE_IGNORE
    951       return 0
    952     fi
    953   elif ((char==82||char==110)); then # R or n
    954     if rex='^([0-9]+);([0-9]+)$'; [[ $_ble_decode_csi_args =~ $rex ]]; then
    955       # DSR(6) に対する応答 CPR "CSI Pn ; Pn R"
    956       # Note: Poderosa は DSR(Pn;Pn) "CSI Pn ; Pn n" で返す。
    957       local param1=$((10#0${BASH_REMATCH[1]}))
    958       local param2=$((10#0${BASH_REMATCH[2]}))
    959       ble/term/CPR/notify "$param1" "$param2"
    960       csistat=$_ble_decode_KCODE_IGNORE
    961       return 0
    962     fi
    963   elif ((char==77||char==109)); then # M or m
    964     if rex='^<([0-9]+);([0-9]+);([0-9]+)$'; [[ $_ble_decode_csi_args =~ $rex ]]; then
    965       # マウスイベント
    966       #   button の bit 達
    967       #     modifiers (mask 0x1C): 4  shift, 8  meta, 16 control
    968       #     button: 0 mouse1, 1 mouse2, 2 mouse3, 3 release, 64 wheel_up, 65 wheel_down
    969       #     他のフラグ: 32 移動
    970       #   可能な button のパターン:
    971       #     mouse1 mouse2 mouse3 mouse4 mouse5
    972       #     mouse1up mouse2up mouse3up mouse4up mouse5up
    973       #     mouse1drag mouse2drag mouse3drag mouse4drag mouse5drag
    974       #     wheelup wheeldown mouse_move
    975       local param1=$((10#0${BASH_REMATCH[1]}))
    976       local param2=$((10#0${BASH_REMATCH[2]}))
    977       local param3=$((10#0${BASH_REMATCH[3]}))
    978       local button=$param1
    979       ((_ble_term_mouse_button=button&~0x1C,
    980         char==109&&(_ble_term_mouse_button|=0x70),
    981         _ble_term_mouse_x=param2-1,
    982         _ble_term_mouse_y=param3-1))
    983       local key=$_ble_decode_KCODE_MOUSE
    984       ((button&32)) && key=$_ble_decode_KCODE_MOUSE_MOVE
    985       ble-decode-char/csi/.modify-key "$((button>>2&0x07))"
    986       csistat=$key
    987       return 0
    988     fi
    989   elif ((char==116)); then # t
    990     if rex='^<([0-9]+);([0-9]+)$'; [[ $_ble_decode_csi_args =~ $rex ]]; then
    991       ## mouse_select
    992       local param1=$((10#0${BASH_REMATCH[1]}))
    993       local param2=$((10#0${BASH_REMATCH[2]}))
    994       ((_ble_term_mouse_button=128,
    995         _ble_term_mouse_x=param1-1,
    996         _ble_term_mouse_y=param2-1))
    997       local key=$_ble_decode_KCODE_MOUSE
    998       csistat=$key
    999     fi
   1000   fi
   1001 
   1002   # pc-style "CSI 1; <mod> A" sequences
   1003   key=${_ble_decode_csimap_alpha[char]}
   1004   if [[ $key ]]; then
   1005     if rex='^(1?|>?1;([0-9]+))$' && [[ $_ble_decode_csi_args =~ $rex ]]; then
   1006       local param2=$((10#0${BASH_REMATCH[2]}))
   1007       ble-decode-char/csi/.modify-key "$param2"
   1008       csistat=$key
   1009       return 0
   1010     fi
   1011   fi
   1012 
   1013   csistat=$_ble_decode_KCODE_ERROR
   1014 }
   1015 
   1016 ## @fn ble-decode-char/csi/consume char
   1017 ##   @param[in] char
   1018 ##   @var[out] csistat
   1019 function ble-decode-char/csi/consume {
   1020   csistat=
   1021 
   1022   # 一番頻度の高い物
   1023   ((_ble_decode_csi_mode==0&&$1!=27&&$1!=155)) && return 1
   1024 
   1025   local char=$1
   1026   case $_ble_decode_csi_mode in
   1027   (0)
   1028     # CSI (155) もしくは ESC (27)
   1029     ((_ble_decode_csi_mode=$1==155?2:1))
   1030     _ble_decode_csi_args=
   1031     csistat=_ ;;
   1032   (1)
   1033     if ((char!=91)); then
   1034       _ble_decode_csi_mode=0
   1035       return 1
   1036     else
   1037       _ble_decode_csi_mode=2
   1038       _ble_decode_csi_args=
   1039       csistat=_
   1040     fi ;;
   1041   (2)
   1042     if ((32<=char&&char<64)); then
   1043       local ret; ble/util/c2s "$char"
   1044       _ble_decode_csi_args=$_ble_decode_csi_args$ret
   1045       csistat=_
   1046     elif ((64<=char&&char<127)); then
   1047       _ble_decode_csi_mode=0
   1048       ble-decode-char/csi/.decode "$char"
   1049       ((csistat==27)) && csistat=$_ble_decode_IsolatedESC
   1050     else
   1051       _ble_decode_csi_mode=0
   1052     fi ;;
   1053   esac
   1054 }
   1055 
   1056 # **** ble-decode-char ****
   1057 
   1058 # 内部で使用する変数
   1059 # ble_decode_char_nest=
   1060 # ble_decode_char_sync=
   1061 # ble_decode_char_rest=
   1062 
   1063 _ble_decode_char_buffer=()
   1064 function ble/decode/has-input-for-char {
   1065   ((_ble_decode_input_count)) ||
   1066     ble/util/is-stdin-ready ||
   1067     ble/encoding:"$bleopt_input_encoding"/is-intermediate
   1068 }
   1069 
   1070 _ble_decode_char__hook=
   1071 
   1072 ## @arr _ble_decode_cmap_${_ble_decode_char__seq}[char]
   1073 ##   文字列からキーへの写像を保持する。
   1074 ##   各要素は文字の列 ($_ble_decode_char__seq $char) に対する定義を保持する。
   1075 ##   各要素は以下の形式の何れかである。
   1076 ##   key+ 文字の列がキー key に一意に対応する事を表す。
   1077 ##   _    文字の列が何らかのキーを表す文字列の prefix になっている事を表す。
   1078 ##   key_ 文字の列がキー key に対応すると同時に、
   1079 ##        他のキーの文字列の prefix になっている事を表す。
   1080 _ble_decode_cmap_=()
   1081 
   1082 # _ble_decode_char__seq が設定されている時は、
   1083 # 必ず _ble_decode_char2_reach_key も設定されている様にする。
   1084 _ble_decode_char2_seq=
   1085 _ble_decode_char2_reach_key=
   1086 _ble_decode_char2_reach_seq=
   1087 _ble_decode_char2_modifier=
   1088 _ble_decode_char2_modkcode=
   1089 _ble_decode_char2_modseq=
   1090 function ble-decode-char {
   1091   # 入れ子の ble-decode-char 呼び出しによる入力は後で実行。
   1092   if [[ $ble_decode_char_nest && ! $ble_decode_char_sync ]]; then
   1093     ble/array#push _ble_decode_char_buffer "$@"
   1094     return 148
   1095   fi
   1096   local ble_decode_char_nest=1
   1097 
   1098   local iloop=0
   1099   local ble_decode_char_total=$#
   1100   local ble_decode_char_rest=$#
   1101   local ble_decode_char_char=
   1102   # Note: ループ中で set -- ... を使っている。
   1103 
   1104   local chars ichar char ent
   1105   chars=("$@") ichar=0
   1106   while
   1107     if ((iloop++%50==0)); then
   1108       ((iloop>=200)) && builtin eval -- "$_ble_decode_show_progress_hook"
   1109       if [[ ! $ble_decode_char_sync ]] && ble/decode/has-input-for-char; then
   1110         ble/array#push _ble_decode_char_buffer "${chars[@]:ichar}"
   1111         return 148
   1112       fi
   1113     fi
   1114     # 入れ子の ble-decode-char 呼び出しによる入力。
   1115     if ((${#_ble_decode_char_buffer[@]})); then
   1116       ((ble_decode_char_total+=${#_ble_decode_char_buffer[@]}))
   1117       ((ble_decode_char_rest+=${#_ble_decode_char_buffer[@]}))
   1118       ble/array#set chars "${_ble_decode_char_buffer[@]}" "${chars[@]:ichar}"
   1119       ichar=0
   1120       _ble_decode_char_buffer=()
   1121     fi
   1122     ((ble_decode_char_rest))
   1123   do
   1124     char=${chars[ichar]}
   1125     ble_decode_char_char=$char # 補正前 char (_ble_decode_Macr 判定の為)
   1126     ((ble_decode_char_rest--,ichar++))
   1127 #%if debug_keylogger
   1128     ((_ble_debug_keylog_enabled)) && ble/array#push _ble_debug_keylog_chars "$char"
   1129 #%end
   1130     if [[ $_ble_decode_keylog_chars_enabled ]]; then
   1131       if ! ((char&_ble_decode_Macr)); then
   1132         ble/array#push _ble_decode_keylog_chars "$char"
   1133         ((_ble_decode_keylog_chars_count++))
   1134       fi
   1135     fi
   1136     ((char&=~_ble_decode_Macr))
   1137 
   1138     # decode error character
   1139     if ((char&_ble_decode_Erro)); then
   1140       ((char&=~_ble_decode_Erro))
   1141       if [[ $bleopt_decode_error_char_vbell ]]; then
   1142         local name; ble/util/sprintf name 'U+%04x' "$char"
   1143         ble/term/visible-bell "received a misencoded char $name"
   1144       fi
   1145       [[ $bleopt_decode_error_char_abell ]] && ble/term/audible-bell
   1146       [[ $bleopt_decode_error_char_discard ]] && continue
   1147       # ((char&_ble_decode_Erro)) : 最適化(過去 sequence は全部吐く)?
   1148     fi
   1149 
   1150     # hook for quoted-insert etc
   1151     if [[ $_ble_decode_char__hook ]]; then
   1152       ((char==_ble_decode_IsolatedESC)) && char=27 # isolated ESC -> ESC
   1153       local hook=$_ble_decode_char__hook
   1154       _ble_decode_char__hook=
   1155       ble-decode/widget/.call-async-read "$hook $char" "$char"
   1156       continue
   1157     fi
   1158 
   1159     ble-decode-char/.getent # -> ent
   1160     if [[ ! $ent ]]; then
   1161       # シーケンスが登録されていない時
   1162       if [[ $_ble_decode_char2_reach_key ]]; then
   1163         local key=$_ble_decode_char2_reach_key
   1164         local seq=$_ble_decode_char2_reach_seq
   1165         local rest=${_ble_decode_char2_seq:${#seq}}
   1166         ble/string#split-words rest "${rest//_/ } $ble_decode_char_char"
   1167 
   1168         _ble_decode_char2_seq=
   1169         _ble_decode_char2_reach_key=
   1170         _ble_decode_char2_reach_seq=
   1171         ble-decode-char/csi/clear
   1172 
   1173         ble-decode-char/.send-modified-key "$key" "$seq"
   1174         ((ble_decode_char_total+=${#rest[@]}))
   1175         ((ble_decode_char_rest+=${#rest[@]}))
   1176         chars=("${rest[@]}" "${chars[@]:ichar}") ichar=0
   1177       else
   1178         ble-decode-char/.send-modified-key "$char" "_$char"
   1179       fi
   1180     elif [[ $ent == *_ ]]; then
   1181       # /\d*_/ (_ は続き (1つ以上の有効なシーケンス) がある事を示す)
   1182       _ble_decode_char2_seq=${_ble_decode_char2_seq}_$char
   1183       if [[ ${ent%_} ]]; then
   1184         _ble_decode_char2_reach_key=${ent%_}
   1185         _ble_decode_char2_reach_seq=$_ble_decode_char2_seq
   1186       elif [[ ! $_ble_decode_char2_reach_key ]]; then
   1187         # 1文字目
   1188         _ble_decode_char2_reach_key=$char
   1189         _ble_decode_char2_reach_seq=$_ble_decode_char2_seq
   1190       fi
   1191     else
   1192       # /\d+/  (続きのシーケンスはなく ent で確定である事を示す)
   1193       local seq=${_ble_decode_char2_seq}_$char
   1194       _ble_decode_char2_seq=
   1195       _ble_decode_char2_reach_key=
   1196       _ble_decode_char2_reach_seq=
   1197       ble-decode-char/csi/clear
   1198       ble-decode-char/.send-modified-key "$ent" "$seq"
   1199     fi
   1200   done
   1201   return 0
   1202 }
   1203 
   1204 ## @fn ble-decode-char/hook/next-char
   1205 ##   _ble_decode_char__hook で次の文字を
   1206 ##   その場で読み出す時に使います。
   1207 ##   これは bracketed paste の高速化の為に使います。
   1208 ##   @var[out] char
   1209 ##   @var[in,out] iloop ichar chars ble_decode_char_rest
   1210 ##
   1211 ##   @remarks
   1212 ##     この関数を経由して読み取られた文字は keylog に残りません。
   1213 ##     正しい動作を期待する為には _ble_debug_keylog_enabled (非零)
   1214 ##     及び _ble_decode_keylog_chars_enabled (非空) が設定されて
   1215 ##     いない事を確認してから呼び出す必要があります。
   1216 ##
   1217 function ble/decode/char-hook/next-char {
   1218   ((ble_decode_char_rest)) || return 1
   1219   ((char=chars[ichar]&~_ble_decode_Macr))
   1220   ((char&_ble_decode_Erro)) && return 1
   1221   ((iloop%1000==0)) && return 1
   1222   ((char==_ble_decode_IsolatedESC)) && char=27
   1223   ((ble_decode_char_rest--,ichar++,iloop++))
   1224   return 0
   1225 }
   1226 
   1227 ## @fn ble-decode-char/.getent
   1228 ##   @var[in] _ble_decode_char2_seq
   1229 ##   @var[in] char
   1230 ##   @var[out] ent
   1231 function ble-decode-char/.getent {
   1232   builtin eval "ent=\${_ble_decode_cmap_$_ble_decode_char2_seq[char]-}"
   1233   if [[ $ent == ?*_ || $ent == _ && $_ble_decode_char2_seq == _27 ]]; then
   1234     ble/decode/wait-input 5 char || ent=${ent%_}
   1235   fi
   1236 
   1237   # CSI sequence
   1238   #   ent=     の時 → (CSI の結果)
   1239   #   ent=_    の時 → (CSI の結果) + _
   1240   #   ent=num  の時 → num のまま (CSI の結果に拘わらず確定)
   1241   #   ent=num_ の時 → num_ のまま
   1242   local csistat=
   1243   ble-decode-char/csi/consume "$char"
   1244   if [[ $csistat && ! ${ent%_} ]]; then
   1245     if ((csistat==_ble_decode_KCODE_ERROR)); then
   1246       if [[ $bleopt_decode_error_cseq_vbell ]]; then
   1247         local ret; ble-decode-unkbd ${_ble_decode_char2_seq//_/ } $char
   1248         ble/term/visible-bell "unrecognized CSI sequence: $ret"
   1249       fi
   1250       [[ $bleopt_decode_error_cseq_abell ]] && ble/term/audible-bell
   1251       if [[ $bleopt_decode_error_cseq_discard ]]; then
   1252         csistat=$_ble_decode_KCODE_IGNORE
   1253       else
   1254         csistat=
   1255       fi
   1256     fi
   1257     if [[ ! $ent ]]; then
   1258       ent=$csistat
   1259     else
   1260       ent=${csistat%_}_
   1261     fi
   1262   fi
   1263 
   1264   # ble/util/assert '[[ $ent =~ ^[0-9]*_?$ ]]'
   1265 }
   1266 
   1267 function ble-decode-char/.process-modifier {
   1268   local mflag1=$1 mflag=$_ble_decode_char2_modifier
   1269   if ((mflag1&mflag)); then
   1270     # 既に同じ修飾がある場合は通常と同じ処理をする。
   1271     # 例えば ESC ESC は3番目に来る文字に Meta 修飾をするのではなく、
   1272     # 2番目の ESC (C-[ に翻訳される) に対して
   1273     # 更に Meta 修飾をして C-M-[ を出力する。
   1274     return 1
   1275   else
   1276     # ※以下では key 内に既に mflag
   1277     # と重複する修飾がある場合は考慮していない。
   1278     # 重複があったという情報はここで消える。
   1279     ((_ble_decode_char2_modkcode=key|mflag,
   1280       _ble_decode_char2_modifier=mflag1|mflag))
   1281     _ble_decode_char2_modseq=${_ble_decode_char2_modseq}$2
   1282     return 0
   1283   fi
   1284 }
   1285 
   1286 ## @fn ble-decode-char/.send-modified-key key seq
   1287 ##   指定されたキーを修飾して ble-decode-key に渡します。
   1288 ##   key = 0..31,127 は C-@ C-a ... C-z C-[ C-\ C-] C-^ C-_ C-? に変換されます。
   1289 ##   ESC は次に来る文字を meta 修飾します。
   1290 ##   _ble_decode_IsolatedESC は meta にならずに ESC として渡されます。
   1291 ##   @param[in] key
   1292 ##     処理対象のキーコードを指定します。
   1293 ##   @param[in] seq
   1294 ##     指定したキーを表現する文字シーケンスを指定します。
   1295 ##     /(_文字コード)+/ の形式の文字コードの列です。
   1296 function ble-decode-char/.send-modified-key {
   1297   local key=$1 seq=$2
   1298   ((key==_ble_decode_KCODE_IGNORE)) && return 0
   1299 
   1300   if ((0<=key&&key<32)); then
   1301     ((key|=(key==0||key>26?64:96)|_ble_decode_Ctrl))
   1302   elif ((key==127)); then # C-?
   1303     ((key=63|_ble_decode_Ctrl))
   1304   fi
   1305 
   1306   if (($1==27)); then
   1307     ble-decode-char/.process-modifier "$_ble_decode_Meta" "$seq" && return 0
   1308   elif (($1==_ble_decode_IsolatedESC)); then
   1309     ((key=(_ble_decode_Ctrl|91)))
   1310     if ! ble/decode/uses-isolated-esc; then
   1311       ble-decode-char/.process-modifier "$_ble_decode_Meta" "$seq" && return 0
   1312     fi
   1313   elif ((_ble_decode_KCODE_SHIFT<=$1&&$1<=_ble_decode_KCODE_HYPER)); then
   1314     case $1 in
   1315     ($_ble_decode_KCODE_SHIFT)
   1316       ble-decode-char/.process-modifier "$_ble_decode_Shft" "$seq" && return 0 ;;
   1317     ($_ble_decode_KCODE_CONTROL)
   1318       ble-decode-char/.process-modifier "$_ble_decode_Ctrl" "$seq" && return 0 ;;
   1319     ($_ble_decode_KCODE_ALTER)
   1320       ble-decode-char/.process-modifier "$_ble_decode_Altr" "$seq" && return 0 ;;
   1321     ($_ble_decode_KCODE_META)
   1322       ble-decode-char/.process-modifier "$_ble_decode_Meta" "$seq" && return 0 ;;
   1323     ($_ble_decode_KCODE_SUPER)
   1324       ble-decode-char/.process-modifier "$_ble_decode_Supr" "$seq" && return 0 ;;
   1325     ($_ble_decode_KCODE_HYPER)
   1326       ble-decode-char/.process-modifier "$_ble_decode_Hypr" "$seq" && return 0 ;;
   1327     esac
   1328   fi
   1329 
   1330   if [[ $_ble_decode_char2_modifier ]]; then
   1331     local mflag=$_ble_decode_char2_modifier
   1332     local mcode=$_ble_decode_char2_modkcode
   1333     local mseq=$_ble_decode_char2_modseq
   1334     _ble_decode_char2_modifier=
   1335     _ble_decode_char2_modkcode=
   1336     _ble_decode_char2_modseq=
   1337     if ((key&mflag)); then
   1338       local CHARS
   1339       ble/string#split-words CHARS "${mseq//_/ }"
   1340       ble-decode-key "$mcode"
   1341     else
   1342       seq=$mseq$seq
   1343       ((key|=mflag))
   1344     fi
   1345   fi
   1346 
   1347   local CHARS
   1348   ble/string#split-words CHARS "${seq//_/ }"
   1349   ble-decode-key "$key"
   1350 }
   1351 
   1352 function ble-decode-char/is-intermediate { [[ $_ble_decode_char2_seq ]]; }
   1353 
   1354 function ble-decode-char/bind {
   1355   local -a seq; ble/string#split-words seq "$1"
   1356   local kc=$2
   1357 
   1358   local i iN=${#seq[@]} char tseq=
   1359   for ((i=0;i<iN;i++)); do
   1360     local char=${seq[i]}
   1361 
   1362     builtin eval "local okc=\${_ble_decode_cmap_$tseq[char]-}"
   1363     if ((i+1==iN)); then
   1364       if [[ ${okc//[0-9]} == _ ]]; then
   1365         builtin eval "_ble_decode_cmap_$tseq[char]=\${kc}_"
   1366       else
   1367         builtin eval "_ble_decode_cmap_$tseq[char]=\${kc}"
   1368       fi
   1369     else
   1370       if [[ ! $okc ]]; then
   1371         builtin eval "_ble_decode_cmap_$tseq[char]=_"
   1372       else
   1373         builtin eval "_ble_decode_cmap_$tseq[char]=\${okc%_}_"
   1374       fi
   1375       tseq=${tseq}_$char
   1376     fi
   1377   done
   1378 }
   1379 function ble-decode-char/unbind {
   1380   local -a seq; ble/string#split-words seq "$1"
   1381 
   1382   local tseq=
   1383   local i iN=${#seq}
   1384   for ((i=0;i<iN-1;i++)); do
   1385     tseq=${tseq}_${seq[i]}
   1386   done
   1387 
   1388   local char=${seq[iN-1]}
   1389   local isfirst=1 ent=
   1390   while
   1391     builtin eval "ent=\${_ble_decode_cmap_$tseq[char]-}"
   1392 
   1393     if [[ $isfirst ]]; then
   1394       # 数字を消す
   1395       isfirst=
   1396       if [[ $ent == *_ ]]; then
   1397         # ent = 1234_ (両方在る時は片方消して終わり)
   1398         builtin eval "_ble_decode_cmap_$tseq[char]=_"
   1399         break
   1400       fi
   1401     else
   1402       # _ を消す
   1403       if [[ $ent != _ ]]; then
   1404         # ent = 1234_ (両方在る時は片方消して終わり)
   1405         builtin eval "_ble_decode_cmap_$tseq[char]=${ent%_}"
   1406         break
   1407       fi
   1408     fi
   1409 
   1410     builtin unset -v "_ble_decode_cmap_$tseq[char]"
   1411     builtin eval "((\${#_ble_decode_cmap_$tseq[@]}!=0))" && break
   1412 
   1413     [[ $tseq ]]
   1414   do
   1415     char=${tseq##*_}
   1416     tseq=${tseq%_*}
   1417   done
   1418 }
   1419 ## @fn ble-decode-char/print [tseq nseq...]
   1420 ##   @var[in] ble_bind_print sgr0 sgrf sgrq sgrc sgro
   1421 function ble-decode-char/print {
   1422   [[ $ble_bind_print ]] || local sgr0= sgrf= sgrq= sgrc= sgro=
   1423   local IFS=$_ble_term_IFS q=\' Q="'\''"
   1424   local tseq=$1 nseq ccode
   1425   nseq=("${@:2}")
   1426   builtin eval "local -a ccodes; ccodes=(\${!_ble_decode_cmap_$tseq[@]})"
   1427   for ccode in "${ccodes[@]}"; do
   1428     local ret; ble-decode-unkbd "$ccode"
   1429     local cnames
   1430     cnames=("${nseq[@]}" "$ret")
   1431 
   1432     builtin eval "local ent=\${_ble_decode_cmap_$tseq[ccode]}"
   1433     if [[ ${ent%_} ]]; then
   1434       local key=${ent%_} ret
   1435 
   1436       local qkspec qcnames
   1437       if [[ $sgrq ]]; then
   1438         ble-decode-unkbd "$key"
   1439         ble/string#quote-word "$ret" quote-empty:sgrq="$sgrq":sgr0="$sgr0"; qkspec=$ret
   1440         ble/string#quote-word "${cnames[*]}" quote-empty:sgrq="$sgrq":sgr0="$sgr0"; qcnames=$ret
   1441       else
   1442         ble-decode-unkbd "$key"
   1443         qkspec="'${ret//$q/$Q}'"
   1444         qcnames="'${cnames[*]//$q/$Q}'" # WA #D1570 checked
   1445       fi
   1446       ble/util/print "${sgrf}ble-bind$sgr0 $sgro-k$sgr0 $qcnames $qkspec"
   1447     fi
   1448 
   1449     if [[ ${ent//[0-9]} == _ ]]; then
   1450       ble-decode-char/print "${tseq}_$ccode" "${cnames[@]}"
   1451     fi
   1452   done
   1453 }
   1454 
   1455 # **** ble-decode-key ****
   1456 
   1457 ## @arr _ble_decode_${keymap}_kmap_${_ble_decode_key__seq}[key]
   1458 ##   各 keymap は (キーシーケンス, コマンド) の集合と等価です。
   1459 ##   この配列は keymap の内容を以下の形式で格納します。
   1460 ##
   1461 ##   @param[in] keymap
   1462 ##     対象の keymap の名称を指定します。
   1463 ##
   1464 ##   @param[in] _ble_decode_key__seq
   1465 ##   @param[in] key
   1466 ##     _ble_decode_key__seq key の組合せでキーシーケンスを表します。
   1467 ##
   1468 ##   @value
   1469 ##     以下の形式の何れかです。
   1470 ##     - "_" [TIMEOUT]
   1471 ##     - "_" [TIMEOUT] ":command"
   1472 ##     - "1:command"
   1473 ##
   1474 ##     始めの文字が "_" の場合はキーシーケンスに続きがある事を表します。
   1475 ##     つまり、このキーシーケンスを prefix とするより長いキーシーケンスが登録されている事を表します。
   1476 ##     command が指定されている場合には、より長いシーケンスでの一致に全て失敗した時点で
   1477 ##     command が実行されます。シーケンスを受け取った段階では実行されません。
   1478 ##     TIMEOUT (整数値) が指定されている場合は、このキーを受け取った後に続きのキーが TIMEOUT msec
   1479 ##     以内に到着しなかった時に限りその場で command を実行します。
   1480 ##
   1481 ##     初めの文字が "1" の場合はキーシーケンスが確定的である事を表します。
   1482 ##     つまり、このキーシーケンスを prefix とするより長いシーケンスが登録されてなく、
   1483 ##     このシーケンスを受け取った段階で command を実行する事が確定する事を表します。
   1484 ##
   1485 
   1486 ## @var _ble_decode_keymap_list := ( ':' kmap )+
   1487 ##   初期化済みの kmap の名前の一覧を保持します。
   1488 ##   既定の kmap (名前無し) は含まれません。
   1489 _ble_decode_keymap_list=
   1490 function ble/decode/keymap#registered {
   1491   [[ :$_ble_decode_keymap_list: == *:"$1":* ]]
   1492 }
   1493 ## @fn ble/decode/keymap#.register kmap
   1494 ##   @exit 新しく keymap が登録された時に成功します。
   1495 ##     既存の keymap だった時に失敗します。
   1496 ##   @remarks
   1497 ##     この関数は keymap cache から読み出されます。
   1498 function ble/decode/keymap#.register {
   1499   local kmap=$1
   1500   if [[ $kmap && :$_ble_decode_keymap_list: != *:"$kmap":* ]]; then
   1501     _ble_decode_keymap_list=$_ble_decode_keymap_list:$kmap
   1502   fi
   1503 }
   1504 function ble/decode/keymap#.unregister {
   1505   _ble_decode_keymap_list=$_ble_decode_keymap_list:
   1506   _ble_decode_keymap_list=${_ble_decode_keymap_list//:"$1":/:}
   1507   _ble_decode_keymap_list=${_ble_decode_keymap_list%:}
   1508 }
   1509 function ble/decode/is-keymap {
   1510   ble/decode/keymap#registered "$1" || ble/is-function "ble-decode/keymap:$1/define"
   1511 }
   1512 
   1513 function ble/decode/keymap#is-empty {
   1514   ! ble/is-array "_ble_decode_${1}_kmap_" ||
   1515     builtin eval -- "((\${#_ble_decode_${1}_kmap_[*]}==0))"
   1516 }
   1517 
   1518 function ble/decode/keymap#.onload {
   1519   local kmap=$1
   1520   local delay=$_ble_base_run/$$.bind.delay.$kmap
   1521   if [[ -s $delay ]]; then
   1522     source "$delay"
   1523     : >| "$delay"
   1524   fi
   1525 }
   1526 function ble/decode/keymap#load {
   1527   local opts=:$2:
   1528   ble/decode/keymap#registered "$1" && return 0
   1529 
   1530   local init=ble-decode/keymap:$1/define
   1531   ble/is-function "$init" || return 1
   1532 
   1533   ble/decode/keymap#.register "$1"
   1534   local ble_bind_keymap=$1
   1535   if ! "$init" || ble/decode/keymap#is-empty "$1"; then
   1536     ble/decode/keymap#.unregister "$1"
   1537     return 1
   1538   fi
   1539 
   1540   [[ $opts == *:dump:* ]] &&
   1541     ble/decode/keymap#dump "$1" >&3
   1542   ble/decode/keymap#.onload "$1"
   1543   return 0
   1544 }
   1545 ## @fn ble/decode/keymap#unload [keymap_name...]
   1546 function ble/decode/keymap#unload {
   1547   if (($#==0)); then
   1548     local list; ble/string#split-words list "${_ble_decode_keymap_list//:/ }"
   1549     set -- "${list[@]}"
   1550   fi
   1551 
   1552   while (($#)); do
   1553     local array_names array_name
   1554     builtin eval -- "array_names=(\"\${!_ble_decode_${1}_kmap_@}\")"
   1555     for array_name in "${array_names[@]}"; do
   1556       builtin unset -v "$array_name"
   1557     done
   1558     ble/decode/keymap#.unregister "$1"
   1559     shift
   1560   done
   1561 }
   1562 
   1563 if [[ ${_ble_decode_kmaps-} ]]; then
   1564   ## @fn ble/decode/keymap/cleanup-old-keymaps
   1565   ##   古い形式の keymap を削除する (#D1076)
   1566   ##   0.4.0-devel1+e13e979 以前は unload 時に keymaps を削除していなかった為に、
   1567   ##   reload した時に keycode 不整合で無限ループになってしまうバグがあった。
   1568   function ble/decode/keymap/cleanup-old-keymaps {
   1569     # Note: 古い形式では必ずしも _ble_decode_kmaps に keymap
   1570     #   が登録されていなかったので、配列データから抽出する必要がある。
   1571     local -a list=()
   1572     local var
   1573     for var in "${!_ble_decode_@}"; do
   1574       [[ $var == _ble_decode_*_kmap_ ]] || continue
   1575       var=${var#_ble_decode_}
   1576       var=${var%_kmap_}
   1577       ble/array#push list "$var"
   1578     done
   1579 
   1580     local keymap_name
   1581     for keymap_name in "${list[@]}"; do
   1582       ble/decode/keymap#unload "$keymap_name"
   1583     done
   1584     builtin unset -v _ble_decode_kmaps
   1585   }
   1586   ble/decode/keymap/cleanup-old-keymaps
   1587 fi
   1588 
   1589 function ble/decode/keymap#dump {
   1590   if (($#)); then
   1591     local kmap=$1 arrays
   1592     builtin eval "arrays=(\"\${!_ble_decode_${kmap}_kmap_@}\")"
   1593     ble/util/print "ble/decode/keymap#.register $kmap"
   1594     ble/util/declare-print-definitions "${arrays[@]}"
   1595     ble/util/print "ble/decode/keymap#.onload $kmap"
   1596   else
   1597     local list; ble/string#split-words list "${_ble_decode_keymap_list//:/ }"
   1598     local keymap_name
   1599     for keymap_name in "${list[@]}"; do
   1600       ble/decode/keymap#dump "$keymap_name"
   1601     done
   1602   fi
   1603 }
   1604 
   1605 ## @fn ble-decode/GET_BASEMAP -v varname
   1606 ##   既定の基底 keymap を返します。
   1607 function ble-decode/GET_BASEMAP {
   1608   [[ $1 == -v ]] || return 1
   1609   local ret; bleopt/get:default_keymap
   1610   [[ $ret == vi ]] && ret=vi_imap
   1611   builtin eval "$2=\$ret"
   1612 }
   1613 ## @fn[custom] ble-decode/INITIALIZE_DEFMAP -v varname
   1614 ##   既定の keymap を決定します。
   1615 ##   ble-decode.sh 使用コードで上書きして使用します。
   1616 function ble-decode/INITIALIZE_DEFMAP {
   1617   ble-decode/GET_BASEMAP "$@" &&
   1618     ble/decode/keymap#load "${!2}" &&
   1619     return 0
   1620 
   1621   # fallback
   1622   ble/decode/keymap#load safe &&
   1623     builtin eval -- "$2=safe" &&
   1624     bleopt_default_keymap=safe
   1625 }
   1626 
   1627 ## @fn[custom] ble/widget/.SHELL_COMMAND command
   1628 ##   ble-bind -c で登録されたコマンドを処理します。
   1629 function ble/widget/.SHELL_COMMAND { local IFS=$_ble_term_IFS; builtin eval -- "$*"; }
   1630 ## @fn[custom] ble/widget/.EDIT_COMMAND command
   1631 ##   ble-bind -x で登録されたコマンドを処理します。
   1632 function ble/widget/.EDIT_COMMAND { local IFS=$_ble_term_IFS; builtin eval -- "$*"; }
   1633 
   1634 ## @fn ble-decode-key/bind keymap keys command
   1635 ##   @param[in] keymap keys command
   1636 function ble-decode-key/bind {
   1637   if ! ble/decode/keymap#registered "$1"; then
   1638     ble/util/print-quoted-command "$FUNCNAME" "$@" >> "$_ble_base_run/$$.bind.delay.$1"
   1639     return 0
   1640   fi
   1641 
   1642   local kmap=$1 keys=$2 cmd=$3
   1643 
   1644   # Check existence of widget
   1645   if local widget=${cmd%%[$_ble_term_IFS]*}; ! ble/is-function "$widget"; then
   1646     local message="ble-bind: Unknown widget \`${widget#'ble/widget/'}'."
   1647     [[ $command == ble/widget/ble/widget/* ]] &&
   1648       message="$message Note: The prefix 'ble/widget/' is redundant."
   1649     ble/util/print "$message" 1>&2
   1650     return 1
   1651   fi
   1652 
   1653   local dicthead=_ble_decode_${kmap}_kmap_
   1654   local -a seq; ble/string#split-words seq "$keys"
   1655 
   1656   local i iN=${#seq[@]} tseq=
   1657   for ((i=0;i<iN;i++)); do
   1658     local key=${seq[i]}
   1659 
   1660     builtin eval "local ocmd=\${$dicthead$tseq[key]}"
   1661     if ((i+1==iN)); then
   1662       if [[ ${ocmd::1} == _ ]]; then
   1663         builtin eval "$dicthead$tseq[key]=${ocmd%%:*}:\$cmd"
   1664       else
   1665         builtin eval "$dicthead$tseq[key]=1:\$cmd"
   1666       fi
   1667     else
   1668       if [[ ! $ocmd ]]; then
   1669         builtin eval "$dicthead$tseq[key]=_"
   1670       elif [[ ${ocmd::1} == 1 ]]; then
   1671         builtin eval "$dicthead$tseq[key]=_:\${ocmd#*:}"
   1672       fi
   1673       tseq=${tseq}_$key
   1674     fi
   1675   done
   1676 }
   1677 
   1678 function ble-decode-key/set-timeout {
   1679   if ! ble/decode/keymap#registered "$1"; then
   1680     ble/util/print-quoted-command "$FUNCNAME" "$@" >> "$_ble_base_run/$$.bind.delay.$1"
   1681     return 0
   1682   fi
   1683 
   1684   local kmap=$1 keys=$2 timeout=$3
   1685   local dicthead=_ble_decode_${kmap}_kmap_
   1686   local -a seq; ble/string#split-words seq "$keys"
   1687   [[ $timeout == - ]] && timeout=
   1688 
   1689   local i iN=${#seq[@]}
   1690   local key=${seq[iN-1]}
   1691   local tseq=
   1692   for ((i=0;i<iN-1;i++)); do
   1693     tseq=${tseq}_${seq[i]}
   1694   done
   1695 
   1696   builtin eval "local ent=\${$dicthead$tseq[key]}"
   1697   if [[ $ent == _* ]]; then
   1698     local cmd=; [[ $ent == *:* ]] && cmd=${ent#*:}
   1699     builtin eval "$dicthead$tseq[key]=_$timeout${cmd:+:}\$cmd"
   1700   else
   1701     ble/util/print "ble-bind -T: specified partial keyspec not found." >&2
   1702     return 1
   1703   fi
   1704 }
   1705 
   1706 ## @fn ble-decode-key/unbind keymap keys
   1707 function ble-decode-key/unbind {
   1708   if ! ble/decode/keymap#registered "$1"; then
   1709     ble/util/print-quoted-command "$FUNCNAME" "$@" >> "$_ble_base_run/$$.bind.delay.$1"
   1710     return 0
   1711   fi
   1712 
   1713   local kmap=$1 keys=$2
   1714   local dicthead=_ble_decode_${kmap}_kmap_
   1715   local -a seq; ble/string#split-words seq "$keys"
   1716 
   1717   local i iN=${#seq[@]}
   1718   local key=${seq[iN-1]}
   1719   local tseq=
   1720   for ((i=0;i<iN-1;i++)); do
   1721     tseq=${tseq}_${seq[i]}
   1722   done
   1723 
   1724   local isfirst=1 ent=
   1725   while
   1726     builtin eval "ent=\${$dicthead$tseq[key]}"
   1727 
   1728     if [[ $isfirst ]]; then
   1729       # command を消す
   1730       isfirst=
   1731       if [[ ${ent::1} == _ ]]; then
   1732         # ent = _[TIMEOUT] または _[TIMEOUT]:command の時は、単に command を消して終わる。
   1733         # (未だ bind が残っているので、登録は削除せず break)。
   1734         builtin eval "$dicthead$tseq[key]=\${ent%%:*}"
   1735         break
   1736       fi
   1737     else
   1738       # prefix の ent は _ か _:command のどちらかの筈。
   1739       if [[ $ent == *:* ]]; then
   1740         # _:command の場合には 1:command に書き換える。
   1741         # (1:command の bind が残っているので登録は削除せず break)。
   1742         builtin eval "$dicthead$tseq[key]=1:\${ent#*:}"
   1743         break
   1744       fi
   1745     fi
   1746 
   1747     builtin unset -v "$dicthead$tseq[key]"
   1748     builtin eval "((\${#$dicthead$tseq[@]}!=0))" && break
   1749 
   1750     [[ $tseq ]]
   1751   do
   1752     key=${tseq##*_}
   1753     tseq=${tseq%_*}
   1754   done
   1755 }
   1756 
   1757 function ble/decode/keymap#get-cursor {
   1758   cursor=_ble_decode_${1}_kmap_cursor
   1759   cursor=${!cursor-}
   1760 }
   1761 function ble/decode/keymap#set-cursor {
   1762   local keymap=$1 cursor=$2
   1763   if ! ble/decode/keymap#registered "$keymap"; then
   1764     ble/util/print-quoted-command "$FUNCNAME" "$@" >> "$_ble_base_run/$$.bind.delay.$keymap"
   1765     return 0
   1766   fi
   1767   builtin eval "_ble_decode_${keymap}_kmap_cursor=\$cursor"
   1768   if [[ $keymap == "$_ble_decode_keymap" && $cursor ]]; then
   1769     ble/term/cursor-state/set-internal "$((cursor))"
   1770   fi
   1771 }
   1772 
   1773 ## @fn ble/decode/keymap#print keymap [tseq nseq]
   1774 ##   @param keymap
   1775 ##   @param[in,internal] tseq nseq
   1776 ##   @var[in] ble_bind_print sgr0 sgrf sgrq sgrc sgro
   1777 function ble/decode/keymap#print {
   1778   # 引数の無い場合: 全ての kmap を dump
   1779   local kmap
   1780   if (($#==0)); then
   1781     for kmap in ${_ble_decode_keymap_list//:/ }; do
   1782       ble/util/print "$sgrc# keymap $kmap$sgr0"
   1783       ble/decode/keymap#print "$kmap"
   1784     done
   1785     return 0
   1786   fi
   1787 
   1788   [[ $ble_bind_print ]] || local sgr0= sgrf= sgrq= sgrc= sgro=
   1789   local kmap=$1 tseq=$2 nseq=$3
   1790   local dicthead=_ble_decode_${kmap}_kmap_
   1791   local kmapopt=
   1792   [[ $kmap ]] && kmapopt=" $sgro-m$sgr0 $sgrq'$kmap'$sgr0"
   1793 
   1794   local q=\' Q="'\''"
   1795   local key keys
   1796   builtin eval "keys=(\${!$dicthead$tseq[@]})"
   1797   for key in "${keys[@]}"; do
   1798     local ret; ble-decode-unkbd "$key"
   1799     local knames=$nseq${nseq:+ }$ret
   1800     builtin eval "local ent=\${$dicthead$tseq[key]}"
   1801 
   1802     local qknames
   1803     if [[ $sgrq ]]; then
   1804       ble/string#quote-word "$knames" quote-empty:sgrq="$sgrq":sgr0="$sgr0"; qknames=$ret
   1805     else
   1806       qknames="'${knames//$q/$Q}'"
   1807     fi
   1808     if [[ $ent == *:* ]]; then
   1809       local cmd=${ent#*:}
   1810 
   1811       local o v
   1812       case $cmd in
   1813       ('ble/widget/.SHELL_COMMAND '*) o=c v=${cmd#'ble/widget/.SHELL_COMMAND '}; builtin eval "v=$v" ;;
   1814       ('ble/widget/.EDIT_COMMAND '*)  o=x v=${cmd#'ble/widget/.EDIT_COMMAND '} ; builtin eval "v=$v" ;;
   1815       ('ble/widget/.MACRO '*)         o=s; ble/util/chars2keyseq ${cmd#*' '}; v=$ret ;;
   1816       ('ble/widget/'*)                o=f v=${cmd#ble/widget/} ;;
   1817       (*)                             o=@ v=$cmd  ;;
   1818       esac
   1819 
   1820       local qv
   1821       if [[ $sgrq ]]; then
   1822         ble/string#quote-word "$v" quote-empty:sgrq="$sgrq":sgr0="$sgr0"; qv=$ret
   1823       else
   1824         qv="'${v//$q/$Q}'"
   1825       fi
   1826       ble/util/print "${sgrf}ble-bind$sgr0$kmapopt $sgro-$o$sgr0 $qknames $qv"
   1827     fi
   1828 
   1829     if [[ ${ent::1} == _ ]]; then
   1830       ble/decode/keymap#print "$kmap" "${tseq}_$key" "$knames"
   1831       if [[ $ent == _[0-9]* ]]; then
   1832         local timeout=${ent%%:*}; timeout=${timeout:1}
   1833         ble/util/print "${sgrf}ble-bind$sgr0$kmapopt $sgro-T$sgr0 $qknames $timeout"
   1834       fi
   1835     fi
   1836   done
   1837 }
   1838 
   1839 ## @var _ble_decode_keymap
   1840 ##
   1841 ##   現在選択されている keymap
   1842 ##
   1843 ## @arr _ble_decode_keymap_stack
   1844 ##
   1845 ##   呼び出し元の keymap を記録するスタック
   1846 ##
   1847 _ble_decode_keymap=
   1848 _ble_decode_keymap_stack=()
   1849 
   1850 ## @fn ble/decode/keymap/push kmap
   1851 function ble/decode/keymap/push {
   1852   if ble/decode/keymap#registered "$1"; then
   1853     ble/array#push _ble_decode_keymap_stack "$_ble_decode_keymap"
   1854     _ble_decode_keymap=$1
   1855 
   1856     # set cursor-state
   1857     local cursor; ble/decode/keymap#get-cursor "$1"
   1858     [[ $cursor ]] && ble/term/cursor-state/set-internal "$((cursor))"
   1859     return 0
   1860   elif ble/decode/keymap#load "$1" && ble/decode/keymap#registered "$1"; then
   1861     ble/decode/keymap/push "$1" # 再実行
   1862   else
   1863     ble/util/print "[ble: keymap '$1' not found]" >&2
   1864     return 1
   1865   fi
   1866 }
   1867 ## @fn ble/decode/keymap/pop
   1868 function ble/decode/keymap/pop {
   1869   local count=${#_ble_decode_keymap_stack[@]}
   1870   local last=$((count-1))
   1871   ble/util/assert '((last>=0))' || return 1
   1872 
   1873   # reset cursor-state
   1874   local cursor
   1875   ble/decode/keymap#get-cursor "$_ble_decode_keymap"
   1876   if [[ $cursor ]]; then
   1877     local i
   1878     for ((i=last;i>=0;i--)); do
   1879       ble/decode/keymap#get-cursor "${_ble_decode_keymap_stack[i]}"
   1880       [[ $cursor ]] && break
   1881     done
   1882     ble/term/cursor-state/set-internal "$((${cursor:-0}))"
   1883   fi
   1884 
   1885   local old_keymap=_ble_decode_keymap
   1886   _ble_decode_keymap=${_ble_decode_keymap_stack[last]}
   1887   builtin unset -v '_ble_decode_keymap_stack[last]'
   1888 }
   1889 ## @fn ble/decode/keymap/get-parent
   1890 ##   @var[out] ret
   1891 function ble/decode/keymap/get-parent {
   1892   local len=${#_ble_decode_keymap_stack[@]}
   1893   if ((len)); then
   1894     ret=${_ble_decode_keymap_stack[len-1]}
   1895   else
   1896     ret=
   1897   fi
   1898 }
   1899 
   1900 ## @var _ble_decode_key__seq
   1901 ##   今迄に入力された未処理のキーの列を保持します
   1902 ##   /(_\d+)*/ の形式の文字列です。
   1903 _ble_decode_key__seq=
   1904 
   1905 ## @var _ble_decode_key__hook
   1906 ##   キー処理に対する hook を外部から設定する為の変数です。
   1907 _ble_decode_key__hook=
   1908 
   1909 ## @fn ble-decode-key/is-intermediate
   1910 ##   未処理のキーがあるかどうかを判定します。
   1911 function ble-decode-key/is-intermediate { [[ $_ble_decode_key__seq ]]; }
   1912 
   1913 ## @arr _ble_decode_key_batch
   1914 _ble_decode_key_batch=()
   1915 
   1916 ## @fn ble-decode-key/batch/flush
   1917 function ble-decode-key/batch/flush {
   1918   ((${#_ble_decode_key_batch[@]})) || return 1
   1919   local dicthead=_ble_decode_${_ble_decode_keymap}_kmap_
   1920   builtin eval "local command=\${${dicthead}[_ble_decode_KCODE_BATCH_CHAR]-}"
   1921   command=${command:2}
   1922   if [[ $command ]]; then
   1923     local chars; chars=("${_ble_decode_key_batch[@]}")
   1924     _ble_decode_key_batch=()
   1925     ble/decode/widget/call-interactively "$command" "${chars[@]}"; local ext=$?
   1926     ((ext!=125)) && return 0
   1927   fi
   1928 
   1929   ble/decode/widget/call-interactively ble/widget/__batch_char__.default "${chars[@]}"; local ext=$?
   1930   return "$ext"
   1931 }
   1932 function ble/widget/__batch_char__.default {
   1933   builtin eval "local widget_defchar=\${${dicthead}[_ble_decode_KCODE_DEFCHAR]-}"
   1934   widget_defchar=${widget_defchar:2}
   1935   builtin eval "local widget_default=\${${dicthead}[_ble_decode_KCODE_DEFAULT]-}"
   1936   widget_default=${widget_default:2}
   1937 
   1938   local -a unprocessed_chars=()
   1939   local key command
   1940   for key in "${KEYS[@]}"; do
   1941     if [[ $widget_defchar ]]; then
   1942       ble/decode/widget/call-interactively "$widget_defchar" "$key"; local ext=$?
   1943       ((ext!=125)) && continue
   1944     fi
   1945     if [[ $widget_default ]]; then
   1946       ble/decode/widget/call-interactively "$widget_default" "$key"; local ext=$?
   1947       ((ext!=125)) && continue
   1948     fi
   1949 
   1950     ble/array#push unprocessed_chars "$key"
   1951   done
   1952 
   1953   if ((${#unprocessed_chars[@]})); then
   1954     local ret; ble-decode-unkbd "${unprocessed_chars[@]}"
   1955     [[ $bleopt_decode_error_kseq_vbell ]] && ble/term/visible-bell "unprocessed chars: $ret"
   1956     [[ $bleopt_decode_error_kseq_abell ]] && ble/term/audible-bell
   1957   fi
   1958   return 0
   1959 }
   1960 
   1961 
   1962 ## @fn ble-decode-key key...
   1963 ##   キー入力の処理を行います。登録されたキーシーケンスに一致した場合、
   1964 ##   関連付けられたコマンドを実行します。
   1965 ##   登録されたキーシーケンスの前方部分に一致する場合、即座に処理は行わず
   1966 ##   入力されたキーの列を _ble_decode_key__seq に記録します。
   1967 ##
   1968 ##   @param[in] key
   1969 ##     入力されたキー
   1970 ##
   1971 function ble-decode-key {
   1972   local key
   1973   while (($#)); do
   1974     key=$1; shift
   1975 #%if debug_keylogger
   1976     ((_ble_debug_keylog_enabled)) && ble/array#push _ble_debug_keylog_keys "$key"
   1977 #%end
   1978     if [[ $_ble_decode_keylog_keys_enabled && $_ble_decode_keylog_depth == 0 ]]; then
   1979       ble/array#push _ble_decode_keylog_keys "$key"
   1980       ((_ble_decode_keylog_keys_count++))
   1981     fi
   1982 
   1983     # Note: マウス移動はシーケンスの一部と見做さず独立に処理する。
   1984     #   widget が登録されていれば処理しそれ以外は無視。
   1985     local dicthead=_ble_decode_${_ble_decode_keymap}_kmap_
   1986     if (((key&_ble_decode_MaskChar)==_ble_decode_KCODE_MOUSE_MOVE)); then
   1987       builtin eval "local command=\${${dicthead}[key]-}"
   1988       command=${command:2}
   1989       ble-decode/widget/.call-keyseq
   1990       continue
   1991     fi
   1992 
   1993     if [[ $_ble_decode_key__hook ]]; then
   1994       local hook=$_ble_decode_key__hook
   1995       _ble_decode_key__hook=
   1996       ble-decode/widget/.call-async-read "$hook $key" "$key"
   1997       continue
   1998     fi
   1999 
   2000     builtin eval "local ent=\${$dicthead$_ble_decode_key__seq[key]-}"
   2001 
   2002     # TIMEOUT: timeout が設定されている場合はその時間だけ待って
   2003     # 続きを処理するかその場で確定するか判断する。
   2004     if [[ $ent == _[0-9]* ]]; then
   2005       local node_type=_
   2006       if (($#==0)) && ! ble/decode/has-input; then
   2007         local timeout=${ent%%:*}; timeout=${timeout:1}
   2008         ble/decode/wait-input "$timeout" || node_type=1
   2009       fi
   2010       if [[ $ent == *:* ]]; then
   2011         ent=$node_type:${ent#*:}
   2012       else
   2013         ent=$node_type
   2014       fi
   2015     fi
   2016 
   2017     if [[ $ent == 1:* ]]; then
   2018       # /1:command/    (続きのシーケンスはなく ent で確定である事を示す)
   2019       local command=${ent:2}
   2020       if [[ $command ]]; then
   2021         ble-decode/widget/.call-keyseq
   2022       else
   2023         _ble_decode_key__seq=
   2024       fi
   2025     elif [[ $ent == _ || $ent == _:* ]]; then
   2026       # /_(:command)?/ (続き (1つ以上の有効なシーケンス) がある事を示す)
   2027       _ble_decode_key__seq=${_ble_decode_key__seq}_$key
   2028     else
   2029       # 遡って適用 (部分一致、または、既定動作)
   2030       ble-decode-key/.invoke-partial-match "$key" && continue
   2031 
   2032       # エラーの表示
   2033       local kseq=${_ble_decode_key__seq}_$key ret
   2034       ble-decode-unkbd "${kseq//_/ }"
   2035       local kspecs=$ret
   2036       [[ $bleopt_decode_error_kseq_vbell ]] && ble/term/visible-bell "unbound keyseq: $kspecs"
   2037       [[ $bleopt_decode_error_kseq_abell ]] && ble/term/audible-bell
   2038 
   2039       # 残っている文字の処理
   2040       if [[ $_ble_decode_key__seq ]]; then
   2041         if [[ $bleopt_decode_error_kseq_discard ]]; then
   2042           _ble_decode_key__seq=
   2043         else
   2044           local -a keys
   2045           ble/string#split-words keys "${_ble_decode_key__seq//_/ } $key"
   2046           _ble_decode_key__seq=
   2047           # 2文字目以降を処理
   2048           ble-decode-key "${keys[@]:1}"
   2049         fi
   2050       fi
   2051     fi
   2052 
   2053   done
   2054 
   2055   if ((${#_ble_decode_key_batch[@]})); then
   2056     if ! ble/decode/has-input || ((${#_ble_decode_key_batch[@]}>=50)); then
   2057       ble-decode-key/batch/flush
   2058     fi
   2059   fi
   2060   return 0
   2061 }
   2062 
   2063 ## @fn ble-decode-key/.invoke-partial-match fail
   2064 ##   これまでのキー入力に対する部分一致を試みます。
   2065 ##   登録されている部分一致がない場合には単体のキーに対して既定の動作を呼び出します。
   2066 ##   既定の動作も登録されていない場合には関数は失敗します。
   2067 ##   @var[in,out] _ble_decode_key__seq
   2068 ##   @var[in]     next
   2069 ##     _ble_decode_key__seq は既に入力された未処理のキー列を指定します。
   2070 ##     next には今回入力されたキーの列を指定します。
   2071 ##     この関数は _ble_decode_key__seq next からなるキー列に対する部分一致を試みます。
   2072 ##
   2073 ##   この関数は以下の様に動作します。
   2074 ##   1 先ず、_ble_decode_key__seq に対して部分一致がないか確認し、部分一致する
   2075 ##     binding があればそれを実行します。
   2076 ##     - _ble_decode_key__seq + key の全体に対する一致は試みない事に注意して下
   2077 ##       さい。全体一致については既にチェックして失敗しているという前提です。
   2078 ##       何故なら部分一致を試みるのは常に最長一致が失敗した時だけだからです。
   2079 ##   2 _ble_decode_key__seq に対する部分一致が存在しない場合には、
   2080 ##     ch = _ble_decode_key__seq + key の最初のキーについて登録されている既定の
   2081 ##     動作を実行します。ch はつまり、_ble_decode_key__seq が空でない時はその先
   2082 ##     頭で、空の場合は key になります。
   2083 ##   3 一致が存在して処理が実行された場合には、その後一旦 _ble_decode_key__seq
   2084 ##     がクリアされ、一致しなかった残りの部分に対して再度 ble-decode-key を呼
   2085 ##     び出して再解釈が行われます。
   2086 ##     1, 2 のいずれでも一致が見付からなかった場合には、_ble_decode_key__seq を
   2087 ##     呼出時の状態に戻し関数は失敗します。つまり、この場合 _ble_decode_key__seq
   2088 ##     は、呼出元からは変化していない様に見えます。
   2089 ##
   2090 function ble-decode-key/.invoke-partial-match {
   2091   local dicthead=_ble_decode_${_ble_decode_keymap}_kmap_
   2092 
   2093   local next=$1
   2094   if [[ $_ble_decode_key__seq ]]; then
   2095     local last=${_ble_decode_key__seq##*_}
   2096     _ble_decode_key__seq=${_ble_decode_key__seq%_*}
   2097 
   2098     builtin eval "local ent=\${$dicthead$_ble_decode_key__seq[last]-}"
   2099     if [[ $ent == _*:* ]]; then
   2100       local command=${ent#*:}
   2101       if [[ $command ]]; then
   2102         ble-decode/widget/.call-keyseq
   2103       else
   2104         _ble_decode_key__seq=
   2105       fi
   2106       ble-decode-key "$next"
   2107       return 0
   2108     else # ent = _
   2109       if ble-decode-key/.invoke-partial-match "$last"; then
   2110         ble-decode-key "$next"
   2111         return 0
   2112       else
   2113         # 元に戻す
   2114         _ble_decode_key__seq=${_ble_decode_key__seq}_$last
   2115         return 1
   2116       fi
   2117     fi
   2118   else
   2119     # ここでは指定した単体のキーに対する既定の処理を実行する
   2120     # $next 単体でも設定がない場合はここに来る。
   2121     # 通常の文字などは全てここに流れてくる事になる。
   2122 
   2123     # 既定の文字ハンドラ
   2124     local key=$1
   2125     if ble-decode-key/ischar "$key"; then
   2126       if ble/decode/has-input && builtin eval "[[ \${${dicthead}[_ble_decode_KCODE_BATCH_CHAR]-} ]]"; then
   2127         ble/array#push _ble_decode_key_batch "$key"
   2128         return 0
   2129       fi
   2130 
   2131       builtin eval "local command=\${${dicthead}[_ble_decode_KCODE_DEFCHAR]-}"
   2132       command=${command:2}
   2133       if [[ $command ]]; then
   2134         local seq_save=$_ble_decode_key__seq
   2135         ble-decode/widget/.call-keyseq; local ext=$?
   2136         ((ext!=125)) && return 0
   2137         _ble_decode_key__seq=$seq_save # 125 の時はまた元に戻して次の試行を行う
   2138       fi
   2139     fi
   2140 
   2141     # 既定のキーハンドラ
   2142     builtin eval "local command=\${${dicthead}[_ble_decode_KCODE_DEFAULT]-}"
   2143     command=${command:2}
   2144     ble-decode/widget/.call-keyseq; local ext=$?
   2145     ((ext!=125)) && return 0
   2146 
   2147     return 1
   2148   fi
   2149 }
   2150 
   2151 function ble-decode-key/ischar {
   2152   local key=$1
   2153   (((key&_ble_decode_MaskFlag)==0&&32<=key&&key<_ble_decode_FunctionKeyBase))
   2154 }
   2155 
   2156 #------------------------------------------------------------------------------
   2157 # ble-decode/widget
   2158 
   2159 ## @var _ble_decode_widget_last
   2160 ##   次のコマンドで LASTWIDGET として使用するコマンド名を保持します。
   2161 ##   以下の関数で使用されます。
   2162 ##
   2163 ##   - ble-decode/widget/.call-keyseq
   2164 ##   - ble-decode/widget/.call-async-read
   2165 ##   - ble/decode/widget/call
   2166 ##   - ble/decode/widget/call-interactively
   2167 ##   - (keymap/vi.sh) ble/keymap:vi/repeat/invoke
   2168 ##
   2169 _ble_decode_widget_last=
   2170 
   2171 function ble-decode/widget/.invoke-hook {
   2172   local key=$1
   2173   local dicthead=_ble_decode_${_ble_decode_keymap}_kmap_
   2174   builtin eval "local hook=\${$dicthead[key]-}"
   2175   hook=${hook:2}
   2176 #%if leakvar
   2177 ble/debug/leakvar#check $"leakvar" "widget.hook.0"
   2178 #%end.i
   2179   [[ $hook ]] && builtin eval -- "$hook"
   2180 #%if leakvar
   2181 ble/debug/leakvar#check $"leakvar" "widget.hook.1 $hook"
   2182 #%end.i
   2183 }
   2184 
   2185 ## @fn ble-decode/widget/.call-keyseq
   2186 ##   コマンドが有効な場合に、指定したコマンドを適切な環境で実行します。
   2187 ##   @var[in] command
   2188 ##     起動するコマンドを指定します。空の場合コマンドは実行されません。
   2189 ##   @var[in] _ble_decode_key__seq
   2190 ##   @var[in] key
   2191 ##     _ble_decode_key__seq は前回までに受け取ったキーの列です。
   2192 ##     key は今回新しく受け取ったキーの列です。
   2193 ##     _ble_decode_key__seq と key の組合せで現在入力されたキーシーケンスになります。
   2194 ##     コマンドを実行した場合 _ble_decode_key__seq はクリアされます。
   2195 ##     コマンドを実行しなかった場合
   2196 ##   @return
   2197 ##     コマンドが実行された場合に 0 を返します。それ以外の場合は 1 です。
   2198 ##
   2199 ##   コマンドの実行時に次の変数が定義されます。
   2200 ##   これらの変数はコマンドの内部から参照する事ができます。
   2201 ##   @var[out] KEYS
   2202 ##     このコマンドの起動に用いられたキーシーケンスが格納されます。
   2203 ##
   2204 #
   2205 # 実装の注意
   2206 #
   2207 #   呼び出したコマンドの内部で keymap の switch があっても良い様に、
   2208 #   _ble_decode_key__seq + key は厳密に現在のコマンドに対応するシーケンスである必要がある事、
   2209 #   コマンドを呼び出す時には常に _ble_decode_key__seq が空になっている事に注意。
   2210 #   部分一致などの場合に後続のキーが存在する場合には、それらは呼出元で管理しなければならない。
   2211 #
   2212 function ble-decode/widget/.call-keyseq {
   2213   ble-decode-key/batch/flush
   2214   [[ $command ]] || return 125
   2215 
   2216   # for keylog suppress
   2217   local _ble_decode_keylog_depth=$((_ble_decode_keylog_depth+1))
   2218 
   2219   # set up variables
   2220   local WIDGET=$command KEYMAP=$_ble_decode_keymap LASTWIDGET=$_ble_decode_widget_last
   2221   local -a KEYS; ble/string#split-words KEYS "${_ble_decode_key__seq//_/ } $key"
   2222   _ble_decode_widget_last=$WIDGET
   2223   _ble_decode_key__seq=
   2224 
   2225   ble-decode/widget/.invoke-hook "$_ble_decode_KCODE_BEFORE_WIDGET"
   2226 #%if leakvar
   2227 ble/debug/leakvar#check $"leakvar" widget.0
   2228 #%end.i
   2229   builtin eval -- "$WIDGET"; local ext=$?
   2230 #%if leakvar
   2231 ble/debug/leakvar#check $"leakvar" "widget $WIDGET"
   2232 #%end.i
   2233   ble-decode/widget/.invoke-hook "$_ble_decode_KCODE_AFTER_WIDGET"
   2234   ((_ble_decode_keylog_depth==1)) &&
   2235     _ble_decode_keylog_chars_count=0 _ble_decode_keylog_keys_count=0
   2236   return "$ext"
   2237 }
   2238 ## @fn ble-decode/widget/.call-async-read widget keys
   2239 ##   _ble_decode_{char,key}__hook の呼び出しに使用します。
   2240 ##   _ble_decode_widget_last は更新しません。
   2241 function ble-decode/widget/.call-async-read {
   2242   # for keylog suppress
   2243   local _ble_decode_keylog_depth=$((_ble_decode_keylog_depth+1))
   2244 
   2245   # set up variables
   2246   local WIDGET=$1 KEYMAP=$_ble_decode_keymap LASTWIDGET=$_ble_decode_widget_last
   2247   local -a KEYS; ble/string#split-words KEYS "$2"
   2248   builtin eval -- "$WIDGET"; local ext=$?
   2249   ((_ble_decode_keylog_depth==1)) &&
   2250     _ble_decode_keylog_chars_count=0 _ble_decode_keylog_keys_count=0
   2251   return "$ext"
   2252 }
   2253 ## @fn ble/decode/widget/call-interactively widget keys...
   2254 ## @fn ble/decode/widget/call widget keys...
   2255 ##   指定した名前の widget を呼び出します。
   2256 ##   call-interactively では、現在の keymap に応じた __before_widget__
   2257 ##   及び __after_widget__ フックも呼び出します。
   2258 function ble/decode/widget/call-interactively {
   2259   local WIDGET=$1 KEYMAP=$_ble_decode_keymap LASTWIDGET=$_ble_decode_widget_last
   2260   local -a KEYS; KEYS=("${@:2}")
   2261   _ble_decode_widget_last=$WIDGET
   2262   ble-decode/widget/.invoke-hook "$_ble_decode_KCODE_BEFORE_WIDGET"
   2263 #%if leakvar
   2264 ble/debug/leakvar#check $"leakvar" widget.0
   2265 #%end.i
   2266   builtin eval -- "$WIDGET"; local ext=$?
   2267 #%if leakvar
   2268 ble/debug/leakvar#check $"leakvar" "widget $WIDGET"
   2269 #%end.i
   2270   ble-decode/widget/.invoke-hook "$_ble_decode_KCODE_AFTER_WIDGET"
   2271   return "$ext"
   2272 }
   2273 function ble/decode/widget/call {
   2274   local WIDGET=$1 KEYMAP=$_ble_decode_keymap LASTWIDGET=$_ble_decode_widget_last
   2275   local -a KEYS; KEYS=("${@:2}")
   2276   _ble_decode_widget_last=$WIDGET
   2277 #%if leakvar
   2278 ble/debug/leakvar#check $"leakvar" widget.0
   2279 #%end.i
   2280   builtin eval -- "$WIDGET"
   2281 #%if leakvar
   2282 ble/debug/leakvar#check $"leakvar" "widget $WIDGET"
   2283 #%end.i
   2284 }
   2285 ## @fn ble/decode/widget/dispatch widget args...
   2286 function ble/decode/widget/dispatch {
   2287   local ret; ble/string#quote-command "ble/widget/$@"
   2288   local WIDGET=$ret
   2289   _ble_decode_widget_last=$WIDGET
   2290 #%if leakvar
   2291 ble/debug/leakvar#check $"leakvar" widget.0
   2292 #%end.i
   2293   builtin eval -- "$WIDGET"
   2294 #%if leakvar
   2295 ble/debug/leakvar#check $"leakvar" "widget $WIDGET"
   2296 #%end.i
   2297 }
   2298 ## @fn ble/decode/widget/suppress-widget
   2299 ##   __before_widget__ に登録された関数から呼び出します。
   2300 ##   __before_widget__ 内で必要な処理を完了した時に、
   2301 ##   WIDGET の呼び出しをキャンセルします。
   2302 ##   __after_widget__ の呼び出しはキャンセルされません。
   2303 function ble/decode/widget/suppress-widget {
   2304   WIDGET=
   2305 }
   2306 
   2307 ## @fn ble/decode/widget/redispatch-by-keys
   2308 ##   @var[in] KEYS
   2309 ##   @var[out] _ble_decode_keylog_depth
   2310 function ble/decode/widget/redispatch-by-keys {
   2311   if ((_ble_decode_keylog_depth==1)); then
   2312     # Note: 一旦 pop してから _ble_decode_keylog_depth=0
   2313     #   で ble-decode-key を呼び出す事により再記録させる。
   2314     # Note: 更に _ble_decode_keylog_depth=0 にする事で、
   2315     #   _ble_decode_keylog_chars_count の呼び出し元によるクリアを抑制する。
   2316     ble/decode/keylog#pop
   2317     _ble_decode_keylog_depth=0
   2318   fi
   2319   ble-decode-key "$@"
   2320 }
   2321 function ble/decode/widget/skip-lastwidget {
   2322   _ble_decode_widget_last=$LASTWIDGET
   2323 }
   2324 
   2325 ## @fn ble/decode/widget/keymap-dispatch args
   2326 ##   関数 ble/widget/NAME の中から呼び出します。
   2327 ##   現在の keymap に固有の同名の関数 "ble/widget/KEYMAP/NAME" が
   2328 ##   存在する場合にはそれを呼びします。
   2329 ##   それ以外の場合には "ble/widget/default/NAME" を呼び出します。
   2330 function ble/decode/widget/keymap-dispatch {
   2331   local name=${FUNCNAME[1]#ble/widget/}
   2332   local widget=ble/widget/$_ble_decode_keymap/$name
   2333   ble/is-function "$widget" || widget=ble/widget/default/$name
   2334   "$widget" "$@"
   2335 }
   2336 
   2337 #------------------------------------------------------------------------------
   2338 # ble/decode/has-input
   2339 
   2340 ## @fn ble/decode/has-input
   2341 ##   ユーザからの未処理の入力があるかどうかを判定します。
   2342 ##
   2343 ##   @exit
   2344 ##     ユーザからの未処理の入力がある場合に成功します。
   2345 ##     それ以外の場合に失敗します。
   2346 ##
   2347 ##   Note: Bash 4.0 未満では read -t 0 が使えない為、
   2348 ##     正しく判定する事ができません。
   2349 ##
   2350 function ble/decode/has-input {
   2351   ((_ble_decode_input_count||ble_decode_char_rest)) ||
   2352     ble/util/is-stdin-ready ||
   2353     ble/encoding:"$bleopt_input_encoding"/is-intermediate ||
   2354     ble-decode-char/is-intermediate
   2355 
   2356   # Note: 文字の途中やキーのエスケープシーケンスの途中の時には、
   2357   #   標準有力に文字がなくても Readline が先読みして溜めているので、
   2358   #   それも考慮に入れて未処理の入力があるかどうかを判定する。
   2359   #
   2360   # Note: キーシーケンスの途中の時には Readline が溜めているという事もないし、
   2361   #   またユーザが続きを入力するのを待っている状態なので idle と思って良い。
   2362   #   従って ble-decode-key/is-intermediate についてはチェックしない。
   2363 }
   2364 
   2365 ## @fn ble/decode/has-input-char
   2366 ##   cseq (char -> key) にとって次の文字が来ているかどうか
   2367 function ble/decode/has-input-char {
   2368   ((_ble_decode_input_count||ble_decode_char_rest)) ||
   2369     ble/util/is-stdin-ready ||
   2370     ble/encoding:"$bleopt_input_encoding"/is-intermediate
   2371 }
   2372 
   2373 ## @fn ble/decode/wait-input timeout [type]
   2374 function ble/decode/wait-input {
   2375   local timeout=$1 type=${2-}
   2376   if [[ $type == char ]]; then
   2377     ble/decode/has-input-char && return 0
   2378   else
   2379     ble/decode/has-input && return 0
   2380   fi
   2381 
   2382   while ((timeout>0)); do
   2383     local w=$((timeout<20?timeout:20))
   2384     ble/util/msleep "$w"
   2385     ((timeout-=w))
   2386     ble/util/is-stdin-ready && return 0
   2387   done
   2388   return 1
   2389 }
   2390 
   2391 function ble/util/idle/IS_IDLE {
   2392   ! ble/decode/has-input
   2393 }
   2394 
   2395 # 
   2396 #------------------------------------------------------------------------------
   2397 # logging
   2398 
   2399 #%if debug_keylogger
   2400 _ble_debug_keylog_enabled=0
   2401 _ble_debug_keylog_bytes=()
   2402 _ble_debug_keylog_chars=()
   2403 _ble_debug_keylog_keys=()
   2404 function ble/debug/keylog#start {
   2405   _ble_debug_keylog_enabled=1
   2406 }
   2407 function ble/debug/keylog#end {
   2408   {
   2409     local IFS=$_ble_term_IFS
   2410     ble/util/print '===== bytes ====='
   2411     ble/util/print "${_ble_debug_keylog_bytes[*]}"
   2412     ble/util/print
   2413     ble/util/print '===== chars ====='
   2414     local ret; ble-decode-unkbd "${_ble_debug_keylog_chars[@]}"
   2415     ble/string#split ret ' ' "$ret"
   2416     ble/util/print "${ret[*]}"
   2417     ble/util/print
   2418     ble/util/print '===== keys ====='
   2419     local ret; ble-decode-unkbd "${_ble_debug_keylog_keys[@]}"
   2420     ble/string#split ret ' ' "$ret"
   2421     ble/util/print "${ret[*]}"
   2422     ble/util/print
   2423   } | fold -w 40
   2424 
   2425   _ble_debug_keylog_enabled=0
   2426   _ble_debug_keylog_bytes=()
   2427   _ble_debug_keylog_chars=()
   2428   _ble_debug_keylog_keys=()
   2429 }
   2430 #%else
   2431 _ble_debug_keylog_enabled=0
   2432 #%end
   2433 
   2434 ## @var _ble_decode_keylog_depth
   2435 ##   現在の widget 呼び出しの深さを表します。
   2436 ##   入れ子の ble-decode-char, ble-decode-key による
   2437 ##   文字・キーを記録しない様にする為に用います。
   2438 ## @var _ble_decode_keylog_keys_enabled
   2439 ##   現在キーの記録が有効かどうかを保持します。
   2440 ## @arr _ble_decode_keylog_keys
   2441 ##   記録したキーを保持します。
   2442 ## @var _ble_decode_keylog_chars_enabled
   2443 ##   現在文字の記録が有効かどうかを保持します。
   2444 ## @arr _ble_decode_keylog_chars
   2445 ##   記録した文字を保持します。
   2446 ## @var _ble_decode_keylog_chars_count
   2447 ##   1 widget を呼び出す迄に記録された文字の数です。
   2448 _ble_decode_keylog_depth=0
   2449 _ble_decode_keylog_keys_enabled=
   2450 _ble_decode_keylog_keys_count=0
   2451 _ble_decode_keylog_keys=()
   2452 _ble_decode_keylog_chars_enabled=
   2453 _ble_decode_keylog_chars_count=0
   2454 _ble_decode_keylog_chars=()
   2455 
   2456 ## @fn ble/decode/keylog#start [tag]
   2457 function ble/decode/keylog#start {
   2458   [[ $_ble_decode_keylog_keys_enabled ]] && return 1
   2459   _ble_decode_keylog_keys_enabled=${1:-1}
   2460   _ble_decode_keylog_keys=()
   2461 }
   2462 ## @fn ble/decode/keylog#end
   2463 ##   @var[out] ret
   2464 function ble/decode/keylog#end {
   2465   ret=("${_ble_decode_keylog_keys[@]}")
   2466   _ble_decode_keylog_keys_enabled=
   2467   _ble_decode_keylog_keys=()
   2468 }
   2469 ## @fn ble/decode/keylog#pop
   2470 ##   現在の WIDGET 呼び出しに対応する KEYS が記録されているとき、これを削除します。
   2471 ##   @var[in] _ble_decode_keylog_depth
   2472 ##   @var[in] _ble_decode_keylog_keys_enabled
   2473 ##   @arr[in] KEYS
   2474 function ble/decode/keylog#pop {
   2475   [[ $_ble_decode_keylog_keys_enabled && $_ble_decode_keylog_depth == 1 ]] || return 1
   2476   local new_size=$((${#_ble_decode_keylog_keys[@]}-_ble_decode_keylog_keys_count))
   2477   ((new_size<0)) && new_size=0
   2478   _ble_decode_keylog_keys=("${_ble_decode_keylog_keys[@]::new_size}")
   2479   _ble_decode_keylog_keys_count=0
   2480 }
   2481 
   2482 ## @fn ble/decode/charlog#start [tag]
   2483 function ble/decode/charlog#start {
   2484   [[ $_ble_decode_keylog_chars_enabled ]] && return 1
   2485   _ble_decode_keylog_chars_enabled=${1:-1}
   2486   _ble_decode_keylog_chars=()
   2487 }
   2488 ## @fn ble/decode/charlog#end
   2489 ##   @var[out] ret
   2490 function ble/decode/charlog#end {
   2491   [[ $_ble_decode_keylog_chars_enabled ]] || { ret=(); return 1; }
   2492   ret=("${_ble_decode_keylog_chars[@]}")
   2493   _ble_decode_keylog_chars_enabled=
   2494   _ble_decode_keylog_chars=()
   2495 }
   2496 ## @fn ble/decode/charlog#end-exclusive
   2497 ##   現在の WIDGET 呼び出しに対応する文字を除いて記録を取得して完了します。
   2498 ##   @var[out] ret
   2499 function ble/decode/charlog#end-exclusive {
   2500   ret=()
   2501   [[ $_ble_decode_keylog_chars_enabled ]] || return 1
   2502   local size=$((${#_ble_decode_keylog_chars[@]}-_ble_decode_keylog_chars_count))
   2503   ((size>0)) && ret=("${_ble_decode_keylog_chars[@]::size}")
   2504   _ble_decode_keylog_chars_enabled=
   2505   _ble_decode_keylog_chars=()
   2506 }
   2507 ## @fn ble/decode/charlog#end-exclusive-depth1
   2508 ##   トップレベルの WIDGET 呼び出しの時は end-exclusive にします。
   2509 ##   二次的な WIDGET 呼び出しの時には inclusive に end します。
   2510 ##
   2511 ##   @var[out] ret
   2512 ##     記録を返します。
   2513 ##
   2514 ##   これは exit-default -> end-keyboard-macro という具合に
   2515 ##   WIDGET が呼び出されて記録が完了する場合がある為です。
   2516 ##   この場合 exit-default は記録に残したいので自身を呼び出した
   2517 ##   文字の列も記録に含ませる必要があります。
   2518 ##   但し、マクロ再生中に呼び出される end-keyboard-macro
   2519 ##   は無視する必要があります。
   2520 ##
   2521 function ble/decode/charlog#end-exclusive-depth1 {
   2522   if ((_ble_decode_keylog_depth==1)); then
   2523     ble/decode/charlog#end-exclusive
   2524   else
   2525     ble/decode/charlog#end
   2526   fi
   2527 }
   2528 
   2529 ## @fn ble/decode/charlog#encode chars...
   2530 function ble/decode/charlog#encode {
   2531   local -a buff=()
   2532   for char; do
   2533     ((char==0)) && char=$_ble_decode_EscapedNUL
   2534     ble/util/c2s "$char"
   2535     ble/array#push buff "$ret"
   2536   done
   2537   IFS= builtin eval 'ret="${buff[*]}"'
   2538 }
   2539 ## @fn ble/decode/charlog#decode text
   2540 function ble/decode/charlog#decode {
   2541   local text=$1 n=${#1} i chars
   2542   chars=()
   2543   for ((i=0;i<n;i++)); do
   2544     ble/util/s2c "${text:i:1}"
   2545     ((ret==_ble_decode_EscapedNUL)) && ret=0
   2546     ble/array#push chars "$ret"
   2547   done
   2548   ret=("${chars[@]}")
   2549 }
   2550 
   2551 ## @fn ble/decode/keylog#encode keys...
   2552 ##   キーの列からそれに対応する文字列を構築します
   2553 function ble/decode/keylog#encode {
   2554   ret=
   2555   ble/util/c2s 155; local csi=$ret
   2556 
   2557   local key
   2558   local -a buff=()
   2559   for key; do
   2560     # 通常の文字
   2561     if ble-decode-key/ischar "$key"; then
   2562       ble/util/c2s "$key"
   2563 
   2564       # Note: 現在の LC_CTYPE で表現できない Unicode の時、
   2565       #   ret == \u???? もしくは \U???????? の形式になる。
   2566       #   その場合はここで処理せず、後の部分で CSI 27;1;code ~ の形式で記録する。
   2567       if ((${#ret}==1)); then
   2568         ble/array#push buff "$ret"
   2569         continue
   2570       fi
   2571     fi
   2572 
   2573     local c=$((key&_ble_decode_MaskChar))
   2574 
   2575     # C-? は制御文字として登録する
   2576     if (((key&_ble_decode_MaskFlag)==_ble_decode_Ctrl&&(c==64||91<=c&&c<=95||97<=c&&c<=122))); then
   2577       # Note: ^@ (NUL) は文字列にできないので除外
   2578       if ((c!=64)); then
   2579         ble/util/c2s "$((c&0x1F))"
   2580         ble/array#push buff "$ret"
   2581         continue
   2582       fi
   2583     fi
   2584 
   2585     # Note: Meta 修飾は単体の ESC と紛らわしいので CSI 27 で記録する。
   2586     local mod=1
   2587     (((key&_ble_decode_Shft)&&(mod+=0x01),
   2588       (key&_ble_decode_Altr)&&(mod+=0x02),
   2589       (key&_ble_decode_Ctrl)&&(mod+=0x04),
   2590       (key&_ble_decode_Supr)&&(mod+=0x08),
   2591       (key&_ble_decode_Hypr)&&(mod+=0x10),
   2592       (key&_ble_decode_Meta)&&(mod+=0x20)))
   2593     ble/array#push buff "${csi}27;$mod;$c~"
   2594   done
   2595   IFS= builtin eval 'ret="${buff[*]-}"'
   2596 }
   2597 ## @fn ble/decode/keylog#decode-chars text
   2598 function ble/decode/keylog#decode-chars {
   2599   local text=$1 n=${#1} i
   2600   local -a chars=()
   2601   for ((i=0;i<n;i++)); do
   2602     ble/util/s2c "${text:i:1}"
   2603     ((ret==27)) && ret=$_ble_decode_IsolatedESC
   2604     ble/array#push chars "$ret"
   2605   done
   2606   ret=("${chars[@]}")
   2607 }
   2608 
   2609 ## @fn ble/widget/.MACRO char...
   2610 ##   bind '"keyseq":"macro"' の束縛に使用する。
   2611 _ble_decode_macro_count=0
   2612 function ble/widget/.MACRO {
   2613   # マクロ無限再帰検出
   2614   if ((ble_decode_char_char&_ble_decode_Macr)); then
   2615     if ((_ble_decode_macro_count++>=bleopt_decode_macro_limit)); then
   2616       ((_ble_decode_macro_count==bleopt_decode_macro_limit+1)) &&
   2617         ble/term/visible-bell "Macro invocation is cancelled by decode_macro_limit"
   2618       return 1
   2619     fi
   2620   else
   2621     _ble_decode_macro_count=0
   2622   fi
   2623 
   2624   local -a chars=()
   2625   local char
   2626   for char; do
   2627     ble/array#push chars "$((char|_ble_decode_Macr))"
   2628   done
   2629   ble-decode-char "${chars[@]}"
   2630 }
   2631 
   2632 ## @fn ble/widget/.CHARS char...
   2633 ##   lib/init-bind.sh で特別なバイト列を受信するのに使う関数
   2634 function ble/widget/.CHARS {
   2635   ble-decode-char "$@"
   2636 }
   2637 
   2638 #------------------------------------------------------------------------------
   2639 # key definitions (c.f. init-cmap.sh)                              @decode.cmap
   2640 
   2641 ## @fn ble/decode/c2dqs code
   2642 ##   bash builtin bind で用いる事のできるキー表記に変換します。
   2643 ##   @var[out] ret
   2644 function ble/decode/c2dqs {
   2645   local i=$1
   2646 
   2647   # bind で用いる
   2648   # リテラル "~" 内で特別な表記にする必要がある物
   2649   if ((0<=i&&i<32)); then
   2650     # C0 characters
   2651     if ((1<=i&&i<=26)); then
   2652       ble/util/c2s "$((i+96))"
   2653       ret="\\C-$ret"
   2654     elif ((i==27)); then
   2655       ret="\\e"
   2656     elif ((i==28)); then
   2657       # Workaround \C-\\, \C-\ in Bash-3.0..5.0
   2658       ret="\\x1c"
   2659     else
   2660       ble/decode/c2dqs "$((i+64))"
   2661       ret="\\C-$ret"
   2662     fi
   2663   elif ((32<=i&&i<127)); then
   2664     ble/util/c2s "$i"
   2665 
   2666     # \" and \\
   2667     if ((i==34||i==92)); then
   2668       ret='\'"$ret"
   2669     fi
   2670   elif ((128<=i&&i<160)); then
   2671     # C1 characters
   2672     ble/util/sprintf ret '\\%03o' "$i"
   2673   else
   2674     # others
   2675     ble/util/sprintf ret '\\%03o' "$i"
   2676     # ble/util/c2s だと UTF-8 encode されてしまうので駄目
   2677   fi
   2678 }
   2679 
   2680 ## @fn binder; ble/decode/cmap/.generate-binder-template
   2681 ##   3文字以上の bind -x を _ble_decode_cmap から自動的に行うソースを生成
   2682 ##   binder には bind を行う関数を指定する。
   2683 #
   2684 # ※この関数は bash-3.1 では使えない。
   2685 #   bash-3.1 ではバグで呼出元と同名の配列を定義できないので
   2686 #   local -a ccodes が空になってしまう。
   2687 #   幸いこの関数は bash-3.1 では使っていないのでこのままにしてある。
   2688 #   追記: 公開されている patch を見たら bash-3.1.4 で修正されている様だ。
   2689 #
   2690 function ble/decode/cmap/.generate-binder-template {
   2691   local tseq=$1 qseq=$2 nseq=$3 depth=${4:-1} ccode
   2692   local apos="'" escapos="'\\''"
   2693   builtin eval "local -a ccodes; ccodes=(\${!_ble_decode_cmap_$tseq[@]})"
   2694   for ccode in "${ccodes[@]}"; do
   2695     local ret
   2696     ble/decode/c2dqs "$ccode"
   2697     local qseq1=$qseq$ret
   2698     local nseq1="$nseq $ccode"
   2699 
   2700     builtin eval "local ent=\${_ble_decode_cmap_$tseq[ccode]}"
   2701     if [[ ${ent%_} ]]; then
   2702       if ((depth>=3)); then
   2703         ble/util/print "\$binder \"$qseq1\" \"${nseq1# }\""
   2704       fi
   2705     fi
   2706 
   2707     if [[ ${ent//[0-9]} == _ ]]; then
   2708       ble/decode/cmap/.generate-binder-template "${tseq}_$ccode" "$qseq1" "$nseq1" "$((depth+1))"
   2709     fi
   2710   done
   2711 }
   2712 
   2713 function ble/decode/cmap/.emit-bindx {
   2714   local q="'" Q="'\''"
   2715   ble/util/print "builtin bind -x '\"${1//$q/$Q}\":ble-decode/.hook $2; builtin eval -- \"\$_ble_decode_bind_hook\"'"
   2716 }
   2717 function ble/decode/cmap/.emit-bindr {
   2718   ble/util/print "builtin bind -r \"$1\""
   2719 }
   2720 
   2721 _ble_decode_cmap_initialized=
   2722 function ble/decode/cmap/initialize {
   2723   [[ $_ble_decode_cmap_initialized ]] && return 0
   2724   _ble_decode_cmap_initialized=1
   2725 
   2726   local init=$_ble_base/lib/init-cmap.sh
   2727   local dump=$_ble_base_cache/decode.cmap.$_ble_decode_kbd_ver.$TERM.dump
   2728   if [[ -s $dump && $dump -nt $init ]]; then
   2729     source "$dump"
   2730   else
   2731     ble/edit/info/immediate-show text 'ble.sh: generating "'"$dump"'"...'
   2732     source "$init"
   2733     ble-bind -D | ble/bin/awk '
   2734       {
   2735         sub(/^declare +(-[aAilucnrtxfFgGI]+ +)?/, "");
   2736         sub(/^-- +/, "");
   2737       }
   2738       /^_ble_decode_(cmap|csimap|kbd)/ {
   2739         if (!($0 ~ /^_ble_decode_csimap_kitty_u/))
   2740           gsub(/["'\'']/, "");
   2741         print
   2742       }
   2743     ' >| "$dump"
   2744   fi
   2745 
   2746   if ((_ble_bash>=40300)); then
   2747     # 3文字以上 bind/unbind ソースの生成 (init-bind.sh bindAllSeq で使用)
   2748     local fbinder=$_ble_base_cache/decode.cmap.allseq
   2749     _ble_decode_bind_fbinder=$fbinder
   2750     if ! [[ -s $_ble_decode_bind_fbinder.bind && $_ble_decode_bind_fbinder.bind -nt $init &&
   2751               -s $_ble_decode_bind_fbinder.unbind && $_ble_decode_bind_fbinder.unbind -nt $init ]]; then
   2752       ble/edit/info/immediate-show text  'ble.sh: initializing multichar sequence binders... '
   2753       ble/decode/cmap/.generate-binder-template >| "$fbinder"
   2754       binder=ble/decode/cmap/.emit-bindx source "$fbinder" >| "$fbinder.bind"
   2755       binder=ble/decode/cmap/.emit-bindr source "$fbinder" >| "$fbinder.unbind"
   2756       ble/edit/info/immediate-show text  'ble.sh: initializing multichar sequence binders... done'
   2757     fi
   2758   fi
   2759 }
   2760 
   2761 function ble/decode/cmap/decode-chars.hook {
   2762   ble/array#push ble_decode_bind_keys "$1"
   2763   _ble_decode_key__hook=ble/decode/cmap/decode-chars.hook
   2764 }
   2765 ## @fn ble/decode/cmap/decode-chars chars...
   2766 ##   文字コードの列からキーの列へ変換します。
   2767 ##   @arr[out] keys
   2768 function ble/decode/cmap/decode-chars {
   2769   ble/decode/cmap/initialize
   2770 
   2771   # initialize
   2772   local _ble_decode_csi_mode=0
   2773   local _ble_decode_csi_args=
   2774   local _ble_decode_char2_seq=
   2775   local _ble_decode_char2_reach_key=
   2776   local _ble_decode_char2_reach_seq=
   2777   local _ble_decode_char2_modifier=
   2778   local _ble_decode_char2_modkcode=
   2779 
   2780   # suppress unrelated triggers
   2781   local _ble_decode_char__hook=
   2782 #%if debug_keylogger
   2783   local _ble_debug_keylog_enabled=
   2784 #%end
   2785   local _ble_decode_keylog_keys_enabled=
   2786   local _ble_decode_keylog_chars_enabled=
   2787   local _ble_decode_show_progress_hook=
   2788   local _ble_decode_erase_progress_hook=
   2789 
   2790   # suppress errors
   2791   local bleopt_decode_error_cseq_abell=
   2792   local bleopt_decode_error_cseq_vbell=
   2793   local bleopt_decode_error_cseq_discard=
   2794 
   2795   # set up hook and run
   2796   local -a ble_decode_bind_keys=()
   2797   local _ble_decode_key__hook=ble/decode/cmap/decode-chars.hook
   2798   local ble_decode_char_sync=1 # ユーザ入力があっても中断しない
   2799   ble-decode-char "$@"
   2800 
   2801   keys=("${ble_decode_bind_keys[@]}")
   2802 }
   2803 
   2804 #------------------------------------------------------------------------------
   2805 # **** binder for bash input ****                                  @decode.bind
   2806 
   2807 _ble_decode_bind_hook=
   2808 
   2809 # **** ^U ^V ^W ^? 対策 ****                                   @decode.bind.uvw
   2810 
   2811 # ref #D0003, #D1092
   2812 _ble_decode_bind__uvwflag=
   2813 function ble/decode/bind/adjust-uvw {
   2814   [[ $_ble_decode_bind__uvwflag ]] && return 0
   2815   _ble_decode_bind__uvwflag=1
   2816 
   2817   # 何故か stty 設定直後には bind できない物たち
   2818   # Note: bind 'set bind-tty-special-chars on' の時に以下が必要である (#D1092)
   2819   builtin bind -x $'"\025":ble-decode/.hook 21; builtin eval -- "$_ble_decode_bind_hook"'  # ^U
   2820   builtin bind -x $'"\026":ble-decode/.hook 22; builtin eval -- "$_ble_decode_bind_hook"'  # ^V
   2821   builtin bind -x $'"\027":ble-decode/.hook 23; builtin eval -- "$_ble_decode_bind_hook"'  # ^W
   2822   builtin bind -x $'"\177":ble-decode/.hook 127; builtin eval -- "$_ble_decode_bind_hook"' # ^?
   2823   # Note: 更に terminology は erase を DEL ではなく HT に設定しているので、以下
   2824   # も再設定する必要がある。他の端末でも似た物があるかもしれないので、念の為端
   2825   # 末判定はせずに常に上書きを実行する様にする。
   2826   builtin bind -x $'"\010":ble-decode/.hook 8; builtin eval -- "$_ble_decode_bind_hook"'   # ^H
   2827 }
   2828 
   2829 # **** POSIXLY_CORRECT workaround ****
   2830 
   2831 # ble.pp の関数を上書き
   2832 #
   2833 # Note: bash で set -o vi の時、
   2834 #   builtin unset -v POSIXLY_CORRECT や local POSIXLY_CORRECT が設定されると、
   2835 #   C-i の既定の動作の切り替えに伴って C-i の束縛が消滅する。
   2836 #   ユーザが POSIXLY_CORRECT を触った時や自分で触った時に、
   2837 #   改めて束縛し直す必要がある。
   2838 #
   2839 function ble/base/workaround-POSIXLY_CORRECT {
   2840   [[ $_ble_decode_bind_state == none ]] && return 0
   2841   builtin bind -x '"\C-i":ble-decode/.hook 9; builtin eval -- "$_ble_decode_bind_hook"'
   2842 }
   2843 
   2844 # **** ble-decode-bind ****                                   @decode.bind.main
   2845 
   2846 ## @fn ble/decode/bind/.generate-source-to-unbind-default
   2847 ##   既存の ESC で始まる binding を削除するコードを生成し標準出力に出力します。
   2848 ##   更に、既存の binding を復元する為のコードを同時に生成し tmp/$$.bind.save に保存します。
   2849 function ble/decode/bind/.generate-source-to-unbind-default {
   2850   # 1 ESC で始まる既存の binding を全て削除
   2851   # 2 bind を全て記録 at $$.bind.save
   2852   {
   2853     if ((_ble_bash>=40300)); then
   2854       ble/util/print '__BINDX__'
   2855       builtin bind -X
   2856     fi
   2857     ble/util/print '__BINDP__'
   2858     builtin bind -sp
   2859   } | ble/decode/bind/.generate-source-to-unbind-default/.process
   2860 
   2861   # Note: 2>/dev/null は、(1) bind -X のエラーメッセージ、及び、
   2862   # (2) LC_ALL 復元時のエラーメッセージ (外側の値が不正な時) を捨てる為に必要。
   2863 } 2>/dev/null
   2864 function ble/decode/bind/.generate-source-to-unbind-default/.process {
   2865   # Note: #D1355 LC_ALL 切り替えに伴うエラーメッセージは呼び出し元で /dev/null に繋いでいる。
   2866   local q=\' Q="'\''"
   2867   LC_ALL=C ble/bin/awk -v q="$q" '
   2868     BEGIN {
   2869       IS_XPG4 = AWKTYPE == "xpg4";
   2870       rep_Q         = str2rep(q "\\" q q);
   2871       rep_bslash    = str2rep("\\");
   2872       rep_kseq_1c5c = str2rep("\"\\x1c\\x5c\"");
   2873       rep_kseq_1c   = str2rep("\"\\x1c\"");
   2874       mode = 1;
   2875     }
   2876 
   2877 #%  # Note: Solaris xpg4 awk では gsub の置換後のエスケープシーケンス
   2878 #%  #   も処理されるので、バックスラッシュをエスケープする。
   2879     function str2rep(str) {
   2880       if (IS_XPG4) sub(/\\/, "\\\\\\\\", str);
   2881       return str;
   2882     }
   2883 
   2884     function quote(text) {
   2885       gsub(q, rep_Q, text);
   2886       return q text q;
   2887     }
   2888 
   2889     function unescape_control_modifier(str, _, i, esc, chr) {
   2890       for (i = 0; i < 32; i++) {
   2891         if (i == 0 || i == 31)
   2892           esc = sprintf("\\\\C-%c", i + 64);
   2893         else if (27 <= i && i <= 30)
   2894           esc = sprintf("\\\\C-\\%c", i + 64);
   2895         else
   2896           esc = sprintf("\\\\C-%c", i + 96);
   2897 
   2898         chr = sprintf("%c", i);
   2899         gsub(esc, chr, str);
   2900       }
   2901       gsub(/\\C-\?/, sprintf("%c", 127), str);
   2902       return str;
   2903     }
   2904     function unescape(str) {
   2905       if (str ~ /\\C-/)
   2906         str = unescape_control_modifier(str);
   2907       gsub(/\\e/, sprintf("%c", 27), str);
   2908       gsub(/\\"/, "\"", str);
   2909       gsub(/\\\\/, rep_bslash, str);
   2910       return str;
   2911     }
   2912 
   2913     function output_bindr(line0, _seq) {
   2914       if (match(line0, /^"(([^"\\]|\\.)+)"/) > 0) {
   2915         _seq = substr(line0, 2, RLENGTH - 2);
   2916 
   2917 #%      # ※bash-3.1 では bind -sp で \e ではなく \M- と表示されるが、
   2918 #%      #   bind -r では \M- ではなく \e と指定しなければ削除できない。
   2919         gsub(/\\M-/, "\\e", _seq);
   2920 
   2921         print "builtin bind -r " quote(_seq);
   2922       }
   2923     }
   2924 
   2925     /^__BINDP__$/ { mode = 1; next; }
   2926     /^__BINDX__$/ { mode = 2; next; }
   2927 
   2928     mode == 1 && $0 ~ /^"/ {
   2929       # Workaround Bash-5.0 bug (cf #D1078)
   2930       sub(/^"\\C-\\\\\\"/, rep_kseq_1c5c);
   2931       sub(/^"\\C-\\\\?"/, rep_kseq_1c);
   2932 
   2933       output_bindr($0);
   2934 
   2935       print "builtin bind " quote($0) > "/dev/stderr";
   2936     }
   2937 
   2938     mode == 2 && $0 ~ /^"/ {
   2939       output_bindr($0);
   2940 
   2941       line = $0;
   2942 
   2943 #%    # ※bash-4.3..5.0 では bind -r しても bind -X に残る。
   2944 #%    #   再登録を防ぐ為 ble-decode-bind を明示的に避ける
   2945       if (line ~ /(^|[^[:alnum:]])ble-decode\/.hook($|[^[:alnum:]])/) next;
   2946 
   2947 #%    # ※bind -X で得られた物は直接 bind -x に用いる事はできない。
   2948 #%    #   コマンド部分の "" を外して中の escape を外す必要がある。
   2949 #%    #   escape には以下の種類がある: \C-a など \C-? \e \\ \"
   2950 #%    #     \n\r\f\t\v\b\a 等は使われない様だ。
   2951       if (match(line, /^("([^"\\]|\\.)*":) "(([^"\\]|\\.)*)"/) > 0) {
   2952         rlen = RLENGTH;
   2953         match(line, /^"([^"\\]|\\.)*":/);
   2954         rlen1 = RLENGTH;
   2955         rlen2 = rlen - rlen1 - 3;
   2956         sequence = substr(line, 1        , rlen1);
   2957         command  = substr(line, rlen1 + 3, rlen2);
   2958 
   2959         if (command ~ /\\/)
   2960           command = unescape(command);
   2961 
   2962         line = sequence command;
   2963       }
   2964 
   2965       print "builtin bind -x " quote(line) > "/dev/stderr";
   2966     }
   2967   ' 2>| "$_ble_base_run/$$.bind.save"
   2968 }
   2969 
   2970 ## @var _ble_decode_bind_state
   2971 ##   none, emacs, vi
   2972 _ble_decode_bind_state=none
   2973 _ble_decode_bind_bindp=
   2974 _ble_decode_bind_encoding=
   2975 
   2976 function ble/decode/bind/bind {
   2977   _ble_decode_bind_encoding=$bleopt_input_encoding
   2978   local file=$_ble_base_cache/decode.bind.$_ble_bash.$_ble_decode_bind_encoding.bind
   2979   [[ -s $file && $file -nt $_ble_base/lib/init-bind.sh ]] || source "$_ble_base/lib/init-bind.sh"
   2980 
   2981   # * 一時的に 'set convert-meta off' にする。
   2982   #
   2983   #   bash-3.0 - 5.0a 全てにおいて 'set convert-meta on' の時、
   2984   #   128-255 を bind しようとすると 0-127 を bind してしまう。
   2985   #   32 bit 環境で LC_CTYPE=C で起動すると 'set convert-meta on' になる様だ。
   2986   #
   2987   #   一応、以下の関数は ble/term/initialize で呼び出しているので、
   2988   #   ble/decode/bind/bind の呼び出しが ble/term/initialize より後なら大丈夫の筈だが、
   2989   #   念の為にここでも呼び出しておく事にする。
   2990   #
   2991   ble/term/rl-convert-meta/enter
   2992 
   2993   source "$file"
   2994   _ble_decode_bind__uvwflag=
   2995   ble/util/assign _ble_decode_bind_bindp 'builtin bind -p' # TERM 変更検出用
   2996 }
   2997 function ble/decode/bind/unbind {
   2998   ble/function#try ble/encoding:"$bleopt_input_encoding"/clear
   2999   source "$_ble_base_cache/decode.bind.$_ble_bash.$_ble_decode_bind_encoding.unbind"
   3000 }
   3001 function ble/decode/rebind {
   3002   [[ $_ble_decode_bind_state == none ]] && return 0
   3003   ble/decode/bind/unbind
   3004   ble/decode/bind/bind
   3005 }
   3006 
   3007 #------------------------------------------------------------------------------
   3008 # ble-bind                                                      @decode.blebind
   3009 
   3010 function ble-bind/.initialize-kmap {
   3011   [[ $kmap ]] && return 0
   3012   ble-decode/GET_BASEMAP -v kmap
   3013   if ! ble/decode/is-keymap "$kmap"; then
   3014     ble/util/print "ble-bind: the default keymap '$kmap' is unknown." >&2
   3015     flags=R$flags
   3016     return 1
   3017   fi
   3018   return 0
   3019 }
   3020 
   3021 function ble-bind/option:help {
   3022   ble/util/cat <<EOF
   3023 ble-bind --help
   3024 ble-bind -k [TYPE:]cspecs [[TYPE:]kspec]
   3025 ble-bind --csi PsFt [TYPE:]kspec
   3026 ble-bind [-m keymap] -fxc@s [TYPE:]kspecs command
   3027 ble-bind [-m keymap] -T [TYPE:]kspecs timeout
   3028 ble-bind [-m keymap] --cursor cursor_code
   3029 ble-bind [-m keymap]... (-PD|--print|--dump)
   3030 ble-bind (-L|--list-widgets)
   3031 
   3032 TYPE:SPEC
   3033   TYPE specifies the format of SPEC. The default is  "kspecs".
   3034 
   3035   kspecs  ble.sh keyboard spec
   3036   keys    List of key codes
   3037   chars   List of character codes in Unicode
   3038   keyseq  Key sequence in the Readline format
   3039   raw     Raw byte sequence
   3040 
   3041 TIMEOUT
   3042   specifies the timeout duration in milliseconds.
   3043 
   3044 CURSOR_CODE
   3045   specifies the cursor shape by the DECSCUSR code.
   3046 
   3047 EOF
   3048 }
   3049 
   3050 function ble-bind/check-argument {
   3051   if (($3<$2)); then
   3052     flags=E$flags
   3053     if (($2==1)); then
   3054       ble/util/print "ble-bind: the option \`$1' requires an argument." >&2
   3055     else
   3056       ble/util/print "ble-bind: the option \`$1' requires $2 arguments." >&2
   3057     fi
   3058     return 2
   3059   fi
   3060 }
   3061 
   3062 function ble-bind/option:csi {
   3063   local ret key=
   3064   if [[ $2 ]]; then
   3065     ble-decode-kbd "$2"
   3066     ble/string#split-words key "$ret"
   3067     if ((${#key[@]}!=1)); then
   3068       ble/util/print "ble-bind --csi: the second argument is not a single key!" >&2
   3069       return 1
   3070     elif ((key&~_ble_decode_MaskChar)); then
   3071       ble/util/print "ble-bind --csi: the second argument should not have modifiers!" >&2
   3072       return 1
   3073     fi
   3074   fi
   3075 
   3076   local rex
   3077   if rex='^([1-9][0-9]*)~$' && [[ $1 =~ $rex ]]; then
   3078     # --csi '<num>~' kname
   3079     #
   3080     #   以下のシーケンスを有効にする。
   3081     #   - CSI <num> ~         kname
   3082     #   - CSI <num> ; <mod> ~ Mod-kname (modified function key)
   3083     #   - CSI <num> $         S-kname (rxvt)
   3084     #   - CSI <num> ^         C-kname (rxvt)
   3085     #   - CSI <num> @         C-S-kname (rxvt)
   3086     #
   3087     _ble_decode_csimap_tilde[BASH_REMATCH[1]]=$key
   3088 
   3089     # "CSI <num> $" は CSI sequence の形式に沿っていないので、
   3090     # 個別に登録する必要がある。
   3091     local -a cseq
   3092     cseq=(27 91)
   3093     local ret i iN num="${BASH_REMATCH[1]}\$"
   3094     for ((i=0,iN=${#num};i<iN;i++)); do
   3095       ble/util/s2c "${num:i:1}"
   3096       ble/array#push cseq "$ret"
   3097     done
   3098 
   3099     local IFS=$_ble_term_IFS
   3100     if [[ $key ]]; then
   3101       ble-decode-char/bind "${cseq[*]}" "$((key|_ble_decode_Shft))"
   3102     else
   3103       ble-decode-char/unbind "${cseq[*]}"
   3104     fi
   3105   elif [[ $1 == [a-zA-Z] ]]; then
   3106     # --csi '<Ft>' kname
   3107     local ret; ble/util/s2c "$1"
   3108     _ble_decode_csimap_alpha[ret]=$key
   3109   else
   3110     ble/util/print "ble-bind --csi: not supported type of csi sequences: CSI \`$1'." >&2
   3111     return 1
   3112   fi
   3113 }
   3114 
   3115 function ble-bind/option:list-widgets {
   3116   declare -f | ble/bin/sed -n 's/^ble\/widget\/\([a-zA-Z][^.[:space:]();&|]\{1,\}\)[[:space:]]*()[[:space:]]*$/\1/p'
   3117 }
   3118 function ble-bind/option:dump {
   3119   if (($#)); then
   3120     local keymap
   3121     for keymap; do
   3122       ble/decode/keymap#dump "$keymap"
   3123     done
   3124   else
   3125     ble/util/declare-print-definitions "${!_ble_decode_kbd__@}" "${!_ble_decode_cmap_@}" "${!_ble_decode_csimap_@}"
   3126     ble/decode/keymap#dump
   3127   fi
   3128 }
   3129 ## @fn ble-bind/option:print
   3130 ##   @var[in] flags
   3131 function ble-bind/option:print {
   3132   local ble_bind_print=1
   3133   local sgr0= sgrf= sgrq= sgrc= sgro=
   3134   if [[ $flags == *c* || $flags != *n* && -t 1 ]]; then
   3135     local ret
   3136     ble/color/face2sgr command_function; sgrf=$ret
   3137     ble/color/face2sgr syntax_quoted; sgrq=$ret
   3138     ble/color/face2sgr syntax_comment; sgrc=$ret
   3139     ble/color/face2sgr argument_option; sgro=$ret
   3140     sgr0=$_ble_term_sgr0
   3141   fi
   3142 
   3143   local keymap
   3144   ble-decode/INITIALIZE_DEFMAP -v keymap # 初期化を強制する
   3145   if (($#)); then
   3146     for keymap; do
   3147       ble/decode/keymap#print "$keymap"
   3148     done
   3149   else
   3150     ble-decode-char/csi/print
   3151     ble-decode-char/print
   3152     ble/decode/keymap#print
   3153   fi
   3154 }
   3155 
   3156 function ble-bind {
   3157   # @var flags
   3158   #   D ... something done
   3159   #   E ... parse error
   3160   #   R ... runtime error
   3161   #   c ... color=always
   3162   #   n ... color=none
   3163   local flags= kmap=${ble_bind_keymap-} ret
   3164   local -a keymaps; keymaps=()
   3165   ble/decode/initialize
   3166 
   3167   local IFS=$_ble_term_IFS q=\' Q="''\'"
   3168 
   3169   local arg c
   3170   while (($#)); do
   3171     local arg=$1; shift
   3172     if [[ $arg == --?* ]]; then
   3173       case ${arg:2} in
   3174       (color|color=always)
   3175         flags=c${flags//[cn]} ;;
   3176       (color=never)
   3177         flags=n${flags//[cn]} ;;
   3178       (color=auto)
   3179         flags=${flags//[cn]} ;;
   3180       (help)
   3181         ble-bind/option:help
   3182         flags=D$flags ;;
   3183       (csi)
   3184         flags=D$flags
   3185         ble-bind/check-argument --csi 2 $# || break
   3186         ble-bind/option:csi "$1" "$2"
   3187         shift 2 ;;
   3188       (cursor)
   3189         flags=D$flags
   3190         ble-bind/check-argument --cursor 1 $# || break
   3191         ble-bind/.initialize-kmap &&
   3192           ble/decode/keymap#set-cursor "$kmap" "$1"
   3193         shift 1 ;;
   3194       (list-widgets|list-functions)
   3195         flags=D$flags
   3196         ble-bind/option:list-widgets ;;
   3197       (dump)
   3198         flags=D$flags
   3199         ble-bind/option:dump "${keymaps[@]}" ;;
   3200       (print)
   3201         flags=D$flags
   3202         ble-bind/option:print "${keymaps[@]}" ;;
   3203       (*)
   3204         flags=E$flags
   3205         ble/util/print "ble-bind: unrecognized long option $arg" >&2 ;;
   3206       esac
   3207     elif [[ $arg == -?* ]]; then
   3208       arg=${arg:1}
   3209       while ((${#arg})); do
   3210         c=${arg::1} arg=${arg:1}
   3211         case $c in
   3212         (k)
   3213           flags=D$flags
   3214           if (($#<2)); then
   3215             ble/util/print "ble-bind: the option \`-k' requires two arguments." >&2
   3216             flags=E$flags
   3217             break
   3218           fi
   3219 
   3220           ble-decode-kbd "$1"; local cseq=$ret
   3221           if [[ $2 && $2 != - ]]; then
   3222             ble-decode-kbd "$2"; local kc=$ret
   3223             ble-decode-char/bind "$cseq" "$kc"
   3224           else
   3225             ble-decode-char/unbind "$cseq"
   3226           fi
   3227           shift 2 ;;
   3228         (m)
   3229           ble-bind/check-argument -m 1 $# || break
   3230           if ! ble/decode/is-keymap "$1"; then
   3231             ble/util/print "ble-bind: the keymap '$1' is unknown." >&2
   3232             flags=E$flags
   3233             shift
   3234             continue
   3235           fi
   3236           kmap=$1
   3237           ble/array#push keymaps "$1"
   3238           shift ;;
   3239         (D)
   3240           flags=D$flags
   3241           ble-bind/option:dump "${keymaps[@]}" ;;
   3242         ([Pd])
   3243           flags=D$flags
   3244           ble-bind/option:print "${keymaps[@]}" ;;
   3245         (['fxc@s'])
   3246           flags=D$flags
   3247 
   3248           # 旧形式の指定 -xf や -cf に対応する処理
   3249           [[ $c != f && $arg == f* ]] && arg=${arg:1}
   3250           ble-bind/check-argument "-$c" 2 $# || break
   3251 
   3252           ble-decode-kbd "$1"; local kbd=$ret
   3253           if [[ $2 && $2 != - ]]; then
   3254             local command=$2
   3255 
   3256             # コマンドの種類
   3257             case $c in
   3258             (f) command=ble/widget/$command ;; # ble/widget/ 関数
   3259             (x) command="ble/widget/.EDIT_COMMAND '${command//$q/$Q}'" ;; # 編集用の関数
   3260             (c) command="ble/widget/.SHELL_COMMAND '${command//$q/$Q}'" ;; # コマンド実行
   3261             (s) local ret; ble/util/keyseq2chars "$command"; command="ble/widget/.MACRO ${ret[*]}" ;;
   3262             ('@') ;; # 直接実行
   3263             (*)
   3264               ble/util/print "error: unsupported binding type \`-$c'." 1>&2
   3265               continue ;;
   3266             esac
   3267 
   3268             ble-bind/.initialize-kmap &&
   3269               ble-decode-key/bind "$kmap" "$kbd" "$command"
   3270           else
   3271             ble-bind/.initialize-kmap &&
   3272               ble-decode-key/unbind "$kmap" "$kbd"
   3273           fi
   3274           shift 2 ;;
   3275         (T)
   3276           flags=D$flags
   3277           ble-decode-kbd "$1"; local kbd=$ret
   3278           ble-bind/check-argument -T 2 $# || break
   3279           ble-bind/.initialize-kmap &&
   3280             ble-decode-key/set-timeout "$kmap" "$kbd" "$2"
   3281           shift 2 ;;
   3282         (L)
   3283           flags=D$flags
   3284           ble-bind/option:list-widgets ;;
   3285         (*)
   3286           ble/util/print "ble-bind: unrecognized short option \`-$c'." >&2
   3287           flags=E$flags ;;
   3288         esac
   3289       done
   3290     else
   3291       ble/util/print "ble-bind: unrecognized argument \`$arg'." >&2
   3292       flags=E$flags
   3293     fi
   3294   done
   3295 
   3296   [[ $flags == *E* ]] && return 2
   3297   [[ $flags == *R* ]] && return 1
   3298   [[ $flags == *D* ]] || ble-bind/option:print "${keymaps[@]}"
   3299   return 0
   3300 }
   3301 
   3302 #------------------------------------------------------------------------------
   3303 # ble/decode/read-inputrc                                       @decode.inputrc
   3304 
   3305 function ble/decode/read-inputrc/test {
   3306   local text=$1
   3307   if [[ ! $text ]]; then
   3308     ble/util/print "ble.sh (bind):\$if: test condition is not supplied." >&2
   3309     return 1
   3310   elif local rex=$'[ \t]*([<>]=?|[=!]?=)[ \t]*(.*)$'; [[ $text =~ $rex ]]; then
   3311     local op=${BASH_REMATCH[1]}
   3312     local rhs=${BASH_REMATCH[2]}
   3313     local lhs=${text::${#text}-${#BASH_REMATCH}}
   3314   else
   3315     local lhs=application
   3316     local rhs=$text
   3317   fi
   3318 
   3319   case $lhs in
   3320   (application)
   3321     local ret; ble/string#tolower "$rhs"
   3322     [[ $ret == bash || $ret == blesh ]]
   3323     return "$?" ;;
   3324 
   3325   (mode)
   3326     if [[ -o emacs ]]; then
   3327       builtin test emacs "$op" "$rhs"
   3328     elif [[ -o vi ]]; then
   3329       builtin test vi "$op" "$rhs"
   3330     else
   3331       false
   3332     fi
   3333     return "$?" ;;
   3334 
   3335   (term)
   3336     if [[ $op == '!=' ]]; then
   3337       builtin test "$TERM" "$op" "$rhs" && builtin test "${TERM%%-*}" "$op" "$rhs"
   3338     else
   3339       builtin test "$TERM" "$op" "$rhs" || builtin test "${TERM%%-*}" "$op" "$rhs"
   3340     fi
   3341     return "$?" ;;
   3342 
   3343   (version)
   3344     local lhs_major lhs_minor
   3345     if ((_ble_bash<40400)); then
   3346       ((lhs_major=2+_ble_bash/10000,
   3347         lhs_minor=_ble_bash/100%100))
   3348     elif ((_ble_bash<50000)); then
   3349       ((lhs_major=7,lhs_minor=0))
   3350     else
   3351       ((lhs_major=3+_ble_bash/10000,
   3352         lhs_minor=_ble_bash/100%100))
   3353     fi
   3354 
   3355     local rhs_major rhs_minor
   3356     if [[ $rhs == *.* ]]; then
   3357       local version
   3358       ble/string#split version . "$rhs"
   3359       rhs_major=${version[0]}
   3360       rhs_minor=${version[1]}
   3361     else
   3362       ((rhs_major=rhs,rhs_minor=0))
   3363     fi
   3364 
   3365     local lhs_ver=$((lhs_major*10000+lhs_minor))
   3366     local rhs_ver=$((rhs_major*10000+rhs_minor))
   3367     [[ $op == '=' ]] && op='=='
   3368     let "$lhs_ver$op$rhs_ver"
   3369     return "$?" ;;
   3370 
   3371   (*)
   3372     if local ret; ble/util/rlvar#read "$lhs"; then
   3373       builtin test "$ret" "$op" "$rhs"
   3374       return "$?"
   3375     else
   3376       ble/util/print "ble.sh (bind):\$if: unknown readline variable '${lhs//$q/$Q}'." >&2
   3377       return 1
   3378     fi ;;
   3379   esac
   3380 }
   3381 
   3382 function ble/decode/read-inputrc {
   3383   local file=$1 ref=$2 q=\' Q="''\'"
   3384   if [[ -f $ref && $ref == */* && $file != /* ]]; then
   3385     local relative_file=${ref%/*}/$file
   3386     [[ -f $relative_file ]] && file=$relative_file
   3387   fi
   3388   if [[ ! -f $file ]]; then
   3389     ble/util/print "ble.sh (bind):\$include: the file '${1//$q/$Q}' not found." >&2
   3390     return 1
   3391   fi
   3392 
   3393   local -a script=()
   3394   local ret line= iline=0
   3395   while ble/bash/read line || [[ $line ]]; do
   3396     ((++iline))
   3397     ble/string#trim "$line"; line=$ret
   3398     [[ ! $line || $line == '#'* ]] && continue
   3399 
   3400     if [[ $line == '$'* ]]; then
   3401       local directive=${line%%[$IFS]*}
   3402       case $directive in
   3403       ('$if')
   3404         local args=${line#'$if'}
   3405         ble/string#trim "$args"; args=$ret
   3406         ble/array#push script "if ble/decode/read-inputrc/test '${args//$q/$Q}'; then :" ;;
   3407       ('$else')  ble/array#push script 'else :' ;;
   3408       ('$endif') ble/array#push script 'fi' ;;
   3409       ('$include')
   3410         local args=${line#'$include'}
   3411         ble/string#trim "$args"; args=$ret
   3412         ble/array#push script "ble/decode/read-inputrc '${args//$q/$Q}' '${file//$q/$Q}'" ;;
   3413       (*)
   3414         ble/util/print "ble.sh (bind):$file:$iline: unrecognized directive '$directive'." >&2 ;;
   3415       esac
   3416     else
   3417       ble/array#push script "ble/builtin/bind/.process -- '${line//$q/$Q}'"
   3418     fi
   3419   done < "$file"
   3420 
   3421   IFS=$'\n' builtin eval 'script="${script[*]}"'
   3422   builtin eval -- "$script"
   3423 }
   3424 
   3425 #------------------------------------------------------------------------------
   3426 # ble/builtin/bind                                                @builtin.bind
   3427 
   3428 _ble_builtin_bind_keymap=
   3429 function ble/builtin/bind/set-keymap {
   3430   local opt_keymap= flags=
   3431   ble/builtin/bind/option:m "$1" &&
   3432     _ble_builtin_bind_keymap=$opt_keymap
   3433   return 0
   3434 }
   3435 
   3436 ## @fn ble/builtin/bind/option:m keymap
   3437 ##   @var[in,out] opt_keymap flags
   3438 function ble/builtin/bind/option:m {
   3439   local name=$1
   3440   local ret; ble/string#tolower "$name"; local keymap=$ret
   3441   case $keymap in
   3442   (emacs|emacs-standard|emacs-meta|emacs-ctlx) ;;
   3443   (vi|vi-command|vi-move|vi-insert) ;;
   3444   (*) keymap= ;;
   3445   esac
   3446   if [[ ! $keymap ]]; then
   3447     ble/util/print "ble.sh (bind): unrecognized keymap name '$name'" >&2
   3448     flags=e$flags
   3449     return 1
   3450   else
   3451     opt_keymap=$keymap
   3452     return 0
   3453   fi
   3454 }
   3455 ## @fn ble/builtin/bind/.decompose-pair spec
   3456 ##   keyseq:command の形式の文字列を keyseq と command に分離します。
   3457 ##   @var[out] keyseq value
   3458 function ble/builtin/bind/.decompose-pair {
   3459   local LC_ALL= LC_CTYPE=C
   3460   local ret; ble/string#trim "$1"
   3461   local spec=$ret ifs=$_ble_term_IFS q=\' Q="'\''"
   3462   keyseq= value=
   3463 
   3464   # bind '' と指定した時は無視する
   3465   [[ ! $spec || $spec == 'set'["$ifs"]* ]] && return 3
   3466 
   3467   # split keyseq / value
   3468   local rex='^(("([^\"]|\\.)*"|[^":'$ifs'])*("([^\"]|\\.)*)?)['$ifs']*(:['$ifs']*)?'
   3469   [[ $spec =~ $rex ]]
   3470   keyseq=${BASH_REMATCH[1]} value=${spec:${#BASH_REMATCH}}
   3471 
   3472   # check values
   3473   if [[ $keyseq == '$'* ]]; then
   3474     # Parser directives such as $if, $else, $endif, $include
   3475     return 3
   3476   elif [[ ! $keyseq ]]; then
   3477     ble/util/print "ble.sh (bind): empty keyseq in spec:'${spec//$q/$Q}'" >&2
   3478     flags=e$flags
   3479     return 1
   3480   elif rex='^"([^\"]|\\.)*$'; [[ $keyseq =~ $rex ]]; then
   3481     ble/util/print "ble.sh (bind): no closing '\"' in keyseq:'${keyseq//$q/$Q}'" >&2
   3482     flags=e$flags
   3483     return 1
   3484   elif rex='^"([^\"]|\\.)*"'; [[ $keyseq =~ $rex ]]; then
   3485     local rematch=${BASH_REMATCH[0]}
   3486     if ((${#rematch}<${#keyseq})); then
   3487       local fragment=${keyseq:${#rematch}}
   3488       ble/util/print "ble.sh (bind): warning: unprocessed fragments in keyseq '${fragment//$q/$Q}'" >&2
   3489     fi
   3490     keyseq=$rematch
   3491     return 0
   3492   else
   3493     return 0
   3494   fi
   3495 }
   3496 ble/function#suppress-stderr ble/builtin/bind/.decompose-pair
   3497 ## @fn ble/builtin/bind/.parse-keyname keyname
   3498 ##   @var[out] chars
   3499 function ble/builtin/bind/.parse-keyname {
   3500   local ret mflags=
   3501   ble/string#tolower "$1"; local lower=$ret
   3502   if [[ $1 == *-* ]]; then
   3503     ble/string#split ret - "$lower"
   3504     local mod
   3505     for mod in "${ret[@]::${#ret[@]}-1}"; do
   3506       case $mod in
   3507       (*m|*meta) mflags=m$mflags ;;
   3508       (*c|*ctrl|*control) mflags=c$mflags ;;
   3509       esac
   3510     done
   3511   fi
   3512 
   3513   local name=${lower##*-} ch=
   3514   case $name in
   3515   (rubout|del) ch=$'\177' ;;
   3516   (escape|esc) ch=$'\033' ;;
   3517   (newline|lfd) ch=$'\n' ;;
   3518   (return|ret) ch=$'\r' ;;
   3519   (space|spc) ch=' ' ;;
   3520   (tab) ch=$'\t' ;;
   3521   (*) ble/util/substr "${1##*-}" 0 1; ch=$ret ;;
   3522   esac
   3523   ble/util/s2c "$ch"; local key=$ret
   3524 
   3525   [[ $mflags == *c* ]] && ((key&=0x1F))
   3526   [[ $mflags == *m* ]] && ((key|=0x80))
   3527   chars=("$key")
   3528 }
   3529 
   3530 ## @fn ble/builtin/bind/.initialize-kmap keymap
   3531 ##   @var[in,out] keys
   3532 ##   @var[out] kmap
   3533 function ble/builtin/bind/.initialize-kmap {
   3534   local keymap=$1
   3535   kmap=
   3536   case $keymap in
   3537   (emacs|emacs-standard) kmap=emacs ;;
   3538   (emacs-ctlx) kmap=emacs; keys=(24 "${keys[@]}") ;;
   3539   (emacs-meta) kmap=emacs; keys=(27 "${keys[@]}") ;;
   3540   (vi-insert) kmap=vi_imap ;;
   3541   (vi|vi-command|vi-move) kmap=vi_nmap ;;
   3542   (*) ble-decode/GET_BASEMAP -v kmap ;;
   3543   esac
   3544 
   3545   if ! ble/decode/is-keymap "$kmap"; then
   3546     ble/util/print "ble/builtin/bind: the keymap '$kmap' is unknown." >&2
   3547     return 1
   3548   fi
   3549 
   3550   return 0
   3551 }
   3552 ## @fn ble/builtin/bind/.initialize-keys-and-value
   3553 ##   @var[out] keys value
   3554 function ble/builtin/bind/.initialize-keys-and-value {
   3555   local spec=$1 opts=$2
   3556   keys= value=
   3557 
   3558   local keyseq
   3559   ble/builtin/bind/.decompose-pair "$spec" || return "$?"
   3560 
   3561   local chars
   3562   if [[ $keyseq == \"*\" ]]; then
   3563     local ret; ble/util/keyseq2chars "${keyseq:1:${#keyseq}-2}"
   3564     chars=("${ret[@]}")
   3565     ((${#chars[@]})) || ble/util/print "ble.sh (bind): warning: empty keyseq" >&2
   3566   else
   3567     [[ :$opts: == *:nokeyname:* ]] &&
   3568       ble/util/print "ble.sh (bind): warning: readline \"bind -x\" does not support \"keyname\" spec" >&2
   3569     ble/builtin/bind/.parse-keyname "$keyseq"
   3570   fi
   3571   ble/decode/cmap/decode-chars "${chars[@]}"
   3572 }
   3573 
   3574 ## @fn ble/builtin/bind/option:x spec
   3575 ##   @var[in] opt_keymap
   3576 function ble/builtin/bind/option:x {
   3577   local q=\' Q="''\'"
   3578   local keys value kmap
   3579   if ! ble/builtin/bind/.initialize-keys-and-value "$1" nokeyname; then
   3580     ble/util/print "ble.sh (bind): unrecognized readline command '${1//$q/$Q}'." >&2
   3581     flags=e$flags
   3582     return 1
   3583   elif ! ble/builtin/bind/.initialize-kmap "$opt_keymap"; then
   3584     ble/util/print "ble.sh (bind): sorry, failed to initialize keymap:'$opt_keymap'." >&2
   3585     flags=e$flags
   3586     return 1
   3587   fi
   3588 
   3589   if [[ $value == \"* ]]; then
   3590     local ifs=$_ble_term_IFS
   3591     local rex='^"(([^\"]|\\.)*)"'
   3592     if ! [[ $value =~ $rex ]]; then
   3593       ble/util/print "ble.sh (bind): no closing '\"' in spec:'${1//$q/$Q}'" >&2
   3594       flags=e$flags
   3595       return 1
   3596     fi
   3597 
   3598     if ((${#BASH_REMATCH}<${#value})); then
   3599       local fragment=${value:${#BASH_REMATCH}}
   3600       ble/util/print "ble.sh (bind): warning: unprocessed fragments:'${fragment//$q/$Q}' in spec:'${1//$q/$Q}'" >&2
   3601     fi
   3602 
   3603     value=${BASH_REMATCH[1]}
   3604   fi
   3605 
   3606   [[ $value == \"*\" ]] && value=${value:1:${#value}-2}
   3607   local command="ble/widget/.EDIT_COMMAND '${value//$q/$Q}'"
   3608   ble-decode-key/bind "$kmap" "${keys[*]}" "$command"
   3609 }
   3610 ## @fn ble/builtin/bind/option:r keyseq
   3611 ##   @var[in] opt_keymap
   3612 function ble/builtin/bind/option:r {
   3613   local keyseq=$1
   3614 
   3615   local ret chars keys
   3616   ble/util/keyseq2chars "$keyseq"; chars=("${ret[@]}")
   3617   ble/decode/cmap/decode-chars "${chars[@]}"
   3618 
   3619   local kmap
   3620   ble/builtin/bind/.initialize-kmap "$opt_keymap" || return 1
   3621   ble-decode-key/unbind "$kmap" "${keys[*]}"
   3622 }
   3623 
   3624 _ble_decode_rlfunc2widget_emacs=()
   3625 _ble_decode_rlfunc2widget_vi_imap=()
   3626 _ble_decode_rlfunc2widget_vi_nmap=()
   3627 function ble/builtin/bind/rlfunc2widget {
   3628   local kmap=$1 rlfunc=$2
   3629   local IFS=$_ble_term_IFS
   3630 
   3631   local rlfunc_file= rlfunc_dict=
   3632   case $kmap in
   3633   (emacs)   rlfunc_file=$_ble_base/lib/core-decode.emacs-rlfunc.txt
   3634             rlfunc_dict=_ble_decode_rlfunc2widget_emacs ;;
   3635   (vi_imap) rlfunc_file=$_ble_base/lib/core-decode.vi_imap-rlfunc.txt
   3636             rlfunc_dict=_ble_decode_rlfunc2widget_vi_imap ;;
   3637   (vi_nmap) rlfunc_file=$_ble_base/lib/core-decode.vi_nmap-rlfunc.txt
   3638             rlfunc_dict=_ble_decode_rlfunc2widget_vi_nmap ;;
   3639   esac
   3640 
   3641   if [[ $rlfunc_file ]]; then
   3642     local dict script='
   3643     ((${#DICT[@]})) ||
   3644       ble/util/mapfile DICT < "$rlfunc_file"
   3645     dict=("${DICT[@]}")'
   3646     builtin eval -- "${script//DICT/$rlfunc_dict}"
   3647 
   3648     local line
   3649     for line in "${dict[@]}"; do
   3650       [[ $line == "$rlfunc "* ]] || continue
   3651       local rl widget; ble/bash/read rl widget <<< "$line"
   3652       if [[ $widget == - ]]; then
   3653         ble/util/print "ble.sh (bind): unsupported readline function '${rlfunc//$q/$Q}' for keymap '$kmap'." >&2
   3654         return 1
   3655       elif [[ $widget == '<IGNORE>' ]]; then
   3656         return 2
   3657       fi
   3658       ret=ble/widget/$widget
   3659       return 0
   3660     done
   3661   fi
   3662 
   3663   if ble/is-function ble/widget/"${rlfunc%%[$IFS]*}"; then
   3664     ret=ble/widget/$rlfunc
   3665     return 0
   3666   fi
   3667 
   3668   ble/util/print "ble.sh (bind): unsupported readline function '${rlfunc//$q/$Q}'." >&2
   3669   return 1
   3670 }
   3671 
   3672 ## @fn ble/builtin/bind/option:u function
   3673 ##   @var[in] opt_keymap
   3674 function ble/builtin/bind/option:u {
   3675   local rlfunc=$1
   3676 
   3677   local kmap
   3678   if ! ble/builtin/bind/.initialize-kmap "$opt_keymap" || ! ble/decode/keymap#load "$kmap"; then
   3679     ble/util/print "ble.sh (bind): sorry, failed to initialize keymap:'$opt_keymap'." >&2
   3680     flags=e$flags
   3681     return 1
   3682   fi
   3683   local ret
   3684   ble/builtin/bind/rlfunc2widget "$kmap" "$rlfunc" || return 0
   3685   local command=$ret
   3686 
   3687   # recursive search
   3688   local -a unbind_keys_list=()
   3689   ble/builtin/bind/option:u/search-recursive "$kmap"
   3690 
   3691   # unbind
   3692   local keys
   3693   for keys in "${unbind_keys_list[@]}"; do
   3694     ble-decode-key/unbind "$kmap" "$keys"
   3695   done
   3696 }
   3697 function ble/builtin/bind/option:u/search-recursive {
   3698   local kmap=$1 tseq=$2
   3699   local dicthead=_ble_decode_${kmap}_kmap_
   3700   local key keys
   3701   builtin eval "keys=(\${!$dicthead$tseq[@]})"
   3702   for key in "${keys[@]}"; do
   3703     builtin eval "local ent=\${$dicthead$tseq[key]}"
   3704     if [[ ${ent:2} == "$command" ]]; then
   3705       ble/array#push unbind_keys_list "${tseq//_/ } $key"
   3706     fi
   3707     if [[ ${ent::1} == _ ]]; then
   3708       ble/builtin/bind/option:u/search-recursive "$kmap" "${tseq}_$key"
   3709     fi
   3710   done
   3711 }
   3712 ## @fn ble/builtin/bind/option:-
   3713 ##   @var[in] opt_keymap
   3714 function ble/builtin/bind/option:- {
   3715   local ret; ble/string#trim "$1"; local arg=$ret
   3716 
   3717   # Note (#D1820): これまで行の途中から始まるコメントを除去していたが、実際に
   3718   # inputrc 色々書き込んで調べると特に無視されている訳では無い事が分かった。
   3719   # なので、行頭に # がある場合にのみ処理を中断することにする。
   3720   [[ ! $arg || $arg == '#'* ]] && return 0
   3721 
   3722   # # コメント除去 (quote されていない "空白+#" 以降はコメント)
   3723   # local q=\' ifs=$_ble_term_IFS
   3724   # local rex='^(([^\"'$q$ifs']|"([^\"]|\\.)*"|'$q'([^\'$q']|\\.)*'$q'|\\.|['$ifs']+[^#'$_ifs'])*)['$ifs']+#'
   3725   # [[ $arg =~ $rex ]] && arg=${BASH_REMATCH[1]}
   3726 
   3727   local ifs=$_ble_term_IFS
   3728   if [[ $arg == 'set'["$ifs"]* ]]; then
   3729     if [[ $_ble_decode_bind_state != none ]]; then
   3730       local variable= value= rex=$'^set[ \t]+([^ \t]+)[ \t]+([^ \t].*)$'
   3731       [[ $arg =~ $rex ]] && variable=${BASH_REMATCH[1]} value=${BASH_REMATCH[2]}
   3732 
   3733       case $variable in
   3734       (keymap)
   3735         ble/builtin/bind/set-keymap "$value"
   3736         return 0 ;;
   3737       (editing-mode)
   3738         _ble_builtin_bind_keymap= ;;
   3739       esac
   3740 
   3741       ble/function#try ble/builtin/bind/set:"$variable" "$value" && return 0
   3742       builtin bind "$arg"
   3743     fi
   3744     return 0
   3745   fi
   3746 
   3747   local keys value kmap
   3748   if ! ble/builtin/bind/.initialize-keys-and-value "$arg"; then
   3749     local q=\' Q="''\'"
   3750     ble/util/print "ble.sh (bind): unrecognized readline command '${arg//$q/$Q}'." >&2
   3751     flags=e$flags
   3752     return 1
   3753   elif ! ble/builtin/bind/.initialize-kmap "$opt_keymap"; then
   3754     ble/util/print "ble.sh (bind): sorry, failed to initialize keymap:'$opt_keymap'." >&2
   3755     flags=e$flags
   3756     return 1
   3757   fi
   3758 
   3759   if [[ $value == \"* ]]; then
   3760     # keyboard macro
   3761     local bind_keys="${keys[*]}"
   3762     value=${value#\"} value=${value%\"}
   3763     local ret chars; ble/util/keyseq2chars "$value"; chars=("${ret[@]}")
   3764     local command="ble/widget/.MACRO ${chars[*]}"
   3765     ble/decode/cmap/decode-chars "${chars[@]}"
   3766     [[ ${keys[*]} != "$bind_keys" ]] &&
   3767       ble-decode-key/bind "$kmap" "$bind_keys" "$command"
   3768   elif [[ $value ]]; then
   3769     local ret; ble/builtin/bind/rlfunc2widget "$kmap" "$value"; local ext=$?
   3770     if ((ext==0)); then
   3771       local command=$ret
   3772       ble-decode-key/bind "$kmap" "${keys[*]}" "$command"
   3773       return 0
   3774     elif ((ext==2)); then
   3775       return 0
   3776     else
   3777       flags=e$flags
   3778       return 1
   3779     fi
   3780   else
   3781     ble/util/print "ble.sh (bind): readline function name is not specified ($arg)." >&2
   3782     return 1
   3783   fi
   3784 }
   3785 function ble/builtin/bind/.process {
   3786   flags=
   3787   local IFS=$_ble_term_IFS
   3788   local opt_literal= opt_keymap=$_ble_builtin_bind_keymap opt_print=
   3789   local -a opt_queries=()
   3790   while (($#)); do
   3791     local arg=$1; shift
   3792     if [[ ! $opt_literal ]]; then
   3793       case $arg in
   3794       (--) opt_literal=1
   3795            continue ;;
   3796       (--help)
   3797         if ((_ble_bash<40400)); then
   3798           ble/util/print "ble.sh (bind): unrecognized option $arg" >&2
   3799           flags=e$flags
   3800         else
   3801           # Note: Bash-4.4, 5.0 のバグで unwind_frame が壊れているので
   3802           #   サブシェルで評価 #D0918
   3803           #   https://lists.gnu.org/archive/html/bug-bash/2019-02/msg00033.html
   3804           [[ $_ble_decode_bind_state != none ]] &&
   3805             (builtin bind --help)
   3806           flags=h$flags
   3807         fi
   3808         continue ;;
   3809       (--*)
   3810         ble/util/print "ble.sh (bind): unrecognized option $arg" >&2
   3811         flags=e$flags
   3812         continue ;;
   3813       (-*)
   3814         local i n=${#arg} c
   3815         for ((i=1;i<n;i++)); do
   3816           c=${arg:i:1}
   3817           case $c in
   3818           ([lpPsSvVX])
   3819             opt_print=$opt_print$c ;;
   3820           ([mqurfx])
   3821             if ((!$#)); then
   3822               ble/util/print "ble.sh (bind): missing option argument for -$c" >&2
   3823               flags=e$flags
   3824             else
   3825               local optarg=$1; shift
   3826               case $c in
   3827               (m) ble/builtin/bind/option:m "$optarg" ;;
   3828               (x) ble/builtin/bind/option:x "$optarg" ;;
   3829               (r) ble/builtin/bind/option:r "$optarg" ;;
   3830               (u) ble/builtin/bind/option:u "$optarg" ;;
   3831               (q) ble/array#push opt_queries "$optarg" ;;
   3832               (f) ble/decode/read-inputrc "$optarg" ;;
   3833               (*)
   3834                 ble/util/print "ble.sh (bind): unsupported option -$c $optarg" >&2
   3835                 flags=e$flags ;;
   3836               esac
   3837             fi ;;
   3838           (*)
   3839             ble/util/print "ble.sh (bind): unrecognized option -$c" >&2
   3840             flags=e$flags ;;
   3841           esac
   3842         done
   3843         continue ;;
   3844       esac
   3845     fi
   3846 
   3847     ble/builtin/bind/option:- "$arg"
   3848     opt_literal=1
   3849   done
   3850 
   3851   if [[ $_ble_decode_bind_state != none ]]; then
   3852     if [[ $opt_print == *[pPsSX]* ]] || ((${#opt_queries[@]})); then
   3853       # Note: サブシェル内でバインディングを復元してから出力
   3854       ( ble/decode/bind/unbind
   3855         [[ -s "$_ble_base_run/$$.bind.save" ]] &&
   3856           source "$_ble_base_run/$$.bind.save"
   3857         [[ $opt_print ]] &&
   3858           builtin bind ${opt_keymap:+-m $opt_keymap} -$opt_print
   3859         declare rlfunc
   3860         for rlfunc in "${opt_queries[@]}"; do
   3861           builtin bind ${opt_keymap:+-m $opt_keymap} -q "$rlfunc"
   3862         done )
   3863     elif [[ $opt_print ]]; then
   3864       builtin bind ${opt_keymap:+-m $opt_keymap} -$opt_print
   3865     fi
   3866   fi
   3867 
   3868   return 0
   3869 }
   3870 # inputrc の読み込み
   3871 _ble_builtin_bind_inputrc_done=
   3872 function ble/builtin/bind/initialize-inputrc {
   3873   [[ $_ble_builtin_bind_inputrc_done ]] && return 0
   3874   _ble_builtin_bind_inputrc_done=1
   3875 
   3876   if [[ $1 == all ]]; then
   3877     local sys_inputrc=/etc/inputrc
   3878     [[ -e $sys_inputrc ]] && ble/decode/read-inputrc "$sys_inputrc"
   3879   fi
   3880   local inputrc=${INPUTRC:-$HOME/.inputrc}
   3881   [[ -e $inputrc ]] && ble/decode/read-inputrc "$inputrc"
   3882 }
   3883 
   3884 # user 設定の読み込み
   3885 _ble_builtin_bind_user_settings_loaded=
   3886 function ble/builtin/bind/read-user-settings/.collect {
   3887   local map
   3888   for map in vi-insert vi-command emacs; do
   3889     local cache=$_ble_base_cache/decode.readline.$_ble_bash.$map.txt
   3890     if ! [[ -s $cache && $cache -nt $_ble_base/ble.sh ]]; then
   3891       INPUTRC=/dev/null "$BASH" --noprofile --norc -i -c "builtin bind -m $map -p" |
   3892         LC_ALL= LC_CTYPE=C ble/bin/sed '/^#/d;s/"\\M-/"\\e/' >| "$cache.part" &&
   3893         ble/bin/mv "$cache.part" "$cache" || continue
   3894     fi
   3895     local cache_content
   3896     ble/util/readfile cache_content "$cache"
   3897 
   3898     ble/util/print __CLEAR__
   3899     ble/util/print KEYMAP="$map"
   3900     ble/util/print __BIND0__
   3901     ble/util/print "${cache_content%$_ble_term_nl}"
   3902     if ((_ble_bash>=40300)); then
   3903       ble/util/print __BINDX__
   3904       builtin bind -m "$map" -X
   3905     fi
   3906     ble/util/print __BINDS__
   3907     builtin bind -m "$map" -s
   3908     ble/util/print __BINDP__
   3909     builtin bind -m "$map" -p
   3910     ble/util/print __PRINT__
   3911   done
   3912 }
   3913 function ble/builtin/bind/read-user-settings/.reconstruct {
   3914   local collect q=\'
   3915   ble/util/assign collect ble/builtin/bind/read-user-settings/.collect
   3916   <<< "$collect" LC_ALL= LC_CTYPE=C ble/bin/awk -v q="$q" -v _ble_bash="$_ble_bash" '
   3917     function keymap_register(key, val, type) {
   3918       if (!haskey[key]) {
   3919         keys[nkey++] = key;
   3920         haskey[key] = 1;
   3921       }
   3922       keymap[key] = val;
   3923       keymap_type[key] = type;
   3924     }
   3925     function keymap_clear(_, i, key) {
   3926       for(i = 0; i < nkey; i++) {
   3927         key = keys[i];
   3928         delete keymap[key];
   3929         delete keymap_type[key];
   3930         delete keymap0[key];
   3931         haskey[key] = 0;
   3932       }
   3933       nkey = 0;
   3934     }
   3935     function keymap_print(_, i, key, type, value, text, line) {
   3936       for (i = 0; i < nkey; i++) {
   3937         key = keys[i];
   3938         type = keymap_type[key];
   3939         value = keymap[key];
   3940         if (type == "" && value == keymap0[key]) continue;
   3941 
   3942         text = key ": " value;
   3943         gsub(/'$q'/, q "\\" q q, text);
   3944 
   3945         line = "bind";
   3946         if (KEYMAP != "") line = line " -m " KEYMAP;
   3947         if (type == "x") line = line " -x";
   3948         line = line " " q text q;
   3949         print line;
   3950       }
   3951     }
   3952 
   3953     /^__BIND0__$/ { mode = 0; next; }
   3954     /^__BINDX__$/ { mode = 1; next; }
   3955     /^__BINDS__$/ { mode = 2; next; }
   3956     /^__BINDP__$/ { mode = 3; next; }
   3957     /^__CLEAR__$/ { keymap_clear(); next; }
   3958     /^__PRINT__$/ { keymap_print(); next; }
   3959     sub(/^KEYMAP=/, "") { KEYMAP = $0; }
   3960 
   3961     /ble-decode\/.hook / { next; }
   3962 
   3963     function workaround_bashbug(keyseq, _, rex, out, unit) {
   3964       out = "";
   3965       while (keyseq != "") {
   3966         if (mode == 0 || mode == 3) {
   3967           match(keyseq, /^\\C-\\(\\"$)?|^\\M-|^\\.|^./);
   3968         } else {
   3969 #%        # bind -X, bind -s には問題はない
   3970           match(keyseq, /^\\[CM]-|^\\.|^./);
   3971         }
   3972         unit = substr(keyseq, 1, RLENGTH);
   3973         keyseq = substr(keyseq, 1 + RLENGTH);
   3974 
   3975         if (unit == "\\C-\\") {
   3976 #%        # Bash 3.0--5.0 Bug https://lists.gnu.org/archive/html/bug-bash/2020-01/msg00037.html
   3977           unit = unit "\\";
   3978         } else if (unit == "\\M-") {
   3979 #%        # Bash 3.1 以下では ESC は \M- と出力される
   3980           unit = "\\e";
   3981         }
   3982         out = out unit;
   3983       }
   3984       return out;
   3985     }
   3986 
   3987     match($0, /^"(\\.|[^"])+": /) {
   3988       key = substr($0, 1, RLENGTH - 2);
   3989       val = substr($0, 1 + RLENGTH);
   3990       if (_ble_bash < 50100)
   3991         key = workaround_bashbug(key);
   3992       if (mode) {
   3993         type = mode == 1 ? "x" : mode == 2 ? "s" : "";
   3994         keymap_register(key, val, type);
   3995       } else {
   3996         keymap0[key] = val;
   3997       }
   3998     }
   3999   ' 2>/dev/null # suppress LC_ALL error messages
   4000 }
   4001 
   4002 ## @fn ble/builtin/bind/read-user-settings/.cache-enabled
   4003 ##   @var[in] delay_prefix
   4004 function ble/builtin/bind/read-user-settings/.cache-enabled {
   4005   local keymap use_cache=1
   4006   for keymap in emacs vi_imap vi_nmap; do
   4007     ble/decode/keymap#registered "$keymap" && return 1
   4008     [[ -s $delay_prefix.$keymap ]] && return 1
   4009   done
   4010   return 0
   4011 }
   4012 ## @fn ble/builtin/bind/read-user-settings/.cache-alive
   4013 ##   @var[in] settings
   4014 ##   @var[in] cache_prefix
   4015 function ble/builtin/bind/read-user-settings/.cache-alive {
   4016   [[ -e $cache_prefix.settings ]] || return 1
   4017   [[ $cache_prefix.settings -nt $_ble_base/lib/init-cmap.sh  ]] || return 1
   4018   local keymap
   4019   for keymap in emacs vi_imap vi_nmap; do
   4020     [[ $cache_prefix.settings -nt $_ble_base/core-decode.$cache-rlfunc.txt ]] || return 1
   4021     [[ -e $cache_prefix.$keymap ]] || return 1
   4022   done
   4023   local content
   4024   ble/util/readfile content "$cache_prefix.settings"
   4025   [[ ${content%$'\n'} == "$settings" ]]
   4026 }
   4027 ## @fn ble/builtin/bind/read-user-settings/.cache-save
   4028 ##   @var[in] delay_prefix
   4029 ##   @var[in] cache_prefix
   4030 function ble/builtin/bind/read-user-settings/.cache-save {
   4031   local keymap content fail=
   4032   for keymap in emacs vi_imap vi_nmap; do
   4033     if [[ -s $delay_prefix.$keymap ]]; then
   4034       ble/util/copyfile "$delay_prefix.$keymap" "$cache_prefix.$keymap"
   4035     else
   4036       : >| "$cache_prefix.$keymap"
   4037     fi || fail=1
   4038   done
   4039   [[ $fail ]] && return 1
   4040   ble/util/print "$settings" >| "$cache_prefix.settings"
   4041 }
   4042 ## @fn ble/builtin/bind/read-user-settings/.cache-load
   4043 ##   @var[in] delay_prefix
   4044 ##   @var[in] cache_prefix
   4045 function ble/builtin/bind/read-user-settings/.cache-load {
   4046   local keymap
   4047   for keymap in emacs vi_imap vi_nmap; do
   4048     ble/util/copyfile "$cache_prefix.$keymap" "$delay_prefix.$keymap"
   4049   done
   4050 }
   4051 
   4052 function ble/builtin/bind/read-user-settings {
   4053   if [[ $_ble_decode_bind_state == none ]]; then
   4054     [[ $_ble_builtin_bind_user_settings_loaded ]] && return 0
   4055     _ble_builtin_bind_user_settings_loaded=1
   4056     builtin bind # inputrc を読ませる
   4057     local settings
   4058     ble/util/assign settings ble/builtin/bind/read-user-settings/.reconstruct
   4059     [[ $settings ]] || return 0
   4060 
   4061     local cache_prefix=$_ble_base_cache/decode.inputrc.$_ble_decode_kbd_ver.$TERM
   4062     local delay_prefix=$_ble_base_run/$$.bind.delay
   4063     if ble/builtin/bind/read-user-settings/.cache-enabled; then
   4064       if ble/builtin/bind/read-user-settings/.cache-alive; then
   4065         ble/builtin/bind/read-user-settings/.cache-load
   4066       else
   4067         builtin eval -- "$settings"
   4068         ble/builtin/bind/read-user-settings/.cache-save
   4069       fi
   4070     else
   4071       builtin eval -- "$settings"
   4072     fi
   4073   fi
   4074 }
   4075 
   4076 function ble/builtin/bind {
   4077   local set shopt; ble/base/.adjust-bash-options set shopt
   4078 
   4079   [[ ! $_ble_attached || $_ble_edit_exec_inside_userspace ]] &&
   4080     ble/base/adjust-BASH_REMATCH
   4081 
   4082   ble/decode/initialize
   4083   local flags= ext=0
   4084   ble/builtin/bind/.process "$@"
   4085   if [[ $_ble_decode_bind_state == none ]]; then
   4086     builtin bind "$@"; ext=$?
   4087   elif [[ $flags == *[eh]* ]]; then
   4088     [[ $flags == *e* ]] &&
   4089       builtin bind --usage 2>&1 1>/dev/null | ble/bin/grep ^bind >&2
   4090     ext=2
   4091   fi
   4092 
   4093   [[ ! $_ble_attached || $_ble_edit_exec_inside_userspace ]] &&
   4094     ble/base/restore-BASH_REMATCH
   4095   ble/base/.restore-bash-options set shopt
   4096   return "$ext"
   4097 }
   4098 function bind { ble/builtin/bind "$@"; }
   4099 
   4100 #------------------------------------------------------------------------------
   4101 # ble/decode/initialize, attach, detach                          @decode.attach
   4102 
   4103 function ble/decode/initialize/.has-broken-suse-inputrc {
   4104   # 1. Check if this is openSUSE and has /etc/input.keys.
   4105   local content=
   4106   [[ -s /etc/inputrc.keys && -r /etc/os-release ]] &&
   4107     ble/util/readfile content /etc/os-release &&
   4108     [[ $content == *'openSUSE'* ]] || return 1
   4109 
   4110   # Note #1926: Even after the fix
   4111   # https://github.com/openSUSE/aaa_base/pull/84, "inputrc.keys" causes
   4112   # problems through extra bindings of the [home]/[end] escape sequences to
   4113   # [prior], [Enter] to "accept-line", etc.  Thus, we comment out the following
   4114   # part of codes and always return 0 when there is "/etc/inputrc.keys".
   4115 
   4116   # 2. Check if the file "inputrc.keys" has the bug.
   4117   # ((_ble_bash<50000)) || return 1 # Bash 5.0+ are not suffered
   4118   # ble/util/readfile content /etc/inputrc.keys &&
   4119   #   [[ $content == *'"\M-[2~":'* ]] || return 1
   4120 
   4121   return 0
   4122 }
   4123 
   4124 _ble_decode_initialized=
   4125 _ble_decode_initialize_inputrc=auto
   4126 function ble/decode/initialize {
   4127   [[ $_ble_decode_initialized ]] && return 0
   4128   _ble_decode_initialized=1
   4129   ble/decode/cmap/initialize
   4130 
   4131   if [[ $_ble_decode_initialize_inputrc == auto ]]; then
   4132     if ble/decode/initialize/.has-broken-suse-inputrc; then
   4133       # Note: #D1662 WA openSUSE (aaa_base < 202102) has broken /etc/inputrc
   4134       [[ ${INPUTRC-} == /etc/inputrc || ${INPUTRC-} == /etc/inputrc.keys ]] &&
   4135         local INPUTRC=~/.inputrc
   4136       _ble_decode_initialize_inputrc=user
   4137     else
   4138       _ble_decode_initialize_inputrc=diff
   4139     fi
   4140   fi
   4141   case $_ble_decode_initialize_inputrc in
   4142   (all)
   4143     ble/builtin/bind/initialize-inputrc all ;;
   4144   (user)
   4145     ble/builtin/bind/initialize-inputrc ;;
   4146   (diff)
   4147     ble/builtin/bind/read-user-settings ;;
   4148   esac
   4149 }
   4150 
   4151 function ble/decode/reset-default-keymap {
   4152   # 現在の ble-decode/keymap の設定
   4153   local old_base_keymap=${_ble_decode_keymap_stack[0]:-$_ble_decode_keymap}
   4154   ble-decode/INITIALIZE_DEFMAP -v _ble_decode_keymap # 0ms
   4155   _ble_decode_keymap_stack=()
   4156   if [[ $_ble_decode_keymap != "$old_base_keymap" ]]; then
   4157     [[ $old_base_keymap ]] &&
   4158       _ble_decode_keymap=$old_base_keymap ble-decode/widget/.invoke-hook "$_ble_decode_KCODE_DETACH"
   4159     ble-decode/widget/.invoke-hook "$_ble_decode_KCODE_ATTACH" # 7ms for vi-mode
   4160 
   4161     # update cursor
   4162     local cursor; ble/decode/keymap#get-cursor "$_ble_decode_keymap"
   4163     [[ $cursor ]] && ble/term/cursor-state/set-internal "$((cursor))"
   4164   fi
   4165 }
   4166 
   4167 ## @fn ble/decode/attach
   4168 ##   @var[in] _ble_decode_keymap
   4169 ##     この関数を呼び出す前に ble/decode/reset-default-keymap を用いて
   4170 ##     _ble_decode_keymap が使用可能な状態になっている必要がある。
   4171 function ble/decode/attach {
   4172   # 失敗すると悲惨なことになるのでチェック
   4173   if ble/decode/keymap#is-empty "$_ble_decode_keymap"; then
   4174     ble/util/print "ble.sh: The keymap '$_ble_decode_keymap' is empty." >&2
   4175     return 1
   4176   fi
   4177 
   4178   [[ $_ble_decode_bind_state != none ]] && return 0
   4179   ble/util/save-editing-mode _ble_decode_bind_state
   4180   [[ $_ble_decode_bind_state == none ]] && return 1
   4181 
   4182   # bind/unbind 中に C-c で中断されると大変なので先に stty を設定する必要がある
   4183   ble/term/initialize # 3ms
   4184 
   4185   # 既定の keymap に戻す
   4186   ble/util/reset-keymap-of-editing-mode
   4187 
   4188   # 元のキー割り当ての保存・unbind
   4189   builtin eval -- "$(ble/decode/bind/.generate-source-to-unbind-default)" # 21ms
   4190 
   4191   # ble.sh bind の設置
   4192   ble/decode/bind/bind # 20ms
   4193 
   4194   case $TERM in
   4195   (linux)
   4196     # Note #D1213: linux コンソール (kernel 5.0.0) は "\e[>"
   4197     #  でエスケープシーケンスを閉じてしまう。5.4.8 は大丈夫。
   4198     _ble_term_TERM=linux:- ;;
   4199   (st|st-*)
   4200     # st の unknown csi sequence メッセージに対して文句を言う人がいた。
   4201     # st は TERM で判定できるので DA2 はスキップできる。
   4202     _ble_term_TERM=st:- ;;
   4203   (*)
   4204     ble/util/buffer $'\e[>c' # DA2 要求 (ble-decode-char/csi/.decode で受信)
   4205   esac
   4206   return 0
   4207 }
   4208 
   4209 function ble/decode/detach {
   4210   [[ $_ble_decode_bind_state != none ]] || return 1
   4211 
   4212   local current_editing_mode=
   4213   ble/util/save-editing-mode current_editing_mode
   4214   [[ $_ble_decode_bind_state == "$current_editing_mode" ]] || ble/util/restore-editing-mode _ble_decode_bind_state
   4215 
   4216   ble/term/finalize
   4217 
   4218   # ble.sh bind の削除
   4219   ble/decode/bind/unbind
   4220 
   4221   # 元のキー割り当ての復元
   4222   if [[ -s "$_ble_base_run/$$.bind.save" ]]; then
   4223     source "$_ble_base_run/$$.bind.save"
   4224     : >| "$_ble_base_run/$$.bind.save"
   4225   fi
   4226 
   4227   [[ $_ble_decode_bind_state == "$current_editing_mode" ]] || ble/util/restore-editing-mode current_editing_mode
   4228 
   4229   _ble_decode_bind_state=none
   4230 }
   4231 
   4232 #------------------------------------------------------------------------------
   4233 # **** encoding = UTF-8 ****
   4234 
   4235 function ble/encoding:UTF-8/generate-binder { :; }
   4236 
   4237 # 以下は lib/init-bind.sh の中にある物と等価なので殊更に設定しなくて良い。
   4238 
   4239 # ## @fn ble/encoding:UTF-8/generate-binder
   4240 # ##   lib/init-bind.sh の esc1B==3 の設定用。
   4241 # ##   lib/init-bind.sh の中から呼び出される。
   4242 # function ble/encoding:UTF-8/generate-binder {
   4243 #   ble/init:bind/bind-s '"\C-@":"\xC0\x80"'
   4244 #   ble/init:bind/bind-s '"\e":"\xDF\xBF"' # isolated ESC (U+07FF)
   4245 #   local i ret
   4246 #   for i in {0..255}; do
   4247 #     ble/decode/c2dqs "$i"
   4248 #     ble/init:bind/bind-s "\"\e$ret\": \"\xC0\x9B$ret\""
   4249 #   done
   4250 # }
   4251 
   4252 _ble_encoding_utf8_decode_mode=0
   4253 _ble_encoding_utf8_decode_code=0
   4254 _ble_encoding_utf8_decode_table=(
   4255   'M&&E,A[i++]='{0..127}
   4256   'C=C<<6|'{0..63}',--M==0&&(A[i++]=C)'
   4257   'M&&E,C='{0..31}',M=1'
   4258   'M&&E,C='{0..15}',M=2'
   4259   'M&&E,C='{0..7}',M=3'
   4260   'M&&E,C='{0..3}',M=4'
   4261   'M&&E,C='{0..1}',M=5'
   4262   'M&&E,A[i++]=_ble_decode_Erro|'{254,255}
   4263 )
   4264 function ble/encoding:UTF-8/clear {
   4265   _ble_encoding_utf8_decode_mode=0
   4266   _ble_encoding_utf8_decode_code=0
   4267 }
   4268 function ble/encoding:UTF-8/is-intermediate {
   4269   ((_ble_encoding_utf8_decode_mode))
   4270 }
   4271 function ble/encoding:UTF-8/decode {
   4272   local C=$_ble_encoding_utf8_decode_code
   4273   local M=$_ble_encoding_utf8_decode_mode
   4274   local E='M=0,A[i++]=_ble_decode_Erro|C'
   4275   local -a A=()
   4276   local i=0 b
   4277   for b; do
   4278     ((_ble_encoding_utf8_decode_table[b&255]))
   4279   done
   4280   _ble_encoding_utf8_decode_code=$C
   4281   _ble_encoding_utf8_decode_mode=$M
   4282   ((i)) && ble-decode-char "${A[@]}"
   4283 }
   4284 
   4285 ## @fn ble/encoding:UTF-8/c2bc code
   4286 ##   @param[in]  code
   4287 ##   @var  [out] ret
   4288 function ble/encoding:UTF-8/c2bc {
   4289   local code=$1
   4290   ((ret=code<0x80?1:
   4291     (code<0x800?2:
   4292     (code<0x10000?3:
   4293     (code<0x200000?4:5)))))
   4294 }
   4295 
   4296 ## @fn ble/encoding:C/generate-binder
   4297 ##   lib/init-bind.sh の esc1B==3 の設定用。
   4298 ##   lib/init-bind.sh の中から呼び出される。
   4299 function ble/encoding:C/generate-binder {
   4300   ble/init:bind/bind-s '"\C-@":"\x9B\x80"'
   4301   ble/init:bind/bind-s '"\e":"\x9B\x8B"' # isolated ESC (U+07FF)
   4302   local i ret
   4303   for i in {0..255}; do
   4304     ble/decode/c2dqs "$i"
   4305     ble/init:bind/bind-s "\"\e$ret\": \"\x9B\x9B$ret\""
   4306   done
   4307 }
   4308 
   4309 ## @fn ble/encoding:C/decode byte
   4310 ##
   4311 ##   受け取ったバイトをそのまま文字コードと解釈する。
   4312 ##   但し、bind の都合 (bashbug の回避) により以下の変換を行う。
   4313 ##
   4314 ##   \x9B\x80 (155 128) → C-@
   4315 ##   \x9B\x8B (155 139) → isolated ESC \u07FF (2047)
   4316 ##   \x9B\x9B (155 155) → ESC
   4317 ##
   4318 ##   実際にこの組み合わせの入力が来ると誤変換されるが、
   4319 ##   この組み合わせは不正な CSI シーケンスなので、
   4320 ##   入力に混入した時の動作は元々保証外である。
   4321 ##
   4322 _ble_encoding_c_csi=
   4323 function ble/encoding:C/clear {
   4324   _ble_encoding_c_csi=
   4325 }
   4326 function ble/encoding:C/is-intermediate {
   4327   [[ $_ble_encoding_c_csi ]]
   4328 }
   4329 function ble/encoding:C/decode {
   4330   local -a A=()
   4331   local i=0 b
   4332   for b; do
   4333     if [[ $_ble_encoding_c_csi ]]; then
   4334       _ble_encoding_c_csi=
   4335       case $b in
   4336       (155) A[i++]=27 # ESC
   4337             continue ;;
   4338       (139) A[i++]=2047 # isolated ESC
   4339             continue ;;
   4340       (128) A[i++]=0 # C-@
   4341             continue ;;
   4342       esac
   4343       A[i++]=155
   4344     fi
   4345 
   4346     if ((b==155)); then
   4347       _ble_encoding_c_csi=1
   4348     else
   4349       A[i++]=$b
   4350     fi
   4351   done
   4352   ((i)) && ble-decode-char "${A[@]}"
   4353 }
   4354 
   4355 ## @fn ble/encoding:C/c2bc charcode
   4356 ##   @var[out] ret
   4357 function ble/encoding:C/c2bc {
   4358   ret=1
   4359 }