From 993448d552532951653bf4b615b0da582775b837 Mon Sep 17 00:00:00 2001 From: Fabian Boehm Date: Mon, 27 Jun 2022 16:59:12 +0200 Subject: [PATCH] argparse: Allow usage without optspecs It's still useful without, for instance to implement a command that takes no options, or to check min-args or max-args. (technically no optspecs, no min/max args and --ignore-unknown does nothing, but that's a very specific error that we don't need to forbid) Fixes #9006 --- doc_src/cmds/argparse.rst | 5 +++-- src/builtins/argparse.cpp | 13 ++++++------- tests/checks/argparse.fish | 27 +++++++++++++++++---------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/doc_src/cmds/argparse.rst b/doc_src/cmds/argparse.rst index 49c148266..02ebb9c12 100644 --- a/doc_src/cmds/argparse.rst +++ b/doc_src/cmds/argparse.rst @@ -64,10 +64,11 @@ If ``$argv`` is empty then there is nothing to parse and ``argparse`` returns ze The ``or return`` means that the function returns ``argparse``'s status if it failed, so if it goes on ``argparse`` succeeded. -The ``--`` argument is required. You do not have to include any arguments after the ``--`` but you must include the ``--``. For example, this is acceptable:: +The ``--`` argument is required. You do not have to include any option specifications or arguments after the ``--`` but you must include the ``--``. For example, this is acceptable:: - set -l argv + set -l argv foo argparse 'h/help' 'n/name' -- $argv + argparse --min-args=1 -- $argv But this is not:: diff --git a/src/builtins/argparse.cpp b/src/builtins/argparse.cpp index 996d1466a..892b698c7 100644 --- a/src/builtins/argparse.cpp +++ b/src/builtins/argparse.cpp @@ -353,11 +353,6 @@ static int collect_option_specs(argparse_cmd_opts_t &opts, int *optind, int argc return STATUS_INVALID_ARGS; } - if (opts.options.empty()) { - streams.err.append_format(_(L"%ls: No option specs were provided\n"), cmd); - return STATUS_INVALID_ARGS; - } - return STATUS_CMD_OK; } @@ -428,9 +423,13 @@ static int parse_cmd_opts(argparse_cmd_opts_t &opts, int *optind, //!OCLINT(hig if (opts.print_help) return STATUS_CMD_OK; - if (argc == w.woptind || std::wcscmp(L"--", argv[w.woptind - 1]) == 0) { + if (std::wcscmp(L"--", argv[w.woptind - 1]) == 0) { + --w.woptind; + } + + if (argc == w.woptind) { // The user didn't specify any option specs. - streams.err.append_format(_(L"%ls: No option specs were provided\n"), cmd); + streams.err.append_format(_(L"%ls: Missing -- separator\n"), cmd); return STATUS_INVALID_ARGS; } diff --git a/tests/checks/argparse.fish b/tests/checks/argparse.fish index 9a5885de1..5545f00d9 100644 --- a/tests/checks/argparse.fish +++ b/tests/checks/argparse.fish @@ -1,15 +1,16 @@ #RUN: %fish %s ########## -set -g LANG C +# NOTE: This uses argparse, which touches the local variables. +# Any call that isn't an error should be enclosed in a begin/end block! # Start by verifying a bunch of error conditions. # These are *argparse* errors, and therefore bugs in the script, # so they print a stack trace. -# No args is an error +# No args (not even --) is an error argparse -#CHECKERR: argparse: No option specs were provided +#CHECKERR: argparse: Missing -- separator #CHECKERR: checks/argparse.fish (line {{\d+}}): #CHECKERR: argparse #CHECKERR: ^ @@ -23,13 +24,14 @@ argparse h/help #CHECKERR: ^ #CHECKERR: (Type 'help argparse' for related documentation) -# Flags but no option specs is an error -argparse -s -- hello -#CHECKERR: argparse: No option specs were provided -#CHECKERR: checks/argparse.fish (line {{\d+}}): -#CHECKERR: argparse -s -- hello -#CHECKERR: ^ -#CHECKERR: (Type 'help argparse' for related documentation) +# Flags but no option specs is not an error +begin + argparse -s -- hello + echo $status + # CHECK: 0 + set -l + # CHECK: argv hello +end # Invalid option specs argparse h- @@ -67,7 +69,10 @@ argparse h-help=x begin argparse --name min-max --min-args 1 h/help -- #CHECKERR: min-max: expected >= 1 arguments; got 0 + argparse --name min-max --min-args 1 -- + #CHECKERR: min-max: expected >= 1 arguments; got 0 argparse --name min-max --min-args 1 --max-args 3 h/help -- arg1 + argparse --name min-max --min-args 1 --max-args 3 -- arg1 argparse --name min-max --min-args 1 --max-args 3 h/help -- arg1 arg2 argparse --name min-max --min-args 1 --max-args 3 h/help -- --help arg1 arg2 arg3 argparse --name min-max --min-args 1 --max-args 3 h/help -- arg1 arg2 -h arg3 arg4 @@ -76,6 +81,8 @@ begin argparse --name min-max --max-args 1 h/help -- arg1 argparse --name min-max --max-args 1 h/help -- arg1 arg2 #CHECKERR: min-max: expected <= 1 arguments; got 2 + argparse --name min-max --max-args 1 -- arg1 arg2 + #CHECKERR: min-max: expected <= 1 arguments; got 2 end # Invalid \"#-val\" spec