diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index c483100e75..b6c6326cdc 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -311,8 +311,8 @@ Each line of options has this format: ``:: its format is the short option character, then the long option name separated by a comma. Both parts are not required, though at least one - is necessary. `h,help`, `dry-run` and `f` are all three correct - ``. + is necessary. May not contain any of the `` characters. + `h,help`, `dry-run` and `f` are examples of correct ``. ``:: `` are of `*`, `=`, `?` or `!`. diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index b6232390a6..02d747dcb1 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -371,6 +371,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) N_("output in stuck long form")), OPT_END(), }; + static const char * const flag_chars = "*=?!"; struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT; const char **usage = NULL; @@ -400,7 +401,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) /* parse: (|,|)[*=?!]*? SP+ */ while (strbuf_getline(&sb, stdin, '\n') != EOF) { const char *s; - const char *end; + const char *help; struct option *o; if (!sb.len) @@ -410,45 +411,23 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) memset(opts + onb, 0, sizeof(opts[onb])); o = &opts[onb++]; - s = strchr(sb.buf, ' '); - if (!s || *sb.buf == ' ') { + help = strchr(sb.buf, ' '); + if (!help || *sb.buf == ' ') { o->type = OPTION_GROUP; o->help = xstrdup(skipspaces(sb.buf)); continue; } o->type = OPTION_CALLBACK; - o->help = xstrdup(skipspaces(s)); + o->help = xstrdup(skipspaces(help)); o->value = &parsed; o->flags = PARSE_OPT_NOARG; o->callback = &parseopt_dump; - /* Possible argument name hint */ - end = s; - while (s > sb.buf && strchr("*=?!", s[-1]) == NULL) - --s; - if (s != sb.buf && s != end) - o->argh = xmemdupz(s, end - s); - if (s == sb.buf) - s = end; - - while (s > sb.buf && strchr("*=?!", s[-1])) { - switch (*--s) { - case '=': - o->flags &= ~PARSE_OPT_NOARG; - break; - case '?': - o->flags &= ~PARSE_OPT_NOARG; - o->flags |= PARSE_OPT_OPTARG; - break; - case '!': - o->flags |= PARSE_OPT_NONEG; - break; - case '*': - o->flags |= PARSE_OPT_HIDDEN; - break; - } - } + /* name(s) */ + s = strpbrk(sb.buf, flag_chars); + if (s == NULL) + s = help; if (s - sb.buf == 1) /* short option only */ o->short_name = *sb.buf; @@ -458,6 +437,30 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) o->short_name = *sb.buf; o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2); } + + /* flags */ + while (s < help) { + switch (*s++) { + case '=': + o->flags &= ~PARSE_OPT_NOARG; + continue; + case '?': + o->flags &= ~PARSE_OPT_NOARG; + o->flags |= PARSE_OPT_OPTARG; + continue; + case '!': + o->flags |= PARSE_OPT_NONEG; + continue; + case '*': + o->flags |= PARSE_OPT_HIDDEN; + continue; + } + s--; + break; + } + + if (s < help) + o->argh = xmemdupz(s, help - s); } strbuf_release(&sb); diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh index ebe7c3b87c..310f93fd30 100755 --- a/t/t1502-rev-parse-parseopt.sh +++ b/t/t1502-rev-parse-parseopt.sh @@ -3,7 +3,40 @@ test_description='test git rev-parse --parseopt' . ./test-lib.sh -sed -e 's/^|//' >expect <<\END_EXPECT +test_expect_success 'setup optionspec' ' + sed -e "s/^|//" >optionspec <<\EOF +|some-command [options] ... +| +|some-command does foo and bar! +|-- +|h,help show the help +| +|foo some nifty option --foo +|bar= some cool option --bar with an argument +|b,baz a short and long option +| +| An option group Header +|C? option C with an optional argument +|d,data? short and long option with an optional argument +| +| Argument hints +|B=arg short option required argument +|bar2=arg long option required argument +|e,fuz=with-space short and long option required argument +|s?some short option optional argument +|long?data long option optional argument +|g,fluf?path short and long option optional argument +|longest=very-long-argument-hint a very long argument hint +|pair=key=value with an equals sign in the hint +|short-hint=a with a one symbol hint +| +|Extras +|extra1 line above used to cause a segfault but no longer does +EOF +' + +test_expect_success 'test --parseopt help output' ' + sed -e "s/^|//" >expect <<\END_EXPECT && |cat <<\EOF |usage: some-command [options] ... | @@ -28,49 +61,23 @@ sed -e 's/^|//' >expect <<\END_EXPECT | -g, --fluf[=] short and long option optional argument | --longest | a very long argument hint +| --pair with an equals sign in the hint +| --short-hint with a one symbol hint | |Extras | --extra1 line above used to cause a segfault but no longer does | |EOF END_EXPECT - -sed -e 's/^|//' >optionspec <<\EOF -|some-command [options] ... -| -|some-command does foo and bar! -|-- -|h,help show the help -| -|foo some nifty option --foo -|bar= some cool option --bar with an argument -|b,baz a short and long option -| -| An option group Header -|C? option C with an optional argument -|d,data? short and long option with an optional argument -| -| Argument hints -|B=arg short option required argument -|bar2=arg long option required argument -|e,fuz=with-space short and long option required argument -|s?some short option optional argument -|long?data long option optional argument -|g,fluf?path short and long option optional argument -|longest=very-long-argument-hint a very long argument hint -| -|Extras -|extra1 line above used to cause a segfault but no longer does -EOF - -test_expect_success 'test --parseopt help output' ' test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec && test_i18ncmp expect output ' -cat > expect < expect < output && @@ -82,9 +89,11 @@ test_expect_success 'test --parseopt with mixed options and arguments' ' test_cmp expect output ' -cat > expect < expect < output && @@ -96,54 +105,66 @@ test_expect_success 'test --parseopt --stop-at-non-option' ' test_cmp expect output ' -cat > expect < expect < output && test_cmp expect output ' -cat >expect <expect <output && test_cmp expect output ' -cat > expect < expect <output && test_cmp expect output ' -cat > expect < expect <output && test_cmp expect output ' -cat > expect < expect <output && test_cmp expect output ' -cat > expect < expect <output &&