diff --git a/src/shared/verbs.c b/src/shared/verbs.c index e488af63fe..aab24c0681 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -7,6 +7,7 @@ #include "log.h" #include "string-util.h" #include "strv.h" +#include "terminal-util.h" #include "verbs.h" #include "virt.h" @@ -160,6 +161,72 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { return _dispatch_verb_with_args(strv_skip(argv, optind), verbs, verbs + n, userdata); } +#define VERB_SYNOPSIS_WIDTH_SANE 25 + +static const char* find_point_to_break(const char *s, size_t max_width) { + /* Locate the first space, preferably after max_width, or the last space otherwise. + * Return the part after the space. */ + + if (strlen(s) <= max_width) + return NULL; + + const char *p = strchr(s + max_width, ' ') ?: strrchr(s, ' '); + return p ? p + 1 : NULL; +} + +static int verb_add_help_one(Table *table, const Verb *verb) { + assert(table); + assert(verb); + + bool is_default = FLAGS_SET(verb->flags, VERB_DEFAULT); + int r; + + /* We indent the option string by two spaces. We could set the minimum cell width and + * right-align for a similar result, but that'd be more work. This is only used for + * display. */ + _cleanup_free_ char *s = strjoin(" ", + is_default ? "[" : "", + verb->verb, + verb->argspec ? " " : "", + strempty(verb->argspec), + is_default ? "]" : ""); + if (!s) + return log_oom(); + + const char *ss = NULL; + if (columns() < VERB_SYNOPSIS_WIDTH_SANE * 4) { + /* If the synopsis is very wide, try to split it up. But do this only if the terminal + * is not very wide. If it _is_ wide, the broken up synopsis would look silly. */ + const char *p = find_point_to_break(s, VERB_SYNOPSIS_WIDTH_SANE), *p2 = NULL; + if (p) { + const char *s1 = strndupa_safe(s, p - s), *s2 = NULL; + + p2 = find_point_to_break(p, VERB_SYNOPSIS_WIDTH_SANE - 4); /* we indent by two spaces more */ + if (p2) + s2 = strndupa_safe(p, p2 - p); + + if (s2) + ss = strjoina(s1, "\n ", s2, "\n ", p2); + else + ss = strjoina(s1, "\n ", p); + } + } + + r = table_add_cell(table, NULL, TABLE_STRING, ss ?: s); + if (r < 0) + return table_log_add_error(r); + + _cleanup_strv_free_ char **t = strv_split(verb->help, /* separators= */ NULL); + if (!t) + return log_oom(); + + r = table_add_many(table, TABLE_STRV_WRAPPED, t); + if (r < 0) + return table_log_add_error(r); + + return 0; +} + int _verbs_get_help_table( const Verb verbs[], const Verb verbs_end[], @@ -192,27 +259,9 @@ int _verbs_get_help_table( /* No help string — we do not show the verb */ continue; - bool is_default = FLAGS_SET(verb->flags, VERB_DEFAULT); - - /* We indent the option string by two spaces. We could set the minimum cell width and - * right-align for a similar result, but that'd be more work. This is only used for - * display. */ - r = table_add_cell_stringf(table, NULL, " %s%s%s%s%s", - is_default ? "[" : "", - verb->verb, - verb->argspec ? " " : "", - strempty(verb->argspec), - is_default ? "]" : ""); + r = verb_add_help_one(table, verb); if (r < 0) - return table_log_add_error(r); - - _cleanup_strv_free_ char **s = strv_split(verb->help, /* separators= */ NULL); - if (!s) - return log_oom(); - - r = table_add_many(table, TABLE_STRV_WRAPPED, s); - if (r < 0) - return table_log_add_error(r); + return r; } table_set_header(table, false);