gsu: Don't export functions.
[gsu.git] / misc / gsu / subcommand
1 #!/bin/bash
2 # (C) 2006-2011 Andre Noll
3
4 if [[ $(type -t gsu_is_a_number) != "function" ]]; then
5 GSU_DIR=${GSU_DIR:=$HOME/.gsu}
6 . $GSU_DIR/common || exit 1
7 fi
8
9 _gsu_usage()
10 {
11 gsu_short_msg "# Usage: $_gsu_self command [options]"
12 }
13
14 # Each line matching this is recognized as a subcommand. The name
15 # of the subcommand is the first subexpression.
16 export gsu_command_regex='^com_\([-a-zA-Z_0-9]\+\)()'
17
18 _gsu_available_commands()
19 {
20 result="$({
21 printf "help\nman\nprefs\n"
22 sed -ne "s/$gsu_command_regex/\1/g;T;p" $0
23 } | sort | tr '\n' ' ')"
24 }
25
26 _gsu_print_available_commands()
27 {(
28 local i count
29 gsu_short_msg "Available commands:"
30 for i in $gsu_cmds; do
31 printf "$i"
32 count=$(($count + 1))
33 if test $(($count % 4)) -eq 0; then
34 echo
35 else
36 printf "\t"
37 if test ${#i} -lt 8; then
38 printf "\t"
39 fi
40 fi
41 done
42 echo
43 ) 2>&1
44 }
45
46 export gsu_prefs_txt="
47 Print the current preferences.
48
49 Usage: prefs [-e]
50
51 If -e is given, the config file is opened with the default editor. Without
52 options, the command prints out a list of all cmt config variables, together
53 with their current value and the default value."
54 _com_prefs()
55 {
56 local i conf="${gsu_config_file:=$HOME/.$gsu_name.rc}"
57
58 if [[ "$1" = "-e" ]]; then
59 ret=-$E_GSU_MKDIR
60 result="${conf%/*}"
61 mkdir -p "$result"
62 [[ $? -ne 0 ]] && return
63 ret=-$E_GSU_EDITOR
64 result="${EDITOR:-vi}"
65 "$result" "$conf"
66 [[ $? -ne 0 ]] && return
67 ret=$GSU_SUCCESS
68 return
69 fi
70
71 for ((i=0; i < ${#gsu_options[@]}; i++)); do
72 local name= option_type= default_value= required=
73 local description= help_text=
74 eval "${gsu_options[$i]}"
75 eval val='"$'${gsu_config_var_prefix}_$name'"'
76 case "$required" in
77 true|yes)
78 printf "# required"
79 ;;
80 *)
81 printf "# optional"
82 ;;
83 esac
84 printf " $option_type: $description"
85 if [[ "$required" != "yes" && "$required" != "true" ]]; then
86 printf " [$default_value]"
87 fi
88 echo
89 [[ -n "$help_text" ]] && sed -e '/^[ ]*$/d; s/^[ ]*/# /g' <<< "$help_text"
90 printf "$name=$val"
91 [[ "$val" == "$default_value" ]] && printf " # default"
92 echo
93 done
94 }
95
96 export gsu_man_txt="
97 Print the manual.
98
99 Usage: man"
100
101 _com_man()
102 {
103 local equal_signs="=================================================="
104 local minus_signs="--------------------------------------------------"
105 local com num
106
107 echo "$_gsu_self (_${gsu_banner_txt}_) manual"
108 echo "${equal_signs:0:${#_gsu_self} + ${#gsu_banner_txt} + 16}"
109 echo
110
111 sed -e '1,/^#\{70,\}/d' -e '/^#\{70,\}/,$d' $0 -e 's/^# *//'
112 echo "----"
113 echo
114 echo "$_gsu_self usage"
115 echo "${minus_signs:0:${#_gsu_self} + 6}"
116 printf "\t"
117 _gsu_usage 2>&1
118 echo "Each command has its own set of options as described below."
119 echo
120 echo "----"
121 echo
122 echo "Available commands:"
123
124 _gsu_available_commands
125 for com in $result; do
126 num=${#com}
127 if test $num -lt 4; then
128 num=4
129 fi
130 echo "${minus_signs:0:$num}"
131 echo "$com"
132 echo "${minus_signs:0:$num}"
133 $0 help $com
134 echo
135 done
136 ret=$GSU_SUCCESS
137 }
138
139 _gsu_banner_msg()
140 {
141 local txt="### $_gsu_self --"
142 if test -z "$gsu_banner_txt"; then
143 txt="$txt set \$gsu_banner_txt to customize this message"
144 else
145 txt="$txt $gsu_banner_txt"
146 fi
147 gsu_short_msg "$txt ###"
148 }
149
150 export gsu_help_txt="
151 Print online help.
152
153 Usage: help [command]
154
155 Without arguments, print the list of available commands. Otherwise,
156 print the help text for the given command."
157
158 _com_help()
159 {
160 local a b
161 if test -z "$1"; then
162 _gsu_banner_msg 2>&1
163 _gsu_usage 2>&1
164 {
165 printf "com_help()\n$gsu_help_txt" | head -n 4; echo "--"
166 printf "com_man()\n$gsu_man_txt" | head -n 4; echo "--"
167 printf "com_prefs()\n$gsu_prefs_txt" | head -n 4; echo "--"
168 grep -A 2 "$gsu_command_regex" $0
169 } | grep -v -- '--' \
170 | sed -e "/$gsu_command_regex/bs" \
171 -e 'H;$!d;x;s/\n//g;b' \
172 -e :s \
173 -e 'x;s/\n//g;${p;x;}' \
174 | sed -e "s/${gsu_command_regex}#*/\1\t/" \
175 | sort \
176 | while read a b; do
177 printf "$a\t"
178 if test ${#a} -lt 8; then
179 printf "\t"
180 fi
181 echo "$b"
182 done
183 echo
184 echo "# Try $_gsu_self help <command> for info on <command>."
185 ret=$GSU_SUCCESS
186 return
187 fi
188 if test "$1" = "help"; then
189 echo "$gsu_help_txt"
190 ret=$GSU_SUCCESS
191 return
192 fi
193 if test "$1" = "man"; then
194 echo "$gsu_man_txt"
195 ret=$GSU_SUCCESS
196 return
197 fi
198 if test "$1" = "prefs"; then
199 echo "$gsu_prefs_txt"
200 ret=$GSU_SUCCESS
201 return
202 fi
203 ret=$GSU_SUCCESS
204 if grep -q "^com_$1()" $0; then
205 sed -e "1,/^com_$1()$/d" -e '/^{/,$d' -e 's/^## *//' $0
206 return
207 fi
208 _gsu_print_available_commands
209 result="$1"
210 ret=-$E_GSU_BAD_COMMAND
211 }
212
213 # Wrapper for bash's getopts.
214 #
215 # Aborts on programming errors such as missing or invalid option string. On
216 # success $result contains shell code that can be eval'ed. For each defined
217 # option x, the local variable o_x will be created when calling eval "$result".
218 # o_x contains true/false for options without an argument or the emtpy string/the
219 # given argument, depending on whether this option was contained in the "$@"
220 # array.
221 #
222 # Example:
223 # gsu_getopts abc:x:y
224 # eval "$result"
225 # [[ $ret -lt 0 ]] && return
226 #
227 # [[ "$o_a" = "true ]] && echo "The -a flag was given"
228 # [[ -n "$o_c" ]] && echo "The -c option was given with arg $o_c"
229 gsu_getopts()
230 {
231 local i c tab=' ' cr='
232 '
233
234 gsu_check_arg_count $# 1 1
235 if [[ $ret -lt 0 ]]; then
236 gsu_err_msg
237 exit 1
238 fi
239
240 ret=-$E_GSU_GETOPTS
241 result="invalid optstring $1"
242 if [[ -z "$1" ]] || grep -q '::' <<< "$1" ; then
243 gsu_err_msg
244 exit 1
245 fi
246
247 for ((i=0; i < ${#1}; i++)); do
248 c=${1:$i:1}
249 case "$c" in
250 [a-zA-Z:]);;
251 *)
252 ret=-$E_GSU_GETOPTS
253 result="invalid character $c in optstring"
254 gsu_err_msg
255 exit 1
256 esac
257 done
258 result="local opt"
259 for ((i=0; i < ${#1}; i++)); do
260 c1=${1:$i:1}
261 c2=${1:$(($i + 1)):1}
262 result+=" o_$c1"
263 if [[ "$c2" = ":" ]]; then
264 let i++
265 else
266 result+="=false"
267 fi
268 done
269 result+="
270 OPTIND=1
271 while getopts $1 opt \"\$@\"; do
272 case \"\$opt\" in
273 "
274 for ((i=0; i < ${#1}; i++)); do
275 c1=${1:$i:1}
276 c2=${1:$(($i + 1)):1}
277 result+="$tab$tab$c1) o_$c1="
278 if [[ "$c2" = ":" ]]; then
279 result+="\"\$OPTARG\""
280 let i++
281 else
282 result+="true"
283 fi
284 result+=";;$cr"
285 done
286 result+="
287 *)
288 ret=-\$E_GSU_GETOPTS
289 result=\"invalid option given\"
290 return
291 ;;
292 esac
293 done
294 shift \$((\$OPTIND - 1))
295 "
296 ret=$GSU_SUCCESS
297 }
298
299 gsu()
300 {
301 local i
302 _gsu_setup
303 _gsu_available_commands
304 gsu_cmds="$result"
305 if test $# -eq 0; then
306 _gsu_usage
307 _gsu_print_available_commands
308 exit 1
309 fi
310 arg="$1"
311 shift
312 # check internal commands
313 if [[ "$arg" = "help" || "$arg" = "man" || "$arg" = "prefs" ]]; then
314 _com_$arg "$@"
315 if [[ "$ret" -lt 0 ]]; then
316 gsu_err_msg
317 exit 1
318 fi
319 exit 0
320 fi
321
322 # external commands
323 for i in $gsu_cmds; do
324 if test "$arg" = "$i"; then
325 com_$arg "$@"
326 if [[ "$ret" -lt 0 ]]; then
327 gsu_err_msg
328 exit 1
329 fi
330 exit 0
331 fi
332 done
333
334 ret=-$E_GSU_BAD_COMMAND
335 result="$arg"
336 gsu_err_msg
337 _gsu_print_available_commands
338 exit 1
339 }
340
341 # Check number of arguments.
342 #
343 # Usage: gsu_check_arg_count <num_given> <num1> [<num2>]
344 #
345 # Check that <num_given> is between <num1> and <num2> inclusively.
346 # If only <num1> ist given, num2 is assumed to be infinity.
347 #
348 # Examples:
349 # 0 0 no argument allowed
350 # 1 1 exactly one argument required
351 # 0 2 at most two arguments admissible
352 # 2 at least two arguments reqired
353 #
354 gsu_check_arg_count()
355 {
356 ret=-$E_GSU_BAD_ARG_COUNT
357 if [[ $# -eq 2 ]]; then # only num1 is given
358 result="at least $2 args required, $1 given"
359 [[ $1 -lt $2 ]] && return
360 ret=$GSU_SUCCESS
361 return
362 fi
363 # num1 and num2 given
364 result="need at least $2 args, $1 given"
365 [[ $1 -lt $2 ]] && return
366 result="need at most $3 args, $1 given"
367 [[ $1 -gt $3 ]] && return
368 ret=$GSU_SUCCESS
369 }