diff options
Diffstat (limited to '')
-rw-r--r-- | common/shell_line_editor.sh | 418 |
1 files changed, 224 insertions, 194 deletions
diff --git a/common/shell_line_editor.sh b/common/shell_line_editor.sh index 5e38ef4..8d6833a 100644 --- a/common/shell_line_editor.sh +++ b/common/shell_line_editor.sh @@ -4,214 +4,244 @@ LE_print_debug() { LE_debug="$1" LE_debug_msg="$2" - [ -n "$LE_debug" ] && printf "\nDBG: (%s)\n" "$LE_debug_msg" > outfile.debug + [ -n "$LE_debug" ] && printf "\nDBG: (%s)\n" "$LE_debug_msg" >outfile.debug } LE() { - # Shell Line Editor. Extremely slow and stupid code. However it - # should work on ansi/vt100/linux derived terminals on POSIX - # systems. - # Understands some emacs key bindings: CTRL-(A,B,D,E,F,H,K,L) - # plus the CTRL-W and CTRL-U normal killword and kill. - # no Meta-X key, but handling of <Left>, <Right>, <Home>, <End> - # <Suppr>. - # - # Args: - # [1]: prompt (\x sequences recognized, defaults to "") - # [2]: max input length (unlimited if < 0, (default)) - # [3]: fill character when erasing (defaults to space) - # [4]: initial value. - # [5]: whether to output debugfiles (outfile.debug and outfile.raw.debug) - # Returns: - # 0: OK - # 1: od(d) error or CTRL-C hit + # Shell Line Editor. Extremely slow and stupid code. However it + # should work on ansi/vt100/linux derived terminals on POSIX + # systems. + # Understands some emacs key bindings: CTRL-(A,B,D,E,F,H,K,L) + # plus the CTRL-W and CTRL-U normal killword and kill. + # no Meta-X key, but handling of <Left>, <Right>, <Home>, <End> + # <Suppr>. + # + # Args: + # [1]: prompt (\x sequences recognized, defaults to "") + # [2]: max input length (unlimited if < 0, (default)) + # [3]: fill character when erasing (defaults to space) + # [4]: initial value. + # [5]: whether to output debugfiles (outfile.debug and outfile.raw.debug) + # Returns: + # 0: OK + # 1: od(d) error or CTRL-C hit - LE_prompt="$1" - LE_max=${2--1} - LE_fill=${3-" "} - LE_debug="$5" + LE_prompt="$1" + LE_max=${2--1} + LE_fill=${3-" "} + LE_debug="$5" - LE_backward() { - LE_substract="$1" - while [ -n "$LE_substract" ]; do - printf '\b%s' "$2" - LE_substract=${LE_substract%?} - done - } + LE_backward() { + LE_substract="$1" + while [ -n "$LE_substract" ]; do + printf '\b%s' "$2" + LE_substract=${LE_substract%?} + done + } - LE_fill() { - LE_substract="$1" - while [ -n "$LE_substract" ]; do - printf '%s' "$LE_fill" - LE_substract=${LE_substract%?} - done - } + LE_fill() { + LE_substract="$1" + while [ -n "$LE_substract" ]; do + printf '%s' "$LE_fill" + LE_substract=${LE_substract%?} + done + } - # Used but not right now - # shellcheck disable=2016 - LE_restore='stty "$LE_tty" + # Used but not right now + # shellcheck disable=2016 + LE_restore='stty "$LE_tty" LC_COLLATE='${LC_COLLATE-"; unset LC_COLLATE"} - # LE_tty is used in the restore above - # shellcheck disable=2034 - LE_ret=1 LE_tty=$(stty -g) LC_COLLATE=C + # LE_tty is used in the restore above + # shellcheck disable=2034 + LE_ret=1 LE_tty=$(stty -g) LC_COLLATE=C - # text on the right of the cursor - LE_left=$4 - # text on the left of the cursor - LE_right='' + # text on the right of the cursor + LE_left=$4 + # text on the left of the cursor + LE_right='' - # Tell the terminal to show us every char inputted - stty -icanon -echo -isig min 3 time 1 -istrip - printf '%b%s' "$LE_prompt" "$LE_left" + # Tell the terminal to show us every char inputted + stty -icanon -echo -isig min 3 time 1 -istrip + printf '%b%s' "$LE_prompt" "$LE_left" - # clear the output - [ -n "$LE_debug" ] && printf "" > outfile.debug - [ -n "$LE_debug" ] && printf "" > outfile.raw.debug + # clear the output + [ -n "$LE_debug" ] && printf "" >outfile.debug + [ -n "$LE_debug" ] && printf "" >outfile.raw.debug - # The value needs to be split for it to work (and it's either way just numbers) - # shellcheck disable=2046 - while set -- $(dd bs=3 count=1 2> /dev/null | od -vAn -to1); do - while [ "$#" -gt 0 ]; do - [ -n "$LE_debug" ] && printf "%b" "\0$1" >> outfile.debug - [ -n "$LE_debug" ] && printf "%s " "$1" >> outfile.raw.debug - LE_current_key=$1 - shift - - # 033 is ^[ (`printf "\\$1\n" | cat -v`) - if [ "$LE_current_key" = 033 ]; then - case "$1$2$3" in - # [ C | O C -> ^F forward - 133103*|117103*) shift 2; LE_current_key=006;; - # [ D | O D -> ^B backward - 133104*|117104*) shift 2; LE_current_key=002;; - # [ H | O H -> ^A beginning of line - 133110*|117110*) shift 2; LE_current_key=001;; - # [ P | O P -> ^D del char - 133120*|117120*) shift 2; LE_current_key=004;; - # [ F | O F -> ^E end of line - 133106*|117106*) shift 2; LE_current_key=005;; - # [ 1 ~ -> ^A beginning of line - 133061176) shift 3; LE_current_key=001;; - # [ 4 ~ -> ^E end of line - 133064176) shift 3; LE_current_key=005;; - # [ 3 ~ -> ^D del char - 133063176) shift 3; LE_current_key=004;; - # [ | O - 133*|117*) + # The value needs to be split for it to work (and it's either way just numbers) + # shellcheck disable=2046 + while set -- $(dd bs=3 count=1 2>/dev/null | od -vAn -to1); do + while [ "$#" -gt 0 ]; do + [ -n "$LE_debug" ] && printf "%b" "\0$1" >>outfile.debug + [ -n "$LE_debug" ] && printf "%s " "$1" >>outfile.raw.debug + LE_current_key=$1 shift - # Is $1 in ge 0 AND le 9 OR eq ';'? - # These are control sequences for things like colors; Ignore them - while [ "0$1" -ge 060 ] && [ "0$1" -le 071 ] || - [ "0$1" -eq 073 ]; do - shift - done;; - esac - fi - case "$LE_current_key" in - 001) # ^A beginning of line - LE_backward "$LE_left" - LE_right="$LE_left$LE_right" - LE_left=;; - 002) # ^B backward - if [ "$LE_left" = "" ]; then - # bell - printf '\a' - LE_print_debug "$LE_debug" "backward with empty left" - else - printf '\b' - LE_tmp="${LE_left%?}" - LE_right="${LE_left#"$LE_tmp"}$LE_right" - LE_left="$LE_tmp" - fi;; - 003) # CTRL-C - break 2;; - 004) # ^D del char - if [ "$LE_right" = "" ]; then - # bell (tell the user that the line is empty) - printf '\a' - LE_print_debug "$LE_debug" "delete with empty right" - else - LE_right="${LE_right#?}" - printf '%s\b' "$LE_right$LE_fill" - LE_backward "$LE_right" - fi;; - 012|015) # NL or CR - LE_ret=0 - break 2;; - 005) # ^E end of line - printf '%s' "$LE_right" - LE_left="$LE_left$LE_right" - LE_right=;; - 006) # ^F forward - if [ "$LE_right" = "" ]; then - # bell (tell the user that the line is empty) - printf '\a' - LE_print_debug "$LE_debug" "forward with empty right" - else - LE_tmp="${LE_right#?}" - LE_left="$LE_left${LE_right%"$LE_tmp"}" - printf %s "${LE_right%"$LE_tmp"}" - LE_right="$LE_tmp" - fi;; - 010|177) # backspace or del - if [ "$LE_left" = "" ]; then - # bell - printf '\a' - LE_print_debug "$LE_debug" "backspace with empty left" - else - printf '\b%s\b' "$LE_right$LE_fill" - LE_backward "$LE_right" - LE_left="${LE_left%?}" - fi;; - 013) # ^K kill to end of line - LE_fill "$LE_right" - LE_backward "$LE_right" - LE_right="" - ;; - 014) # ^L redraw - printf '\r%b%s' "$LE_prompt" "$LE_left$LE_right" - LE_backward "$LE_right";; - 025) # ^U kill line - LE_backward "$LE_left" - LE_fill "$LE_left$LE_right" - LE_backward "$LE_left$LE_right" - LE_left="" - LE_right="" - ;; - 027) # ^W kill word - if [ "$LE_left" = "" ]; then - # bell - printf '\a' - else - LE_tmp="${LE_left% *}" - LE_backward "${LE_left#"$LE_tmp"}" - LE_fill "${LE_left#"$LE_tmp"}" - LE_backward "${LE_left#"$LE_tmp"}" - LE_left="$LE_tmp" - fi;; - # Print the received key, as it did not match a special key - [02][4-7]?|[13]??) # 040 -> 177, 240 -> 377 - # was assuming iso8859-x at the time - if [ "$LE_max" -gt 0 ] && LE_tmp="$LE_left$LE_right" \ - && [ "${#LE_tmp}" -eq "$LE_max" ]; then - # bell, when the user is trying to cross the line limit - printf '\a' - LE_print_debug "$LE_debug" "max output reached" - else - LE_left="$LE_left$(printf '%b' "\0$LE_current_key")" - printf '%b%s' "\0$LE_current_key" "$LE_right" - LE_backward "$LE_right" - fi;; - *) - LE_print_debug "$LE_debug" "key not recognized: $(printf "%b" "\0$LE_current_key")" - printf '\a';; - esac + # 033 is ^[ (`printf "\\$1\n" | cat -v`) + if [ "$LE_current_key" = 033 ]; then + case "$1$2$3" in + # [ C | O C -> ^F forward + 133103* | 117103*) + shift 2 + LE_current_key=006 + ;; + # [ D | O D -> ^B backward + 133104* | 117104*) + shift 2 + LE_current_key=002 + ;; + # [ H | O H -> ^A beginning of line + 133110* | 117110*) + shift 2 + LE_current_key=001 + ;; + # [ P | O P -> ^D del char + 133120* | 117120*) + shift 2 + LE_current_key=004 + ;; + # [ F | O F -> ^E end of line + 133106* | 117106*) + shift 2 + LE_current_key=005 + ;; + # [ 1 ~ -> ^A beginning of line + 133061176) + shift 3 + LE_current_key=001 + ;; + # [ 4 ~ -> ^E end of line + 133064176) + shift 3 + LE_current_key=005 + ;; + # [ 3 ~ -> ^D del char + 133063176) + shift 3 + LE_current_key=004 + ;; + # [ | O + 133* | 117*) + shift + # Is $1 in ge 0 AND le 9 OR eq ';'? + # These are control sequences for things like colors; Ignore them + while [ "0$1" -ge 060 ] && [ "0$1" -le 071 ] || + [ "0$1" -eq 073 ]; do + shift + done + ;; + esac + fi + + case "$LE_current_key" in + 001) # ^A beginning of line + LE_backward "$LE_left" + LE_right="$LE_left$LE_right" + LE_left= + ;; + 002) # ^B backward + if [ "$LE_left" = "" ]; then + # bell + printf '\a' + LE_print_debug "$LE_debug" "backward with empty left" + else + printf '\b' + LE_tmp="${LE_left%?}" + LE_right="${LE_left#"$LE_tmp"}$LE_right" + LE_left="$LE_tmp" + fi ;; + 003) # CTRL-C + break 2 ;; + 004) # ^D del char + if [ "$LE_right" = "" ]; then + # bell (tell the user that the line is empty) + printf '\a' + LE_print_debug "$LE_debug" "delete with empty right" + else + LE_right="${LE_right#?}" + printf '%s\b' "$LE_right$LE_fill" + LE_backward "$LE_right" + fi ;; + 012 | 015) # NL or CR + LE_ret=0 + break 2 + ;; + 005) # ^E end of line + printf '%s' "$LE_right" + LE_left="$LE_left$LE_right" + LE_right= + ;; + 006) # ^F forward + if [ "$LE_right" = "" ]; then + # bell (tell the user that the line is empty) + printf '\a' + LE_print_debug "$LE_debug" "forward with empty right" + else + LE_tmp="${LE_right#?}" + LE_left="$LE_left${LE_right%"$LE_tmp"}" + printf %s "${LE_right%"$LE_tmp"}" + LE_right="$LE_tmp" + fi ;; + 010 | 177) # backspace or del + if [ "$LE_left" = "" ]; then + # bell + printf '\a' + LE_print_debug "$LE_debug" "backspace with empty left" + else + printf '\b%s\b' "$LE_right$LE_fill" + LE_backward "$LE_right" + LE_left="${LE_left%?}" + fi ;; + 013) # ^K kill to end of line + LE_fill "$LE_right" + LE_backward "$LE_right" + LE_right="" + ;; + 014) # ^L redraw + printf '\r%b%s' "$LE_prompt" "$LE_left$LE_right" + LE_backward "$LE_right" + ;; + 025) # ^U kill line + LE_backward "$LE_left" + LE_fill "$LE_left$LE_right" + LE_backward "$LE_left$LE_right" + LE_left="" + LE_right="" + ;; + 027) # ^W kill word + if [ "$LE_left" = "" ]; then + # bell + printf '\a' + else + LE_tmp="${LE_left% *}" + LE_backward "${LE_left#"$LE_tmp"}" + LE_fill "${LE_left#"$LE_tmp"}" + LE_backward "${LE_left#"$LE_tmp"}" + LE_left="$LE_tmp" + fi ;; + # Print the received key, as it did not match a special key + [02][4-7]? | [13]??) # 040 -> 177, 240 -> 377 + # was assuming iso8859-x at the time + if [ "$LE_max" -gt 0 ] && LE_tmp="$LE_left$LE_right" && + [ "${#LE_tmp}" -eq "$LE_max" ]; then + # bell, when the user is trying to cross the line limit + printf '\a' + LE_print_debug "$LE_debug" "max output reached" + else + LE_left="$LE_left$(printf '%b' "\0$LE_current_key")" + printf '%b%s' "\0$LE_current_key" "$LE_right" + LE_backward "$LE_right" + fi ;; + *) + LE_print_debug "$LE_debug" "key not recognized: $(printf "%b" "\0$LE_current_key")" + printf '\a' + ;; + esac + done done - done - eval "$LE_restore" - REPLY=$LE_left$LE_right - echo - return "$LE_ret" + eval "$LE_restore" + REPLY=$LE_left$LE_right + echo + return "$LE_ret" } |