shared/options: split out helper to format -o/--option=
This commit is contained in:
+57
-32
@@ -311,6 +311,59 @@ size_t option_parser_get_n_args(const OptionParser *state) {
|
||||
return state->argc - state->positional_offset;
|
||||
}
|
||||
|
||||
char* option_get_synopsis(const char *prefix, const Option *opt, const char *joiner, bool show_metavar) {
|
||||
assert(opt);
|
||||
assert(!FLAGS_SET(opt->flags, OPTION_GROUP_MARKER)); /* A group marker should not be displayed */
|
||||
|
||||
if (!prefix)
|
||||
prefix = "";
|
||||
|
||||
if (opt->flags & (OPTION_HELP_ENTRY_VERBATIM | OPTION_POSITIONAL_ENTRY))
|
||||
return strjoin(prefix, ASSERT_PTR(opt->long_code));
|
||||
|
||||
/* The option formatted appropriately for --help strings, error messages, and similar:
|
||||
* <prefix>-<short><joiner>--<long>=[<metavar>]
|
||||
* "=" is shown only when a long form is defined: -l --long=ARG, --long=ARG, -s ARG.
|
||||
* The joiner arg is used between the short and long forms.
|
||||
* As a special case, if the option has no long form and show_metavar is true,
|
||||
* a space is used ('-a ARG' or '-a [ARG]').
|
||||
*/
|
||||
assert(opt->short_code != 0 || opt->long_code);
|
||||
|
||||
char sc[3] = "";
|
||||
if (opt->short_code != 0)
|
||||
xsprintf(sc, "-%c", opt->short_code);
|
||||
|
||||
if (show_metavar && opt->metavar && !opt->long_code)
|
||||
joiner = " "; /* Return '-x ARG', no matter what joiner was specified. */
|
||||
else if (opt->short_code == 0 || !opt->long_code)
|
||||
joiner = "";
|
||||
else if (!joiner)
|
||||
joiner = " ";
|
||||
|
||||
bool need_eq = option_takes_arg(opt) && opt->long_code;
|
||||
if (!show_metavar)
|
||||
return strjoin(prefix,
|
||||
sc,
|
||||
joiner,
|
||||
opt->long_code ? "--" : "",
|
||||
strempty(opt->long_code),
|
||||
need_eq ? "=" : "");
|
||||
|
||||
bool need_quote = opt->metavar && strchr(opt->metavar, ' ');
|
||||
return strjoin(prefix,
|
||||
sc,
|
||||
joiner,
|
||||
opt->long_code ? "--" : "",
|
||||
strempty(opt->long_code),
|
||||
option_arg_optional(opt) ? "[" : "",
|
||||
need_eq ? "=" : "",
|
||||
need_quote ? "'" : "",
|
||||
strempty(opt->metavar),
|
||||
need_quote ? "'" : "",
|
||||
option_arg_optional(opt) ? "]" : "");
|
||||
}
|
||||
|
||||
int _option_parser_get_help_table(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
@@ -341,38 +394,10 @@ int _option_parser_get_help_table(
|
||||
/* No help string — we do not show the option */
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
if (FLAGS_SET(opt->flags, OPTION_HELP_ENTRY_VERBATIM)) {
|
||||
assert(opt->long_code);
|
||||
|
||||
s = strjoin(" ",
|
||||
opt->long_code);
|
||||
} else {
|
||||
char sc[3] = " ";
|
||||
if (opt->short_code != 0)
|
||||
xsprintf(sc, "-%c", opt->short_code);
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* "=" is shown only when a long option is defined: -l --long=ARG, --long=ARG, -s ARG.
|
||||
*/
|
||||
bool need_eq = option_takes_arg(opt) && opt->long_code;
|
||||
bool need_quote = opt->metavar && strchr(opt->metavar, ' ');
|
||||
s = strjoin(" ",
|
||||
sc,
|
||||
" ",
|
||||
opt->long_code ? "--" : "",
|
||||
strempty(opt->long_code),
|
||||
option_arg_optional(opt) ? "[" : "",
|
||||
need_eq ? "=" : "",
|
||||
need_quote ? "'" : "",
|
||||
strempty(opt->metavar),
|
||||
need_quote ? "'" : "",
|
||||
option_arg_optional(opt) ? "]" : "");
|
||||
}
|
||||
/* 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 = option_get_synopsis(" ", opt, " ", /* show_metavar= */ true);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
|
||||
@@ -172,6 +172,7 @@ char* option_parser_consume_next_arg(OptionParser *state);
|
||||
|
||||
char** option_parser_get_args(const OptionParser *state);
|
||||
size_t option_parser_get_n_args(const OptionParser *state);
|
||||
char* option_get_synopsis(const char *prefix, const Option *opt, const char *joiner, bool show_metavar);
|
||||
|
||||
int _option_parser_get_help_table(
|
||||
const Option options[],
|
||||
|
||||
@@ -1302,4 +1302,53 @@ TEST(option_optional_arg_consume) {
|
||||
}
|
||||
}
|
||||
|
||||
static void test_option_get_synopsis_one(
|
||||
const Option *opt,
|
||||
const char *joiner,
|
||||
bool show_metavar,
|
||||
const char *expected) {
|
||||
log_debug("%s", expected);
|
||||
_cleanup_free_ char *s = option_get_synopsis(". ", opt, joiner, show_metavar);
|
||||
ASSERT_STREQ(s, expected);
|
||||
}
|
||||
|
||||
TEST(option_get_synopsis) {
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "X" }, "/", true, ". -x/--xxx=X");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "X" }, NULL, true, ". -x --xxx=X");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "X" }, "/", false, ". -x/--xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "X" }, " ", true, ". -x --xxx=X");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "X" }, " ", false, ". -x --xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 0, "xxx", "X" }, "+", true, ". --xxx=X" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 0, "xxx", "X" }, "+", false, ". --xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', NULL, "X" }, " ", true, ". -x X" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', NULL, "X" }, "/", false, ". -x" );
|
||||
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "A B" }, "/", true, ". -x/--xxx='A B'");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "A B" }, " ", true, ". -x --xxx='A B'");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 0, "xxx", "A B" }, "+", true, ". --xxx='A B'" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', NULL, "A B" }, " ", true, ". -x 'A B'" );
|
||||
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "X" }, "/", true, ". -x/--xxx[=X]");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "X" }, NULL, true, ". -x --xxx[=X]");
|
||||
/* Note: --xxx[=] would be silly, so we show --xxx=. It's a corner case. Maybe this should change. */
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "X" }, "/", false, ". -x/--xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "X" }, " ", true, ". -x --xxx[=X]");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "X" }, " ", false, ". -x --xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 0, "xxx", "X" }, "+", true, ". --xxx[=X]" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 0, "xxx", "X" }, "+", false, ". --xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', NULL, "X" }, " ", true, ". -x [X]" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', NULL, "X" }, "/", false, ". -x" );
|
||||
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "A B" }, "/", true, ". -x/--xxx[='A B']");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "A B" }, " ", true, ". -x --xxx[='A B']");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 0, "xxx", "A B" }, "+", true, ". --xxx[='A B']" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', NULL, "A B" }, " ", true, ". -x ['A B']" );
|
||||
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG | OPTION_HELP_ENTRY | OPTION_STOPS_PARSING,
|
||||
'x', "xxx", "A B" }, "/", true, ". -x/--xxx[='A B']");
|
||||
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_HELP_ENTRY_VERBATIM, 'u', "special special", "unused" }, "/", true, ". special special");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_POSITIONAL_ENTRY, 'u', "(fixed)", "unused" }, "/", true, ". (fixed)");
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
||||
Reference in New Issue
Block a user