mirror of
https://github.com/moparisthebest/pacman
synced 2024-12-23 00:08:50 -05:00
8679cd68d8
This will replace our current options parser used in pacman-key, makepkg, and ideally elsewhere. It follows heuristics closer to that of GNU getopt long (and thus pacman itself), with the exception that it does not allow for options with optional arguments. Due to the way this parser will be used, this sort of functionality will not be needed. Instead of relying on eval+set, options are normalized into an array, OPTRET, which callers should expect to be populated after returning from parseopts. This avoids problems with quotes and spaces in arguments, assuming that the user quotes properly when passing into the application. A new test harness for parseopts is added in test/scripts. Signed-off-by: Dave Reisner <dreisner@archlinux.org>
142 lines
3.1 KiB
Bash
142 lines
3.1 KiB
Bash
# getopt-like parser
|
|
parseopts() {
|
|
local opt= optarg= i= shortopts=$1
|
|
local -a longopts=() unused_argv=()
|
|
|
|
shift
|
|
while [[ $1 && $1 != '--' ]]; do
|
|
longopts+=("$1")
|
|
shift
|
|
done
|
|
shift
|
|
|
|
longoptmatch() {
|
|
local o longmatch=()
|
|
for o in "${longopts[@]}"; do
|
|
if [[ ${o%:} = "$1" ]]; then
|
|
longmatch=("$o")
|
|
break
|
|
fi
|
|
[[ ${o%:} = "$1"* ]] && longmatch+=("$o")
|
|
done
|
|
|
|
case ${#longmatch[*]} in
|
|
1)
|
|
# success, override with opt and return arg req (0 == none, 1 == required)
|
|
opt=${longmatch%:}
|
|
if [[ $longmatch = *: ]]; then
|
|
return 1
|
|
else
|
|
return 0
|
|
fi ;;
|
|
0)
|
|
# fail, no match found
|
|
return 255 ;;
|
|
*)
|
|
# fail, ambiguous match
|
|
printf "@SCRIPTNAME@: $(gettext "option '%s' is ambiguous; possibilities:")" "--$1"
|
|
printf " '%s'" "${longmatch[@]%:}"
|
|
printf '\n'
|
|
return 254 ;;
|
|
esac >&2
|
|
}
|
|
|
|
while (( $# )); do
|
|
case $1 in
|
|
--) # explicit end of options
|
|
shift
|
|
break
|
|
;;
|
|
-[!-]*) # short option
|
|
for (( i = 1; i < ${#1}; i++ )); do
|
|
opt=${1:i:1}
|
|
|
|
# option doesn't exist
|
|
if [[ $shortopts != *$opt* ]]; then
|
|
printf "@SCRIPTNAME@: $(gettext "invalid option") -- '%s'\n" "$opt" >&2
|
|
OPTRET=(--)
|
|
return 1
|
|
fi
|
|
|
|
OPTRET+=("-$opt")
|
|
# option requires optarg
|
|
if [[ $shortopts = *$opt:* ]]; then
|
|
# if we're not at the end of the option chunk, the rest is the optarg
|
|
if (( i < ${#1} - 1 )); then
|
|
OPTRET+=("${1:i+1}")
|
|
break
|
|
# if we're at the end, grab the the next positional, if it exists
|
|
elif (( i == ${#1} - 1 )) && [[ $2 ]]; then
|
|
OPTRET+=("$2")
|
|
shift
|
|
break
|
|
# parse failure
|
|
else
|
|
printf "@SCRIPTNAME@: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2
|
|
OPTRET=(--)
|
|
return 1
|
|
fi
|
|
fi
|
|
done
|
|
;;
|
|
--?*=*|--?*) # long option
|
|
IFS='=' read -r opt optarg <<< "${1#--}"
|
|
longoptmatch "$opt"
|
|
case $? in
|
|
0)
|
|
# parse failure
|
|
if [[ $optarg ]]; then
|
|
printf "@SCRIPTNAME@: $(gettext "option '%s' does not allow an argument")\n" "--$opt" >&2
|
|
OPTRET=(--)
|
|
return 1
|
|
# --longopt
|
|
else
|
|
OPTRET+=("--$opt")
|
|
shift
|
|
continue 2
|
|
fi
|
|
;;
|
|
1)
|
|
# --longopt=optarg
|
|
if [[ $optarg ]]; then
|
|
OPTRET+=("--$opt" "$optarg")
|
|
shift
|
|
# --longopt optarg
|
|
elif [[ $2 ]]; then
|
|
OPTRET+=("--$opt" "$2" )
|
|
shift 2
|
|
# parse failure
|
|
else
|
|
printf "@SCRIPTNAME@: $(gettext "option '%s' requires an argument")\n" "--$opt" >&2
|
|
OPTRET=(--)
|
|
return 1
|
|
fi
|
|
continue 2
|
|
;;
|
|
254)
|
|
# ambiguous option -- error was reported for us by longoptmatch()
|
|
OPTRET=(--)
|
|
return 1
|
|
;;
|
|
255)
|
|
# parse failure
|
|
printf "@SCRIPTNAME@: $(gettext "invalid option") '--%s'\n" "$opt" >&2
|
|
OPTRET=(--)
|
|
return 1
|
|
;;
|
|
esac
|
|
;;
|
|
*) # non-option arg encountered, add it as a parameter
|
|
unused_argv+=("$1")
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# add end-of-opt terminator and any leftover positional parameters
|
|
OPTRET+=('--' "${unused_argv[@]}" "$@")
|
|
unset longoptmatch
|
|
|
|
return 0
|
|
}
|