]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
tools subcmd: allow parsing distinct --opt and --no-opt
authorTomas Glozar <tglozar@redhat.com>
Thu, 28 May 2026 10:32:51 +0000 (12:32 +0200)
committerTomas Glozar <tglozar@redhat.com>
Thu, 28 May 2026 11:02:48 +0000 (13:02 +0200)
libsubcmd automatically generates for every option --opt an equivalent
negated option, --no-opt, to unset the option. Vice versa, for every
option declared as --no-opt, a shorthand --opt is declared for
convenience.

Add a flag, PARSE_OPT_NOAUTONEG, to disable this behavior. This new flag
behaves similarly to the already existing PARSE_OPT_NONEG, only it does
not reject the --no-opt variant, but leaves it undefined. That is useful
when there is a conflicting distinct --no-opt option in the syntax of
the tool.

PARSE_OPT_NOAUTONEG is enabled per-option, allowing to unset other
options that do not have this conflict.

Link: https://lore.kernel.org/r/20260528103254.2990068-4-tglozar@redhat.com
Signed-off-by: Tomas Glozar <tglozar@redhat.com>
tools/lib/subcmd/parse-options.c
tools/lib/subcmd/parse-options.h

index 664b2053bb7729940b1f5e818fff9aadd3de0e7b..e83200e9f56a439233c67d6cf829eac015de7c32 100644 (file)
@@ -427,7 +427,8 @@ retry:
                        return 0;
                }
                if (!rest) {
-                       if (strstarts(options->long_name, "no-")) {
+                       if (strstarts(options->long_name, "no-") &&
+                           !(options->flags & PARSE_OPT_NOAUTONEG)) {
                                /*
                                 * The long name itself starts with "no-", so
                                 * accept the option without "no-" so that users
@@ -465,12 +466,12 @@ is_abbreviated:
                                continue;
                        }
                        /* negated and abbreviated very much? */
-                       if (strstarts("no-", arg)) {
+                       if (strstarts("no-", arg) && !(options->flags & PARSE_OPT_NOAUTONEG)) {
                                flags |= OPT_UNSET;
                                goto is_abbreviated;
                        }
                        /* negated? */
-                       if (strncmp(arg, "no-", 3))
+                       if (strncmp(arg, "no-", 3) || (options->flags & PARSE_OPT_NOAUTONEG))
                                continue;
                        flags |= OPT_UNSET;
                        rest = skip_prefix(arg + 3, options->long_name);
@@ -1019,7 +1020,8 @@ opt:
                if (strstarts(opts->long_name, optstr))
                        print_option_help(opts, 0);
                if (strstarts("no-", optstr) &&
-                   strstarts(opts->long_name, optstr + 3))
+                   strstarts(opts->long_name, optstr + 3) &&
+                       !(opts->flags & PARSE_OPT_NOAUTONEG))
                        print_option_help(opts, 0);
        }
 
index c573a0ca5ca66f20ccde3480614a2524105c355f..38df5fd2196382ae62a621275e527226b66f1262 100644 (file)
@@ -47,6 +47,7 @@ enum parse_opt_option_flags {
        PARSE_OPT_NOEMPTY  = 128,
        PARSE_OPT_NOBUILD  = 256,
        PARSE_OPT_CANSKIP  = 512,
+       PARSE_OPT_NOAUTONEG = 1024,
 };
 
 struct option;
@@ -149,6 +150,8 @@ struct option {
        { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
 #define OPT_CALLBACK(s, l, v, a, h, f) \
        { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f) }
+#define OPT_CALLBACK_FLAG(s, l, v, a, h, f, fl) \
+       { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .flags = (fl) }
 #define OPT_CALLBACK_SET(s, l, v, os, a, h, f) \
        { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .set = check_vtype(os, bool *)}
 #define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \