Merge branch 'ib/scripted-parse-opt-better-hint-string'
The "rev-parse --parseopt" mode parsed the option specification and the argument hint in a strange way to allow '=' and other special characters in the option name while forbidding them from the argument hint. This made it impossible to define an option like "--pair <key>=<value>" with "pair=key=value" specification, which instead would have defined a "--pair=key <value>" option. * ib/scripted-parse-opt-better-hint-string: rev-parse --parseopt: allow [*=?!] in argument hints
This commit is contained in:
commit
2bf2d819e1
@ -311,8 +311,8 @@ Each line of options has this format:
|
|||||||
`<opt-spec>`::
|
`<opt-spec>`::
|
||||||
its format is the short option character, then the long option name
|
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
|
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 `<flags>` characters.
|
||||||
`<opt-spec>`.
|
`h,help`, `dry-run` and `f` are examples of correct `<opt-spec>`.
|
||||||
|
|
||||||
`<flags>`::
|
`<flags>`::
|
||||||
`<flags>` are of `*`, `=`, `?` or `!`.
|
`<flags>` are of `*`, `=`, `?` or `!`.
|
||||||
|
@ -371,6 +371,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
|
|||||||
N_("output in stuck long form")),
|
N_("output in stuck long form")),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
};
|
};
|
||||||
|
static const char * const flag_chars = "*=?!";
|
||||||
|
|
||||||
struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT;
|
struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT;
|
||||||
const char **usage = NULL;
|
const char **usage = NULL;
|
||||||
@ -400,7 +401,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
|
|||||||
/* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */
|
/* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */
|
||||||
while (strbuf_getline(&sb, stdin, '\n') != EOF) {
|
while (strbuf_getline(&sb, stdin, '\n') != EOF) {
|
||||||
const char *s;
|
const char *s;
|
||||||
const char *end;
|
const char *help;
|
||||||
struct option *o;
|
struct option *o;
|
||||||
|
|
||||||
if (!sb.len)
|
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]));
|
memset(opts + onb, 0, sizeof(opts[onb]));
|
||||||
|
|
||||||
o = &opts[onb++];
|
o = &opts[onb++];
|
||||||
s = strchr(sb.buf, ' ');
|
help = strchr(sb.buf, ' ');
|
||||||
if (!s || *sb.buf == ' ') {
|
if (!help || *sb.buf == ' ') {
|
||||||
o->type = OPTION_GROUP;
|
o->type = OPTION_GROUP;
|
||||||
o->help = xstrdup(skipspaces(sb.buf));
|
o->help = xstrdup(skipspaces(sb.buf));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
o->type = OPTION_CALLBACK;
|
o->type = OPTION_CALLBACK;
|
||||||
o->help = xstrdup(skipspaces(s));
|
o->help = xstrdup(skipspaces(help));
|
||||||
o->value = &parsed;
|
o->value = &parsed;
|
||||||
o->flags = PARSE_OPT_NOARG;
|
o->flags = PARSE_OPT_NOARG;
|
||||||
o->callback = &parseopt_dump;
|
o->callback = &parseopt_dump;
|
||||||
|
|
||||||
/* Possible argument name hint */
|
/* name(s) */
|
||||||
end = s;
|
s = strpbrk(sb.buf, flag_chars);
|
||||||
while (s > sb.buf && strchr("*=?!", s[-1]) == NULL)
|
if (s == NULL)
|
||||||
--s;
|
s = help;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s - sb.buf == 1) /* short option only */
|
if (s - sb.buf == 1) /* short option only */
|
||||||
o->short_name = *sb.buf;
|
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->short_name = *sb.buf;
|
||||||
o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
|
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);
|
strbuf_release(&sb);
|
||||||
|
|
||||||
|
@ -3,7 +3,40 @@
|
|||||||
test_description='test git rev-parse --parseopt'
|
test_description='test git rev-parse --parseopt'
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
sed -e 's/^|//' >expect <<\END_EXPECT
|
test_expect_success 'setup optionspec' '
|
||||||
|
sed -e "s/^|//" >optionspec <<\EOF
|
||||||
|
|some-command [options] <args>...
|
||||||
|
|
|
||||||
|
|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
|
|cat <<\EOF
|
||||||
|usage: some-command [options] <args>...
|
|usage: some-command [options] <args>...
|
||||||
|
|
|
|
||||||
@ -28,49 +61,23 @@ sed -e 's/^|//' >expect <<\END_EXPECT
|
|||||||
| -g, --fluf[=<path>] short and long option optional argument
|
| -g, --fluf[=<path>] short and long option optional argument
|
||||||
| --longest <very-long-argument-hint>
|
| --longest <very-long-argument-hint>
|
||||||
| a 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
|
|Extras
|
||||||
| --extra1 line above used to cause a segfault but no longer does
|
| --extra1 line above used to cause a segfault but no longer does
|
||||||
|
|
|
|
||||||
|EOF
|
|EOF
|
||||||
END_EXPECT
|
END_EXPECT
|
||||||
|
|
||||||
sed -e 's/^|//' >optionspec <<\EOF
|
|
||||||
|some-command [options] <args>...
|
|
||||||
|
|
|
||||||
|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_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec &&
|
||||||
test_i18ncmp expect output
|
test_i18ncmp expect output
|
||||||
'
|
'
|
||||||
|
|
||||||
cat > expect <<EOF
|
test_expect_success 'setup expect.1' "
|
||||||
|
cat > expect <<EOF
|
||||||
set -- --foo --bar 'ham' -b -- 'arg'
|
set -- --foo --bar 'ham' -b -- 'arg'
|
||||||
EOF
|
EOF
|
||||||
|
"
|
||||||
|
|
||||||
test_expect_success 'test --parseopt' '
|
test_expect_success 'test --parseopt' '
|
||||||
git rev-parse --parseopt -- --foo --bar=ham --baz arg < optionspec > output &&
|
git rev-parse --parseopt -- --foo --bar=ham --baz arg < optionspec > output &&
|
||||||
@ -82,9 +89,11 @@ test_expect_success 'test --parseopt with mixed options and arguments' '
|
|||||||
test_cmp expect output
|
test_cmp expect output
|
||||||
'
|
'
|
||||||
|
|
||||||
cat > expect <<EOF
|
test_expect_success 'setup expect.2' "
|
||||||
|
cat > expect <<EOF
|
||||||
set -- --foo -- 'arg' '--bar=ham'
|
set -- --foo -- 'arg' '--bar=ham'
|
||||||
EOF
|
EOF
|
||||||
|
"
|
||||||
|
|
||||||
test_expect_success 'test --parseopt with --' '
|
test_expect_success 'test --parseopt with --' '
|
||||||
git rev-parse --parseopt -- --foo -- arg --bar=ham < optionspec > output &&
|
git rev-parse --parseopt -- --foo -- arg --bar=ham < optionspec > output &&
|
||||||
@ -96,54 +105,66 @@ test_expect_success 'test --parseopt --stop-at-non-option' '
|
|||||||
test_cmp expect output
|
test_cmp expect output
|
||||||
'
|
'
|
||||||
|
|
||||||
cat > expect <<EOF
|
test_expect_success 'setup expect.3' "
|
||||||
|
cat > expect <<EOF
|
||||||
set -- --foo -- '--' 'arg' '--bar=ham'
|
set -- --foo -- '--' 'arg' '--bar=ham'
|
||||||
EOF
|
EOF
|
||||||
|
"
|
||||||
|
|
||||||
test_expect_success 'test --parseopt --keep-dashdash' '
|
test_expect_success 'test --parseopt --keep-dashdash' '
|
||||||
git rev-parse --parseopt --keep-dashdash -- --foo -- arg --bar=ham < optionspec > output &&
|
git rev-parse --parseopt --keep-dashdash -- --foo -- arg --bar=ham < optionspec > output &&
|
||||||
test_cmp expect output
|
test_cmp expect output
|
||||||
'
|
'
|
||||||
|
|
||||||
cat >expect <<EOF
|
test_expect_success 'setup expect.4' "
|
||||||
|
cat >expect <<EOF
|
||||||
set -- --foo -- '--' 'arg' '--spam=ham'
|
set -- --foo -- '--' 'arg' '--spam=ham'
|
||||||
EOF
|
EOF
|
||||||
|
"
|
||||||
|
|
||||||
test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option with --' '
|
test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option with --' '
|
||||||
git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo -- arg --spam=ham <optionspec >output &&
|
git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo -- arg --spam=ham <optionspec >output &&
|
||||||
test_cmp expect output
|
test_cmp expect output
|
||||||
'
|
'
|
||||||
|
|
||||||
cat > expect <<EOF
|
test_expect_success 'setup expect.5' "
|
||||||
|
cat > expect <<EOF
|
||||||
set -- --foo -- 'arg' '--spam=ham'
|
set -- --foo -- 'arg' '--spam=ham'
|
||||||
EOF
|
EOF
|
||||||
|
"
|
||||||
|
|
||||||
test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option without --' '
|
test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option without --' '
|
||||||
git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo arg --spam=ham <optionspec >output &&
|
git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo arg --spam=ham <optionspec >output &&
|
||||||
test_cmp expect output
|
test_cmp expect output
|
||||||
'
|
'
|
||||||
|
|
||||||
cat > expect <<EOF
|
test_expect_success 'setup expect.6' "
|
||||||
|
cat > expect <<EOF
|
||||||
set -- --foo --bar='z' --baz -C'Z' --data='A' -- 'arg'
|
set -- --foo --bar='z' --baz -C'Z' --data='A' -- 'arg'
|
||||||
EOF
|
EOF
|
||||||
|
"
|
||||||
|
|
||||||
test_expect_success 'test --parseopt --stuck-long' '
|
test_expect_success 'test --parseopt --stuck-long' '
|
||||||
git rev-parse --parseopt --stuck-long -- --foo --bar=z -b arg -CZ -dA <optionspec >output &&
|
git rev-parse --parseopt --stuck-long -- --foo --bar=z -b arg -CZ -dA <optionspec >output &&
|
||||||
test_cmp expect output
|
test_cmp expect output
|
||||||
'
|
'
|
||||||
|
|
||||||
cat > expect <<EOF
|
test_expect_success 'setup expect.7' "
|
||||||
|
cat > expect <<EOF
|
||||||
set -- --data='' -C --baz -- 'arg'
|
set -- --data='' -C --baz -- 'arg'
|
||||||
EOF
|
EOF
|
||||||
|
"
|
||||||
|
|
||||||
test_expect_success 'test --parseopt --stuck-long and empty optional argument' '
|
test_expect_success 'test --parseopt --stuck-long and empty optional argument' '
|
||||||
git rev-parse --parseopt --stuck-long -- --data= arg -C -b <optionspec >output &&
|
git rev-parse --parseopt --stuck-long -- --data= arg -C -b <optionspec >output &&
|
||||||
test_cmp expect output
|
test_cmp expect output
|
||||||
'
|
'
|
||||||
|
|
||||||
cat > expect <<EOF
|
test_expect_success 'setup expect.8' "
|
||||||
|
cat > expect <<EOF
|
||||||
set -- --data --baz -- 'arg'
|
set -- --data --baz -- 'arg'
|
||||||
EOF
|
EOF
|
||||||
|
"
|
||||||
|
|
||||||
test_expect_success 'test --parseopt --stuck-long and long option with unset optional argument' '
|
test_expect_success 'test --parseopt --stuck-long and long option with unset optional argument' '
|
||||||
git rev-parse --parseopt --stuck-long -- --data arg -b <optionspec >output &&
|
git rev-parse --parseopt --stuck-long -- --data arg -b <optionspec >output &&
|
||||||
|
Loading…
Reference in New Issue
Block a user