aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBadlop <badlop@process-one.net>2021-04-15 19:24:17 +0200
committerBadlop <badlop@process-one.net>2021-04-15 20:25:52 +0200
commit0ec69f0279e6a1eb74a1c0229951a78cf6bf23e1 (patch)
tree0f2271dbd30aa72f1a49a7a50ab387e2a03857ef
parentChange tag name because there's already a command called "stats" (diff)
Major changes in ejabberdctl help output (#3569)
ejabberdctl: show list of commands ejabberdctl some-command: if wrong number of arguments, shows command help ejabberdctl help: show explanation of how to use "help" ejabberdctl help tags: list tags with list of commands ejabberdctl help commands: list tags with commands details ejabberdctl help whatever*: filters commands and tags
-rw-r--r--src/ejabberd_ctl.erl221
1 files changed, 153 insertions, 68 deletions
diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl
index 4597ae322..0f6793a56 100644
--- a/src/ejabberd_ctl.erl
+++ b/src/ejabberd_ctl.erl
@@ -174,27 +174,38 @@ process(["help" | Mode], Version) ->
{MaxC, ShCode} = get_shell_info(),
case Mode of
[] ->
- print_usage(dual, MaxC, ShCode, Version),
- ?STATUS_USAGE;
+ print_usage_help(MaxC, ShCode),
+ ?STATUS_SUCCESS;
["--dual"] ->
print_usage(dual, MaxC, ShCode, Version),
?STATUS_USAGE;
["--long"] ->
print_usage(long, MaxC, ShCode, Version),
?STATUS_USAGE;
- ["--tags"] ->
+ ["tags"] ->
print_usage_tags(MaxC, ShCode, Version),
?STATUS_SUCCESS;
- ["--tags", Tag] ->
- print_usage_tags(Tag, MaxC, ShCode, Version),
+ ["--tags"] -> % deprecated in favor of "tags"
+ print_usage_tags(MaxC, ShCode, Version),
?STATUS_SUCCESS;
- ["help"] ->
- print_usage_help(MaxC, ShCode),
+ ["commands"] ->
+ print_usage_tags_long(MaxC, ShCode, Version),
+ ?STATUS_SUCCESS;
+ ["--tags", Tag] -> % deprecated in favor of simply "Tag"
+ print_usage_tags(Tag, MaxC, ShCode, Version),
?STATUS_SUCCESS;
- [CmdString | _] ->
- CmdStringU = ejabberd_regexp:greplace(
- list_to_binary(CmdString), <<"-">>, <<"_">>),
- print_usage_commands2(binary_to_list(CmdStringU), MaxC, ShCode, Version),
+ [String | _] ->
+ case determine_string_type(String, Version) of
+ no_idea ->
+ io:format("No tag or command matches '~ts'~n", [String]);
+ both ->
+ print_usage_tags(String, MaxC, ShCode, Version),
+ print_usage_commands2(String, MaxC, ShCode, Version);
+ tag ->
+ print_usage_tags(String, MaxC, ShCode, Version);
+ command ->
+ print_usage_commands2(String, MaxC, ShCode, Version)
+ end,
?STATUS_SUCCESS
end;
@@ -250,6 +261,21 @@ process2(Args, AccessCommands, Auth, Version) ->
{"Erroneous result: " ++ io_lib:format("~p", [Other]), ?STATUS_ERROR}
end.
+determine_string_type(String, Version) ->
+ TagsCommands = ejabberd_commands:get_tags_commands(Version),
+ CommandsNames = case lists:keysearch(String, 1, TagsCommands) of
+ {value, {String, CNs}} -> CNs;
+ false -> []
+ end,
+ AllCommandsNames = [atom_to_list(Name) || {Name, _, _} <- ejabberd_commands:list_commands(Version)],
+ Cmds = filter_commands(AllCommandsNames, String),
+ case {CommandsNames, Cmds} of
+ {[], []} -> no_idea;
+ {[], _} -> command;
+ {_, []} -> tag;
+ {_, _} -> both
+ end.
+
%%-----------------------------
%% Command calling
%%-----------------------------
@@ -323,7 +349,8 @@ call_command([CmdString | Args], Auth, _AccessCommands, Version) ->
{L1, L2} when L1 < L2 -> {L2-L1, "less argument"};
{L1, L2} when L1 > L2 -> {L1-L2, "more argument"}
end,
- {io_lib:format("Error: the command ~p requires ~p ~ts.",
+ process(["help" | [CmdString]]),
+ {io_lib:format("Error: the command '~ts' requires ~p ~ts.",
[CmdString, NumCompa, TextCompa]),
wrong_command_arguments}
end.
@@ -472,15 +499,25 @@ is_supported_args(Args) ->
%% Print help
%%-----------------------------
-%% Bold
+%% Commands are Bold
-define(B1, "\e[1m").
--define(B2, "\e[22m").
--define(B(S), case ShCode of true -> [?B1, S, ?B2]; false -> S end).
+-define(B2, "\e[21m").
+-define(C(S), case ShCode of true -> [?B1, S, ?B2]; false -> S end).
+
+%% Arguments are Dim
+-define(D1, "\e[2m").
+-define(D2, "\e[22m").
+-define(A(S), case ShCode of true -> [?D1, S, ?D2]; false -> S end).
-%% Underline
+%% Tags are Underline
-define(U1, "\e[4m").
-define(U2, "\e[24m").
--define(U(S), case ShCode of true -> [?U1, S, ?U2]; false -> S end).
+-define(T(S), case ShCode of true -> [?U1, S, ?U2]; false -> S end).
+
+%% B are Nothing
+-define(N1, "\e[0m").
+-define(N2, "\e[0m").
+-define(B(S), case ShCode of true -> [?N1, S, ?N2]; false -> S end).
print_usage(Version) ->
{MaxC, ShCode} = get_shell_info(),
@@ -491,22 +528,15 @@ print_usage(HelpMode, MaxC, ShCode, Version) ->
{"status", [], "Get ejabberd status"},
{"stop", [], "Stop ejabberd"},
{"restart", [], "Restart ejabberd"},
- {"help", ["[--tags [tag] | com?*]"], "Show help (try: ejabberdctl help help)"},
{"mnesia", ["[info]"], "show information of Mnesia system"}] ++
get_list_commands(Version),
print(
- ["Usage: ", ?B("ejabberdctl"), " [--no-timeout] [--node ", ?U("nodename"), "] [--version ", ?U("api_version"), "] ",
- ?U("command"), " [", ?U("options"), "]\n"
+ ["Usage: ", "ejabberdctl", " [--no-timeout] [--node ", ?A("nodename"), "] [--version ", ?A("api_version"), "] ",
+ ?C("command"), " [", ?A("arguments"), "]\n"
"\n"
"Available commands in this ejabberd node:\n"], []),
- print_usage_commands(HelpMode, MaxC, ShCode, AllCommands),
- print(
- ["\n"
- "Examples:\n"
- " ejabberdctl restart\n"
- " ejabberdctl --node ejabberd@host restart\n"],
- []).
+ print_usage_commands(HelpMode, MaxC, ShCode, AllCommands).
print_usage_commands(HelpMode, MaxC, ShCode, Commands) ->
CmdDescsSorted = lists:keysort(1, Commands),
@@ -550,8 +580,24 @@ get_shell_info() ->
_:_ -> {78, false}
end.
+%% Erlang/OTP 20.0 introduced string:find/2, but we must support old 19.3
+string_find([], _SearchPattern) ->
+ nomatch;
+string_find([A | String], [A]) ->
+ String;
+string_find([_ | String], SearchPattern) ->
+ string_find(String, SearchPattern).
+
%% Split this command description in several lines of proper length
prepare_description(DescInit, MaxC, Desc) ->
+ case string_find(Desc, "\n") of
+ nomatch ->
+ prepare_description2(DescInit, MaxC, Desc);
+ _ ->
+ Desc
+ end.
+
+prepare_description2(DescInit, MaxC, Desc) ->
Words = string:tokens(Desc, " "),
prepare_long_line(DescInit, MaxC, Words).
@@ -598,21 +644,27 @@ format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, dual)
%% If the space available for descriptions is too narrow, enforce long help mode
format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, long);
+format_command_lines(CALD, _MaxCmdLen, _MaxC, ShCode, short) ->
+ lists:map(
+ fun({Cmd, Args, _CmdArgsL, _Desc}) ->
+ [" ", ?C(Cmd), [[" ", ?A(Arg)] || Arg <- Args], "\n"]
+ end, CALD);
+
format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, dual) ->
lists:map(
fun({Cmd, Args, CmdArgsL, Desc}) ->
DescFmt = prepare_description(MaxCmdLen+4, MaxC, Desc),
- [" ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args],
- string:chars($\s, MaxCmdLen - CmdArgsL + 1),
+ [" ", ?C(Cmd), [[" ", ?A(Arg)] || Arg <- Args],
+ lists:duplicate(MaxCmdLen - CmdArgsL + 1, $\s),
DescFmt, "\n"]
end, CALD);
format_command_lines(CALD, _MaxCmdLen, MaxC, ShCode, long) ->
lists:map(
fun({Cmd, Args, _CmdArgsL, Desc}) ->
- DescFmt = prepare_description(8, MaxC, Desc),
- ["\n ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], "\n", " ",
- DescFmt, "\n"]
+ DescFmt = prepare_description(13, MaxC, Desc),
+ [" ", ?C(Cmd), [[" ", ?A(Arg)] || Arg <- Args], "\n",
+ " ", DescFmt, "\n"]
end, CALD).
@@ -621,20 +673,42 @@ format_command_lines(CALD, _MaxCmdLen, MaxC, ShCode, long) ->
%%-----------------------------
print_usage_tags(MaxC, ShCode, Version) ->
- print("Available tags and commands:", []),
+ print("Available tags and list of commands:", []),
TagsCommands = ejabberd_commands:get_tags_commands(Version),
lists:foreach(
fun({Tag, Commands} = _TagCommands) ->
- print(["\n\n ", ?B(Tag), "\n "], []),
+ print(["\n\n ", ?T(Tag), "\n "], []),
Words = lists:sort(Commands),
Desc = prepare_long_line(5, MaxC, Words),
- print(Desc, [])
+ print(?C(Desc), [])
end,
TagsCommands),
print("\n\n", []).
+print_usage_tags_long(MaxC, ShCode, Version) ->
+ print("Available tags and commands details:", []),
+ TagsCommands = ejabberd_commands:get_tags_commands(Version),
+ print("\n", []),
+ lists:foreach(
+ fun({Tag, CommandsNames} = _TagCommands) ->
+ print(["\n ", ?T(Tag), "\n"], []),
+ CommandsList = lists:map(
+ fun(NameString) ->
+ C = ejabberd_commands:get_command_definition(
+ list_to_atom(NameString), Version),
+ #ejabberd_commands{name = Name,
+ args = Args,
+ desc = Desc} = C,
+ tuple_command_help({Name, Args, Desc})
+ end,
+ CommandsNames),
+ print_usage_commands(short, MaxC, ShCode, CommandsList)
+ end,
+ TagsCommands),
+ print("\n", []).
+
print_usage_tags(Tag, MaxC, ShCode, Version) ->
- print(["Available commands with tag ", ?B(Tag), ":", "\n"], []),
+ print(["Available commands with tag ", ?T(Tag), ":", "\n", "\n"], []),
HelpMode = long,
TagsCommands = ejabberd_commands:get_tags_commands(Version),
CommandsNames = case lists:keysearch(Tag, 1, TagsCommands) of
@@ -661,26 +735,29 @@ print_usage_tags(Tag, MaxC, ShCode, Version) ->
print_usage_help(MaxC, ShCode) ->
LongDesc =
- ["The special 'help' ejabberdctl command provides help of ejabberd commands.\n\n"
- "The format is:\n ", ?B("ejabberdctl"), " ", ?B("help"), " [", ?B("--tags"), " ", ?U("[tag]"), " | ", ?U("com?*"), "]\n\n"
+ ["This special ", ?C("help"), " command provides help of ejabberd commands.\n\n"
+ "The format is:\n ", ?B("ejabberdctl"), " ", ?C("help"),
+ " [", ?A("tags"), " | ", ?A("commands"), " | ", ?T("tag"), " | ", ?C("command"), " | ", ?C("com?*"), "]\n\n"
"The optional arguments:\n"
- " ",?B("--tags")," Show all tags and the names of commands in each tag\n"
- " ",?B("--tags"), " ", ?U("tag")," Show description of commands in this tag\n"
- " ",?U("command")," Show detailed description of the command\n"
- " ",?U("com?*")," Show detailed description of commands that match this glob.\n"
- " You can use ? to match a simple character,\n"
- " and * to match several characters.\n"
+ " ",?A("tags")," Show all tags and commands names in each tag\n"
+ " ",?A("commands")," Show all tags and commands details in each tag\n"
+ " ",?T("tag")," Show commands related to this tag\n"
+ " ",?C("command")," Show detailed description of this command\n"
+ " ",?C("com?*")," Show commands that match this glob.\n"
+ " (? will match a simple character, and\n"
+ " * will match several characters)\n"
"\n",
"Some example usages:\n",
- " ejabberdctl help\n",
- " ejabberdctl help --tags\n",
- " ejabberdctl help --tags accounts\n",
- " ejabberdctl help register\n",
- " ejabberdctl help regist*\n",
+ " ejabberdctl ", ?C("help"), "\n",
+ " ejabberdctl ", ?C("help"), " ", ?A("tags"), "\n",
+ " ejabberdctl ", ?C("help"), " ", ?A("commands"), "\n",
+ " ejabberdctl ", ?C("help"), " ", ?T("accounts"), "\n",
+ " ejabberdctl ", ?C("help"), " ", ?C("register"), "\n",
+ " ejabberdctl ", ?C("help"), " ", ?C("regist*"), "\n",
"\n",
- "Please note that 'ejabberdctl help' shows all ejabberd commands,\n",
+ "Please note that 'ejabberdctl' shows all ejabberd commands,\n",
"even those that cannot be used in the shell with ejabberdctl.\n",
- "Those commands can be identified because the description starts with: *"],
+ "Those commands can be identified because their description starts with: *"],
ArgsDef = [],
C = #ejabberd_commands{
name = help,
@@ -701,23 +778,26 @@ print_usage_commands2(CmdSubString, MaxC, ShCode, Version) ->
AllCommandsNames = [atom_to_list(Name) || {Name, _, _} <- ejabberd_commands:list_commands(Version)],
Cmds = filter_commands(AllCommandsNames, CmdSubString),
case Cmds of
- [] -> io:format("Error: no command found that match: ~p~n", [CmdSubString]);
+ [] -> io:format("Error: no command found that match '~ts'~n", [CmdSubString]);
_ -> print_usage_commands3(lists:sort(Cmds), MaxC, ShCode, Version)
end.
+print_usage_commands3([Cmd], MaxC, ShCode, Version) ->
+ print_usage_command(Cmd, MaxC, ShCode, Version);
print_usage_commands3(Cmds, MaxC, ShCode, Version) ->
- %% Then for each one print it
- lists:mapfoldl(
- fun(Cmd, Remaining) ->
- print_usage_command(Cmd, MaxC, ShCode, Version),
- case Remaining > 1 of
- true -> print([" ", lists:duplicate(MaxC, 126), " \n"], []);
- false -> ok
- end,
- {ok, Remaining-1}
- end,
- length(Cmds),
- Cmds).
+ CommandsList = lists:map(
+ fun(NameString) ->
+ C = ejabberd_commands:get_command_definition(
+ list_to_atom(NameString), Version),
+ #ejabberd_commands{name = Name,
+ args = Args,
+ desc = Desc} = C,
+ tuple_command_help({Name, Args, Desc})
+ end,
+ Cmds),
+
+ print_usage_commands(long, MaxC, ShCode, CommandsList), %% que aqui solo muestre un par de lineas
+ ok.
filter_commands(All, SubString) ->
case lists:member(SubString, All) of
@@ -752,7 +832,7 @@ print_usage_command2(Cmd, C, MaxC, ShCode) ->
longdesc = LongDesc,
result = ResultDef} = C,
- NameFmt = [" ", ?B("Command Name"), ": ", Cmd, "\n"],
+ NameFmt = [" ", ?B("Command Name"), ": ", ?C(Cmd), "\n"],
%% Initial indentation of result is 13 = length(" Arguments: ")
Args = [format_usage_ctype(ArgDef, 13) || ArgDef <- ArgsDef],
@@ -769,9 +849,9 @@ print_usage_command2(Cmd, C, MaxC, ShCode) ->
XmlrpcFmt = "", %%+++ [" ",?B("XML-RPC"),": ", format_usage_xmlrpc(ArgsDef, ResultDef), "\n\n"],
- TagsFmt = [" ",?B("Tags"),": ", prepare_long_line(8, MaxC, [atom_to_list(TagA) || TagA <- TagsAtoms])],
+ TagsFmt = [" ",?B("Tags"),":", prepare_long_line(8, MaxC, [?T(atom_to_list(TagA)) || TagA <- TagsAtoms])],
- DescFmt = [" ",?B("Description"),": ", prepare_description(15, MaxC, Desc)],
+ DescFmt = [" ",?B("Description"),":", prepare_description(15, MaxC, Desc)],
LongDescFmt = case LongDesc of
"" -> "";
@@ -783,7 +863,12 @@ print_usage_command2(Cmd, C, MaxC, ShCode) ->
false -> [" ", ?B("Note:"), " This command cannot be executed using ejabberdctl. Try ejabberd_xmlrpc.\n\n"]
end,
- print(["\n", NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, "\n\n", XmlrpcFmt, TagsFmt, "\n\n", DescFmt, "\n\n", LongDescFmt, NoteEjabberdctl], []).
+ case Cmd of
+ "help" -> ok;
+ _ -> print([NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt,
+ "\n\n", XmlrpcFmt, TagsFmt, "\n\n", DescFmt, "\n\n"], [])
+ end,
+ print([LongDescFmt, NoteEjabberdctl], []).
format_usage_ctype(Type, _Indentation)
when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) or (Type==rescode) or (Type==restuple)->