File 6141-argparse-Accept-the-progname-in-the-command-path.patch of Package erlang

From 04d4c9268fbe1c5eb30d70b3ce3dc43d6db563e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-S=C3=A9bastien=20P=C3=A9dron?=
 <jean-sebastien.pedron@dumbbell.fr>
Date: Mon, 9 Dec 2024 12:15:09 +0100
Subject: [PATCH] argparse: Accept the progname in the command path

[Why]
The documentation of `cmd_path()` states that it always starts with the
progname. Indeed, `argparse:parse/{1,2}` returns a command path with the
progname at the beginning.

However, if `argparse:help/2` is called with `#{command => CmdPath}` in
the parser options with `CmdPath` starting with the progname, it crashes
with a `badkey` exception because it expects the command path to only
contain commands and sub-commands.

[How]
The patch drops the first element of the command path if it matches the
progname.

This adds support for the correct values of `CmdPath` while retaining
backward compatibility with callers that dropped the progname themselves
to work around the issue.
---
 lib/stdlib/src/argparse.erl        | 16 +++++++++++++++-
 lib/stdlib/test/argparse_SUITE.erl |  9 ++++++---
 2 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/lib/stdlib/src/argparse.erl b/lib/stdlib/src/argparse.erl
index 3536bec696..c39302ef03 100644
--- a/lib/stdlib/src/argparse.erl
+++ b/lib/stdlib/src/argparse.erl
@@ -1592,7 +1592,21 @@ is_valid_command_help(_) ->
 
 format_help({ProgName, Root}, Format) ->
     Prefix = hd(maps:get(prefixes, Format, [$-])),
-    Nested = maps:get(command, Format, []),
+    Nested0 = maps:get(command, Format, [ProgName]),
+    %% The command path should always start with the progname, that's why it is
+    %% dropped here to keep the command and sub-commands only.
+    %%
+    %% However, earlier versions of this function did not drop that progname.
+    %% The function thus used to crash with a badkey excception if the caller
+    %% passed the `CmdPath' returned by `parse/2' to this function's `command'.
+    %% Therefore, to keep backward compatibility, if the command path does not
+    %% start with the progname, it uses the entire list untouched.
+    Nested = case Nested0 of
+                 [ProgName | Tail] ->
+                     Tail;
+                 _ ->
+                     Nested0
+             end,
     %% descent into commands collecting all options on the way
     {_CmdName, Cmd, AllArgs} = collect_options(ProgName, Root, Nested, []),
     %% split arguments into Flags, Options, Positional, and create help lines
diff --git a/lib/stdlib/test/argparse_SUITE.erl b/lib/stdlib/test/argparse_SUITE.erl
index f0cd7c39f7..3c67a6d0c8 100644
--- a/lib/stdlib/test/argparse_SUITE.erl
+++ b/lib/stdlib/test/argparse_SUITE.erl
@@ -782,6 +782,9 @@ usage(Config) when is_list(Config) ->
         "  --unsafe     unsafe atom (atom)\n"
         "  --safe       safe atom (existing atom)\n"
         "  -foobar      foobaring option\n",
+    ?assertEqual(Usage, unicode:characters_to_list(argparse:help(Cmd,
+        #{progname => "erl", command => ["erl", "start"]}))),
+    %% Same assertion for the backward-compatible way of calling `argparse:help/2'.
     ?assertEqual(Usage, unicode:characters_to_list(argparse:help(Cmd,
         #{progname => "erl", command => ["start"]}))),
     FullCmd = "Usage:\n  erl"
@@ -812,7 +815,7 @@ usage(Config) when is_list(Config) ->
         "  --float     floating-point long form argument (float), default: 3.14\n"
         "  ---extra    extra option very deep\n",
     ?assertEqual(CrawlerStatus, unicode:characters_to_list(argparse:help(Cmd,
-        #{progname => "erl", command => ["status", "crawler"]}))),
+        #{progname => "erl", command => ["erl", "status", "crawler"]}))),
     ok.
 
 usage_help_binary() ->
@@ -821,7 +824,7 @@ usage_required_args() ->
 usage_required_args(Config) when is_list(Config) ->
     Cmd = #{commands => #{"test" => #{arguments => [#{name => required, required => true, long => "-req"}]}}},
     Expected = "Usage:\n  " ++ prog() ++ " test --req <required>\n\nOptional arguments:\n  --req required\n",
-    ?assertEqual(Expected, unicode:characters_to_list(argparse:help(Cmd, #{command => ["test"]}))).
+    ?assertEqual(Expected, unicode:characters_to_list(argparse:help(Cmd, #{command => ["erl", "test"]}))).
 
 usage_template() ->
     [{doc, "Tests templates in help/usage"}].
@@ -888,7 +891,7 @@ usage_args_ordering(Config) when is_list(Config) ->
         "  second second\n"
         "  third  third\n"
         "  fourth fourth\n",
-        unicode:characters_to_list(argparse:help(Cmd, #{command => ["cmd"]}))),
+        unicode:characters_to_list(argparse:help(Cmd, #{command => ["erl", "cmd"]}))),
     ok.
 
 parser_error_usage() ->
-- 
2.43.0

openSUSE Build Service is sponsored by