Skip to content

Commit 534d9a9

Browse files
tools subcmd: support optarg as separate argument
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. Link: https://lore.kernel.org/r/20260528103254.2990068-3-tglozar@redhat.com Signed-off-by: Tomas Glozar <tglozar@redhat.com>
1 parent 48209d7 commit 534d9a9

2 files changed

Lines changed: 46 additions & 8 deletions

File tree

tools/lib/subcmd/parse-options.c

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ static int get_value(struct parse_opt_ctx_t *p,
7272
const char *s, *arg = NULL;
7373
const int unset = flags & OPT_UNSET;
7474
int err;
75+
bool force_defval = false;
7576

7677
if (unset && p->opt)
7778
return opterror(opt, "takes no value", flags);
@@ -123,6 +124,42 @@ static int get_value(struct parse_opt_ctx_t *p,
123124
}
124125
}
125126

127+
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
128+
if (!(p->flags & PARSE_OPT_OPTARG_ALLOW_NEXT)) {
129+
/*
130+
* If the option has an optional argument, and the argument is not
131+
* provided in the option itself, do not attempt to get it from
132+
* the next argument, unless PARSE_OPT_OPTARG_ALLOW_NEXT is set.
133+
*
134+
* This prevents a non-option argument from being interpreted as an
135+
* optional argument of a preceding option, for example:
136+
*
137+
* $ cmd --opt val
138+
* -> is "val" argument of "--opt" or a separate non-option
139+
* argument?
140+
*
141+
* With PARSE_OPT_OPTARG_ALLOW_NEXT, "val" is interpreted as
142+
* the argument of "--opt", i.e. the same as "--opt=val".
143+
* Without PARSE_OPT_OPTARG_ALLOW_NEXT, --opt is interpreted
144+
* as having the default value, and "val" as a separate non-option
145+
* argument.
146+
*
147+
* PARSE_OPT_OPTARG_ALLOW_NEXT is useful for commands that take no
148+
* non-option arguments and want to allow more flexibility in
149+
* optional argument passing.
150+
*/
151+
force_defval = true;
152+
}
153+
154+
if (p->argc <= 1 || p->argv[1][0] == '-') {
155+
/*
156+
* If next argument is an option or does not exist,
157+
* use the default value.
158+
*/
159+
force_defval = true;
160+
}
161+
}
162+
126163
if (opt->flags & PARSE_OPT_NOBUILD) {
127164
char reason[128];
128165
bool noarg = false;
@@ -148,7 +185,7 @@ static int get_value(struct parse_opt_ctx_t *p,
148185
noarg = true;
149186
if (opt->flags & PARSE_OPT_NOARG)
150187
noarg = true;
151-
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
188+
if (force_defval)
152189
noarg = true;
153190

154191
switch (opt->type) {
@@ -212,7 +249,7 @@ static int get_value(struct parse_opt_ctx_t *p,
212249
err = 0;
213250
if (unset)
214251
*(const char **)opt->value = NULL;
215-
else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
252+
else if (force_defval)
216253
*(const char **)opt->value = (const char *)opt->defval;
217254
else
218255
err = get_arg(p, opt, flags, (const char **)opt->value);
@@ -244,7 +281,7 @@ static int get_value(struct parse_opt_ctx_t *p,
244281
return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
245282
if (opt->flags & PARSE_OPT_NOARG)
246283
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
247-
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
284+
if (force_defval)
248285
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
249286
if (get_arg(p, opt, flags, &arg))
250287
return -1;
@@ -255,7 +292,7 @@ static int get_value(struct parse_opt_ctx_t *p,
255292
*(int *)opt->value = 0;
256293
return 0;
257294
}
258-
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
295+
if (force_defval) {
259296
*(int *)opt->value = opt->defval;
260297
return 0;
261298
}
@@ -271,7 +308,7 @@ static int get_value(struct parse_opt_ctx_t *p,
271308
*(unsigned int *)opt->value = 0;
272309
return 0;
273310
}
274-
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
311+
if (force_defval) {
275312
*(unsigned int *)opt->value = opt->defval;
276313
return 0;
277314
}
@@ -289,7 +326,7 @@ static int get_value(struct parse_opt_ctx_t *p,
289326
*(long *)opt->value = 0;
290327
return 0;
291328
}
292-
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
329+
if (force_defval) {
293330
*(long *)opt->value = opt->defval;
294331
return 0;
295332
}
@@ -305,7 +342,7 @@ static int get_value(struct parse_opt_ctx_t *p,
305342
*(unsigned long *)opt->value = 0;
306343
return 0;
307344
}
308-
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
345+
if (force_defval) {
309346
*(unsigned long *)opt->value = opt->defval;
310347
return 0;
311348
}
@@ -321,7 +358,7 @@ static int get_value(struct parse_opt_ctx_t *p,
321358
*(u64 *)opt->value = 0;
322359
return 0;
323360
}
324-
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
361+
if (force_defval) {
325362
*(u64 *)opt->value = opt->defval;
326363
return 0;
327364
}

tools/lib/subcmd/parse-options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ enum parse_opt_flags {
3333
PARSE_OPT_KEEP_ARGV0 = 4,
3434
PARSE_OPT_KEEP_UNKNOWN = 8,
3535
PARSE_OPT_NO_INTERNAL_HELP = 16,
36+
PARSE_OPT_OPTARG_ALLOW_NEXT = 32,
3637
};
3738

3839
enum parse_opt_option_flags {

0 commit comments

Comments
 (0)