blob: 8d6833ade04e10f78c5113f568b7161a7b635e1b (
plain) (
tree)
|
|
#! /usr/bin/env sh
# Taken in verbatim from: https://unix.stackexchange.com/a/113450, and somewhat changed
LE_print_debug() {
LE_debug="$1"
LE_debug_msg="$2"
[ -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
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_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"
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
# 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"
# 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*)
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
eval "$LE_restore"
REPLY=$LE_left$LE_right
echo
return "$LE_ret"
}
|