shared/verbs: add command_print_verb_help()
A helper to print the help of a single verb, derived entirely from the data in the SYSTEMD_VERBS and SYSTEMD_OPTIONS sections: the synopsis is built from the verb name and argspec, the one-line verb help serves as the abstract, the options come from the verb's option namespace, and the man page links from the command the verb belongs to. A flag allows suppressing the man page links for callers which append custom sections.
This commit is contained in:
@@ -564,6 +564,72 @@ int _command_print_help_full(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _command_print_verb_help(
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
const char *name) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
const Verb *verb = ASSERT_PTR(_verbs_find_verb(verbs, verbs_end, name));
|
||||
|
||||
/* The owning command is described by the closest preceding VERB_COMMAND_MARKER entry. */
|
||||
const Verb *cmdverb = verb;
|
||||
while (!FLAGS_SET(cmdverb->flags, VERB_COMMAND_MARKER)) {
|
||||
assert(cmdverb > verbs);
|
||||
cmdverb--;
|
||||
}
|
||||
const CommandDescription *cmd = (const CommandDescription*) ASSERT_PTR(cmdverb->data);
|
||||
|
||||
if (cmd->pager_flags)
|
||||
pager_open(*cmd->pager_flags);
|
||||
|
||||
/* If argspec is empty, print one line with no argspec. Use " " as placeholder for the empty spec. */
|
||||
NULSTR_FOREACH(spec, verb->argspec ?: " \0") {
|
||||
bool some = !streq(spec, " ");
|
||||
_cleanup_free_ char *line = strjoin(
|
||||
"[OPTION…] ", verb->verb,
|
||||
verb->option_namespace ? " [OPTION…]" : "",
|
||||
some ? " " : NULL,
|
||||
some ? spec : NULL);
|
||||
if (!line)
|
||||
return log_oom();
|
||||
help_cmdline(line);
|
||||
};
|
||||
|
||||
if (verb->help) {
|
||||
const char *s = strjoina(ansi_highlight(), ansi_add_italics()),
|
||||
*t = strjoina(verb->help, ".");
|
||||
|
||||
r = print_wrapped(t, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (verb->option_namespace) {
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
r = _option_parser_get_help_table_full(options, options_end, verb->option_namespace,
|
||||
/* group= */ NULL, &table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_section("Options");
|
||||
r = table_print_or_warn(table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = print_man_links(cmd->man_pages);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to print man page links: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_build_json(
|
||||
const Verb *verb,
|
||||
const Option options[],
|
||||
|
||||
@@ -183,6 +183,24 @@ static inline int verb_help_auto(int argc, char **argv, uintptr_t data, void *us
|
||||
return command_print_help_name((const char*) data);
|
||||
}
|
||||
|
||||
/* Print the help for a single verb: the synopsis, the one-line help as abstract, the options from
|
||||
* the verb's option namespace (see VERB_SCOPE_NS*), and the man page links of the command the verb
|
||||
* belongs to. The verb is attributed to the closest preceding COMMAND() in the verbs section, so:
|
||||
* name must refer to an existing verb owned by such a command (asserted otherwise), verb names
|
||||
* must be unique across the whole binary, commands with COMMAND_VERBS_SHARED are not
|
||||
* disambiguated, and a declared option namespace must contain at least one non-hidden option. */
|
||||
int _command_print_verb_help(
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
const char *name);
|
||||
#define command_print_verb_help(name) \
|
||||
_command_print_verb_help( \
|
||||
__start_SYSTEMD_VERBS, __stop_SYSTEMD_VERBS, \
|
||||
__start_SYSTEMD_OPTIONS, __stop_SYSTEMD_OPTIONS, \
|
||||
name)
|
||||
|
||||
/* Print a machine-readable description of the program's commands, verbs, and options in the format
|
||||
* defined by the CLI-Introspection Specification. One command object is emitted for each
|
||||
* VERB_COMMAND() in the verb table, so multicall binaries are described in full. */
|
||||
|
||||
@@ -114,4 +114,13 @@ TEST(introspect_cli_verb_options) {
|
||||
SD_JSON_FORMAT_OFF));
|
||||
}
|
||||
|
||||
TEST(command_print_verb_help) {
|
||||
ASSERT_OK(_command_print_verb_help(introspect_verbs, introspect_verbs + ELEMENTSOF(introspect_verbs) - 1,
|
||||
introspect_options, introspect_options + ELEMENTSOF(introspect_options) - 1,
|
||||
"alpha"));
|
||||
ASSERT_OK(_command_print_verb_help(introspect_verbs, introspect_verbs + ELEMENTSOF(introspect_verbs) - 1,
|
||||
introspect_options, introspect_options + ELEMENTSOF(introspect_options) - 1,
|
||||
"beta"));
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
||||
Reference in New Issue
Block a user