From 4cf46d47a2d1902c34cd26d2d8981bbea8712223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 20 Aug 2026 00:14:29 +0200 Subject: [PATCH] shared/verbs: allow commands to specify footer highlighting In practice I think this will ever only be used with ansi_highlight_red(), but it's cleaner to add this generic extension rather than e.g. a boolean highlight_footer_red. The options is passed through via command_print_help_full(), not through the COMMAND structure, which means that commands with 'help' verb are not covered by this. The red footer highligting is used for legacy commands, which don't have verbs, so I think this is OK. Making it generic and covering 'help' verbs would require more refactoring, and I think it doesn't make sense to add it until it's actually needed. --- src/resolve/resolvconf-compat.c | 2 +- src/resolve/resolvectl.c | 2 +- src/run/run.c | 4 ++-- src/shared/verbs.c | 17 +++++++++-------- src/shared/verbs.h | 11 ++++++----- src/systemctl/systemctl.c | 2 +- 6 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/resolve/resolvconf-compat.c b/src/resolve/resolvconf-compat.c index cacd92c664..8a07124573 100644 --- a/src/resolve/resolvconf-compat.c +++ b/src/resolve/resolvconf-compat.c @@ -181,7 +181,7 @@ int resolvconf_parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("resolvconf"): {} OPTION_COMMON_HELP: - return command_print_help_full("resolvconf"); + return command_print_help_full("resolvconf", /* footer_ansi_seq= */ NULL); OPTION_COMMON_VERSION: return version(); diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index 48b4e19a60..f2d5946ae6 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -3498,7 +3498,7 @@ static int native_parse_argv(int argc, char *argv[], char ***remaining_args) { OPTION_NAMESPACE("resolvectl"): {} OPTION_COMMON_HELP: - return command_print_help_full("resolvectl"); + return command_print_help_full("resolvectl", /* footer_ansi_seq= */ NULL); OPTION_COMMON_VERSION: return version(); diff --git a/src/run/run.c b/src/run/run.c index 9b14048d56..6aa58f8b5c 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -208,7 +208,7 @@ static int parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("systemd-run"): {} OPTION_COMMON_HELP: - return command_print_help_full("systemd-run"); + return command_print_help_full("systemd-run", /* footer_ansi_seq= */ NULL); OPTION_COMMON_VERSION: return version(); @@ -784,7 +784,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { OPTION_NAMESPACE("run0"): {} OPTION_COMMON_HELP: - return command_print_help_full("run0"); + return command_print_help_full("run0", /* footer_ansi_seq= */ NULL); OPTION('V', "version", NULL, "Show package version"): return version(); diff --git a/src/shared/verbs.c b/src/shared/verbs.c index f84612ceb3..f4985fc775 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -430,7 +430,7 @@ static bool verbs_array_has_verbs(const Verb verbs[], const Verb verbs_end[]) { return false; } -static int print_wrapped(const char *text, bool abstract) { +static int print_wrapped(const char *text, const char *ansi_seq) { /* Print the text wrapped at spaces to the specified width. Newlines embedded in the text * are preserved and each line is wrapped independently, so explicit line breaks (e.g. @@ -456,10 +456,9 @@ static int print_wrapped(const char *text, bool abstract) { putchar('\n'); STRV_FOREACH(line, lines2) - if (abstract) - printf("%s%s%s%s\n", - ansi_highlight(), - ansi_add_italics(), + if (ansi_seq) + printf("%s%s%s\n", + ansi_seq, *line, ansi_normal()); else @@ -473,7 +472,8 @@ int _command_print_help_full( const Verb verbs_end[], const Option options[], const Option options_end[], - const char *name) { + const char *name, + const char *footer_ansi_seq) { int r; const Verb *cmdverb = verbs_get_command(verbs, verbs_end, name); @@ -498,7 +498,8 @@ int _command_print_help_full( help_cmdline(have_verbs ? "[OPTION…] COMMAND …" : "[OPTION…]"); } - r = print_wrapped(cmd->abstract, /* abstract= */ true); + const char *seq = strjoina(ansi_highlight(), ansi_add_italics()); + r = print_wrapped(cmd->abstract, seq); if (r < 0) return r; @@ -506,7 +507,7 @@ int _command_print_help_full( if (r < 0) return log_error_errno(r, "Failed to print verb&option help: %m"); - r = print_wrapped(cmd->footer, /* abstract= */ false); + r = print_wrapped(cmd->footer, footer_ansi_seq); if (r < 0) return r; diff --git a/src/shared/verbs.h b/src/shared/verbs.h index 5a5e31e23b..8c9e7baaf2 100644 --- a/src/shared/verbs.h +++ b/src/shared/verbs.h @@ -143,16 +143,17 @@ int _command_print_help_full( const Verb verbs_end[], const Option options[], const Option options_end[], - const char *name); -#define command_print_help_full(name) \ + const char *name, + const char *footer_ansi_seq); +#define command_print_help_full(name, footer_ansi_seq) \ _command_print_help_full( \ __start_SYSTEMD_VERBS, __stop_SYSTEMD_VERBS, \ __start_SYSTEMD_OPTIONS, __stop_SYSTEMD_OPTIONS, \ - name) -#define command_print_help() command_print_help_full(/* name= */ NULL) + name, footer_ansi_seq) +#define command_print_help() command_print_help_full(/* name= */ NULL, /* footer_ansi_seq= */ NULL) static inline int verb_help_auto(int argc, char **argv, uintptr_t data, void *userdata) { - return command_print_help_full((const char*) data); + return command_print_help_full((const char*) data, /* footer_ansi_seq= */ NULL); } /* Print a machine-readable description of the program's commands, verbs, and options in the format diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 9d70999545..659de527d3 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -351,7 +351,7 @@ static int systemctl_parse_argv(int argc, char *argv[], int log_level_shift, cha OPTION_NAMESPACE("systemctl"): {} OPTION_COMMON_HELP: - return command_print_help_full("systemctl"); + return command_print_help_full("systemctl", /* footer_ansi_seq= */ NULL); OPTION_COMMON_VERSION: return version();