From 61eed33ee7a89b8db304ff33e0b7b08aad321f0d Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Wed, 8 Mar 2023 09:10:24 -0600 Subject: Initial commit --- vellum | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100755 vellum diff --git a/vellum b/vellum new file mode 100755 index 0000000..b436b6c --- /dev/null +++ b/vellum @@ -0,0 +1,170 @@ +#!/bin/sh + +# https://unix.stackexchange.com/a/464963 +readc() { # arg: + if [ -t 0 ]; then + # if stdin is a tty device, put it out of icanon, set min and + # time to sane value, but don't otherwise touch other input or + # or local settings (echo, isig, icrnl...). Take a backup of the + # previous settings beforehand. + saved_tty_settings=$(stty -g) + stty -icanon min 1 time 0 + fi + eval "$1=" + while + # read one byte, using a work around for the fact that command + # substitution strips the last character. + c=$(dd bs=1 count=1 2> /dev/null; echo .) + c=${c%.} + + # break out of the loop on empty input (eof) or if a full character + # has been accumulated in the output variable (using "wc -m" to count + # the number of characters). + [ -n "$c" ] && + eval "$1=\${$1}"'$c + [ "$(($(printf %s "${'"$1"'}" | wc -m)))" -eq 0 ]'; do + continue + done + if [ -t 0 ]; then + # restore settings saved earlier if stdin is a tty device. + stty "$saved_tty_settings" + fi +} + +setup_terminal() { + # Setup the terminal for the TUI. + # '\e[?1049h': Use alternative screen buffer. + # '\e[?7l': Disable line wrapping. + # '\e[?25l': Hide the cursor. + # '\e[2J': Clear the screen. + # '\e[1;Nr': Limit scrolling to scrolling area. + # Also sets cursor to (0,0). + printf '\e[?1049h\e[?7l\e[2J\e[1;%sr' "$((LINES - 1))" + + # Hide echoing of user input + stty -echo +} + +reset_terminal() { + # Reset the terminal to a useable state (undo all changes). + # '\e[?7h': Re-enable line wrapping. + # '\e[?25h': Unhide the cursor. + # '\e[2J': Clear the terminal. + # '\e[;r': Set the scroll region to its default value. + # Also sets cursor to (0,0). + # '\e[?1049l: Restore main screen buffer. + printf '\e[?7h\e[?25h\e[2J\e[;r\e[?1049l' + + # Show user input. + stty echo +} + +printlines() { # printlines file min max + if test -n "$DEBUG"; then + echo --- >&2 + else + clear + fi + sed -n "$2,$3p" "$1" + notify_flush +} + +notify() { # notify STRING + notification="$notification$1" +} + +notify_flush() { # notify_flush + winch + printf '\e[s\e[%s;%sH' 0 "$c" + printf '%s' "$notification" + printf '\e[u' + notification= +} + +scroll() { # scroll nlines -- MUTATES GLOBAL STATE + case "${1:-0}" in + \+*|\-*) n=$((n+$1)) ;; + 0) ;; + *) n=$1 ;; + esac + + if test $n -gt $((max - l)) + then + n=$((max - l)) + notify '_' + fi + + if test $n -lt $min + then + notify '^' + n=$min + fi + + m=$((n+l)) + if $m -ge $max + then + m=$max + fi + # echo "min:$min max:$max n:$n m:$m l:$l" >&2 +} + +ctty() { # ctty + ## Get the controlling TTY interface. + tty <&2 || # only works if not redirecting stderr + echo /proc/self/fd/1 # probably only works on linux +} + +winch() { + stty size > "$wf" + read -r l c < "$wf" + # echo l:$l c:$c >&2 + l=$((l-2)) # last line +} + +cleanup() { + rm "$bf" "$wf" + reset_terminal + exit +} + +vellum() { # vellum [-n NUM] < INPUT + # Set up buffer + bf="$(mktemp)" # buffer + cat "${@:--}" > "$bf" + # Redirect input to the controlling terminal + ## XXX: This assumes that you haven't redirected stderr. + exec < "$(ctty)" + max=$(wc -l < "$bf") + min=1 # min = minimum accessible line num. + n=$min # n = num. first line to show + + # Set up terminal + setup_terminal + wf="$(mktemp)" # winch file + winch; trap winch WINCH + m=$((n+l)) # m = num. last line to show + + trap cleanup EXIT INT QUIT + + # Main loop + while :; do + scroll + printlines "$bf" $n $m + + readc key >/dev/null 2>&1 + case "$key" in + ' ') scroll +$l ;; + '') scroll -$l ;; + j) scroll +1 ;; + k) scroll -1 ;; + g) scroll $min ;; + G) scroll $max ;; + q) break ;; + esac + done + break + echo +} + +test -n "$DEBUG" && { set -x; exec 2> /tmp/vellum.log; } +test -z "$SOURCE" && vellum "$@" -- cgit 1.4.1-21-gabe81