1#! /bin/bash
2#
3#From: kaz@cafe.net (Kaz Kylheku)
4#Newsgroups: comp.unix.shell
5#Subject: Funky little bash script
6#Message-ID: <6mspb9$ft2@espresso.cafe.net>
7#Date: Thu, 25 Jun 1998 06:11:39 GMT
8
9#Here is something I wrote a few years ago when I was bored one day.
10#Warning: this contains control characters.
11
12# Line input routine for GNU Bourne-Again Shell
13# plus terminal-control primitives.
14#
15# by Kaz Kylheku
16# June 1996, Vancouver, Canada
17
18
19#
20# Function to disable canonical input processing.
21# Terminal modes are saved into variable "savetty"
22#
23#
24
25function raw
26{
27	savetty=$(stty -g)
28	stty -icanon -isig -echo -echok -echonl inlcr
29}
30
31#
32# Function to restore terminal settings from savetty variable
33#
34
35function restore
36{
37	stty $savetty
38}
39
40#
41# Set terminal MIN and TIME values.
42# If the input argument is a zero, set up terminal to wait for
43# a keystroke indefinitely. If the argument is non-zero, set up
44# an absolute timeout of that many tenths of a second. The inter-keystroke
45# timer facility of the terminal driver is not exploited.
46#
47
48function settimeout
49# $1 = tenths of a second
50{
51	if [ "$1" = "0" ] ; then
52		min=1
53		timeout=0
54	else
55		min=0
56		timeout="$1"
57	fi
58
59	stty min $min time $timeout
60
61	unset min timeout
62}
63
64#
65# Input a single key using 'dd' and echo it to standard output.
66# Launching an external program to get a single keystroke is a bit
67# of a pig, but it's the best you can do! Maybe we could convince the
68# GNU guys to make 'dd' a bash builtin.
69#
70
71function getkey
72{
73	eval $1="\"\$(dd bs=1 count=1 2> /dev/null)\""
74}
75
76#
77# Input a line of text gracefully.
78# The first argument is the name of a variable where the input line is
79# to be stored. If this variable is not empty, its contents are printed
80# and treated as though the user had entered them.
81# The second argument gives the maximum length of the input line; if it
82# is zero, the input is unlimited (bad idea).
83# ^W is used to delete words
84# ^R redraws the line at any time by backspacing over it and reprinting it
85# ^U backspaces to the beginning
86# ^H or ^? (backspace or del) delete a single character
87# ^M (enter) terminates the input
88# all other control keys are ignored and cause a beep when pressed
89# 
90#
91
92
93function getline
94{
95	settimeout 0			# No keystroke timeout.
96	save_IFS="$IFS"			# Save word delimiter and set it to
97	IFS=""				# to null so ${#line} works correctly.
98	eval line=\${$1}		# Fetch line contents
99	echo -n "$line"			# and print the existing line.
100	while [ 1 ] ; do
101		getkey key		# fetch a single keystroke
102		case "$key" in
103		 |  )				# BS or DEL
104			if [ ${#line} != 0 ] ; then	# if line not empty
105				echo -n " "		# print destructive BS
106				line="${line%%?}"	# chop last character
107			else				# else if line empty
108				echo -n 		# beep the terminal
109			fi
110			;;
111		 )					# kill to line beg
112			while [ ${#line} != 0 ] ; do	# while line not empty
113				echo -n " "		# print BS, space, BS
114				line="${line%?}"	# shorten line by 1
115			done
116			;;
117		 )					# redraw line
118			linesave="$line"		# save the contents
119			while [ ${#line} != 0 ] ; do	# kill to line beg
120				echo -n " "
121				line="${line%?}"
122			done
123			echo -n "$linesave"		# reprint, restore
124			line="$linesave"
125			unset linesave			# forget temp var
126			;;
127		 )
128			while [ "${line% }" != "$line" ] && [ ${#line} != 0 ] ; do
129				echo -n " "
130				line="${line%?}"
131			done
132			while [ "${line% }" = "$line" ] && [ ${#line} != 0 ] ; do
133				echo -n " "
134				line="${line%?}"
135			done
136			;;
137		 |  |  |  |  |  |  |  |  |  |  | 
138 )
139			echo -n 	# ignore various control characters
140			;;		# with an annoying beep
141		 |  |  |  |  |  |  |  |  |  |  |  |  )
142			echo -n 
143			;;
144		'	' |  |  |  |  |  )
145			echo -n 
146			;;
147		'' )			# Break out of loop on carriage return.
148			echo		# Send a newline to the terminal.
149			break		# (Also triggered by NUL char!).
150			;;
151		* )		# Append character to the end of the line.
152				# If length is restricted, and the line is too
153				# long, then beep...
154
155			if [ "$2" != 0 ] && [ $(( ${#line} >= $2 )) = 1 ] ; then
156				echo -n 
157			else				# Otherwise add
158				line="$line$key"	# the character.
159				echo -n "$key"		# And echo it.
160			fi
161			;;
162		esac
163	done
164	eval $1=\"\$line\"
165	IFS="$save_IFS"
166	unset line save_IFS
167}
168
169# uncomment the lines below to create a standalone test program
170#
171echo "Line input demo for the GNU Bourne-Again Shell."
172echo "Hacked by Kaz Kylheku"
173echo
174echo "Use ^H/Backspace/Del to erase, ^W to kill words, ^U to kill the"
175echo "whole line of input, ^R to redraw the line."
176echo "Pass an argument to this program to prime the buffer contents"
177raw
178echo -n "go: "
179if [ ${#1} != 0 ] ; then
180	LINE=$1
181fi
182getline LINE 50
183restore
184
185echo "<$LINE>"
186