aboutsummaryrefslogtreecommitdiffstats
path: root/examples/scripts/line-input.bash
blob: 3f2efaef0a3d756bf6afb1bf4744dbb4803e43d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#! /bin/bash
#
#From: kaz@cafe.net (Kaz Kylheku)
#Newsgroups: comp.unix.shell
#Subject: Funky little bash script
#Message-ID: <6mspb9$ft2@espresso.cafe.net>
#Date: Thu, 25 Jun 1998 06:11:39 GMT

#Here is something I wrote a few years ago when I was bored one day.
#Warning: this contains control characters.

# Line input routine for GNU Bourne-Again Shell
# plus terminal-control primitives.
#
# by Kaz Kylheku
# June 1996, Vancouver, Canada


#
# Function to disable canonical input processing.
# Terminal modes are saved into variable "savetty"
#
#

function raw
{
	savetty=$(stty -g)
	stty -icanon -isig -echo -echok -echonl inlcr
}

#
# Function to restore terminal settings from savetty variable
#

function restore
{
	stty $savetty
}

#
# Set terminal MIN and TIME values.
# If the input argument is a zero, set up terminal to wait for
# a keystroke indefinitely. If the argument is non-zero, set up
# an absolute timeout of that many tenths of a second. The inter-keystroke
# timer facility of the terminal driver is not exploited.
#

function settimeout
# $1 = tenths of a second
{
	if [ "$1" = "0" ] ; then
		min=1
		timeout=0
	else
		min=0
		timeout="$1"
	fi

	stty min $min time $timeout

	unset min timeout
}

#
# Input a single key using 'dd' and echo it to standard output.
# Launching an external program to get a single keystroke is a bit
# of a pig, but it's the best you can do! Maybe we could convince the
# GNU guys to make 'dd' a bash builtin.
#

function getkey
{
	eval $1="\"\$(dd bs=1 count=1 2> /dev/null)\""
}

#
# Input a line of text gracefully.
# The first argument is the name of a variable where the input line is
# to be stored. If this variable is not empty, its contents are printed
# and treated as though the user had entered them.
# The second argument gives the maximum length of the input line; if it
# is zero, the input is unlimited (bad idea).
# ^W is used to delete words
# ^R redraws the line at any time by backspacing over it and reprinting it
# ^U backspaces to the beginning
# ^H or ^? (backspace or del) delete a single character
# ^M (enter) terminates the input
# all other control keys are ignored and cause a beep when pressed
# 
#


function getline
{
	settimeout 0			# No keystroke timeout.
	save_IFS="$IFS"			# Save word delimiter and set it to
	IFS=""				# to null so ${#line} works correctly.
	eval line=\${$1}		# Fetch line contents
	echo -n "$line"			# and print the existing line.
	while [ 1 ] ; do
		getkey key		# fetch a single keystroke
		case "$key" in
		 |  )				# BS or DEL
			if [ ${#line} != 0 ] ; then	# if line not empty
				echo -n " "		# print destructive BS
				line="${line%%?}"	# chop last character
			else				# else if line empty
				echo -n 		# beep the terminal
			fi
			;;
		 )					# kill to line beg
			while [ ${#line} != 0 ] ; do	# while line not empty
				echo -n " "		# print BS, space, BS
				line="${line%?}"	# shorten line by 1
			done
			;;
		 )					# redraw line
			linesave="$line"		# save the contents
			while [ ${#line} != 0 ] ; do	# kill to line beg
				echo -n " "
				line="${line%?}"
			done
			echo -n "$linesave"		# reprint, restore
			line="$linesave"
			unset linesave			# forget temp var
			;;
		 )
			while [ "${line% }" != "$line" ] && [ ${#line} != 0 ] ; do
				echo -n " "
				line="${line%?}"
			done
			while [ "${line% }" = "$line" ] && [ ${#line} != 0 ] ; do
				echo -n " "
				line="${line%?}"
			done
			;;
		 |  |  |  |  |  |  |  |  |  |  | 
 )
			echo -n 	# ignore various control characters
			;;		# with an annoying beep
		 |  |  |  |  |  |  |  |  |  |  |  |  )
			echo -n 
			;;
		'	' |  |  |  |  |  )
			echo -n 
			;;
		'' )			# Break out of loop on carriage return.
			echo		# Send a newline to the terminal.
			break		# (Also triggered by NUL char!).
			;;
		* )		# Append character to the end of the line.
				# If length is restricted, and the line is too
				# long, then beep...

			if [ "$2" != 0 ] && [ $(( ${#line} >= $2 )) = 1 ] ; then
				echo -n 
			else				# Otherwise add
				line="$line$key"	# the character.
				echo -n "$key"		# And echo it.
			fi
			;;
		esac
	done
	eval $1=\"\$line\"
	IFS="$save_IFS"
	unset line save_IFS
}

# uncomment the lines below to create a standalone test program
#
echo "Line input demo for the GNU Bourne-Again Shell."
echo "Hacked by Kaz Kylheku"
echo
echo "Use ^H/Backspace/Del to erase, ^W to kill words, ^U to kill the"
echo "whole line of input, ^R to redraw the line."
echo "Pass an argument to this program to prime the buffer contents"
raw
echo -n "go: "
if [ ${#1} != 0 ] ; then
	LINE=$1
fi
getline LINE 50
restore

echo "<$LINE>"