diff mbox series

[v3,3/6] parse-options: stop supporting "" in the usagestr array

Message ID patch-v3-3.6-e23a8231f38-20210911T190239Z-avarab@gmail.com (mailing list archive)
State New, archived
Headers show
Series parse-options: properly align continued usage output & related | expand

Commit Message

Ævar Arnfjörð Bjarmason Sept. 11, 2021, 7:09 p.m. UTC
The strings in the the "usagestr" array have special handling for the
empty string dating back to f389c808b67 (Rework make_usage to print
the usage message immediately, 2007-10-14).

We'll prefix all strings after the first one with "or: ". Then if we
encountered a "" we'll emit all strings after that point verbatim
without any "or: " prefixing.

In the preceding commits we got rid of the two users of this
undocumented part of the API. Let's remove it in preparation for
improving the output emitted by usage_with_options_internal().

I think we might want to use this in the future, but in that case
we'll be much better off with an API that emulates the
non-parse_options() way that git.c does this.

That git.c code uses a separate "git_usage_string" and
"git_more_info_string". See b7d9681974e (Print info about "git help
COMMAND" on git's main usage pages, 2008-06-06). By splitting the two
up we can emit something in the middle, as indeed git.c does.

I'd like our "git <cmd> -h" info to be more helpful, and I'd also like
parse_options() to handle the "git" command itself, because of the
limitations of how this was done in usage_with_options_internal() we
couldn't migrate a caller like git.c to parse_options().

So let's just remove this for now, it has no users, and once we want
to do this again we can simply add another argument to the relevant
functions, or otherwise hook into things so that we can print
something at the end and/or middle.

It's possible that this change introduce breakage somewhere. We'd only
catch these cases at runtime, and the "git rev-parse --parseopt"
command is used by shellscripts, see bac199b7b17 (Update
git-sh-setup(1) to allow transparent use of git-rev-parse --parseopt,
2007-11-04). I've grepped the codebase for "OPTIONS_SPEC",
"char.*\*.*usage\[\]" etc. I'm fairly sure there no outstanding users
of this functionality.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/rev-parse.c           |  3 +++
 parse-options.c               |  8 +-------
 t/helper/test-parse-options.c |  2 --
 t/t0040-parse-options.sh      |  2 --
 t/t1502-rev-parse-parseopt.sh | 34 ++++++++++++++++++----------------
 5 files changed, 22 insertions(+), 27 deletions(-)

Comments

Junio C Hamano Sept. 12, 2021, 10:26 p.m. UTC | #1
Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
> index ad4746d899a..2910874ece5 100755
> --- a/t/t0040-parse-options.sh
> +++ b/t/t0040-parse-options.sh
> @@ -10,8 +10,6 @@ test_description='our own option parser'
>  cat >expect <<\EOF
>  usage: test-tool parse-options <options>
>  
> -    A helper function for the parse-options API.
> -
>      --yes                 get a boolean
>      -D, --no-doubt        begins with 'no-'
>      -B, --no-fear         be brave

Isn't this, and a lot more importantly the next one ...

> diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
> index b29563fc997..6badc650d64 100755
> --- a/t/t1502-rev-parse-parseopt.sh
> +++ b/t/t1502-rev-parse-parseopt.sh
> @@ -6,8 +6,6 @@ test_description='test git rev-parse --parseopt'
>  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
>  |

... coalmine canaries that tell us that end-user's scripts using the
"git rev-parse --parseopt" in the documented way will be broken?

I'd rather not have to sorry about breaking end-user scripts this
way.  Unlike a very small number of in-tree parse_options() call in
C programs, we have unbounded of them.
Ævar Arnfjörð Bjarmason Sept. 13, 2021, 12:21 a.m. UTC | #2
On Sun, Sep 12 2021, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
>> index ad4746d899a..2910874ece5 100755
>> --- a/t/t0040-parse-options.sh
>> +++ b/t/t0040-parse-options.sh
>> @@ -10,8 +10,6 @@ test_description='our own option parser'
>>  cat >expect <<\EOF
>>  usage: test-tool parse-options <options>
>>  
>> -    A helper function for the parse-options API.
>> -
>>      --yes                 get a boolean
>>      -D, --no-doubt        begins with 'no-'
>>      -B, --no-fear         be brave
>
> Isn't this, and a lot more importantly the next one ...
>
>> diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
>> index b29563fc997..6badc650d64 100755
>> --- a/t/t1502-rev-parse-parseopt.sh
>> +++ b/t/t1502-rev-parse-parseopt.sh
>> @@ -6,8 +6,6 @@ test_description='test git rev-parse --parseopt'
>>  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
>>  |
>
> ... coalmine canaries that tell us that end-user's scripts using the
> "git rev-parse --parseopt" in the documented way will be broken?
>
> I'd rather not have to sorry about breaking end-user scripts this
> way.  Unlike a very small number of in-tree parse_options() call in
> C programs, we have unbounded of them.

I re-rolled this in v4 to not change how the function works at all:
https://lore.kernel.org/git/cover-v4-0.4-00000000000-20210912T235347Z-avarab@gmail.com/

I do think we're going above & beyond what's reasonable in maintaining
these interfaces, i.e. per 21d47835386 (Add a parseopt mode to
git-rev-parse to bring parse-options to shell scripts., 2007-11-04) I
don't think anyone thought that interface would be used outside of
git.git.

But then perhaps I'm wrong, or it has been, and since we ended up
sticking it in rev-parse which was marked as plumbing we can't change
how it or the underlying C APIs it relies on work.

In practice I doubt it would break things for anyone, but per the result
of the discussion to "remove dead shell code" [1] I don't think I'll get
anywhere with that.

I do think that getting some answer to [2] & how we think about the
likes of "git rev-parse --parseopt" going forward would be very
useful.

I.e. would you be OK with us marking these as deprecated / warn on their
out-of-tree use? If so we could after some time remove a lot of code &
simplify things once the last in-tree shellscript user goes away.

Or if we don't see that they can ever be changed that's also useful to
know (and per [2] I think I'd feel obligated to send in a revert
bringing git-parse-remote back).

1. https://lore.kernel.org/git/cover-v3-0.4-00000000000-20210911T111435Z-avarab@gmail.com/
2. https://lore.kernel.org/git/87tuiwjfvi.fsf@evledraar.gmail.com/
diff mbox series

Patch

diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 22c4e1a4ff0..aeebfd52805 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -436,7 +436,10 @@  static int cmd_parseopt(int argc, const char **argv, const char *prefix)
 	for (;;) {
 		if (strbuf_getline(&sb, stdin) == EOF)
 			die(_("premature end of input"));
+		if (!sb.len)
+			die(_("empty lines are not permitted before the `--' separator"));
 		ALLOC_GROW(usage, unb + 1, usz);
+
 		if (!strcmp("--", sb.buf)) {
 			if (unb < 1)
 				die(_("no usage string given before the `--' separator"));
diff --git a/parse-options.c b/parse-options.c
index 2abff136a17..950a8279beb 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -924,18 +924,12 @@  static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
 		fprintf(outfile, "cat <<\\EOF\n");
 
 	fprintf_ln(outfile, _("usage: %s"), _(*usagestr++));
-	while (*usagestr && **usagestr)
+	while (*usagestr) {
 		/*
 		 * TRANSLATORS: the colon here should align with the
 		 * one in "usage: %s" translation.
 		 */
 		fprintf_ln(outfile, _("   or: %s"), _(*usagestr++));
-	while (*usagestr) {
-		if (**usagestr)
-			fprintf_ln(outfile, _("    %s"), _(*usagestr));
-		else
-			fputc('\n', outfile);
-		usagestr++;
 	}
 
 	need_newline = 1;
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 2051ce57db7..e00aef073b0 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -102,8 +102,6 @@  int cmd__parse_options(int argc, const char **argv)
 	const char *prefix = "prefix/";
 	const char *usage[] = {
 		"test-tool parse-options <options>",
-		"",
-		"A helper function for the parse-options API.",
 		NULL
 	};
 	struct string_list expect = STRING_LIST_INIT_NODUP;
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index ad4746d899a..2910874ece5 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -10,8 +10,6 @@  test_description='our own option parser'
 cat >expect <<\EOF
 usage: test-tool parse-options <options>
 
-    A helper function for the parse-options API.
-
     --yes                 get a boolean
     -D, --no-doubt        begins with 'no-'
     -B, --no-fear         be brave
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index b29563fc997..6badc650d64 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -6,8 +6,6 @@  test_description='test git rev-parse --parseopt'
 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
 |
@@ -41,8 +39,6 @@  EOF
 test_expect_success 'setup optionspec-no-switches' '
 	sed -e "s/^|//" >optionspec_no_switches <<\EOF
 |some-command [options] <args>...
-|
-|some-command does foo and bar!
 |--
 EOF
 '
@@ -50,8 +46,6 @@  EOF
 test_expect_success 'setup optionspec-only-hidden-switches' '
 	sed -e "s/^|//" >optionspec_only_hidden_switches <<\EOF
 |some-command [options] <args>...
-|
-|some-command does foo and bar!
 |--
 |hidden1* A hidden switch
 EOF
@@ -62,8 +56,6 @@  test_expect_success 'test --parseopt help output' '
 |cat <<\EOF
 |usage: 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
@@ -103,8 +95,6 @@  test_expect_success 'test --parseopt help output no switches' '
 |cat <<\EOF
 |usage: some-command [options] <args>...
 |
-|    some-command does foo and bar!
-|
 |EOF
 END_EXPECT
 	test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_no_switches &&
@@ -116,8 +106,6 @@  test_expect_success 'test --parseopt help output hidden switches' '
 |cat <<\EOF
 |usage: some-command [options] <args>...
 |
-|    some-command does foo and bar!
-|
 |EOF
 END_EXPECT
 	test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_only_hidden_switches &&
@@ -129,8 +117,6 @@  test_expect_success 'test --parseopt help-all output hidden switches' '
 |cat <<\EOF
 |usage: some-command [options] <args>...
 |
-|    some-command does foo and bar!
-|
 |    --hidden1             A hidden switch
 |
 |EOF
@@ -144,8 +130,6 @@  test_expect_success 'test --parseopt invalid switch help output' '
 |error: unknown option `does-not-exist'\''
 |usage: 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
@@ -282,4 +266,22 @@  test_expect_success 'test --parseopt --stuck-long and short option with unset op
 	test_cmp expect output
 '
 
+test_expect_success 'test --parseopt help output hidden switches' '
+	sed -e "s/^|//" >optionspec-trailing-line <<-\EOF &&
+	|some-command [options] <args>...
+	|
+	|
+	|--
+	|h,help    show the help
+	EOF
+
+	cat >expect <<-\EOF &&
+	fatal: empty lines are not permitted before the `--'"'"' separator
+	EOF
+
+	test_must_fail git rev-parse --parseopt -- -h >out < optionspec-trailing-line 2>actual &&
+	test_must_be_empty out &&
+	test_cmp expect actual
+'
+
 test_done