-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathyon.sh
More file actions
248 lines (232 loc) · 6.52 KB
/
yon.sh
File metadata and controls
248 lines (232 loc) · 6.52 KB
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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#!/bin/sh
########################################################################
# Copyright (c) 2023 T. Schöpping #
# #
# This file is provided under the MIT License with Exception #
########################################################################
# Ask user for confirmation (yes or no) and store answer in shell variable 'YON'.
# Usage: yon [options] [--] <question string>"
yon() {
# variable defauls
retval=0
printhelp=0
printversion=0
defaultanswer=''
timeout=''
defaultontimeout=0
attempts=0
returnvariable=''
prompt=""
# parse options
if ! parsed=$(getopt --options=hvd:t:ea:r: --longoptions=help,version,default:,timeout:,default-on-timeout,attempts:,return-variable: -- "$@"); then
retval=1
fi
eval set -- "$parsed"
# evaluate options
while true; do
case "$1" in
-h|--help)
# set flag
printhelp=1
shift 1
;;
-v|--version)
printversion=1
shift 1
;;
-d|--default)
# check for valid option argument and parse it
case "$2" in
y|Y|yes|Yes|YES) defaultanswer='y' ;;
n|N|no|No|NO) defaultanswer='n' ;;
*) retval=1 ;;
esac
shift 2
;;
-t|--timeout)
# check for valid option argument (positive float)
if expr "$2" : "^\([0-9]\+\)\?\(\.[0-9]\+\)\?$" > /dev/null; then
timeout="$2"
else
retval=1
fi
shift 2
;;
-e|--default-on-timeout)
# set flag
defaultontimeout=1
shift 1
;;
-a|--attempts)
# check for valid option argument (positive integer)
if expr "$2" : "^[0-9]\+$" > /dev/null && [ "$2" -gt 0 ]; then
attempts=$2
else
retval=1
fi
shift 2
;;
-r|--return-variable)
# save an clear return variable
returnvariable=$2
export "$returnvariable"=
shift 2
;;
--)
# all options parsed, break the loop
shift 1
break
;;
*)
# this must never occur
return 255
;;
esac
done
# check help and version flags
if [ $printhelp -eq 0 ] && [ $printversion -eq 0 ]; then
# check for at least one (remaining) argument
if [ $# -gt 0 ]; then
# set prompt
prompt=$*
else
retval=1
fi
fi
# check dependencies
if [ $defaultontimeout -eq 1 ] && [ -z $defaultanswer ]; then
retval=1
fi
# check for errors so far
if [ $retval -ne 0 ]; then
echo "yon: invalid option(s)"
echo "Try 'yon --help' for more information."
return $retval
fi
# check help flag
if [ $printhelp -ne 0 ]; then
echo "Usage: yon [options] [--] <question string>"
echo ""
echo "Ask user for confirmation (yes or no) and store answer in shell variable 'YON'."
echo ""
echo "Options:"
echo " -a, --attempts <val>"
echo " Maximum number of attempts to ask the user for a valid answer."
echo " <val> msut be a positive integer value > 0."
echo " In case the user does not provide a valid answer, error code 3 is returned."
echo " If not specified, an infinite number of attempts are made."
echo " -d, --default <val>"
echo " Specify an assumed answer in case the user does just hit enter."
echo " If this option is not provided, no default answer is assumed."
echo " Allowed values for <val> are:"
echo " y, Y, yes, Yes, YES assume confirmation"
echo " n, N, no, No, NO assume rejection"
echo " -e, --default-on-timeout"
echo " Assume the default answer (cf. -d/--default) on timeout."
echo " Requires -d/--default option."
echo " Has no effect if no timeout is specified (cf. -t/--timeout)."
echo " -h, --help"
echo " Display this help text and exit."
echo " -r, --return-variable <varname>"
echo " Store the answer flag ('y' or 'n') in the variable <varname>."
echo " In contrast to the 'YON' shell variable, <varname> is reset to '' with each call of yon."
echo " -t, --timeout <val>"
echo " Fractional number of seconds when the function will time out."
echo " <val> must be a flating point value >= 0."
echo " Behaviour on timeout depends on the -e/--default-on-timeout flag:"
echo " If the flag is specified, the default answer (cf. -d/--default) is assumed."
echo " Otherwise, error code 2 is returned."
echo " -v, --version"
echo " Display version information and exit."
echo ""
echo "Exit Status:"
echo " 0 User answered properly or the default answer was assumed on timeout."
echo " Shell variable 'YON' is set to either 'y' or 'n'."
echo " 1 Some given options or arguments were invalid."
echo " 2 A timeout occurred, but no default answer was assumed."
echo " 3 The user did not provide a valid answer."
return $retval
fi
# check version flag
if [ $printversion -ne 0 ]; then
echo "yon version 1.0.0"
return $retval
fi
# prepare prompt
case $defaultanswer in
y) prompt="$prompt [Y/n]: " ;;
n) prompt="$prompt [y/N]: " ;;
*) prompt="$prompt [y/n]: " ;;
esac
# print prompt until valid answer or timeout
attempt=0
while true; do
attempt=$(echo "$attempt + 1;" | bc)
# ask user and read answer
printf "%s" "$prompt"
saved_tty_settings=$(stty -g)
if [ -z "$timeout" ]; then
stty -icanon min 1
else
# round timeout to 1/10 second
timeout=$(echo "scale=1; ($timeout + 0.05) / 1;" | bc)
stty -icanon min 0 time "$(echo "scale=0; $timeout / 0.1;" | bc)"
fi
starttime=$(date +%s%N)
answer=$(dd bs=1 count=1 2>/dev/null)
endtime=$(date +%s%N)
stty "$saved_tty_settings"
if [ -n "$answer" ]; then
echo ""
fi
# check for timeout
if [ -n "$timeout" ]; then
dtime=$(echo "scale=3; ($endtime - $starttime) / 10^9;" | bc)
if [ "$(echo "if ($dtime >= $timeout) 2 else 0;" | bc)" -eq 0 ]; then
retval=0
else
echo ""
retval=2
fi
if [ $retval -eq 2 ] && [ $defaultontimeout -ne 0 ]; then
answer=$defaultanswer
retval=0
fi
fi
# return on error
if [ $retval -ne 0 ]; then
return $retval
fi
# evaluate answer
if [ -z "$answer" ] && [ -n "$defaultanswer" ]; then
answer=$defaultanswer
fi
case $answer in
y|Y)
# user confirmed
export YON='y'
if [ -n "$returnvariable" ]; then
export "$returnvariable"='y'
fi
return $retval
;;
n|N)
# user declined
export YON='n'
if [ -n "$returnvariable" ]; then
export "$returnvariable"='n'
fi
return $retval
;;
*)
# neither, attempt again or return
if [ "$attempts" -eq 0 ] || [ "$attempt" -lt "$attempts" ]; then
continue
else
retval=3
return $retval
fi
esac
done
return $retval
}