about summary refs log tree commit diff stats
path: root/common/shell_line_editor.sh
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-04-02 01:12:06 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-04-02 01:12:06 +0200
commitd19f148e703f63793811af56686eccc5580d48d6 (patch)
tree0250b08e0f83b0e1edc566def2b1528704defd8f /common/shell_line_editor.sh
parentchore(version): v0.4.2 (diff)
downloadflake-templates-d19f148e703f63793811af56686eccc5580d48d6.tar.gz
flake-templates-d19f148e703f63793811af56686eccc5580d48d6.zip
feat(common/init): Add a shell script, that populates variables
The line editing can't really shell out to bash, as the default
bash on NixOS is compiled without GNU `readline` support (you need
`bashInterative` for `read -e` to work).
Diffstat (limited to '')
-rw-r--r--common/shell_line_editor.sh217
1 files changed, 217 insertions, 0 deletions
diff --git a/common/shell_line_editor.sh b/common/shell_line_editor.sh
new file mode 100644
index 0000000..5e38ef4
--- /dev/null
+++ b/common/shell_line_editor.sh
@@ -0,0 +1,217 @@
+#! /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"
+}