Skip to content

Commit b2a2c4d

Browse files
committed
Merge branch 'bc/rev-parse-parseopt-fix'
Recent versions of "git rev-parse --parseopt" did not parse the option specification that does not have the optional flags (*=?!) correctly, which has been corrected. * bc/rev-parse-parseopt-fix: parse-options: only insert newline in help text if needed parse-options: write blank line to correct output stream t0040,t1502: Demonstrate parse_options bugs git-rebase: don't ignore unexpected command line arguments rev-parse parseopt: interpret any whitespace as start of help text rev-parse parseopt: do not search help text for flag chars t1502: demonstrate rev-parse --parseopt option mis-parsing
2 parents 5f3108b + a6304fa commit b2a2c4d

File tree

6 files changed

+139
-10
lines changed

6 files changed

+139
-10
lines changed

builtin/rev-parse.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,14 @@ static const char *skipspaces(const char *s)
387387
return s;
388388
}
389389

390+
static char *findspace(const char *s)
391+
{
392+
for (; *s; s++)
393+
if (isspace(*s))
394+
return (char*)s;
395+
return NULL;
396+
}
397+
390398
static int cmd_parseopt(int argc, const char **argv, const char *prefix)
391399
{
392400
static int keep_dashdash = 0, stop_at_non_option = 0;
@@ -434,7 +442,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
434442
/* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */
435443
while (strbuf_getline(&sb, stdin) != EOF) {
436444
const char *s;
437-
const char *help;
445+
char *help;
438446
struct option *o;
439447

440448
if (!sb.len)
@@ -444,15 +452,17 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
444452
memset(opts + onb, 0, sizeof(opts[onb]));
445453

446454
o = &opts[onb++];
447-
help = strchr(sb.buf, ' ');
448-
if (!help || *sb.buf == ' ') {
455+
help = findspace(sb.buf);
456+
if (!help || sb.buf == help) {
449457
o->type = OPTION_GROUP;
450458
o->help = xstrdup(skipspaces(sb.buf));
451459
continue;
452460
}
453461

462+
*help = '\0';
463+
454464
o->type = OPTION_CALLBACK;
455-
o->help = xstrdup(skipspaces(help));
465+
o->help = xstrdup(skipspaces(help+1));
456466
o->value = &parsed;
457467
o->flags = PARSE_OPT_NOARG;
458468
o->callback = &parseopt_dump;

git-rebase.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ do
350350
shift
351351
break
352352
;;
353+
*)
354+
usage
355+
;;
353356
esac
354357
shift
355358
done

parse-options.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,7 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
581581
const struct option *opts, int full, int err)
582582
{
583583
FILE *outfile = err ? stderr : stdout;
584+
int need_newline;
584585

585586
if (!usagestr)
586587
return PARSE_OPT_HELP;
@@ -599,26 +600,31 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
599600
if (**usagestr)
600601
fprintf_ln(outfile, _(" %s"), _(*usagestr));
601602
else
602-
putchar('\n');
603+
fputc('\n', outfile);
603604
usagestr++;
604605
}
605606

606-
if (opts->type != OPTION_GROUP)
607-
fputc('\n', outfile);
607+
need_newline = 1;
608608

609609
for (; opts->type != OPTION_END; opts++) {
610610
size_t pos;
611611
int pad;
612612

613613
if (opts->type == OPTION_GROUP) {
614614
fputc('\n', outfile);
615+
need_newline = 0;
615616
if (*opts->help)
616617
fprintf(outfile, "%s\n", _(opts->help));
617618
continue;
618619
}
619620
if (!full && (opts->flags & PARSE_OPT_HIDDEN))
620621
continue;
621622

623+
if (need_newline) {
624+
fputc('\n', outfile);
625+
need_newline = 0;
626+
}
627+
622628
pos = fprintf(outfile, " ");
623629
if (opts->short_name) {
624630
if (opts->flags & PARSE_OPT_NODASH)

t/helper/test-parse-options.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ int cmd_main(int argc, const char **argv)
9999
const char *prefix = "prefix/";
100100
const char *usage[] = {
101101
"test-parse-options <options>",
102+
"",
103+
"A helper function for the parse-options API.",
102104
NULL
103105
};
104106
struct string_list expect = STRING_LIST_INIT_NODUP;

t/t0040-parse-options.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ test_description='our own option parser'
1010
cat >expect <<\EOF
1111
usage: test-parse-options <options>
1212
13+
A helper function for the parse-options API.
14+
1315
--yes get a boolean
1416
-D, --no-doubt begins with 'no-'
1517
-B, --no-fear be brave

t/t1502-rev-parse-parseopt.sh

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,35 @@ test_expect_success 'setup optionspec' '
2828
|g,fluf?path short and long option optional argument
2929
|longest=very-long-argument-hint a very long argument hint
3030
|pair=key=value with an equals sign in the hint
31+
|aswitch help te=t contains? fl*g characters!`
32+
|bswitch=hint hint has trailing tab character
33+
|cswitch switch has trailing tab character
3134
|short-hint=a with a one symbol hint
3235
|
3336
|Extras
3437
|extra1 line above used to cause a segfault but no longer does
3538
EOF
3639
'
3740

41+
test_expect_success 'setup optionspec-no-switches' '
42+
sed -e "s/^|//" >optionspec_no_switches <<\EOF
43+
|some-command [options] <args>...
44+
|
45+
|some-command does foo and bar!
46+
|--
47+
EOF
48+
'
49+
50+
test_expect_success 'setup optionspec-only-hidden-switches' '
51+
sed -e "s/^|//" >optionspec_only_hidden_switches <<\EOF
52+
|some-command [options] <args>...
53+
|
54+
|some-command does foo and bar!
55+
|--
56+
|hidden1* A hidden switch
57+
EOF
58+
'
59+
3860
test_expect_success 'test --parseopt help output' '
3961
sed -e "s/^|//" >expect <<\END_EXPECT &&
4062
|cat <<\EOF
@@ -62,6 +84,9 @@ test_expect_success 'test --parseopt help output' '
6284
| --longest <very-long-argument-hint>
6385
| a very long argument hint
6486
| --pair <key=value> with an equals sign in the hint
87+
| --aswitch help te=t contains? fl*g characters!`
88+
| --bswitch <hint> hint has trailing tab character
89+
| --cswitch switch has trailing tab character
6590
| --short-hint <a> with a one symbol hint
6691
|
6792
|Extras
@@ -73,19 +98,100 @@ END_EXPECT
7398
test_i18ncmp expect output
7499
'
75100

101+
test_expect_success 'test --parseopt help output no switches' '
102+
sed -e "s/^|//" >expect <<\END_EXPECT &&
103+
|cat <<\EOF
104+
|usage: some-command [options] <args>...
105+
|
106+
| some-command does foo and bar!
107+
|
108+
|EOF
109+
END_EXPECT
110+
test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_no_switches &&
111+
test_i18ncmp expect output
112+
'
113+
114+
test_expect_success 'test --parseopt help output hidden switches' '
115+
sed -e "s/^|//" >expect <<\END_EXPECT &&
116+
|cat <<\EOF
117+
|usage: some-command [options] <args>...
118+
|
119+
| some-command does foo and bar!
120+
|
121+
|EOF
122+
END_EXPECT
123+
test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_only_hidden_switches &&
124+
test_i18ncmp expect output
125+
'
126+
127+
test_expect_success 'test --parseopt help-all output hidden switches' '
128+
sed -e "s/^|//" >expect <<\END_EXPECT &&
129+
|cat <<\EOF
130+
|usage: some-command [options] <args>...
131+
|
132+
| some-command does foo and bar!
133+
|
134+
| --hidden1 A hidden switch
135+
|
136+
|EOF
137+
END_EXPECT
138+
test_expect_code 129 git rev-parse --parseopt -- --help-all > output < optionspec_only_hidden_switches &&
139+
test_i18ncmp expect output
140+
'
141+
142+
test_expect_success 'test --parseopt invalid switch help output' '
143+
sed -e "s/^|//" >expect <<\END_EXPECT &&
144+
|error: unknown option `does-not-exist'\''
145+
|usage: some-command [options] <args>...
146+
|
147+
| some-command does foo and bar!
148+
|
149+
| -h, --help show the help
150+
| --foo some nifty option --foo
151+
| --bar ... some cool option --bar with an argument
152+
| -b, --baz a short and long option
153+
|
154+
|An option group Header
155+
| -C[...] option C with an optional argument
156+
| -d, --data[=...] short and long option with an optional argument
157+
|
158+
|Argument hints
159+
| -B <arg> short option required argument
160+
| --bar2 <arg> long option required argument
161+
| -e, --fuz <with-space>
162+
| short and long option required argument
163+
| -s[<some>] short option optional argument
164+
| --long[=<data>] long option optional argument
165+
| -g, --fluf[=<path>] short and long option optional argument
166+
| --longest <very-long-argument-hint>
167+
| a very long argument hint
168+
| --pair <key=value> with an equals sign in the hint
169+
| --aswitch help te=t contains? fl*g characters!`
170+
| --bswitch <hint> hint has trailing tab character
171+
| --cswitch switch has trailing tab character
172+
| --short-hint <a> with a one symbol hint
173+
|
174+
|Extras
175+
| --extra1 line above used to cause a segfault but no longer does
176+
|
177+
END_EXPECT
178+
test_expect_code 129 git rev-parse --parseopt -- --does-not-exist 1>/dev/null 2>output < optionspec &&
179+
test_i18ncmp expect output
180+
'
181+
76182
test_expect_success 'setup expect.1' "
77183
cat > expect <<EOF
78-
set -- --foo --bar 'ham' -b -- 'arg'
184+
set -- --foo --bar 'ham' -b --aswitch -- 'arg'
79185
EOF
80186
"
81187

82188
test_expect_success 'test --parseopt' '
83-
git rev-parse --parseopt -- --foo --bar=ham --baz arg < optionspec > output &&
189+
git rev-parse --parseopt -- --foo --bar=ham --baz --aswitch arg < optionspec > output &&
84190
test_cmp expect output
85191
'
86192

87193
test_expect_success 'test --parseopt with mixed options and arguments' '
88-
git rev-parse --parseopt -- --foo arg --bar=ham --baz < optionspec > output &&
194+
git rev-parse --parseopt -- --foo arg --bar=ham --baz --aswitch < optionspec > output &&
89195
test_cmp expect output
90196
'
91197

0 commit comments

Comments
 (0)