[pacman-dev] [PATCH v3] libmakepkg: add optional argument support to parseopts
Adds a "?" suffix that can be used to indicate that an option's argument is optional. This allows options to have a default behaviour when the user doesn't specify one, e.g.: --color=[when] being able to behave like --color=auto when only --color is passed Options with optional arguments given on the command line will be returned in the form "--opt=optarg" and "-o=optarg". Despite that not being the syntax for passing an argument with a shortopt (trying to pass -o=foo would make -o's argument "=foo"), this is done to allow the caller to split the option and its optarg easily Signed-off-by: Ethan Sommer <e5ten.arch@gmail.com> --- scripts/libmakepkg/util/parseopts.sh.in | 116 +++++++++++++++--------- test/scripts/parseopts_test.sh | 12 ++- 2 files changed, 83 insertions(+), 45 deletions(-) diff --git a/scripts/libmakepkg/util/parseopts.sh.in b/scripts/libmakepkg/util/parseopts.sh.in index c056cb1e..42540438 100644 --- a/scripts/libmakepkg/util/parseopts.sh.in +++ b/scripts/libmakepkg/util/parseopts.sh.in @@ -18,16 +18,23 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # # A getopt_long-like parser which portably supports longopts and -# shortopts with some GNU extensions. It does not allow for options -# with optional arguments. For both short and long opts, options -# requiring an argument should be suffixed with a colon. After the -# first argument containing the short opts, any number of valid long -# opts may be be passed. The end of the options delimiter must then be -# added, followed by the user arguments to the calling program. +# shortopts with some GNU extensions. For both short and long opts, +# options requiring an argument should be suffixed with a colon, and +# options with optional arguments should be suffixed with a question +# mark. After the first argument containing the short opts, any number +# of valid long opts may be be passed. The end of the options delimiter +# must then be added, followed by the user arguments to the calling +# program. +# +# Options with optional arguments will be returned as "--longopt=optarg" +# for longopts, or "-o=optarg" for shortopts. This isn't actually a valid +# way to pass an optional argument with a shortopt on the command line, +# but is done by parseopts to enable the caller script to split the option +# and its optarg easily. # # Recommended Usage: -# OPT_SHORT='fb:z' -# OPT_LONG=('foo' 'bar:' 'baz') +# OPT_SHORT='fb:zq?' +# OPT_LONG=('foo' 'bar:' 'baz' 'qux?') # if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then # exit 1 # fi @@ -49,29 +56,30 @@ parseopts() { longoptmatch() { local o longmatch=() for o in "${longopts[@]}"; do - if [[ ${o%:} = "$1" ]]; then + if [[ ${o%[:?]} = "$1" ]]; then longmatch=("$o") break fi - [[ ${o%:} = "$1"* ]] && longmatch+=("$o") + [[ ${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 ;; + # success, override with opt and return arg req (0 == none, 1 == required, 2 == optional) + opt=${longmatch%[:?]} + case $longmatch in + *:) return 1 ;; + *\?) return 2 ;; + *) return 0 ;; + esac + ;; 0) # fail, no match found return 255 ;; *) # fail, ambiguous match printf "${0##*/}: $(gettext "option '%s' is ambiguous; possibilities:")" "--$1" - printf " '%s'" "${longmatch[@]%:}" + printf " '%s'" "${longmatch[@]%[:?]}" printf '\n' return 254 ;; esac >&2 @@ -87,32 +95,47 @@ parseopts() { for (( i = 1; i < ${#1}; i++ )); do opt=${1:i:1} - # option doesn't exist - if [[ $shortopts != *$opt* ]]; then - printf "${0##*/}: $(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 "${0##*/}: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2 + case $shortopts in + # option requires optarg + *$opt:*) + # if we're not at the end of the option chunk, the rest is the optarg + if (( i < ${#1} - 1 )); then + OPTRET+=("-$opt" "${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+=("-$opt" "$2") + shift + break + # parse failure + else + printf "${0##*/}: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2 + OPTRET=(--) + return 1 + fi + ;; + # option's optarg is optional + *$opt\?*) + # if we're not at the end of the option chunk, the rest is the optarg + if (( i < ${#1} - 1 )); then + OPTRET+=("-$opt=${1:i+1}") + break + # option has no optarg + else + OPTRET+=("-$opt") + fi + ;; + # option has no optarg + *$opt*) + OPTRET+=("-$opt") + ;; + # option doesn't exist + *) + printf "${0##*/}: $(gettext "invalid option") -- '%s'\n" "$opt" >&2 OPTRET=(--) return 1 - fi - fi + ;; + esac done ;; --?*=*|--?*) # long option @@ -145,6 +168,15 @@ parseopts() { return 1 fi ;; + 2) + # --longopt=optarg + if [[ $1 = *=* ]]; then + OPTRET+=("--$opt=$optarg") + # --longopt + else + OPTRET+=("--$opt") + fi + ;; 254) # ambiguous option -- error was reported for us by longoptmatch() OPTRET=(--) diff --git a/test/scripts/parseopts_test.sh b/test/scripts/parseopts_test.sh index 9674c6a6..7d14bf29 100755 --- a/test/scripts/parseopts_test.sh +++ b/test/scripts/parseopts_test.sh @@ -16,12 +16,12 @@ if ! type -t parseopts &>/dev/null; then fi # borrow opts from makepkg -OPT_SHORT="AcdefFghiLmop:rRsV" +OPT_SHORT="AcdefFghiLmop:rRsVb;" OPT_LONG=('allsource' 'asroot' 'ignorearch' 'check' 'clean:' 'cleanall' 'nodeps' 'noextract' 'force' 'forcever:' 'geninteg' 'help' 'holdver' 'install' 'key:' 'log' 'nocolor' 'nobuild' 'nocheck' 'noprepare' 'nosign' 'pkg:' 'rmdeps' 'repackage' 'skipinteg' 'sign' 'source' 'syncdeps' 'version' 'config:' - 'noconfirm' 'noprogressbar') + 'noconfirm' 'noprogressbar' 'opt;') tap_parse() { local result=$1 tokencount=$2; shift 2 @@ -31,7 +31,7 @@ tap_parse() { unset OPTRET } -tap_plan 50 +tap_plan 54 # usage: tap_parse <expected result> <token count> test-params... # a failed tap_parse will match only the end of options marker '--' @@ -111,4 +111,10 @@ tap_parse '--force --' 2 --force # exact match on possible stem (opt has optarg) tap_parse '--clean foo --' 3 --clean=foo +# long opt with empty, non-empty, and no optional arg +tap_parse '--opt= --opt=foo --opt --' 4 --opt= --opt=foo --opt + +# short opt with and without optional arg, and non-option arg +tap_parse '-b=foo -A -b -- foo' 5 -bfoo -Ab foo + tap_finish -- 2.23.0
On 24/10/19 10:38 am, Ethan Sommer wrote:
Adds a "?" suffix that can be used to indicate that an option's argument is optional.
This allows options to have a default behaviour when the user doesn't specify one, e.g.: --color=[when] being able to behave like --color=auto when only --color is passed
Options with optional arguments given on the command line will be returned in the form "--opt=optarg" and "-o=optarg". Despite that not being the syntax for passing an argument with a shortopt (trying to pass -o=foo would make -o's argument "=foo"), this is done to allow the caller to split the option and its optarg easily
Again... devils advocate. You give an example of '--colour=auto' being equivalent to '--color'. Why would the default when the options is not specified not be default in the codebase? Why not follow the GNU extension to getopt and use '::' for option arguments instead of '?' Allan
Again... devils advocate. You give an example of '--colour=auto' being equivalent to '--color'. Why would the default when the options is not specified not be default in the codebase? Fair enough, another example could be if you wanted to allow specifying a custom log file for whatever reason, one could make -L/--log have an optional argument to specify what file to output the log to, and if no argument is given use the default log file that makepkg currently chooses. Why not follow the GNU extension to getopt and use '::' for option arguments instead of '?' The existing code is built around checking for and removing one trailing character from an option, it'd require both more code change and more code all-around to deal with checking for the same character either once or twice than to just check for a different single trailing character.
On Wed, Oct 23, 2019 at 08:38:08PM -0400, Ethan Sommer wrote:
Adds a "?" suffix that can be used to indicate that an option's argument is optional.
This allows options to have a default behaviour when the user doesn't specify one, e.g.: --color=[when] being able to behave like --color=auto when only --color is passed
Options with optional arguments given on the command line will be returned in the form "--opt=optarg" and "-o=optarg". Despite that not being the syntax for passing an argument with a shortopt (trying to pass -o=foo would make -o's argument "=foo"), this is done to allow the caller to split the option and its optarg easily
Signed-off-by: Ethan Sommer <e5ten.arch@gmail.com> --- scripts/libmakepkg/util/parseopts.sh.in | 116 +++++++++++++++--------- test/scripts/parseopts_test.sh | 12 ++- 2 files changed, 83 insertions(+), 45 deletions(-)
diff --git a/scripts/libmakepkg/util/parseopts.sh.in b/scripts/libmakepkg/util/parseopts.sh.in index c056cb1e..42540438 100644 --- a/scripts/libmakepkg/util/parseopts.sh.in +++ b/scripts/libmakepkg/util/parseopts.sh.in @@ -18,16 +18,23 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # # A getopt_long-like parser which portably supports longopts and -# shortopts with some GNU extensions. It does not allow for options -# with optional arguments. For both short and long opts, options -# requiring an argument should be suffixed with a colon. After the -# first argument containing the short opts, any number of valid long -# opts may be be passed. The end of the options delimiter must then be -# added, followed by the user arguments to the calling program. +# shortopts with some GNU extensions. For both short and long opts, +# options requiring an argument should be suffixed with a colon, and +# options with optional arguments should be suffixed with a question +# mark. After the first argument containing the short opts, any number +# of valid long opts may be be passed. The end of the options delimiter +# must then be added, followed by the user arguments to the calling +# program. +# +# Options with optional arguments will be returned as "--longopt=optarg" +# for longopts, or "-o=optarg" for shortopts. This isn't actually a valid +# way to pass an optional argument with a shortopt on the command line, +# but is done by parseopts to enable the caller script to split the option +# and its optarg easily. # # Recommended Usage: -# OPT_SHORT='fb:z' -# OPT_LONG=('foo' 'bar:' 'baz') +# OPT_SHORT='fb:zq?' +# OPT_LONG=('foo' 'bar:' 'baz' 'qux?') # if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then # exit 1 # fi @@ -49,29 +56,30 @@ parseopts() { longoptmatch() { local o longmatch=() for o in "${longopts[@]}"; do - if [[ ${o%:} = "$1" ]]; then + if [[ ${o%[:?]} = "$1" ]]; then longmatch=("$o") break fi - [[ ${o%:} = "$1"* ]] && longmatch+=("$o") + [[ ${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 ;; + # success, override with opt and return arg req (0 == none, 1 == required, 2 == optional) + opt=${longmatch%[:?]} + case $longmatch in + *:) return 1 ;; + *\?) return 2 ;; + *) return 0 ;; + esac + ;; 0) # fail, no match found return 255 ;; *) # fail, ambiguous match printf "${0##*/}: $(gettext "option '%s' is ambiguous; possibilities:")" "--$1" - printf " '%s'" "${longmatch[@]%:}" + printf " '%s'" "${longmatch[@]%[:?]}" printf '\n' return 254 ;; esac >&2 @@ -87,32 +95,47 @@ parseopts() { for (( i = 1; i < ${#1}; i++ )); do opt=${1:i:1}
- # option doesn't exist - if [[ $shortopts != *$opt* ]]; then - printf "${0##*/}: $(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 "${0##*/}: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2 + case $shortopts in + # option requires optarg + *$opt:*) + # if we're not at the end of the option chunk, the rest is the optarg + if (( i < ${#1} - 1 )); then + OPTRET+=("-$opt" "${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+=("-$opt" "$2") + shift + break + # parse failure + else + printf "${0##*/}: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2 + OPTRET=(--) + return 1 + fi + ;; + # option's optarg is optional + *$opt\?*) + # if we're not at the end of the option chunk, the rest is the optarg + if (( i < ${#1} - 1 )); then + OPTRET+=("-$opt=${1:i+1}") + break + # option has no optarg + else + OPTRET+=("-$opt") + fi + ;; + # option has no optarg + *$opt*) + OPTRET+=("-$opt") + ;; + # option doesn't exist + *) + printf "${0##*/}: $(gettext "invalid option") -- '%s'\n" "$opt" >&2 OPTRET=(--) return 1 - fi - fi + ;; + esac done ;; --?*=*|--?*) # long option @@ -145,6 +168,15 @@ parseopts() { return 1 fi ;; + 2) + # --longopt=optarg + if [[ $1 = *=* ]]; then + OPTRET+=("--$opt=$optarg") + # --longopt + else + OPTRET+=("--$opt") + fi + ;; 254) # ambiguous option -- error was reported for us by longoptmatch() OPTRET=(--) diff --git a/test/scripts/parseopts_test.sh b/test/scripts/parseopts_test.sh index 9674c6a6..7d14bf29 100755 --- a/test/scripts/parseopts_test.sh +++ b/test/scripts/parseopts_test.sh @@ -16,12 +16,12 @@ if ! type -t parseopts &>/dev/null; then fi
# borrow opts from makepkg -OPT_SHORT="AcdefFghiLmop:rRsV" +OPT_SHORT="AcdefFghiLmop:rRsVb;" OPT_LONG=('allsource' 'asroot' 'ignorearch' 'check' 'clean:' 'cleanall' 'nodeps' 'noextract' 'force' 'forcever:' 'geninteg' 'help' 'holdver' 'install' 'key:' 'log' 'nocolor' 'nobuild' 'nocheck' 'noprepare' 'nosign' 'pkg:' 'rmdeps' 'repackage' 'skipinteg' 'sign' 'source' 'syncdeps' 'version' 'config:' - 'noconfirm' 'noprogressbar') + 'noconfirm' 'noprogressbar' 'opt;')
tap_parse() { local result=$1 tokencount=$2; shift 2 @@ -31,7 +31,7 @@ tap_parse() { unset OPTRET }
-tap_plan 50 +tap_plan 54
# usage: tap_parse <expected result> <token count> test-params... # a failed tap_parse will match only the end of options marker '--' @@ -111,4 +111,10 @@ tap_parse '--force --' 2 --force # exact match on possible stem (opt has optarg) tap_parse '--clean foo --' 3 --clean=foo
+# long opt with empty, non-empty, and no optional arg +tap_parse '--opt= --opt=foo --opt --' 4 --opt= --opt=foo --opt + +# short opt with and without optional arg, and non-option arg +tap_parse '-b=foo -A -b -- foo' 5 -bfoo -Ab foo
I think I mentioned it before and I stil think it's weird -- short opts never use = for associating a value. can we just depend on '-afoo' meaning '-a' with the optional value 'foo' and drop the equals?
+
Do these tests really pass? We agreed on '?' rather than ';' as the symbol for optional.
tap_finish -- 2.23.0
Adds a "?" suffix that can be used to indicate that an option's argument is optional. This allows options to have a default behaviour when the user doesn't specify one, e.g.: --color=[when] being able to behave like --color=auto when only --color is passed Options with optional arguments given on the command line will be returned in the form "--opt=optarg" and "-o=optarg". Despite that not being the syntax for passing an argument with a shortopt (trying to pass -o=foo would make -o's argument "=foo"), this is done to allow the caller to split the option and its optarg easily Signed-off-by: Ethan Sommer <e5ten.arch@gmail.com> --- scripts/libmakepkg/util/parseopts.sh.in | 116 +++++++++++++++--------- test/scripts/parseopts_test.sh | 12 ++- 2 files changed, 83 insertions(+), 45 deletions(-) diff --git a/scripts/libmakepkg/util/parseopts.sh.in b/scripts/libmakepkg/util/parseopts.sh.in index c056cb1e..42540438 100644 --- a/scripts/libmakepkg/util/parseopts.sh.in +++ b/scripts/libmakepkg/util/parseopts.sh.in @@ -18,16 +18,23 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # # A getopt_long-like parser which portably supports longopts and -# shortopts with some GNU extensions. It does not allow for options -# with optional arguments. For both short and long opts, options -# requiring an argument should be suffixed with a colon. After the -# first argument containing the short opts, any number of valid long -# opts may be be passed. The end of the options delimiter must then be -# added, followed by the user arguments to the calling program. +# shortopts with some GNU extensions. For both short and long opts, +# options requiring an argument should be suffixed with a colon, and +# options with optional arguments should be suffixed with a question +# mark. After the first argument containing the short opts, any number +# of valid long opts may be be passed. The end of the options delimiter +# must then be added, followed by the user arguments to the calling +# program. +# +# Options with optional arguments will be returned as "--longopt=optarg" +# for longopts, or "-o=optarg" for shortopts. This isn't actually a valid +# way to pass an optional argument with a shortopt on the command line, +# but is done by parseopts to enable the caller script to split the option +# and its optarg easily. # # Recommended Usage: -# OPT_SHORT='fb:z' -# OPT_LONG=('foo' 'bar:' 'baz') +# OPT_SHORT='fb:zq?' +# OPT_LONG=('foo' 'bar:' 'baz' 'qux?') # if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then # exit 1 # fi @@ -49,29 +56,30 @@ parseopts() { longoptmatch() { local o longmatch=() for o in "${longopts[@]}"; do - if [[ ${o%:} = "$1" ]]; then + if [[ ${o%[:?]} = "$1" ]]; then longmatch=("$o") break fi - [[ ${o%:} = "$1"* ]] && longmatch+=("$o") + [[ ${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 ;; + # success, override with opt and return arg req (0 == none, 1 == required, 2 == optional) + opt=${longmatch%[:?]} + case $longmatch in + *:) return 1 ;; + *\?) return 2 ;; + *) return 0 ;; + esac + ;; 0) # fail, no match found return 255 ;; *) # fail, ambiguous match printf "${0##*/}: $(gettext "option '%s' is ambiguous; possibilities:")" "--$1" - printf " '%s'" "${longmatch[@]%:}" + printf " '%s'" "${longmatch[@]%[:?]}" printf '\n' return 254 ;; esac >&2 @@ -87,32 +95,47 @@ parseopts() { for (( i = 1; i < ${#1}; i++ )); do opt=${1:i:1} - # option doesn't exist - if [[ $shortopts != *$opt* ]]; then - printf "${0##*/}: $(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 "${0##*/}: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2 + case $shortopts in + # option requires optarg + *$opt:*) + # if we're not at the end of the option chunk, the rest is the optarg + if (( i < ${#1} - 1 )); then + OPTRET+=("-$opt" "${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+=("-$opt" "$2") + shift + break + # parse failure + else + printf "${0##*/}: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2 + OPTRET=(--) + return 1 + fi + ;; + # option's optarg is optional + *$opt\?*) + # if we're not at the end of the option chunk, the rest is the optarg + if (( i < ${#1} - 1 )); then + OPTRET+=("-$opt=${1:i+1}") + break + # option has no optarg + else + OPTRET+=("-$opt") + fi + ;; + # option has no optarg + *$opt*) + OPTRET+=("-$opt") + ;; + # option doesn't exist + *) + printf "${0##*/}: $(gettext "invalid option") -- '%s'\n" "$opt" >&2 OPTRET=(--) return 1 - fi - fi + ;; + esac done ;; --?*=*|--?*) # long option @@ -145,6 +168,15 @@ parseopts() { return 1 fi ;; + 2) + # --longopt=optarg + if [[ $1 = *=* ]]; then + OPTRET+=("--$opt=$optarg") + # --longopt + else + OPTRET+=("--$opt") + fi + ;; 254) # ambiguous option -- error was reported for us by longoptmatch() OPTRET=(--) diff --git a/test/scripts/parseopts_test.sh b/test/scripts/parseopts_test.sh index 9674c6a6..8f1ea1f3 100755 --- a/test/scripts/parseopts_test.sh +++ b/test/scripts/parseopts_test.sh @@ -16,12 +16,12 @@ if ! type -t parseopts &>/dev/null; then fi # borrow opts from makepkg -OPT_SHORT="AcdefFghiLmop:rRsV" +OPT_SHORT="AcdefFghiLmop:rRsVb?" OPT_LONG=('allsource' 'asroot' 'ignorearch' 'check' 'clean:' 'cleanall' 'nodeps' 'noextract' 'force' 'forcever:' 'geninteg' 'help' 'holdver' 'install' 'key:' 'log' 'nocolor' 'nobuild' 'nocheck' 'noprepare' 'nosign' 'pkg:' 'rmdeps' 'repackage' 'skipinteg' 'sign' 'source' 'syncdeps' 'version' 'config:' - 'noconfirm' 'noprogressbar') + 'noconfirm' 'noprogressbar' 'opt?') tap_parse() { local result=$1 tokencount=$2; shift 2 @@ -31,7 +31,7 @@ tap_parse() { unset OPTRET } -tap_plan 50 +tap_plan 54 # usage: tap_parse <expected result> <token count> test-params... # a failed tap_parse will match only the end of options marker '--' @@ -111,4 +111,10 @@ tap_parse '--force --' 2 --force # exact match on possible stem (opt has optarg) tap_parse '--clean foo --' 3 --clean=foo +# long opt with empty, non-empty, and no optional arg +tap_parse '--opt= --opt=foo --opt --' 4 --opt= --opt=foo --opt + +# short opt with and without optional arg, and non-option arg +tap_parse '-b=foo -A -b -- foo' 5 -bfoo -Ab foo + tap_finish -- 2.23.0
participants (3)
-
Allan McRae
-
Dave Reisner
-
Ethan Sommer