add helpers for unified --help formatting (#41805)

I see --help output formatting as contiuation of @keszybz's work on
options.[ch], hence let's start to add some really basic infrastructure
to unify the --help output more.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-04-25 11:23:18 +02:00
committed by GitHub
7 changed files with 139 additions and 70 deletions
+7 -16
View File
@@ -1,14 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
#include "ansi-color.h"
#include "battery-util.h"
#include "build.h"
#include "format-table.h"
#include "help-util.h"
#include "log.h"
#include "main-func.h"
#include "options.h"
#include "pretty-print.h"
#include "string-util.h"
static bool arg_verbose = false;
@@ -19,29 +17,22 @@ static enum {
} arg_action = ACTION_AC_POWER;
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("systemd-ac-power", "1", &link);
if (r < 0)
return log_oom();
_cleanup_(table_unrefp) Table *options = NULL;
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
printf("%s [OPTIONS...]\n"
"\n%sReport whether we are connected to an external power source.%s\n"
"\nOptions:\n",
program_invocation_short_name,
ansi_highlight(),
ansi_normal());
help_cmdline("[OPTIONS...]");
help_abstract("Report whether we are connected to an external power source.");
help_section("Options:");
r = table_print_or_warn(options);
if (r < 0)
return r;
printf("\nSee the %s for details.\n", link);
help_man_page_reference("systemd-ac-power", "1");
return 0;
}
+11
View File
@@ -91,6 +91,8 @@ bool looks_like_ansi_color_code(const char *str);
#define ANSI_UNDERLINE "\x1B[0;4m"
#define ANSI_ADD_UNDERLINE "\x1B[4m"
#define ANSI_ADD_UNDERLINE_GREY ANSI_ADD_UNDERLINE "\x1B[58:5:245m"
#define ANSI_ITALICS "\x1B[0;3m"
#define ANSI_ADD_ITALICS "\x1B[3m"
#define ANSI_HIGHLIGHT "\x1B[0;1;39m"
#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m"
@@ -180,6 +182,15 @@ static inline const char* ansi_add_underline_grey(void) {
(colors_enabled() ? ANSI_ADD_UNDERLINE_GREY : ANSI_ADD_UNDERLINE) : "";
}
static inline const char* ansi_italics(void) {
/* We hook italics also into the underline checks, close enough */
return underline_enabled() ? ANSI_ITALICS : "";
}
static inline const char* ansi_add_italics(void) {
return underline_enabled() ? ANSI_ADD_ITALICS : "";
}
#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME) \
static inline const char* ansi_##name##_underline(void) { \
return underline_enabled() ? ANSI_##NAME##_UNDERLINE : \
+8 -15
View File
@@ -17,13 +17,13 @@
#include "fdset.h"
#include "format-table.h"
#include "format-util.h"
#include "help-util.h"
#include "log.h"
#include "main-func.h"
#include "notify-recv.h"
#include "options.h"
#include "parse-util.h"
#include "pidref.h"
#include "pretty-print.h"
#include "process-util.h"
#include "signal-util.h"
#include "string-util.h"
@@ -57,31 +57,24 @@ STATIC_DESTRUCTOR_REGISTER(arg_fds, fdset_freep);
STATIC_DESTRUCTOR_REGISTER(arg_fdname, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("systemd-notify", "1", &link);
if (r < 0)
return log_oom();
_cleanup_(table_unrefp) Table *options = NULL;
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
printf("%1$s [OPTIONS...] [VARIABLE=VALUE...]\n"
"%1$s [OPTIONS...] --exec [VARIABLE=VALUE...] ; -- CMDLINE...\n"
"%1$s [OPTIONS...] --fork -- CMDLINE...\n"
"\n%2$sNotify the init system about service status updates.%3$s\n\n",
program_invocation_short_name,
ansi_highlight(),
ansi_normal());
help_cmdline("[OPTIONS...] [VARIABLE=VALUE...]");
help_cmdline("[OPTIONS...] --exec [VARIABLE=VALUE...] ; -- CMDLINE...");
help_cmdline("[OPTIONS...] --fork -- CMDLINE...");
help_abstract("Notify the init system about service status updates.");
help_section("Options:");
r = table_print_or_warn(options);
if (r < 0)
return r;
printf("\nSee the %s for details.\n", link);
help_man_page_reference("systemd-notify", "1");
return 0;
}
+67
View File
@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
#include "ansi-color.h"
#include "help-util.h"
#include "pretty-print.h"
/* These are helpers for putting together --help texts in a uniform way with a common output style. Each
* function generates a separate part of the --help text:
*
* 1. help_cmdline() outputs a brief summary of the command line syntax. (used at least once, in some cases
* multiple times.) This generally comes first in the output.
*
* 2. help_abstract() outputs a brief prose abstract of the command, should carry a single line of text
* that gives the user a hint what this tool does. Use only once, right after the last help_cmdline().
*
* 3. help_section() can be used to format multiple sections of the --help text. It should be used at least
* once for an "Options:" section, but can be used more than once, for programs with many
* options/verbs. The first invocation should come right after help_abstract().
*
* 4. Finally, help_man_page_reference() adds a final line linking the man page of the tool. This should be
* used only once, and terminates the --help text.
*
* Switches and verbs documentation should be inserted after each help_section(). For that ideally use
* options.[ch] APIs. */
void help_cmdline(const char *arguments) {
assert(arguments);
printf("%s>%s %s %s\n",
ansi_grey(),
ansi_normal(),
program_invocation_short_name,
arguments);
}
void help_abstract(const char *text) {
assert(text);
printf("\n%s%s%s%s\n",
ansi_highlight(),
ansi_add_italics(),
text,
ansi_normal());
}
void help_section(const char *title) {
assert(title);
printf("\n%s%s%s\n",
ansi_underline(),
title,
ansi_normal());
}
void help_man_page_reference(const char *page, const char *section) {
assert(page);
assert(section);
/* Displaying --help texts generally should not fail, hence let's fall back to a simple string in
* case of OOM. */
_cleanup_free_ char *link = NULL;
if (terminal_urlify_man(page, section, &link) < 0)
printf("\nSee the %s(%s) man page for details.\n", page, section);
else
printf("\nSee the %s for details.\n", link);
}
+10
View File
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
void help_cmdline(const char *arguments);
void help_abstract(const char *text);
void help_section(const char *title);
void help_man_page_reference(const char *page, const char *section);
+1
View File
@@ -94,6 +94,7 @@ shared_sources = files(
'gnutls-util.c',
'gpt.c',
'group-record.c',
'help-util.c',
'hibernate-util.c',
'hostname-setup.c',
'hwdb-util.c',
+35 -39
View File
@@ -10,6 +10,7 @@
#include "bus-util.h"
#include "capsule-util.h"
#include "extract-word.h"
#include "help-util.h"
#include "image-policy.h"
#include "install.h"
#include "output-mode.h"
@@ -17,7 +18,6 @@
#include "parse-argument.h"
#include "parse-util.h"
#include "path-util.h"
#include "pretty-print.h"
#include "static-destruct.h"
#include "string-table.h"
#include "string-util.h"
@@ -108,19 +108,13 @@ STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_kill_subgroup, freep);
static int systemctl_help(void) {
_cleanup_free_ char *link = NULL;
int r;
pager_open(arg_pager_flags);
r = terminal_urlify_man("systemctl", "1", &link);
if (r < 0)
return log_oom();
help_cmdline("[OPTIONS...] COMMAND ...");
help_abstract("Query or send control commands to the system manager.");
printf("%1$s [OPTIONS...] COMMAND ...\n\n"
"%5$sQuery or send control commands to the system manager.%6$s\n"
"\n%3$sUnit Commands:%4$s\n"
" list-units [PATTERN...] List units currently in memory\n"
help_section("Unit Commands:");
printf(" list-units [PATTERN...] List units currently in memory\n"
" list-automounts [PATTERN...] List automount units currently in memory,\n"
" ordered by path\n"
" list-paths [PATTERN...] List path units currently in memory,\n"
@@ -166,9 +160,10 @@ static int systemctl_help(void) {
" reset-failed [PATTERN...] Reset failed state for all, one, or more\n"
" units\n"
" whoami [PID...] Return unit caller or specified PIDs are\n"
" part of\n"
"\n%3$sUnit File Commands:%4$s\n"
" list-unit-files [PATTERN...] List installed unit files\n"
" part of\n");
help_section("Unit File Commands:");
printf(" list-unit-files [PATTERN...] List installed unit files\n"
" enable [UNIT...|PATH...] Enable one or more unit files\n"
" disable UNIT... Disable one or more unit files\n"
" reenable UNIT... Reenable one or more unit files\n"
@@ -189,25 +184,30 @@ static int systemctl_help(void) {
" on specified one or more units\n"
" edit UNIT... Edit one or more unit files\n"
" get-default Get the name of the default target\n"
" set-default TARGET Set the default target\n"
"\n%3$sMachine Commands:%4$s\n"
" list-machines [PATTERN...] List local containers and host\n"
"\n%3$sJob Commands:%4$s\n"
" list-jobs [PATTERN...] List jobs\n"
" cancel [JOB...] Cancel all, one, or more jobs\n"
"\n%3$sEnvironment Commands:%4$s\n"
" show-environment Dump environment\n"
" set-default TARGET Set the default target\n");
help_section("Machine Commands:");
printf(" list-machines [PATTERN...] List local containers and host\n");
help_section("Job Commands:");
printf(" list-jobs [PATTERN...] List jobs\n"
" cancel [JOB...] Cancel all, one, or more jobs\n");
help_section("Environment Commands:");
printf(" show-environment Dump environment\n"
" set-environment VARIABLE=VALUE... Set one or more environment variables\n"
" unset-environment VARIABLE... Unset one or more environment variables\n"
" import-environment VARIABLE... Import all or some environment variables\n"
"\n%3$sManager State Commands:%4$s\n"
" daemon-reload Reload systemd manager configuration\n"
" import-environment VARIABLE... Import all or some environment variables\n");
help_section("Manager State Commands:");
printf(" daemon-reload Reload systemd manager configuration\n"
" daemon-reexec Reexecute systemd manager\n"
" log-level [LEVEL] Get/set logging threshold for manager\n"
" log-target [TARGET] Get/set logging target for manager\n"
" service-watchdogs [BOOL] Get/set service watchdog state\n"
"\n%3$sSystem Commands:%4$s\n"
" is-system-running Check whether system is fully running\n"
" service-watchdogs [BOOL] Get/set service watchdog state\n");
help_section("System Commands:");
printf(" is-system-running Check whether system is fully running\n"
" default Enter system default mode\n"
" rescue Enter system rescue mode\n"
" emergency Enter system emergency mode\n"
@@ -224,9 +224,10 @@ static int systemctl_help(void) {
" hibernate Hibernate the system\n"
" hybrid-sleep Hibernate and suspend the system\n"
" suspend-then-hibernate Suspend the system, wake after a period of\n"
" time, and hibernate"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" time, and hibernate\n");
help_section("Options:");
printf(" -h --help Show this help\n"
" --version Show package version\n"
" --system Connect to system manager\n"
" --user Connect to user service manager\n"
@@ -319,14 +320,9 @@ static int systemctl_help(void) {
" --drop-in=NAME Edit unit files using the specified drop-in file name\n"
" --when=TIME Schedule halt/power-off/reboot/kexec action after\n"
" a certain timestamp\n"
" --stdin Read new contents of edited file from stdin\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
ansi_underline(),
ansi_normal(),
ansi_highlight(),
ansi_normal());
" --stdin Read new contents of edited file from stdin\n");
help_man_page_reference("systemctl", "1");
return 0;
}