In addition to "-ovalue" and "--opt=value" syntax, allow also "-o value"
and "--opt value" for options with optional argument when the newly
added PARSE_OPT_OPTARG_ALLOW_NEXT flag is set.

This behavior is turned off by default since it does not make sense for
tools using non-option command line arguments. Consider the ambiguity
of "cmd -d x", where "-d x" can mean either "-d with argument of x" or
"-d without argument, followed by non-option argument x". This is not an
issue in the case that the tool takes no non-option arguments.

To implement this, a new local variable, force_defval, is created in
get_value(), along with a comment explaining the logic.

Signed-off-by: Tomas Glozar <[email protected]>
---
 tools/lib/subcmd/parse-options.c | 53 +++++++++++++++++++++++++++-----
 tools/lib/subcmd/parse-options.h |  1 +
 2 files changed, 46 insertions(+), 8 deletions(-)

diff --git a/tools/lib/subcmd/parse-options.c b/tools/lib/subcmd/parse-options.c
index 555d617c1f50..664b2053bb77 100644
--- a/tools/lib/subcmd/parse-options.c
+++ b/tools/lib/subcmd/parse-options.c
@@ -72,6 +72,7 @@ static int get_value(struct parse_opt_ctx_t *p,
        const char *s, *arg = NULL;
        const int unset = flags & OPT_UNSET;
        int err;
+       bool force_defval = false;
 
        if (unset && p->opt)
                return opterror(opt, "takes no value", flags);
@@ -123,6 +124,42 @@ static int get_value(struct parse_opt_ctx_t *p,
                }
        }
 
+       if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+               if (!(p->flags & PARSE_OPT_OPTARG_ALLOW_NEXT)) {
+                       /*
+                        * If the option has an optional argument, and the 
argument is not
+                        * provided in the option itself, do not attempt to get 
it from
+                        * the next argument, unless 
PARSE_OPT_OPTARG_ALLOW_NEXT is set.
+                        *
+                        * This prevents a non-option argument from being 
interpreted as an
+                        * optional argument of a preceding option, for example:
+                        *
+                        * $ cmd --opt val
+                        * -> is "val" argument of "--opt" or a separate 
non-option
+                        * argument?
+                        *
+                        * With PARSE_OPT_OPTARG_ALLOW_NEXT, "val" is 
interpreted as
+                        * the argument of "--opt", i.e. the same as 
"--opt=val".
+                        * Without PARSE_OPT_OPTARG_ALLOW_NEXT, --opt is 
interpreted
+                        * as having the default value, and "val" as a separate 
non-option
+                        * argument.
+                        *
+                        * PARSE_OPT_OPTARG_ALLOW_NEXT is useful for commands 
that take no
+                        * non-option arguments and want to allow more 
flexibility in
+                        * optional argument passing.
+                        */
+                       force_defval = true;
+               }
+
+               if (p->argc <= 1 || p->argv[1][0] == '-') {
+                       /*
+                        * If next argument is an option or does not exist,
+                        * use the default value.
+                        */
+                       force_defval = true;
+               }
+       }
+
        if (opt->flags & PARSE_OPT_NOBUILD) {
                char reason[128];
                bool noarg = false;
@@ -148,7 +185,7 @@ static int get_value(struct parse_opt_ctx_t *p,
                        noarg = true;
                if (opt->flags & PARSE_OPT_NOARG)
                        noarg = true;
-               if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
+               if (force_defval)
                        noarg = true;
 
                switch (opt->type) {
@@ -212,7 +249,7 @@ static int get_value(struct parse_opt_ctx_t *p,
                err = 0;
                if (unset)
                        *(const char **)opt->value = NULL;
-               else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
+               else if (force_defval)
                        *(const char **)opt->value = (const char *)opt->defval;
                else
                        err = get_arg(p, opt, flags, (const char **)opt->value);
@@ -244,7 +281,7 @@ static int get_value(struct parse_opt_ctx_t *p,
                        return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
                if (opt->flags & PARSE_OPT_NOARG)
                        return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
-               if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
+               if (force_defval)
                        return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
                if (get_arg(p, opt, flags, &arg))
                        return -1;
@@ -255,7 +292,7 @@ static int get_value(struct parse_opt_ctx_t *p,
                        *(int *)opt->value = 0;
                        return 0;
                }
-               if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+               if (force_defval) {
                        *(int *)opt->value = opt->defval;
                        return 0;
                }
@@ -271,7 +308,7 @@ static int get_value(struct parse_opt_ctx_t *p,
                        *(unsigned int *)opt->value = 0;
                        return 0;
                }
-               if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+               if (force_defval) {
                        *(unsigned int *)opt->value = opt->defval;
                        return 0;
                }
@@ -289,7 +326,7 @@ static int get_value(struct parse_opt_ctx_t *p,
                        *(long *)opt->value = 0;
                        return 0;
                }
-               if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+               if (force_defval) {
                        *(long *)opt->value = opt->defval;
                        return 0;
                }
@@ -305,7 +342,7 @@ static int get_value(struct parse_opt_ctx_t *p,
                        *(unsigned long *)opt->value = 0;
                        return 0;
                }
-               if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+               if (force_defval) {
                        *(unsigned long *)opt->value = opt->defval;
                        return 0;
                }
@@ -321,7 +358,7 @@ static int get_value(struct parse_opt_ctx_t *p,
                        *(u64 *)opt->value = 0;
                        return 0;
                }
-               if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+               if (force_defval) {
                        *(u64 *)opt->value = opt->defval;
                        return 0;
                }
diff --git a/tools/lib/subcmd/parse-options.h b/tools/lib/subcmd/parse-options.h
index 8e9147358a28..c573a0ca5ca6 100644
--- a/tools/lib/subcmd/parse-options.h
+++ b/tools/lib/subcmd/parse-options.h
@@ -33,6 +33,7 @@ enum parse_opt_flags {
        PARSE_OPT_KEEP_ARGV0 = 4,
        PARSE_OPT_KEEP_UNKNOWN = 8,
        PARSE_OPT_NO_INTERNAL_HELP = 16,
+       PARSE_OPT_OPTARG_ALLOW_NEXT = 32,
 };
 
 enum parse_opt_option_flags {
-- 
2.53.0


Reply via email to