-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathexample.sh
executable file
·267 lines (224 loc) · 7.57 KB
/
example.sh
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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#!/usr/bin/env bash
#
# opt_in example
# This is a bit messy because we play around with different constructs.
# also comments in the opt-in.xtension.sh file for a clean high level example.
# test input:
#
# --buffer 42 --now -Ox -I=imgpack --optimize=gfx publish 400 300
#
# test output:
#
# '-b' '42' '-n' '-O' 'x' '-I' 'imgpack' '-O' 'gfx' '--' 'publish' '400' '300'
#
source $(dirname $0)/'opt-in.source.sh'
#source $(dirname $0)/'opt-in.embed.sh'
#source $(dirname $0)/'opt-in.archive.sh'
appname=$(basename $0);
function usage {
cat << EOF
opt-in option parser example, see README.md
This script will do, and mean, nothing - except for parsing some options.
You may want to experimentally change the opt_init parameters in
the source, or change the opt_user_fail function, or use this script
to prototype your own interfaces.
Usage:
${appname} options [--] <command> file1 ...
${appname} -h | --help
<command>
publish | debug
Options:
-b|-buffer <bufcount> (required)
(don't use -b=42 or -b42 here, we are testing different option types)
-I[=]<package-name>
-c <clone-file>
experimental feature
-l | --lazy
process later
-n | --now
process now
-G | --generate <code>
where code is a short string
-O[=]<setting> | --optimize[=]<setting>
where setting=1|2|3|x|gfx
-
read from STDIN
Example:
${appname} -b 42 -lnOgfx publish
EOF
}
# NOTE: we have modified the extension match functions to take an extra
# name argument as first parameter, so we can remap short option names
# since we cannot remap the combined short flags our second stage
# needs to handle both flag types - this is of course not how you
# would design a real application.
# to match the common value form:
# -O 2 -O2 --optimize 42 --optimize-42 -O=42
#
# usage: opt_user_match_cv name shortname longname default-value
function opt_user_match_cv {
case "$OPT_TOKEN" in
"$3") opt_ev "$1" "$4" ;;
"$2") opt_ev "$1" "$4" ;;
"$3"=*) opt_kv $1 ;;
# comment out the following line for getopt compatibility
"$2"=*) opt_kv $1 ;;
"$2"*) opt_vs "$1" "$2" ;;
*) return 1 ;;
esac
return 0;
}
# to match the flag form:
# -v --verbose
# usage: opt_user_match_cf name shortname longname
function opt_user_match_cf {
case "$OPT_TOKEN" in
"$2") opt_f "$1" ;;
"$3") opt_f "$1" ;;
*) return 1 ;;
esac
return 0;
}
function opt_user_fail {
reason-$1; name=$2; msg=$3;
usage >&2; echo >&2 $msg $2;
exit 1;
}
# Wraps 'opt_next' to also handle combined short flags:
# usage: opt_get arg [ flags ]
# where flags is a string of single letter flags (no ':' or '-'). The flags
# is not like getopt, since it does not deal with values - those are handled
# after the call.
# 'arg' is a command line argument that may contain multiple flags, unlike opt_next 'arg'.
# e.g. with 'tar -xzf <file>', we may have:
# opt_get "-xzf" "xczt"
# which pushes the flags '-x' '-z' and returns with OPT_TOKEN="-f" and returns success.
# if flags are partially matched (as in the above example), or not at all, OPT_TOKEN
# holds the rest with success as return code. It returns false and consumes the entire
# argument if non-options are currently processed.
# If a value is expected, the argument is consumed entirely in eager mode.
# In strict mode, if the first flag matches, it is handled like any other strict
# flag processed after opt_next, and if the first flag does not match, the
# entire token is also given to opt_next and handled the same way. We cannot
# have an expected value conflict inside the token because we stop at the first
# option that requires a value and handle it later.
# Observe that it is not possible to translate flags given as second argument,
# unlike when using opt_f at a later stage.
function opt_get { _opt_tail="$1"
if [ ! $_opt_brk -ne 0 ] && ( [ -z "$_opt_expect" ] || [ $_opt_eager -eq 0 ] ); then
while [ -n "${_opt_tail#-*}" ] && [ "$2" != "${2/${_opt_tail:1:1}/}" ]; do
_opt_dbg _combine_;
opt_next "${_opt_tail:0:2}"; opt_f "$OPT_TOKEN"; _opt_tail="-${_opt_tail:2}";
[ "$_opt_tail" == '-' ] && return 1; done; fi; opt_next "$_opt_tail"; }
opt_init debug silent
for arg
do
if opt_get "$arg" "ln"; then
# if opt_next "$arg"; then
if opt_user_match_cv gen -G --generate '*' ||
opt_user_match_cf lazy -l --lazy;
then continue; else
case "$OPT_TOKEN" in
-h | --help) usage; exit 0 ;;
-b | --buffer) opt_ev -b ;;
--now) opt_f -n ;;
-I=*) opt_kv -I ;;
-I*) opt_v -I ;;
-c) opt_ev -c ;;
-O | --optimize) opt_ev -O ;;
-O=* | --optimize=*) opt_kv -O ;;
-O*) opt_v -O ;;
-) opt_f STDIN ;;
--) opt_break -- ;;
-*) opt_invalid ;;
*) opt_pos -- ;;
esac;
fi
fi
done
opt_final
echo "arg list before \$OPT_IN:"
for arg
do
echo " arg: ($arg)";
done
$OPT_IN
echo "arg list after \$OPT_IN:"
for arg
do
echo " arg: ($arg)";
done
unset fd; unset BUFFER; unset NOW; unset CLONE; unset INC;
OPTIM="";
function check_optim {
case "$1" in
1|2|3|x|gfx) return 0 ;;
*) echo >&2 "Uh - we have no such optimization, aborting ... (try -h)";
exit 1 ;;
esac
}
# Note: 'while [ -n "$1" ]' would not handle empty arguments correctly
while [ $# -gt 0 ]
do
# TODO: warn if value empty or set multiple times;
# optim already handled except multiple identical values.
case "$1" in
-b) BUFFER="$2"; shift ;;
-n) NOW='yes, right now!' ;;
-c) CLONE="$2"; shift ;;
-I) INC="$2"; shift ;;
-O) check_optim $2; OPTIM="${OPTIM}:$2"; shift ;;
gen) echo "ignoring generate option"; shift ;;
-l | lazy) echo "suspending task" ;;
--) shift; break ;;
STDIN) fd=1 ;;
'?') echo "invalid argument (try -h): $2"; exit 1 ;;
*) break ;;
esac
shift
done
echo "arg list after 2nd pass:"
for arg
do
echo " arg: ($arg)";
done
# check for empty or other invalid argument values
# This is incomplete - just for illustration.
[ "$CLONE" == ":" ] &&
echo >&2 "warning: skipping missing clone argument";
[ -z "$BUFFER" ] &&
echo >&2 "error: abort on empty or absent buffer argument (try -h)" &&
exit 1;
function publish {
if [ -z "$1" ]; then
echo >&2 "no point in publishing no files, aborting, sorry ... (try -h)";
exit 1;
else
echo "wee, we are publishing the files: $@";
fi
}
function handler {
case "$1" in
publish) shift; echo "wee - we are publishing $@" ;;
debug)
echo "OPT_ARGS string from opt-in:";
echo "";
echo "$OPT_ARGS";
echo "";
echo "options read:";
echo "";
echo "BUFFER: $BUFFER";
echo "CLONE: $CLONE";
echo "INC: ${INC:-no include specified}";
echo "NOW: ${NOW:-no, no now, job queued}";
echo "OPTIM: ${OPTIM}";
shift;
echo "files: ${@:-no files specified, this is not allowed!}";
;;
*) echo "hmm - we have no support for the command $1" ;;
esac
}
# process the remaining non-options
handler "$@"
echo << COMMENT > /dev/null
COMMENT