[Optparsing] Support options in whatever position

However, it has a precondition that developers MUST respect:
  An option CAN'T have differente meanings/behaviour in different subcommands.
  For example, "-s" means "size" and accept an argument. If you are tempted to add
  an option "-s" (that means, for example "silent", and doesn't accept an argument)
This, however, make sense even from an usability point of view.
Also, option with optional argument are prohibited.

The technique is a "double scan": first, we scan options passing
_every_ possible subcommand options to zparseopts.
This way, we can distinguish between option argument and parameters.
So, we recognize which is the subcommand.
Then, parse again
This commit is contained in:
boyska 2011-07-25 01:00:25 +02:00
parent d0e004412d
commit 6495099344

View File

@ -1066,64 +1066,58 @@ EOF
act "Tomb is now installed." act "Tomb is now installed."
} }
main () { main() {
typeset -A subcommands_opts local -A subcommands_opts
### Options configuration ### Options configuration
#Hi, dear developer! Are you trying to add a new subcommand, or to add some options?
#Well, keep in mind that:
# 1. An option CAN'T have differente meanings/behaviour in different subcommands.
# For example, "-s" means "size" and accept an argument. If you are tempted to add
# an option "-s" (that means, for example "silent", and doesn't accept an argument)
# DON'T DO IT!
# There are two reasons for that:
# I. usability; user expect that "-s" is "size
# II. Option parsing WILL EXPLODE if you do this kind of bad things
# (it will say "option defined more than once, and he's right)
main_opts=(q -quiet=q D -debug=D h -help=h v -verbose=v) main_opts=(q -quiet=q D -debug=D h -help=h v -verbose=v)
subcommands_opts[open]="n -nohook=n k: -key=k o: -mount-options=o" subcommands_opts[open]="n -nohook=n k: -key=k o: -mount-options=o"
subcommands_opts[mount]=${subcommands_opts[open]} subcommands_opts[mount]=${subcommands_opts[open]}
subcommands_opts[create]="s: -size=s" subcommands_opts[create]="s: -size=s"
subcommands_opts[close]="" subcommands_opts[close]=""
subcommands_opts[help]="" subcommands_opts[help]=""
subcommand_opts[slam]="" subcommands_opts[slam]=""
subcommand_opts[list]="" subcommands_opts[list]=""
subcommand_opts[help]="" subcommands_opts[help]=""
subcommand_opts[bury]="" subcommands_opts[bury]=""
subcommand_opts[exhume]="" subcommands_opts[exhume]=""
subcommand_opts[decompose]="" subcommands_opts[decompose]=""
subcommand_opts[recompose]="" subcommands_opts[recompose]=""
subcommand_opts[install]="" subcommands_opts[install]=""
subcommand_opts[askpass]="" subcommands_opts[askpass]=""
subcommand_opts[mktemp]="" subcommands_opts[mktemp]=""
### Parsing global options (wherever they are) ### Detect subcommand
zparseopts -M -E -D -Aglobal_opts $main_opts local -aU every_opts #every_opts behave like a set; that is, an array with unique elements
if [[ -n ${(k)global_opts[-h]} ]]; then # -h is set for optspec in $subcommands_opts$main_opts; do
usage for opt in ${=optspec}; do
exit 0 every_opts+=${opt}
fi done
done
if [[ $? != 0 ]]; then local -a oldstar
error Some error occurred during option processing. See tomb help for more info oldstar=($argv)
exit 127 zparseopts -M -E -D -Adiscardme ${every_opts}
fi unset discardme
typeset -a pars subcommand=$1
set -A pars $* if [[ -z ${(k)subcommands_opts[$subcommand]} ]]; then #there's no such subcommand
typeset -g subcommand error "Subcommand '$subcommand' doesn't exist"
subcommand='' exit 127
for arg in $*; do
if [[ $arg[1] != '-' ]]; then
subcommand=$arg
valid=0
if [[ -z ${(k)subcommands_opts[$subcommand]} ]]; then #command not set
echo "Subcommand $subcommand is not valid; see tomb help"
exit 126
fi
pars[${(k)*[(r)$arg]}]=()
break
fi
done
set -- $pars
if [[ -z $subcommand ]]; then
error You have to specify some subcommand
exit 126
fi fi
### End global options argv=(${oldstar})
unset oldstar
### Parsing global + command-specific options
### Parsing command-specific options
# zsh magic: ${=string} will split to multiple arguments when spaces occur # zsh magic: ${=string} will split to multiple arguments when spaces occur
set -A cmd_opts ${=subcommands_opts[$subcommand]} set -A cmd_opts ${main_opts} ${=subcommands_opts[$subcommand]}
if [[ -n $cmd_opts ]]; then if [[ -n $cmd_opts ]]; then #if there is no option, we don't need parsing
zparseopts -M -E -D -Aopts ${cmd_opts} zparseopts -M -E -D -Aopts ${cmd_opts}
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
error "Some error occurred during option processing. See \"tomb help\" for more info" error "Some error occurred during option processing. See \"tomb help\" for more info"
@ -1139,7 +1133,7 @@ main () {
continue #it shouldnt be appended to PARAM continue #it shouldnt be appended to PARAM
elif [[ $arg[1] == '-' ]]; then elif [[ $arg[1] == '-' ]]; then
if [[ $ok == 0 ]]; then if [[ $ok == 0 ]]; then
echo ERROR: unrecognized option $arg >&2 error "unrecognized option $arg"
exit 127 exit 127
fi fi
fi fi