shared/verbs: add concept of "shared verbs"
This allows commands like systemd-sysext + systemd-confext to be handled
without duplicating entries in the verbs table. The basic idea is to have
the following layout in the SYSTEMD_VERBS section:
VERB_COMMAND_MARKER → "systemd-sysext", .flags = COMMAND_VERBS_SHARED
VERB_COMMAND_MARKER → "systemd-confext", .flags = COMMAND_VERBS_SHARED
VERB("status"),
VERB("merge"),
...
And then in places where we want to work on the verbs, we skip over the
verb entries with VERB_COMMAND_MARKER and COMMAND_VERBS_SHARED flag set.
This commit is contained in:
+27
-3
@@ -467,6 +467,27 @@ static int print_wrapped(const char *text, const char *ansi_seq) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const Verb* verbs_find_cmd_verbs(const Verb *cmdverb, const Verb verbs_end[]) {
|
||||
assert(cmdverb);
|
||||
assert(FLAGS_SET(cmdverb->flags, VERB_COMMAND_MARKER));
|
||||
assert(cmdverb < verbs_end);
|
||||
|
||||
const CommandDescription *cmd = (const CommandDescription*) ASSERT_PTR(cmdverb->data);
|
||||
|
||||
/* With COMMAND_VERBS_SHARED, we have a group of VERB_COMMAND_MARKER entries, and
|
||||
* then the shared verbs below. Thus, we need to skip over them to locate the
|
||||
* first actual verb. Without COMMAND_VERBS_SHARED, the verbs start immediately
|
||||
* after the VERB_COMMAND_MARKER entry. */
|
||||
const Verb *actual = cmdverb + 1;
|
||||
if (FLAGS_SET(cmd->flags, COMMAND_VERBS_SHARED))
|
||||
while (actual < verbs_end &&
|
||||
FLAGS_SET(actual->flags, VERB_COMMAND_MARKER) &&
|
||||
FLAGS_SET(((const CommandDescription*) ASSERT_PTR(actual->data))->flags,
|
||||
COMMAND_VERBS_SHARED))
|
||||
actual++;
|
||||
return actual;
|
||||
}
|
||||
|
||||
int _command_print_help_full(
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
@@ -482,6 +503,7 @@ int _command_print_help_full(
|
||||
"Command %s not known", name);
|
||||
|
||||
const CommandDescription *cmd = (const CommandDescription*) ASSERT_PTR(cmdverb->data);
|
||||
const Verb *verbverbs = verbs_find_cmd_verbs(cmdverb, verbs_end);
|
||||
|
||||
if (cmd->pager_flags)
|
||||
pager_open(*cmd->pager_flags);
|
||||
@@ -494,7 +516,7 @@ int _command_print_help_full(
|
||||
help_cmdline(line);
|
||||
}
|
||||
else {
|
||||
bool have_verbs = verbs_array_has_verbs(cmdverb + 1, verbs_end);
|
||||
bool have_verbs = verbs_array_has_verbs(verbverbs, verbs_end);
|
||||
help_cmdline(have_verbs ? "[OPTION…] COMMAND …" : "[OPTION…]");
|
||||
}
|
||||
|
||||
@@ -503,7 +525,7 @@ int _command_print_help_full(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = print_verb_option_help(cmd, cmdverb + 1, verbs_end, options, options_end);
|
||||
r = print_verb_option_help(cmd, verbverbs, verbs_end, options, options_end);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to print verb&option help: %m");
|
||||
|
||||
@@ -568,9 +590,11 @@ static int command_build_json(
|
||||
|
||||
assert(cmdverb);
|
||||
assert(FLAGS_SET(cmdverb->flags, VERB_COMMAND_MARKER));
|
||||
assert(cmdverb < verbs_end);
|
||||
assert(ret);
|
||||
|
||||
const CommandDescription *cmd = (const CommandDescription*) ASSERT_PTR(cmdverb->data);
|
||||
const Verb *verbverbs = verbs_find_cmd_verbs(cmdverb, verbs_end);
|
||||
|
||||
NULSTR_FOREACH(name, cmd->names) {
|
||||
r = sd_json_variant_append_arrayb(&names, SD_JSON_BUILD_STRING(name));
|
||||
@@ -583,7 +607,7 @@ static int command_build_json(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (const Verb *verb = cmdverb + 1; verb < verbs_end; verb++) {
|
||||
for (const Verb *verb = verbverbs; verb < verbs_end; verb++) {
|
||||
if (FLAGS_SET(verb->flags, VERB_COMMAND_MARKER))
|
||||
break; /* Start of entries for another command */
|
||||
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
typedef enum CommandFlags {
|
||||
COMMAND_HELP_SEPARATE = 1 << 0, /* Do not synchronize the width between verbs and options */
|
||||
COMMAND_VERBS_SHARED = 1 << 1, /* We have a group of COMMANDs with this flag set, and then
|
||||
* the VERB definitions below. The commands share the same
|
||||
* set of actual VERB definitions. This is useful for programs
|
||||
* which are registered under multiple names, but each one
|
||||
* behaves very similarly. */
|
||||
} CommandFlags;
|
||||
|
||||
typedef struct CommandDescription {
|
||||
|
||||
Reference in New Issue
Block a user