lib/getoptions.sh
# shellcheck shell=sh disable=SC2016
# [getoptions] License: Creative Commons Zero v1.0 Universal
getoptions() {
_error='' _on=1 _off='' _export='' _plus='' _mode='' _alt='' _rest=''
_flags='' _nflags='' _opts='' _help='' _abbr='' _cmds='' _init=@empty IFS=' '
_0() { echo "$@"; }
for i in 1 2 3 4 5; do eval "_$i() { _$((${i-}-1)) \" \$@\"; }"; done
quote() {
q="$2'" r=''
while [ "$q" ]; do r="$r${q%%\'*}'\''" && q=${q#*\'}; done
q="'${r%????}'" && q=${q#\'\'} && q=${q%\'\'}
eval "$1=\${q:-\"''\"}"
}
code() {
[ "${1#:}" = "$1" ] && c=3 || c=4
eval "[ ! \${$c:+x} ] || $2 \"\$$c\""
}
kv() { eval "${2-}${1%%:*}=\${1#*:}"; }
loop() { [ $# -gt 1 ] && [ "$2" != -- ]; }
invoke() { eval '"_$@"'; }
prehook() { invoke "$@"; }
for i in setup flag param option disp msg; do
eval "$i() { prehook $i \"\$@\"; }"
done
args() {
on=$_on off=$_off export=$_export init=$_init _hasarg=$1 && shift
while loop "$@" && shift; do
case $1 in
-?) [ "$_hasarg" ] && _opts="$_opts${1#-}" || _flags="$_flags${1#-}" ;;
+?) _plus=1 _nflags="$_nflags${1#+}" ;;
[!-+]*) kv "$1"
esac
done
}
defvar() {
case $init in
@none) : ;;
@export) code "$1" _0 "export $1" ;;
@empty) code "$1" _0 "${export:+export }$1=''" ;;
@unset) code "$1" _0 "unset $1 ||:" "unset OPTARG ||:; ${1#:}" ;;
*)
case $init in @*) eval "init=\"=\${${init#@}}\""; esac
case $init in [!=]*) _0 "$init"; return 0; esac
quote init "${init#=}"
code "$1" _0 "${export:+export }$1=$init" "OPTARG=$init; ${1#:}"
esac
}
_setup() {
[ "${1#-}" ] && _rest=$1
while loop "$@" && shift; do kv "$1" _; done
}
_flag() { args '' "$@"; defvar "$@"; }
_param() { args 1 "$@"; defvar "$@"; }
_option() { args 1 "$@"; defvar "$@"; }
_disp() { args '' "$@"; }
_msg() { args '' _ "$@"; }
cmd() { _mode=@ _cmds="$_cmds${_cmds:+|}'$1'"; }
"$@"
cmd() { :; }
_0 "${_rest:?}=''"
_0 "$2() {"
_1 'OPTIND=$(($#+1))'
_1 'while OPTARG= && [ $# -gt 0 ]; do'
[ "$_abbr" ] && getoptions_abbr "$@"
args() {
sw='' validate='' pattern='' counter='' on=$_on off=$_off export=$_export
while loop "$@" && shift; do
case $1 in
--\{no-\}*) i=${1#--?no-?}; sw="$sw${sw:+|}'--$i'|'--no-$i'" ;;
[-+]? | --*) sw="$sw${sw:+|}'$1'" ;;
*) kv "$1"
esac
done
quote on "$on"
quote off "$off"
}
setup() { :; }
_flag() {
args "$@"
[ "$counter" ] && on=1 off=-1 v="\$((\${$1:-0}+\$OPTARG))" || v=''
_3 "$sw)"
_4 '[ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break'
_4 "eval '[ \${OPTARG+x} ] &&:' && OPTARG=$on || OPTARG=$off"
valid "$1" "${v:-\$OPTARG}"
_4 ';;'
}
_param() {
args "$@"
_3 "$sw)"
_4 '[ $# -le 1 ] && set "required" "$1" && break'
_4 'OPTARG=$2'
valid "$1" '$OPTARG'
_4 'shift ;;'
}
_option() {
args "$@"
_3 "$sw)"
_4 'set -- "$1" "$@"'
_4 '[ ${OPTARG+x} ] && {'
_5 'case $1 in --no-*) set "noarg" "${1%%\=*}"; break; esac'
_5 '[ "${OPTARG:-}" ] && { shift; OPTARG=$2; } ||' "OPTARG=$on"
_4 "} || OPTARG=$off"
valid "$1" '$OPTARG'
_4 'shift ;;'
}
valid() {
set -- "$validate" "$pattern" "$1" "$2"
[ "$1" ] && _4 "$1 || { set -- ${1%% *}:\$? \"\$1\" $1; break; }"
[ "$2" ] && {
_4 "case \$OPTARG in $2) ;;"
_5 '*) set "pattern:'"$2"'" "$1"; break'
_4 "esac"
}
code "$3" _4 "${export:+export }$3=\"$4\"" "${3#:}"
}
_disp() {
args "$@"
_3 "$sw)"
code "$1" _4 "echo \"\${$1}\"" "${1#:}"
_4 'exit 0 ;;'
}
_msg() { :; }
[ "$_alt" ] && _2 'case $1 in -[!-]?*) set -- "-$@"; esac'
_2 'case $1 in'
_wa() { _4 "eval 'set -- $1' \${1+'\"\$@\"'}"; }
_op() {
_3 "$1) OPTARG=\$1; shift"
_wa '"${OPTARG%"${OPTARG#??}"}" '"$2"'"${OPTARG#??}"'
_4 "$3"
}
_3 '--?*=*) OPTARG=$1; shift'
_wa '"${OPTARG%%\=*}" "${OPTARG#*\=}"'
_4 ';;'
_3 '--no-*) unset OPTARG ;;'
[ "$_alt" ] || {
[ "$_opts" ] && _op "-[$_opts]?*" '' ';;'
[ ! "$_flags" ] || _op "-[$_flags]?*" - 'OPTARG= ;;'
}
[ "$_plus" ] && {
[ "$_nflags" ] && _op "+[$_nflags]?*" + 'unset OPTARG ;;'
_3 '+*) unset OPTARG ;;'
}
_2 'esac'
_2 'case $1 in'
"$@"
rest() {
_4 'while [ $# -gt 0 ]; do'
_5 "$_rest=\"\${$_rest}" '\"\${$(($OPTIND-$#))}\""'
_5 'shift'
_4 'done'
_4 'break ;;'
}
_3 '--)'
[ "$_mode" = @ ] || _4 'shift'
rest
_3 "[-${_plus:++}]?*)"
case $_mode in [=#]) rest ;; *) _4 'set "unknown" "$1"; break ;;'; esac
_3 '*)'
case $_mode in
@)
_4 "case \$1 in ${_cmds:-*}) ;;"
_5 '*) set "notcmd" "$1"; break'
_4 'esac'
rest ;;
[+#]) rest ;;
*) _4 "$_rest=\"\${$_rest}" '\"\${$(($OPTIND-$#))}\""'
esac
_2 'esac'
_2 'shift'
_1 'done'
_1 '[ $# -eq 0 ] && { OPTIND=1; unset OPTARG; return 0; }'
_1 'case $1 in'
_2 'unknown) set "Unrecognized option: $2" "$@" ;;'
_2 'noarg) set "Does not allow an argument: $2" "$@" ;;'
_2 'required) set "Requires an argument: $2" "$@" ;;'
_2 'pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;'
_2 'notcmd) set "Not a command: $2" "$@" ;;'
_2 '*) set "Validation error ($1): $2" "$@"'
_1 'esac'
[ "$_error" ] && _1 "$_error" '"$@" >&2 || exit $?'
_1 'echo "$1" >&2'
_1 'exit 1'
_0 '}'
[ ! "$_help" ] || eval "shift 2; getoptions_help $1 $_help" ${3+'"$@"'}
}