Add "namespaces" to option macros, convert systemd-run+run0 (#41844)
This commit is contained in:
+381
-595
File diff suppressed because it is too large
Load Diff
+147
-49
@@ -21,7 +21,8 @@ static bool option_arg_required(const Option *opt) {
|
||||
|
||||
static bool option_is_metadata(const Option *opt) {
|
||||
/* A metadata entry that is not a real option, like the group marker */
|
||||
return ASSERT_PTR(opt)->flags & (OPTION_GROUP_MARKER |
|
||||
return ASSERT_PTR(opt)->flags & (OPTION_NAMESPACE_MARKER |
|
||||
OPTION_GROUP_MARKER |
|
||||
OPTION_POSITIONAL_ENTRY |
|
||||
OPTION_HELP_ENTRY |
|
||||
OPTION_HELP_ENTRY_VERBATIM);
|
||||
@@ -72,21 +73,77 @@ int option_parse(
|
||||
const Option options_end[],
|
||||
OptionParser *state) {
|
||||
|
||||
/* We define this one early, since we use goto below, and need to guarantee its initialization */
|
||||
_cleanup_free_ char *_optname = NULL; /* allocated option name */
|
||||
int r;
|
||||
|
||||
assert(state);
|
||||
|
||||
/* Check and initialize */
|
||||
if (state->optind == 0) {
|
||||
switch (state->state) {
|
||||
|
||||
case OPTION_PARSER_INIT: {
|
||||
assert(state->mode >= 0 && state->mode < _OPTION_PARSER_MODE_MAX);
|
||||
|
||||
if (state->argc < 1 || strv_isempty(state->argv))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "argv cannot be empty");
|
||||
if (state->argc < 1) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "argv cannot be empty");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
assert_se((size_t) state->argc == strv_length(state->argv)); /* Make sure argc/argv are consistent */
|
||||
|
||||
/* Figure out the right range of options */
|
||||
bool in_ns = state->namespace == NULL; /* Are we currently in the section of the array that
|
||||
* forms namespace <namespace>? The first part is the
|
||||
* default unnamed namespace, so if the namespace was
|
||||
* not specified, we are in it. */
|
||||
if (in_ns)
|
||||
state->namespace_start = options;
|
||||
|
||||
const Option *opt;
|
||||
|
||||
/* Verify that the option array didn't get mangled within a namespace. */
|
||||
for (opt = options; opt < options_end; opt++)
|
||||
if (opt + 1 < options_end && !FLAGS_SET((opt + 1)->flags, OPTION_NAMESPACE_MARKER))
|
||||
assert_se(opt->id < (opt + 1)->id);
|
||||
|
||||
for (opt = options; opt < options_end; opt++) {
|
||||
bool ns_marker = FLAGS_SET(opt->flags, OPTION_NAMESPACE_MARKER);
|
||||
if (!in_ns) {
|
||||
in_ns = ns_marker && streq(state->namespace, opt->long_code);
|
||||
if (in_ns)
|
||||
state->namespace_start = opt + 1;
|
||||
continue;
|
||||
}
|
||||
if (ns_marker)
|
||||
break; /* End of namespace */
|
||||
}
|
||||
assert(state->namespace_start);
|
||||
state->namespace_end = opt;
|
||||
|
||||
state->optind = state->positional_offset = 1;
|
||||
state->state = OPTION_PARSER_RUNNING;
|
||||
break;
|
||||
}
|
||||
|
||||
case OPTION_PARSER_RUNNING:
|
||||
case OPTION_PARSER_STOPPING:
|
||||
break;
|
||||
|
||||
case OPTION_PARSER_DONE:
|
||||
goto done;
|
||||
|
||||
case OPTION_PARSER_FAILED:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ESTALE), "Option parser failed before, refusing.");
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
/* Look for the next option */
|
||||
|
||||
const Option *option = NULL; /* initialization to appease gcc 13 */
|
||||
const char *optname = NULL, *optval = NULL;
|
||||
_cleanup_free_ char *_optname = NULL; /* allocated option name */
|
||||
bool separate_optval = false;
|
||||
bool handling_positional_arg = false;
|
||||
|
||||
@@ -94,27 +151,27 @@ int option_parse(
|
||||
/* Handle non-option parameters */
|
||||
for (;;) {
|
||||
if (state->optind == state->argc)
|
||||
goto finished;
|
||||
goto done;
|
||||
|
||||
if (streq(state->argv[state->optind], "--")) {
|
||||
/* No more options. Move "--" before positional args so that
|
||||
* the list of positional args is clean. */
|
||||
shift_arg(state->argv, state->positional_offset++, state->optind++);
|
||||
state->parsing_stopped = true;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (state->parsing_stopped)
|
||||
goto finished;
|
||||
/* If we are in OPTION_PARSER_STOPPING state we only wanted to read one more "--" if
|
||||
* there is one, nothing else, hence it's time to say goodbye now. */
|
||||
if (state->state == OPTION_PARSER_STOPPING)
|
||||
goto done;
|
||||
|
||||
if (state->argv[state->optind][0] == '-' &&
|
||||
state->argv[state->optind][1] != '\0')
|
||||
/* Looks like we found an option parameter */
|
||||
break;
|
||||
|
||||
if (state->mode == OPTION_PARSER_STOP_AT_FIRST_NONOPTION) {
|
||||
state->parsing_stopped = true;
|
||||
goto finished;
|
||||
}
|
||||
if (state->mode == OPTION_PARSER_STOP_AT_FIRST_NONOPTION)
|
||||
goto done;
|
||||
|
||||
if (state->mode == OPTION_PARSER_RETURN_POSITIONAL_ARGS) {
|
||||
handling_positional_arg = true;
|
||||
@@ -131,10 +188,10 @@ int option_parse(
|
||||
|
||||
if (handling_positional_arg)
|
||||
/* We are supposed to return the positional arg to be handled. */
|
||||
for (option = options;; option++) {
|
||||
for (option = state->namespace_start;; option++) {
|
||||
/* If OPTION_PARSER_RETURN_POSITIONAL_ARGS is specified,
|
||||
* OPTION_POSITIONAL must be used. */
|
||||
assert(option < options_end);
|
||||
assert(option < state->namespace_end);
|
||||
|
||||
if (FLAGS_SET(option->flags, OPTION_POSITIONAL_ENTRY))
|
||||
break;
|
||||
@@ -145,8 +202,10 @@ int option_parse(
|
||||
char *eq = strchr(state->argv[state->optind], '=');
|
||||
if (eq) {
|
||||
optname = _optname = strndup(state->argv[state->optind], eq - state->argv[state->optind]);
|
||||
if (!_optname)
|
||||
return log_oom();
|
||||
if (!_optname) {
|
||||
r = log_oom();
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* joined argument */
|
||||
optval = eq + 1;
|
||||
@@ -157,14 +216,22 @@ int option_parse(
|
||||
const Option *last_partial = NULL;
|
||||
unsigned n_partial_matches = 0; /* The commandline option matches a defined prefix. */
|
||||
|
||||
for (option = options;; option++) {
|
||||
if (option >= options_end) {
|
||||
if (n_partial_matches == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: unrecognized option '%s'",
|
||||
program_invocation_short_name, optname);
|
||||
if (n_partial_matches > 1)
|
||||
return partial_match_error(options, options_end, optname, n_partial_matches);
|
||||
for (option = state->namespace_start;; option++) {
|
||||
if (option >= state->namespace_end) {
|
||||
if (n_partial_matches == 0) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: unrecognized option '%s'",
|
||||
program_invocation_short_name, optname);
|
||||
goto fail;
|
||||
}
|
||||
if (n_partial_matches > 1) {
|
||||
r = partial_match_error(
|
||||
state->namespace_start,
|
||||
state->namespace_end,
|
||||
optname,
|
||||
n_partial_matches);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* just one partial — good */
|
||||
option = last_partial;
|
||||
@@ -193,15 +260,19 @@ int option_parse(
|
||||
if (state->short_option_offset > 0) {
|
||||
char optchar = state->argv[state->optind][state->short_option_offset];
|
||||
|
||||
if (asprintf(&_optname, "-%c", optchar) < 0)
|
||||
return log_oom();
|
||||
if (asprintf(&_optname, "-%c", optchar) < 0) {
|
||||
r = log_oom();
|
||||
goto fail;
|
||||
}
|
||||
optname = _optname;
|
||||
|
||||
for (option = options;; option++) {
|
||||
if (option >= options_end)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: unrecognized option '%s'",
|
||||
program_invocation_short_name, optname);
|
||||
for (option = state->namespace_start;; option++) {
|
||||
if (option >= state->namespace_end) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: unrecognized option '%s'",
|
||||
program_invocation_short_name, optname);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (option_is_metadata(option) || optchar != option->short_code)
|
||||
continue;
|
||||
@@ -223,15 +294,19 @@ int option_parse(
|
||||
|
||||
assert(option);
|
||||
|
||||
if (!handling_positional_arg && optval && !option_takes_arg(option))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: option '%s' doesn't allow an argument",
|
||||
program_invocation_short_name, optname);
|
||||
if (!handling_positional_arg && optval && !option_takes_arg(option)) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: option '%s' doesn't allow an argument",
|
||||
program_invocation_short_name, optname);
|
||||
goto fail;
|
||||
}
|
||||
if (!handling_positional_arg && !optval && option_arg_required(option)) {
|
||||
if (!state->argv[state->optind + 1])
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: option '%s' requires an argument",
|
||||
program_invocation_short_name, optname);
|
||||
if (!state->argv[state->optind + 1]) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: option '%s' requires an argument",
|
||||
program_invocation_short_name, optname);
|
||||
goto fail;
|
||||
}
|
||||
optval = state->argv[state->optind + 1];
|
||||
separate_optval = true;
|
||||
}
|
||||
@@ -250,16 +325,23 @@ int option_parse(
|
||||
}
|
||||
|
||||
if (FLAGS_SET(option->flags, OPTION_STOPS_PARSING))
|
||||
state->parsing_stopped = true;
|
||||
state->state = OPTION_PARSER_STOPPING;
|
||||
|
||||
state->opt = option;
|
||||
state->arg = optval;
|
||||
return option->id;
|
||||
|
||||
finished:
|
||||
done:
|
||||
state->state = OPTION_PARSER_DONE;
|
||||
state->opt = NULL;
|
||||
state->arg = NULL;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
/* Invalidate the object for good on the first error */
|
||||
assert(r < 0);
|
||||
state->state = OPTION_PARSER_FAILED;
|
||||
return r;
|
||||
}
|
||||
|
||||
char* option_parser_next_arg(const OptionParser *state) {
|
||||
@@ -292,7 +374,7 @@ char** option_parser_get_args(const OptionParser *state) {
|
||||
* original argv array, so it must not be freed or modified. */
|
||||
|
||||
assert(state->optind > 0);
|
||||
assert(state->optind == state->argc || state->parsing_stopped);
|
||||
assert(state->state == OPTION_PARSER_DONE);
|
||||
assert(state->positional_offset <= state->argc);
|
||||
|
||||
return state->argv + state->positional_offset;
|
||||
@@ -300,7 +382,7 @@ char** option_parser_get_args(const OptionParser *state) {
|
||||
|
||||
size_t option_parser_get_n_args(const OptionParser *state) {
|
||||
assert(state->optind > 0);
|
||||
assert(state->optind == state->argc || state->parsing_stopped);
|
||||
assert(state->state == OPTION_PARSER_DONE);
|
||||
assert(state->positional_offset <= state->argc);
|
||||
|
||||
return state->argc - state->positional_offset;
|
||||
@@ -308,7 +390,8 @@ size_t option_parser_get_n_args(const OptionParser *state) {
|
||||
|
||||
char* option_get_synopsis(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 */
|
||||
assert(!(opt->flags & (OPTION_NAMESPACE_MARKER |
|
||||
OPTION_GROUP_MARKER))); /* The markers should not be displayed */
|
||||
|
||||
if (opt->flags & (OPTION_HELP_ENTRY_VERBATIM | OPTION_POSITIONAL_ENTRY))
|
||||
return strdup(ASSERT_PTR(opt->long_code));
|
||||
@@ -354,9 +437,10 @@ char* option_get_synopsis(const Option *opt, const char *joiner, bool show_metav
|
||||
option_arg_optional(opt) ? "]" : "");
|
||||
}
|
||||
|
||||
int _option_parser_get_help_table(
|
||||
int _option_parser_get_help_table_full(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
const char *namespace,
|
||||
const char *group,
|
||||
Table **ret) {
|
||||
int r;
|
||||
@@ -367,11 +451,23 @@ int _option_parser_get_help_table(
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
bool in_group = group == NULL; /* Are we currently in the section on the array that forms
|
||||
* group <group>? The first part is the default group, so
|
||||
* if the group was not specified, we are in. */
|
||||
bool in_ns = namespace == NULL; /* Are we currently in the section of the array that forms namespace
|
||||
* <namespace>? The first part is the default unnamed namespace, so
|
||||
* if the namespace was not specified, we are in it. */
|
||||
|
||||
bool in_group = group == NULL; /* Are we currently in the section of the array that forms group
|
||||
* <group>? The first part is the default group, so if the group was
|
||||
* not specified, we are in it. */
|
||||
|
||||
for (const Option *opt = options; opt < options_end; opt++) {
|
||||
bool ns_marker = FLAGS_SET(opt->flags, OPTION_NAMESPACE_MARKER);
|
||||
if (!in_ns) {
|
||||
in_ns = ns_marker && streq(namespace, opt->long_code);
|
||||
continue;
|
||||
}
|
||||
if (ns_marker)
|
||||
break; /* End of namespace */
|
||||
|
||||
bool group_marker = FLAGS_SET(opt->flags, OPTION_GROUP_MARKER);
|
||||
if (!in_group) {
|
||||
in_group = group_marker && streq(group, opt->long_code);
|
||||
@@ -409,6 +505,8 @@ int _option_parser_get_help_table(
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
assert(!table_isempty(table)); /* The namespace or group were not found. Something is off. */
|
||||
|
||||
table_set_header(table, false);
|
||||
*ret = TAKE_PTR(table);
|
||||
return 0;
|
||||
|
||||
+49
-11
@@ -4,13 +4,30 @@
|
||||
#include "memory-util.h"
|
||||
#include "shared-forward.h"
|
||||
|
||||
/* Option namespace/group explanation:
|
||||
* the list of options is split into namespaces, and a namespace is split into groups.
|
||||
* By default, options defined in a single program are all placed in a single (unnamed) namespace
|
||||
* and in a single (unnamed) group. OPTION_NAMESPACE() marks the beginning of a named namespace.
|
||||
* OPTION_GROUP() marks the beginning of a named group.
|
||||
*
|
||||
* Note: if multiple namespaces are used, they should all be named, i.e. each separate parse_argv
|
||||
* instance should have OPTION_NAMESPACE first, and then its set of OPTION()s. (This is because
|
||||
* clang reorders OPTIONs coming from different functions. So an unnamed group could end up being
|
||||
* merged with one of the earlier groups. It seems that reordering within a single function does
|
||||
* not happen.)
|
||||
*
|
||||
* When groups are used, the first group may be named (with OPTION_GROUP appearing before any
|
||||
* options), or it may be unnamed. Both variants should work fine.
|
||||
*/
|
||||
|
||||
typedef enum OptionFlags {
|
||||
OPTION_OPTIONAL_ARG = 1U << 0, /* Same as optional_argument in getopt */
|
||||
OPTION_POSITIONAL_ENTRY = 1U << 1, /* The "option" to handle positional arguments */
|
||||
OPTION_STOPS_PARSING = 1U << 2, /* This option acts like "--" */
|
||||
OPTION_GROUP_MARKER = 1U << 3, /* Fake option entry to separate groups */
|
||||
OPTION_HELP_ENTRY = 1U << 4, /* Fake option entry to insert an additional help line */
|
||||
OPTION_HELP_ENTRY_VERBATIM = 1U << 5, /* Same, but use the long_code in the first column as written */
|
||||
OPTION_NAMESPACE_MARKER = 1U << 3, /* Fake option entry to separate namespaces */
|
||||
OPTION_GROUP_MARKER = 1U << 4, /* Fake option entry to separate groups */
|
||||
OPTION_HELP_ENTRY = 1U << 5, /* Fake option entry to insert an additional help line */
|
||||
OPTION_HELP_ENTRY_VERBATIM = 1U << 6, /* Same, but use the long_code in the first column as written */
|
||||
} OptionFlags;
|
||||
|
||||
typedef struct Option {
|
||||
@@ -41,9 +58,13 @@ typedef struct Option {
|
||||
}; \
|
||||
case (0x100 + counter)
|
||||
|
||||
/* Magic entry in the table (which will not be returned) that designates the start of the namespace <ns>.
|
||||
* The define is structured as 'case' so that it can be followed by ':' and indented appropriately. */
|
||||
#define OPTION_NAMESPACE(ns) \
|
||||
_OPTION(__COUNTER__, OPTION_NAMESPACE_MARKER, /* sc= */ 0, /* lc= */ ns, /* mv= */ NULL, /* d= */ 0u, /* h= */ NULL)
|
||||
|
||||
/* Magic entry in the table (which will not be returned) that designates the start of the group <gr>.
|
||||
* The define is structured as 'case' so that it can be followed by ':' and indented appropriately.
|
||||
*/
|
||||
* The define is structured as 'case' so that it can be followed by ':' and indented appropriately. */
|
||||
#define OPTION_GROUP(gr) \
|
||||
_OPTION(__COUNTER__, OPTION_GROUP_MARKER, /* sc= */ 0, /* lc= */ gr, /* mv= */ NULL, /* d= */ 0u, /* h= */ NULL)
|
||||
|
||||
@@ -140,7 +161,7 @@ extern const Option __stop_SYSTEMD_OPTIONS[];
|
||||
|
||||
typedef enum OptionParserMode {
|
||||
/* The default mode. This is the implicit default and doesn't have to be specified. */
|
||||
OPTION_PARSER_NORMAL = 0,
|
||||
OPTION_PARSER_NORMAL,
|
||||
|
||||
/* Same as "+…" for getopt_long — only parse options before the first positional argument. */
|
||||
OPTION_PARSER_STOP_AT_FIRST_NONOPTION,
|
||||
@@ -152,14 +173,28 @@ typedef enum OptionParserMode {
|
||||
_OPTION_PARSER_MODE_MAX,
|
||||
} OptionParserMode;
|
||||
|
||||
typedef enum OptionParserState {
|
||||
OPTION_PARSER_INIT,
|
||||
OPTION_PARSER_RUNNING,
|
||||
OPTION_PARSER_STOPPING, /* We processed an option with OPTION_STOPS_PARSING, and will eat up one
|
||||
* more "--", but nothing else. */
|
||||
OPTION_PARSER_DONE, /* Option parsing completed (could be because we reached the end, or because
|
||||
* "--" was fully processed, or because we hit a terminating option). */
|
||||
OPTION_PARSER_FAILED, /* We encountered a parse error, and terminated option parsing. */
|
||||
_OPTION_PARSER_MAX,
|
||||
} OptionParserState;
|
||||
|
||||
typedef struct OptionParser {
|
||||
/* Those three should stay first so that it's possible to initialize the struct as { argc, argv }
|
||||
* or { argc, argv, mode }. */
|
||||
/* Those four should stay first so that it's possible to initialize the struct as { argc, argv }
|
||||
* or { argc, argv, mode } or { argc, argv, mode, namespace }. */
|
||||
int argc; /* The original argc. */
|
||||
char **argv; /* The argv array, possibly reordered. */
|
||||
OptionParserMode mode;
|
||||
const char *namespace; /* The namespace, may be NULL. */
|
||||
|
||||
bool parsing_stopped; /* We processed "--" or an option that terminates option parsing. */
|
||||
const Option *namespace_start, *namespace_end; /* The range of options that are part of our namespace. */
|
||||
|
||||
OptionParserState state;
|
||||
int optind; /* Position of the parameter being handled.
|
||||
* 0 → option parsing hasn't been started yet. */
|
||||
int short_option_offset; /* Set when we're parsing an argument with one or more short options.
|
||||
@@ -193,12 +228,15 @@ char** option_parser_get_args(const OptionParser *state);
|
||||
size_t option_parser_get_n_args(const OptionParser *state);
|
||||
char* option_get_synopsis(const Option *opt, const char *joiner, bool show_metavar);
|
||||
|
||||
int _option_parser_get_help_table(
|
||||
int _option_parser_get_help_table_full(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
const char *namespace,
|
||||
const char *group,
|
||||
Table **ret);
|
||||
#define option_parser_get_help_table_full(namespace, group, ret) \
|
||||
_option_parser_get_help_table_full(ALIGN_PTR(__start_SYSTEMD_OPTIONS), __stop_SYSTEMD_OPTIONS, namespace, group, ret)
|
||||
#define option_parser_get_help_table_group(group, ret) \
|
||||
_option_parser_get_help_table(ALIGN_PTR(__start_SYSTEMD_OPTIONS), __stop_SYSTEMD_OPTIONS, group, ret)
|
||||
option_parser_get_help_table_full(/* namespace= */ NULL, group, ret)
|
||||
#define option_parser_get_help_table(ret) \
|
||||
option_parser_get_help_table_group(/* group= */ NULL, ret)
|
||||
|
||||
+221
-83
@@ -15,7 +15,8 @@ static void test_option_parse_one(
|
||||
const Option options[],
|
||||
const Entry *entries,
|
||||
char **remaining,
|
||||
OptionParserMode mode) {
|
||||
OptionParserMode mode,
|
||||
const char *namespace) {
|
||||
|
||||
_cleanup_free_ char *joined = strv_join(argv, ", ");
|
||||
log_debug("/* %s(%s) */", __func__, joined);
|
||||
@@ -32,7 +33,7 @@ static void test_option_parse_one(
|
||||
for (const Entry *e = entries; e && (e->long_code || e->short_code != 0); e++)
|
||||
n_entries++;
|
||||
|
||||
OptionParser opts = { argc, argv, mode };
|
||||
OptionParser opts = { argc, argv, mode, namespace };
|
||||
for (int c; (c = option_parse(options, options + n_options, &opts)) != 0; ) {
|
||||
ASSERT_OK(c);
|
||||
ASSERT_NOT_NULL(opts.opt);
|
||||
@@ -90,6 +91,12 @@ TEST(option_parse) {
|
||||
{ 4, .long_code = "required2", .metavar = "ARG" },
|
||||
{ 5, .short_code = 'o', .long_code = "optional1", .metavar = "ARG", .flags = OPTION_OPTIONAL_ARG },
|
||||
{ 6, .long_code = "optional2", .metavar = "ARG", .flags = OPTION_OPTIONAL_ARG },
|
||||
{ 7, .long_code = "NS2", .flags = OPTION_NAMESPACE_MARKER },
|
||||
{ 8, .short_code = 'h', .long_code = "help2" },
|
||||
{ 9, .long_code = "version2" },
|
||||
{ 10, .long_code = "NS3", .flags = OPTION_NAMESPACE_MARKER },
|
||||
{ 11, .short_code = 'h', .long_code = "help3" },
|
||||
{ 12, .long_code = "version3" },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -97,7 +104,8 @@ TEST(option_parse) {
|
||||
options,
|
||||
NULL,
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"string1",
|
||||
@@ -110,7 +118,8 @@ TEST(option_parse) {
|
||||
"string2",
|
||||
"string3",
|
||||
"string4"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"--",
|
||||
@@ -124,7 +133,8 @@ TEST(option_parse) {
|
||||
"--help",
|
||||
"-h",
|
||||
"string4"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"string1",
|
||||
@@ -138,7 +148,8 @@ TEST(option_parse) {
|
||||
"string2",
|
||||
"--",
|
||||
"string4"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"string1",
|
||||
@@ -152,7 +163,8 @@ TEST(option_parse) {
|
||||
"string2",
|
||||
"string3",
|
||||
"string4"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"--help"),
|
||||
@@ -162,7 +174,8 @@ TEST(option_parse) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"--help",
|
||||
@@ -175,7 +188,8 @@ TEST(option_parse) {
|
||||
},
|
||||
STRV_MAKE("string1",
|
||||
"--help"),
|
||||
OPTION_PARSER_STOP_AT_FIRST_NONOPTION);
|
||||
OPTION_PARSER_STOP_AT_FIRST_NONOPTION,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"-h"),
|
||||
@@ -185,7 +199,8 @@ TEST(option_parse) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"--help",
|
||||
@@ -202,7 +217,8 @@ TEST(option_parse) {
|
||||
"string2",
|
||||
"string3",
|
||||
"string4"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"-h",
|
||||
@@ -219,7 +235,8 @@ TEST(option_parse) {
|
||||
"string2",
|
||||
"string3",
|
||||
"string4"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"string1",
|
||||
@@ -236,7 +253,8 @@ TEST(option_parse) {
|
||||
"string2",
|
||||
"string3",
|
||||
"string4"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"string1",
|
||||
@@ -253,7 +271,8 @@ TEST(option_parse) {
|
||||
"string2",
|
||||
"string3",
|
||||
"string4"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"string1",
|
||||
@@ -270,7 +289,8 @@ TEST(option_parse) {
|
||||
"string2",
|
||||
"string3",
|
||||
"string4"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"string1",
|
||||
@@ -287,7 +307,8 @@ TEST(option_parse) {
|
||||
"string2",
|
||||
"string3",
|
||||
"string4"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"--required1", "reqarg1"),
|
||||
@@ -297,7 +318,8 @@ TEST(option_parse) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"-r", "reqarg1"),
|
||||
@@ -307,7 +329,8 @@ TEST(option_parse) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"string1",
|
||||
@@ -320,7 +343,8 @@ TEST(option_parse) {
|
||||
},
|
||||
STRV_MAKE("string1",
|
||||
"string2"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"string1",
|
||||
@@ -331,7 +355,8 @@ TEST(option_parse) {
|
||||
STRV_MAKE("string1",
|
||||
"string2",
|
||||
"-r", "reqarg1"),
|
||||
OPTION_PARSER_STOP_AT_FIRST_NONOPTION);
|
||||
OPTION_PARSER_STOP_AT_FIRST_NONOPTION,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"--optional1=optarg1"),
|
||||
@@ -341,7 +366,8 @@ TEST(option_parse) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_STOP_AT_FIRST_NONOPTION);
|
||||
OPTION_PARSER_STOP_AT_FIRST_NONOPTION,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"--optional1", "string1"),
|
||||
@@ -351,7 +377,8 @@ TEST(option_parse) {
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("string1"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"-ooptarg1"),
|
||||
@@ -361,7 +388,8 @@ TEST(option_parse) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"-o", "string1"),
|
||||
@@ -371,7 +399,8 @@ TEST(option_parse) {
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("string1"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"string1",
|
||||
@@ -441,7 +470,42 @@ TEST(option_parse) {
|
||||
"--help",
|
||||
"--required1",
|
||||
"--optional1"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Check that we can access options from NS2 */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"-h", /* This verifies that we're using the right namespace */
|
||||
"--help2",
|
||||
"--version2",
|
||||
"string1"),
|
||||
options,
|
||||
(Entry[]) {
|
||||
{ "help2" },
|
||||
{ "help2" },
|
||||
{ "version2" },
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("string1"),
|
||||
OPTION_PARSER_NORMAL,
|
||||
"NS2");
|
||||
|
||||
/* Check that we can access options from NS3 */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
"-h", /* This verifies that we're using the right namespace */
|
||||
"--help3",
|
||||
"--version3",
|
||||
"string1"),
|
||||
options,
|
||||
(Entry[]) {
|
||||
{ "help3" },
|
||||
{ "help3" },
|
||||
{ "version3" },
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("string1"),
|
||||
OPTION_PARSER_NORMAL,
|
||||
"NS3");
|
||||
}
|
||||
|
||||
TEST(option_stops_parsing) {
|
||||
@@ -465,7 +529,8 @@ TEST(option_stops_parsing) {
|
||||
},
|
||||
STRV_MAKE("--help",
|
||||
"foo"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Options before --exec are still parsed */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -481,7 +546,8 @@ TEST(option_stops_parsing) {
|
||||
},
|
||||
STRV_MAKE("--version",
|
||||
"bar"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* --exec with no trailing args */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -492,7 +558,8 @@ TEST(option_stops_parsing) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* --exec after positional args */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -509,7 +576,8 @@ TEST(option_stops_parsing) {
|
||||
"--help",
|
||||
"--required",
|
||||
"val"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* "--" after --exec: "--" is still consumed as end-of-options marker. This is needed for
|
||||
* backwards compatibility, systemd-dissect implemented this behaviour. But also, it makes
|
||||
@@ -525,7 +593,8 @@ TEST(option_stops_parsing) {
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("--help"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* "--" before --exec: "--" terminates first, --exec is positional */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -536,7 +605,8 @@ TEST(option_stops_parsing) {
|
||||
NULL,
|
||||
STRV_MAKE("--exec",
|
||||
"--help"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Multiple options then --exec then more option-like args */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -555,17 +625,18 @@ TEST(option_stops_parsing) {
|
||||
STRV_MAKE("-h",
|
||||
"--required",
|
||||
"val2"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
TEST(option_group_marker) {
|
||||
static const Option options[] = {
|
||||
{ 1, .short_code = 'h', .long_code = "help" },
|
||||
{ 2, .long_code = "version" },
|
||||
{ 0, .long_code = "AdvancedGroup", .flags = OPTION_GROUP_MARKER },
|
||||
{ 3, .long_code = "debug" },
|
||||
{ 4, .long_code = "Advance" }, /* prefix match with the group */
|
||||
{ 5, .long_code = "defilbrilate" },
|
||||
{ __COUNTER__, .short_code = 'h', .long_code = "help" },
|
||||
{ __COUNTER__, .long_code = "version" },
|
||||
{ __COUNTER__, .long_code = "AdvancedGroup", .flags = OPTION_GROUP_MARKER },
|
||||
{ __COUNTER__, .long_code = "debug" },
|
||||
{ __COUNTER__, .long_code = "Advance" }, /* prefix match with the group */
|
||||
{ __COUNTER__, .long_code = "defilbrilate" },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -580,7 +651,8 @@ TEST(option_group_marker) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Check that group marker name is ignored */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -593,7 +665,8 @@ TEST(option_group_marker) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Verify that the group marker is not mistaken for an option */
|
||||
test_option_invalid_one(STRV_MAKE("arg0",
|
||||
@@ -621,7 +694,8 @@ TEST(option_group_marker) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Partial match with multiple candidates */
|
||||
test_option_invalid_one(STRV_MAKE("arg0",
|
||||
@@ -645,7 +719,8 @@ TEST(option_optional_arg) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Long option without = does NOT consume the next arg */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -656,7 +731,8 @@ TEST(option_optional_arg) {
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("foo.txt"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Short option with inline arg */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -667,7 +743,8 @@ TEST(option_optional_arg) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Short option without inline arg does NOT consume the next arg */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -678,7 +755,8 @@ TEST(option_optional_arg) {
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("foo.txt"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Optional arg option at end of argv */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -689,7 +767,8 @@ TEST(option_optional_arg) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Mixed: optional arg with other options */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -704,7 +783,8 @@ TEST(option_optional_arg) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Short combo: -ho (h then o with no arg) */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -716,7 +796,8 @@ TEST(option_optional_arg) {
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("pos1"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Short combo: -hobar (h then o with inline arg "bar") */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -728,7 +809,8 @@ TEST(option_optional_arg) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* Test the OPTION, OPTION_LONG, OPTION_SHORT, OPTION_FULL, OPTION_GROUP macros
|
||||
@@ -738,7 +820,8 @@ static void test_macros_parse_one(
|
||||
char **argv,
|
||||
const Entry *entries,
|
||||
char **remaining,
|
||||
OptionParserMode mode) {
|
||||
OptionParserMode mode,
|
||||
const char *namespace) {
|
||||
|
||||
_cleanup_free_ char *joined = strv_join(argv, ", ");
|
||||
log_debug("/* %s(%s) */", __func__, joined);
|
||||
@@ -752,7 +835,7 @@ static void test_macros_parse_one(
|
||||
for (const Entry *e = entries; e && (e->long_code || e->short_code != 0); e++)
|
||||
n_entries++;
|
||||
|
||||
OptionParser opts = { argc, argv, mode };
|
||||
OptionParser opts = { argc, argv, mode, namespace };
|
||||
|
||||
FOREACH_OPTION(c, &opts, assert_not_reached()) {
|
||||
log_debug("%c %s: %s=%s",
|
||||
@@ -814,9 +897,14 @@ static void test_macros_parse_one(
|
||||
OPTION_POSITIONAL:
|
||||
break;
|
||||
|
||||
OPTION_NAMESPACE("namespaced options"): {}
|
||||
|
||||
OPTION('r', "required2", "ARG", "Required arg option"):
|
||||
break;
|
||||
|
||||
default:
|
||||
log_error("Unexpected option id: %d", c);
|
||||
ASSERT_TRUE(false);
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -838,7 +926,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION: short form */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -848,7 +937,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_LONG: only accessible via long form */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -858,7 +948,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_SHORT: only accessible via short form */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -868,7 +959,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION with required arg: long --required=ARG */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -878,7 +970,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION with required arg: long --required ARG */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -888,7 +981,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION with required arg: short -r ARG */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -898,7 +992,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION with required arg: short -rARG */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -908,7 +1003,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_FULL with OPTION_OPTIONAL_ARG: long with = */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -918,7 +1014,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_FULL with OPTION_OPTIONAL_ARG: long without = doesn't consume next */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -928,7 +1025,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("pos1"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_FULL with OPTION_OPTIONAL_ARG: short inline */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -938,7 +1036,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_FULL with OPTION_OPTIONAL_ARG: short without inline */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -948,7 +1047,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("pos1"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_FULL with OPTION_STOPS_PARSING: stops further option parsing */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -961,7 +1061,8 @@ TEST(option_macros) {
|
||||
},
|
||||
STRV_MAKE("--help",
|
||||
"--version"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_STOPS_PARSING: options before are still parsed */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -976,7 +1077,8 @@ TEST(option_macros) {
|
||||
},
|
||||
STRV_MAKE("-h",
|
||||
"--debug"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_STOPS_PARSING with "--": "--" after exec is still consumed */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -988,7 +1090,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("--help"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_STOPS_PARSING with "--": "--" before exec takes precedence */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1000,7 +1103,8 @@ TEST(option_macros) {
|
||||
},
|
||||
STRV_MAKE("--exec",
|
||||
"--help"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_GROUP: group marker is transparent to parsing, --debug in Advanced group works */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1010,7 +1114,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Mixed: all macro types together */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1039,7 +1144,8 @@ TEST(option_macros) {
|
||||
},
|
||||
STRV_MAKE("pos1",
|
||||
"pos2"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Short option combos with macros: -hv (help + verbose) */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1050,7 +1156,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Short option combo with required arg: -hrval (help + required with arg "val") */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1061,7 +1168,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Short option combo with optional arg: -hoval (help + optional with arg "val") */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1072,7 +1180,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_STOPS_PARSING then "--": "--" is still consumed after exec */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1088,7 +1197,8 @@ TEST(option_macros) {
|
||||
},
|
||||
STRV_MAKE("--version",
|
||||
"-h"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_STOPS_PARSING then later "--": "--" is not consumed */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1105,7 +1215,8 @@ TEST(option_macros) {
|
||||
STRV_MAKE("--version",
|
||||
"--",
|
||||
"-h"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* OPTION_STOPS_PARSING then "--" twice: second "--" is not consumed */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1123,7 +1234,8 @@ TEST(option_macros) {
|
||||
STRV_MAKE("--",
|
||||
"--version",
|
||||
"-h"),
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* Basic OPTION_POSITIONAL use */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1139,7 +1251,8 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_RETURN_POSITIONAL_ARGS);
|
||||
OPTION_PARSER_RETURN_POSITIONAL_ARGS,
|
||||
NULL);
|
||||
|
||||
/* OPTION_POSITIONAL combined with OPTION_STOPS_PARSING */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1154,16 +1267,39 @@ TEST(option_macros) {
|
||||
{}
|
||||
},
|
||||
STRV_MAKE("arg2"),
|
||||
OPTION_PARSER_RETURN_POSITIONAL_ARGS);
|
||||
OPTION_PARSER_RETURN_POSITIONAL_ARGS,
|
||||
NULL);
|
||||
|
||||
/* Second namespace, OPTION: long form */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
"--required2=arg"),
|
||||
(Entry[]) {
|
||||
{ "required2", "arg" },
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL,
|
||||
"namespaced options");
|
||||
|
||||
/* Second namespace, OPTION: short form */
|
||||
test_macros_parse_one(STRV_MAKE("arg0",
|
||||
"-rarg"),
|
||||
(Entry[]) {
|
||||
{ "required2", "arg" },
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL,
|
||||
"namespaced options");
|
||||
}
|
||||
|
||||
/* Test the pattern used by nspawn's --user: an optional-arg option that also
|
||||
* peeks at the next arg to handle legacy "space-separated" form. */
|
||||
TEST(option_optional_arg_consume) {
|
||||
static const Option options[] = {
|
||||
{ 1, .short_code = 'h', .long_code = "help" },
|
||||
{ 2, .long_code = "user", .metavar = "NAME", .flags = OPTION_OPTIONAL_ARG },
|
||||
{ 3, .short_code = 'u', .long_code = "uid", .metavar = "USER" },
|
||||
{ __COUNTER__, .short_code = 'h', .long_code = "help" },
|
||||
{ __COUNTER__, .long_code = "user", .metavar = "NAME", .flags = OPTION_OPTIONAL_ARG },
|
||||
{ __COUNTER__, .short_code = 'u', .long_code = "uid", .metavar = "USER" },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -1176,7 +1312,8 @@ TEST(option_optional_arg_consume) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* --user without arg: next arg is an option, so no consumption */
|
||||
test_option_parse_one(STRV_MAKE("arg0",
|
||||
@@ -1189,7 +1326,8 @@ TEST(option_optional_arg_consume) {
|
||||
{}
|
||||
},
|
||||
NULL,
|
||||
OPTION_PARSER_NORMAL);
|
||||
OPTION_PARSER_NORMAL,
|
||||
NULL);
|
||||
|
||||
/* --user without arg: next arg is positional (doesn't start with -).
|
||||
* The option parser returns NULL for the arg. The caller would then
|
||||
|
||||
Reference in New Issue
Block a user