gsu improvements.
[gsu.git] / funcs / gsu
1 #!/bin/sh
2 # gsu -- the global subcommand utility
3 # (C) 2006-2009 Andre Noll
4
5 _gsu_init_errors()
6 {
7 gsu_errors="
8 GSU_SUCCESS success
9 E_GSU_BAD_COMMAND invalid command
10 E_GSU_NOT_A_NUMBER not a number
11 E_GSU_SOURCE error in config file
12 E_GSU_CONFIG bad/missing config file option
13 $gsu_errors
14 "
15 local a b i=0
16 while read a b; do
17 if test -z "$a"; then
18 continue
19 fi
20 #echo "a:$a, b: $b"
21 gsu_error_txt[i]="$b"
22 eval $a=$i
23 i=$(($i + 1))
24 done << EOF
25 $gsu_errors
26 EOF
27 }
28 export -f _gsu_init_errors
29
30 # check if $1 is a number
31 gsu_is_a_number()
32 {
33 result="$1"
34 if test "$1" -eq "$1" &> /dev/null; then
35 ret=$GSU_SUCCESS
36 else
37 ret=-$E_GSU_NOT_A_NUMBER
38 fi
39 }
40 export -f gsu_is_a_number
41
42 gsu_short_msg()
43 {
44 echo "$1" 1>&2
45 }
46 export -f gsu_short_msg
47
48 gsu_msg()
49 {
50 gsu_short_msg "$_gsu_self: $1"
51 }
52 export -f gsu_msg
53
54 gsu_date_msg()
55 {
56 gsu_short_msg "$_gsu_self $(date): $1"
57 }
58 export -f gsu_date_msg
59
60 _gsu_banner_msg()
61 {
62 local txt="*** $_gsu_self --"
63 if test -z "$gsu_banner_txt"; then
64 txt="$txt set \$gsu_banner_txt to customize this message"
65 else
66 txt="$txt $gsu_banner_txt"
67 fi
68 gsu_short_msg "$txt ***"
69 }
70 export -f _gsu_banner_msg
71
72 gsu_err_msg()
73 {
74 local txt="$result" err
75
76 gsu_is_a_number "$ret"
77 if test $ret -lt 0; then
78 gsu_msg "unknown error ($ret:$txt)"
79 exit 1
80 fi
81 if test $result -ge 0; then
82 gsu_msg "unknown error ($result:$txt)"
83 exit 1
84 fi
85 err=$((0 - $result))
86 if test -n "$txt"; then
87 txt="$txt: ${gsu_error_txt[$err]}"
88 else
89 txt="${gsu_error_txt[$err]}"
90 fi
91 gsu_msg "$txt"
92 }
93 export -f gsu_err_msg
94
95 _gsu_usage()
96 {
97 gsu_short_msg "Usage: $_gsu_self command [options]"
98 }
99 export -f _gsu_usage
100
101 _gsu_available_commands()
102 {
103 result="$( (printf "help\nman\n"; grep "^com_[a-z_]\+()" $0) \
104 | sed -e 's/^com_//' -e 's/()//' \
105 | sort \
106 | tr '\n' ' ')"
107 ret=$GSU_SUCCESS
108 }
109 export -f _gsu_available_commands
110
111 _gsu_print_available_commands()
112 {(
113 local i count
114 gsu_short_msg "Available commands:"
115 for i in $gsu_cmds; do
116 printf "$i"
117 count=$(($count + 1))
118 if test $(($count % 4)) -eq 0; then
119 echo
120 else
121 printf "\t"
122 if test ${#i} -lt 8; then
123 printf "\t"
124 fi
125 fi
126 done
127 echo
128 ) 2>&1
129 }
130 export -f _gsu_print_available_commands
131
132 export gsu_man_txt="
133 Print the manual.
134
135 Usage: man"
136
137 com_man()
138 {
139 local equal_signs="=================================================="
140 local minus_signs="--------------------------------------------------"
141 local com num
142
143 echo "$_gsu_self (_${gsu_banner_txt}_) manual"
144 echo "${equal_signs:0:${#_gsu_self} + ${#gsu_banner_txt} + 16}"
145 echo
146
147 sed -e '1,/^#\{70,\}/d' -e '/^#\{70,\}/,$d' $0 -e 's/^# *//'
148 echo "----"
149 echo
150 echo "$_gsu_self usage"
151 echo "${minus_signs:0:${#_gsu_self} + 6}"
152 printf "\t"
153 _gsu_usage 2>&1
154 echo "Each command has its own set of options as described below."
155 echo
156 echo "----"
157 echo
158 echo "Available commands:"
159
160 _gsu_available_commands
161 for com in $result; do
162 num=${#com}
163 if test $num -lt 4; then
164 num=4
165 fi
166 echo "${minus_signs:0:$num}"
167 echo "$com"
168 echo "${minus_signs:0:$num}"
169 $0 help $com
170 echo
171 done
172 ret=$GSU_SUCCESS
173 }
174 export -f com_man
175
176 export gsu_help_txt="
177 Print online help.
178
179 Usage: help [command]
180
181 Without arguments, print the list of available commands. Otherwise,
182 print the help text for the given command."
183
184 com_help()
185 {
186 local a b
187 if test -z "$1"; then
188 _gsu_banner_msg 2>&1
189 _gsu_usage 2>&1
190 # sed is magic, baby
191 (printf "com_help()\n$gsu_help_txt" | head -n 4; echo "--"
192 printf "com_man()\n$gsu_man_txt" | head -n 4; echo "--"
193
194 grep -A 2 "^com_\([a-zA-Z_0-9]\+\)()" $0) \
195 | grep -v -- '--' \
196 | sed -e '/^com_\([a-zA-Z_0-9]\+\)()/bs' \
197 -e 'H;$!d;x;s/\n//g;b' \
198 -e :s \
199 -e 'x;s/\n//g;${p;x;}' \
200 | sed -e 's/^com_\([a-zA-Z_0-9]\+\)()#*/\1\t/' \
201 | sort \
202 | while read a b; do
203 printf "$a\t"
204 if test ${#a} -lt 8; then
205 printf "\t"
206 fi
207 echo "$b"
208 done
209 echo
210 echo "Try $_gsu_self help <command> for info on <command>."
211 ret=$GSU_SUCCESS
212 return
213 fi
214 if test "$1" = "help"; then
215 echo "$gsu_help_txt"
216 ret=$GSU_SUCCESS
217 return
218 fi
219 if test "$1" = "man"; then
220 echo "$gsu_man_txt"
221 ret=$GSU_SUCCESS
222 return
223 fi
224 ret=$GSU_SUCCESS
225 if grep -q "^com_$1()" $0; then
226 sed -e "1,/com_$1()/d" -e '/^{/,$d' -e 's/^## *//' $0
227 return
228 fi
229 _gsu_print_available_commands
230 result="$1"
231 ret=-$E_GSU_BAD_COMMAND
232 }
233 export -f com_help
234
235 _gsu_init_config()
236 {
237 local name val default_val required ty comment
238
239 # set default values
240 while read name default_val required ty comment; do
241 if test -z "$name"; then
242 continue
243 fi
244 eval ${gsu_self}_$name="$default_val"
245 done << EOF
246 $gsu_config_vars
247 EOF
248 result="$HOME/.${gsu_self}rc"
249 # overwrite by custom configuration
250 if [ -r "$result" ]; then
251 ret=-$E_GSU_SOURCE
252 if ! . "$result"; then
253 gsu_err_msg
254 exit 1
255 fi
256 fi
257 while read name default_val required ty comment; do
258 [ -z "$name" ] && continue
259 eval val="\$$name"
260 # abort if any required config var remains unset
261 ret=-$_E_GSU_CONFIG
262 if [ "$val" = "-" -a "$required" = "required" ]; then
263 result="$name"
264 gsu_err_msg
265 exit 1
266 fi
267 if [ $ty == "number" ]; then
268 gsu_is_a_number "$val"
269 if [ $ret -lt 0]; then
270 gsu_err_msg
271 exit 1
272 fi
273 fi
274 eval export ${gsu_self}_$name
275 done << EOF
276 $config_vars
277 EOF
278 }
279 export -f _gsu_init_config
280
281 gsu()
282 {
283 local i
284
285 _gsu_self="$(basename $0)"
286 _gsu_init_errors
287 _gsu_init_config
288 _gsu_available_commands
289 gsu_cmds="$result"
290 if test $# -eq 0; then
291 _gsu_usage
292 _gsu_print_available_commands
293 exit 1
294 fi
295 arg="$1"
296 shift
297 for i in $gsu_cmds; do
298 if test "$arg" = "$i"; then
299 com_$arg "$@"
300 if test $ret -lt 0; then
301 gsu_err_msg
302 exit 1
303 fi
304 exit 0
305 fi
306 done
307 ret=-$E_GSU_BAD_COMMAND
308 result="$arg"
309 gsu_err_msg
310 _gsu_print_available_commands
311 exit 1
312 }
313 export -f gsu