summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xvellum170
1 files changed, 170 insertions, 0 deletions
diff --git a/vellum b/vellum new file mode 100755 index 0000000..b436b6c --- /dev/null +++ b/vellum
@@ -0,0 +1,170 @@
1#!/bin/sh
2
3# https://unix.stackexchange.com/a/464963
4readc() { # arg: <variable-name>
5 if [ -t 0 ]; then
6 # if stdin is a tty device, put it out of icanon, set min and
7 # time to sane value, but don't otherwise touch other input or
8 # or local settings (echo, isig, icrnl...). Take a backup of the
9 # previous settings beforehand.
10 saved_tty_settings=$(stty -g)
11 stty -icanon min 1 time 0
12 fi
13 eval "$1="
14 while
15 # read one byte, using a work around for the fact that command
16 # substitution strips the last character.
17 c=$(dd bs=1 count=1 2> /dev/null; echo .)
18 c=${c%.}
19
20 # break out of the loop on empty input (eof) or if a full character
21 # has been accumulated in the output variable (using "wc -m" to count
22 # the number of characters).
23 [ -n "$c" ] &&
24 eval "$1=\${$1}"'$c
25 [ "$(($(printf %s "${'"$1"'}" | wc -m)))" -eq 0 ]'; do
26 continue
27 done
28 if [ -t 0 ]; then
29 # restore settings saved earlier if stdin is a tty device.
30 stty "$saved_tty_settings"
31 fi
32}
33
34setup_terminal() {
35 # Setup the terminal for the TUI.
36 # '\e[?1049h': Use alternative screen buffer.
37 # '\e[?7l': Disable line wrapping.
38 # '\e[?25l': Hide the cursor.
39 # '\e[2J': Clear the screen.
40 # '\e[1;Nr': Limit scrolling to scrolling area.
41 # Also sets cursor to (0,0).
42 printf '\e[?1049h\e[?7l\e[2J\e[1;%sr' "$((LINES - 1))"
43
44 # Hide echoing of user input
45 stty -echo
46}
47
48reset_terminal() {
49 # Reset the terminal to a useable state (undo all changes).
50 # '\e[?7h': Re-enable line wrapping.
51 # '\e[?25h': Unhide the cursor.
52 # '\e[2J': Clear the terminal.
53 # '\e[;r': Set the scroll region to its default value.
54 # Also sets cursor to (0,0).
55 # '\e[?1049l: Restore main screen buffer.
56 printf '\e[?7h\e[?25h\e[2J\e[;r\e[?1049l'
57
58 # Show user input.
59 stty echo
60}
61
62printlines() { # printlines file min max
63 if test -n "$DEBUG"; then
64 echo --- >&2
65 else
66 clear
67 fi
68 sed -n "$2,$3p" "$1"
69 notify_flush
70}
71
72notify() { # notify STRING
73 notification="$notification$1"
74}
75
76notify_flush() { # notify_flush
77 winch
78 printf '\e[s\e[%s;%sH' 0 "$c"
79 printf '%s' "$notification"
80 printf '\e[u'
81 notification=
82}
83
84scroll() { # scroll nlines -- MUTATES GLOBAL STATE
85 case "${1:-0}" in
86 \+*|\-*) n=$((n+$1)) ;;
87 0) ;;
88 *) n=$1 ;;
89 esac
90
91 if test $n -gt $((max - l))
92 then
93 n=$((max - l))
94 notify '_'
95 fi
96
97 if test $n -lt $min
98 then
99 notify '^'
100 n=$min
101 fi
102
103 m=$((n+l))
104 if $m -ge $max
105 then
106 m=$max
107 fi
108 # echo "min:$min max:$max n:$n m:$m l:$l" >&2
109}
110
111ctty() { # ctty
112 ## Get the controlling TTY interface.
113 tty <&2 || # only works if not redirecting stderr
114 echo /proc/self/fd/1 # probably only works on linux
115}
116
117winch() {
118 stty size > "$wf"
119 read -r l c < "$wf"
120 # echo l:$l c:$c >&2
121 l=$((l-2)) # last line
122}
123
124cleanup() {
125 rm "$bf" "$wf"
126 reset_terminal
127 exit
128}
129
130vellum() { # vellum [-n NUM] < INPUT
131 # Set up buffer
132 bf="$(mktemp)" # buffer
133 cat "${@:--}" > "$bf"
134 # Redirect input to the controlling terminal
135 ## XXX: This assumes that you haven't redirected stderr.
136 exec < "$(ctty)"
137 max=$(wc -l < "$bf")
138 min=1 # min = minimum accessible line num.
139 n=$min # n = num. first line to show
140
141 # Set up terminal
142 setup_terminal
143 wf="$(mktemp)" # winch file
144 winch; trap winch WINCH
145 m=$((n+l)) # m = num. last line to show
146
147 trap cleanup EXIT INT QUIT
148
149 # Main loop
150 while :; do
151 scroll
152 printlines "$bf" $n $m
153
154 readc key >/dev/null 2>&1
155 case "$key" in
156 ' ') scroll +$l ;;
157 '') scroll -$l ;;
158 j) scroll +1 ;;
159 k) scroll -1 ;;
160 g) scroll $min ;;
161 G) scroll $max ;;
162 q) break ;;
163 esac
164 done
165 break
166 echo
167}
168
169test -n "$DEBUG" && { set -x; exec 2> /tmp/vellum.log; }
170test -z "$SOURCE" && vellum "$@"