Code

parse-options: allow positivation of options starting, with no-
authorRené Scharfe <rene.scharfe@lsrfire.ath.cx>
Sat, 25 Feb 2012 19:14:54 +0000 (20:14 +0100)
committerJunio C Hamano <gitster@pobox.com>
Sun, 26 Feb 2012 23:32:53 +0000 (15:32 -0800)
Long options can be negated by adding no- right after the leading
two dashes. This is useful e.g. to override options set by aliases.

For options that are defined to start with no- already, this looks
a bit funny. Allow such options to also be negated by removing the
prefix.

The following thirteen options are affected:

apply          --no-add
bisect--helper --no-checkout
checkout-index --no-create
clone          --no-checkout --no-hardlinks
commit         --no-verify   --no-post-rewrite
format-patch   --no-binary
hash-object    --no-filters
read-tree      --no-sparse-checkout
revert         --no-commit
show-branch    --no-name
update-ref     --no-deref

The following five are NOT affected because they are defined with
PARSE_OPT_NONEG or the non-negated version is defined as well:

branch       --no-merged
format-patch --no-stat             --no-numbered
update-index --no-assume-unchanged --no-skip-worktree

Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/technical/api-parse-options.txt
parse-options.c
t/t0040-parse-options.sh

index 4b92514f60d65232e955cd5dddbeb5318add7274..2527b7e8d7516285aee4a85bf145f391e89463a7 100644 (file)
@@ -39,7 +39,8 @@ The parse-options API allows:
 * Short options may be bundled, e.g. `-a -b` can be specified as `-ab`.
 
 * Boolean long options can be 'negated' (or 'unset') by prepending
-  `no-`, e.g. `\--no-abbrev` instead of `\--abbrev`.
+  `no-`, e.g. `\--no-abbrev` instead of `\--abbrev`. Conversely,
+  options that begin with `no-` can be 'negated' by removing it.
 
 * Options and non-option arguments can clearly be separated using the `\--`
   option, e.g. `-a -b \--option \-- \--this-is-a-file` indicates that
index f0098eb8ea7eed27d8a67952481f043ce7c75d4a..890684166520659106703420fe80be147c7ad75d 100644 (file)
@@ -193,13 +193,14 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                arg_end = arg + strlen(arg);
 
        for (; options->type != OPTION_END; options++) {
-               const char *rest;
-               int flags = 0;
+               const char *rest, *long_name = options->long_name;
+               int flags = 0, opt_flags = 0;
 
-               if (!options->long_name)
+               if (!long_name)
                        continue;
 
-               rest = skip_prefix(arg, options->long_name);
+again:
+               rest = skip_prefix(arg, long_name);
                if (options->type == OPTION_ARGUMENT) {
                        if (!rest)
                                continue;
@@ -212,7 +213,7 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                }
                if (!rest) {
                        /* abbreviated? */
-                       if (!strncmp(options->long_name, arg, arg_end - arg)) {
+                       if (!strncmp(long_name, arg, arg_end - arg)) {
 is_abbreviated:
                                if (abbrev_option) {
                                        /*
@@ -227,7 +228,7 @@ is_abbreviated:
                                if (!(flags & OPT_UNSET) && *arg_end)
                                        p->opt = arg_end + 1;
                                abbrev_option = options;
-                               abbrev_flags = flags;
+                               abbrev_flags = flags ^ opt_flags;
                                continue;
                        }
                        /* negation allowed? */
@@ -239,12 +240,18 @@ is_abbreviated:
                                goto is_abbreviated;
                        }
                        /* negated? */
-                       if (strncmp(arg, "no-", 3))
+                       if (prefixcmp(arg, "no-")) {
+                               if (!prefixcmp(long_name, "no-")) {
+                                       long_name += 3;
+                                       opt_flags |= OPT_UNSET;
+                                       goto again;
+                               }
                                continue;
+                       }
                        flags |= OPT_UNSET;
-                       rest = skip_prefix(arg + 3, options->long_name);
+                       rest = skip_prefix(arg + 3, long_name);
                        /* abbreviated and negated? */
-                       if (!rest && !prefixcmp(options->long_name, arg + 3))
+                       if (!rest && !prefixcmp(long_name, arg + 3))
                                goto is_abbreviated;
                        if (!rest)
                                continue;
@@ -254,7 +261,7 @@ is_abbreviated:
                                continue;
                        p->opt = rest + 1;
                }
-               return get_value(p, options, flags);
+               return get_value(p, options, flags ^ opt_flags);
        }
 
        if (ambiguous_option)
index ca25e588ca4701501e53421ec13c62504ee0e6f2..a44bcb9b3983fa2ba3bc9182702e309577bfc441 100755 (executable)
@@ -107,7 +107,7 @@ test_expect_success 'OPT_BOOL() negation #2' 'check boolean: 0 -D --no-no-doubt'
 test_expect_success 'OPT_BOOL() no negation #1' 'check_unknown --fear'
 test_expect_success 'OPT_BOOL() no negation #2' 'check_unknown --no-no-fear'
 
-test_expect_failure 'OPT_BOOL() positivation' 'check boolean: 0 -D --doubt'
+test_expect_success 'OPT_BOOL() positivation' 'check boolean: 0 -D --doubt'
 
 cat > expect << EOF
 boolean: 2