extern const Option __start_SYSTEMD_OPTIONS[];
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,
+
+ /* Same as "+…" for getopt_long — only parse options before the first positional argument. */
+ OPTION_PARSER_STOP_AT_FIRST_NONOPTION,
+
+ _OPTION_PARSER_MODE_MAX,
+} OptionParserMode;
+
typedef struct OptionParser {
/* Those three should stay first so that it's possible to initialize the struct as { argc, argv }
- * or { argc, argv, true/false }. */
+ * or { argc, argv, mode }. */
int argc; /* The original argc. */
char **argv; /* The argv array, possibly reordered. */
- bool stop_at_first_nonoption; /* Same as "+…" for getopt_long — only parse options before the first
- * positional argument. */
+ OptionParserMode mode;
bool parsing_stopped; /* We processed "--" or an option that terminates option parsing. */
int optind; /* Position of the parameter being handled.
const Option options[],
const Entry *entries,
char **remaining,
- bool stop_at_first_nonoption) {
+ OptionParserMode mode) {
_cleanup_free_ char *joined = strv_join(argv, ", ");
log_debug("/* %s(%s) */", __func__, joined);
for (const Entry *e = entries; e && (e->long_code || e->short_code != 0); e++)
n_entries++;
- OptionParser state = { argc, argv, stop_at_first_nonoption };
+ OptionParser state = { argc, argv, mode };
const Option *opt;
const char *arg;
for (int c; (c = option_parse(options, options + n_options, &state, &opt, &arg)) != 0; ) {
options,
NULL,
NULL,
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"string1",
"string2",
"string3",
"string4"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"--",
"--help",
"-h",
"string4"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"string1",
"string2",
"--",
"string4"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"string1",
"string2",
"string3",
"string4"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"--help"),
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"--help",
},
STRV_MAKE("string1",
"--help"),
- true);
+ OPTION_PARSER_STOP_AT_FIRST_NONOPTION);
test_option_parse_one(STRV_MAKE("arg0",
"-h"),
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"--help",
"string2",
"string3",
"string4"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"-h",
"string2",
"string3",
"string4"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"string1",
"string2",
"string3",
"string4"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"string1",
"string2",
"string3",
"string4"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"string1",
"string2",
"string3",
"string4"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"string1",
"string2",
"string3",
"string4"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"--required1", "reqarg1"),
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"-r", "reqarg1"),
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"string1",
},
STRV_MAKE("string1",
"string2"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"string1",
STRV_MAKE("string1",
"string2",
"-r", "reqarg1"),
- true);
+ OPTION_PARSER_STOP_AT_FIRST_NONOPTION);
test_option_parse_one(STRV_MAKE("arg0",
"--optional1=optarg1"),
{}
},
NULL,
- true);
+ OPTION_PARSER_STOP_AT_FIRST_NONOPTION);
test_option_parse_one(STRV_MAKE("arg0",
"--optional1", "string1"),
{}
},
STRV_MAKE("string1"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"-ooptarg1"),
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"-o", "string1"),
{}
},
STRV_MAKE("string1"),
- false);
+ OPTION_PARSER_NORMAL);
test_option_parse_one(STRV_MAKE("arg0",
"string1",
"--help",
"--required1",
"--optional1"),
- false);
+ OPTION_PARSER_NORMAL);
}
TEST(option_stops_parsing) {
},
STRV_MAKE("--help",
"foo"),
- false);
+ OPTION_PARSER_NORMAL);
/* Options before --exec are still parsed */
test_option_parse_one(STRV_MAKE("arg0",
},
STRV_MAKE("--version",
"bar"),
- false);
+ OPTION_PARSER_NORMAL);
/* --exec with no trailing args */
test_option_parse_one(STRV_MAKE("arg0",
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
/* --exec after positional args */
test_option_parse_one(STRV_MAKE("arg0",
"--help",
"--required",
"val"),
- false);
+ OPTION_PARSER_NORMAL);
/* "--" 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
{}
},
STRV_MAKE("--help"),
- false);
+ OPTION_PARSER_NORMAL);
/* "--" before --exec: "--" terminates first, --exec is positional */
test_option_parse_one(STRV_MAKE("arg0",
NULL,
STRV_MAKE("--exec",
"--help"),
- false);
+ OPTION_PARSER_NORMAL);
/* Multiple options then --exec then more option-like args */
test_option_parse_one(STRV_MAKE("arg0",
STRV_MAKE("-h",
"--required",
"val2"),
- false);
+ OPTION_PARSER_NORMAL);
}
TEST(option_group_marker) {
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
/* Check that group marker name is ignored */
test_option_parse_one(STRV_MAKE("arg0",
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
/* Verify that the group marker is not mistaken for an option */
test_option_invalid_one(STRV_MAKE("arg0",
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
/* Partial match with multiple candidates */
test_option_invalid_one(STRV_MAKE("arg0",
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
/* Long option without = does NOT consume the next arg */
test_option_parse_one(STRV_MAKE("arg0",
{}
},
STRV_MAKE("foo.txt"),
- false);
+ OPTION_PARSER_NORMAL);
/* Short option with inline arg */
test_option_parse_one(STRV_MAKE("arg0",
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
/* Short option without inline arg does NOT consume the next arg */
test_option_parse_one(STRV_MAKE("arg0",
{}
},
STRV_MAKE("foo.txt"),
- false);
+ OPTION_PARSER_NORMAL);
/* Optional arg option at end of argv */
test_option_parse_one(STRV_MAKE("arg0",
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
/* Mixed: optional arg with other options */
test_option_parse_one(STRV_MAKE("arg0",
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
/* Short combo: -ho (h then o with no arg) */
test_option_parse_one(STRV_MAKE("arg0",
{}
},
STRV_MAKE("pos1"),
- false);
+ OPTION_PARSER_NORMAL);
/* Short combo: -hobar (h then o with inline arg "bar") */
test_option_parse_one(STRV_MAKE("arg0",
{}
},
NULL,
- false);
+ OPTION_PARSER_NORMAL);
}
/* Test the OPTION, OPTION_LONG, OPTION_SHORT, OPTION_FULL, OPTION_GROUP macros
{}
},
NULL,
- /* stop_at_first_nonoption= */ false);
+ OPTION_PARSER_NORMAL);
/* --user without arg: next arg is an option, so no consumption */
test_option_parse_one(STRV_MAKE("arg0",
{}
},
NULL,
- /* stop_at_first_nonoption= */ false);
+ OPTION_PARSER_NORMAL);
/* --user without arg: next arg is positional (doesn't start with -).
* The option parser returns NULL for the arg. The caller would then