shared/options: store argc+argv in the OptionParser state struct

After writing the code to parse options in a bunch of places, I think
passing the argc+argv to various functions to query state is annoying.
It seems nicer to just stash them in the state struct once.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-03-26 10:31:23 +01:00
parent 5dd2ae14db
commit f4a5bf92e9
12 changed files with 72 additions and 73 deletions
+3 -3
View File
@@ -48,10 +48,10 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -68,7 +68,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
if (option_parser_get_n_args(&state, argc) > 0)
if (option_parser_get_n_args(&state) > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
return 1;
+3 -3
View File
@@ -71,10 +71,10 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -170,7 +170,7 @@ static int parse_argv(int argc, char *argv[]) {
SET_FLAG(arg_flags, ASK_PASSWORD_HIDE_EMOJI, !r);
}
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
if (!strv_isempty(args)) {
arg_message = strv_join(args, " ");
+3 -3
View File
@@ -137,10 +137,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -165,7 +165,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
break;
}
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
if ((arg_unregister || arg_cat_flags != CAT_CONFIG_OFF) && !strv_isempty(args))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+3 -3
View File
@@ -75,10 +75,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -93,7 +93,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
break;
}
*ret_args = option_parser_get_args(&state, argc, argv);
*ret_args = option_parser_get_args(&state);
return 1;
}
+3 -3
View File
@@ -207,11 +207,11 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const Option *current;
const char *arg;
FOREACH_OPTION_FULL(&state, c, argc, argv, &current, &arg, /* on_error= */ return c)
FOREACH_OPTION_FULL(&state, c, &current, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_NO_PAGER:
@@ -495,7 +495,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_runtime_scope = system_scope_requested && user_scope_requested ? _RUNTIME_SCOPE_INVALID :
system_scope_requested ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
switch (arg_action) {
+3 -3
View File
@@ -233,10 +233,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -288,7 +288,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
break;
}
*ret_args = option_parser_get_args(&state, argc, argv);
*ret_args = option_parser_get_args(&state);
return 1;
}
+3 -3
View File
@@ -158,10 +158,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
@@ -283,7 +283,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
bool have_env = arg_ready || arg_stopping || arg_reloading || arg_status || pidref_is_set(&arg_pid) || !fdset_isempty(arg_fds);
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
switch (arg_action) {
+26 -30
View File
@@ -69,7 +69,6 @@ int option_parse(
const Option options[],
const Option options_end[],
OptionParser *state,
int argc, char *argv[],
const Option **ret_option,
const char **ret_arg) {
@@ -77,13 +76,10 @@ int option_parse(
/* Check and initialize */
if (state->optind == 0) {
if (argc < 1 || strv_isempty(argv))
if (state->argc < 1 || strv_isempty(state->argv))
return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "argv cannot be empty");
*state = (OptionParser) {
.optind = 1,
.positional_offset = 1,
};
state->optind = state->positional_offset = 1;
}
/* Look for the next option */
@@ -96,21 +92,21 @@ int option_parse(
if (state->short_option_offset == 0) {
/* Skip over non-option parameters */
for (;;) {
if (state->optind == argc)
if (state->optind == state->argc)
return 0;
if (streq(argv[state->optind], "--")) {
if (streq(state->argv[state->optind], "--")) {
/* No more options. Move "--" before positional args so that
* the list of positional args is clean. */
shift_arg(argv, state->positional_offset++, state->optind++);
shift_arg(state->argv, state->positional_offset++, state->optind++);
state->parsing_stopped = true;
}
if (state->parsing_stopped)
return 0;
if (argv[state->optind][0] == '-' &&
argv[state->optind][1] != '\0')
if (state->argv[state->optind][0] == '-' &&
state->argv[state->optind][1] != '\0')
/* Looks like we found an option parameter */
break;
@@ -119,13 +115,13 @@ int option_parse(
/* Find matching option entry.
* First, figure out if we have a long option or a short option. */
assert(argv[state->optind][0] == '-');
assert(state->argv[state->optind][0] == '-');
if (argv[state->optind][1] == '-') {
if (state->argv[state->optind][1] == '-') {
/* We have a long option. */
char *eq = strchr(argv[state->optind], '=');
char *eq = strchr(state->argv[state->optind], '=');
if (eq) {
optname = _optname = strndup(argv[state->optind], eq - argv[state->optind]);
optname = _optname = strndup(state->argv[state->optind], eq - state->argv[state->optind]);
if (!_optname)
return log_oom();
@@ -133,7 +129,7 @@ int option_parse(
optval = eq + 1;
} else
/* argument (if any) is separate */
optname = argv[state->optind];
optname = state->argv[state->optind];
const Option *last_partial = NULL;
unsigned n_partial_matches = 0; /* The commandline option matches a defined prefix. */
@@ -172,7 +168,7 @@ int option_parse(
}
if (state->short_option_offset > 0) {
char optchar = argv[state->optind][state->short_option_offset];
char optchar = state->argv[state->optind][state->short_option_offset];
if (asprintf(&_optname, "-%c", optchar) < 0)
return log_oom();
@@ -187,7 +183,7 @@ int option_parse(
if (option_is_metadata(option) || optchar != option->short_code)
continue;
const char *rest = argv[state->optind] + state->short_option_offset + 1;
const char *rest = state->argv[state->optind] + state->short_option_offset + 1;
if (option_takes_arg(option) && !isempty(rest)) {
/* The rest of this parameter is the value. */
@@ -209,19 +205,19 @@ int option_parse(
"%s: option '%s' doesn't allow an argument",
program_invocation_short_name, optname);
if (!optval && option_arg_required(option)) {
if (!argv[state->optind + 1])
if (!state->argv[state->optind + 1])
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: option '%s' requires an argument",
program_invocation_short_name, optname);
optval = argv[state->optind + 1];
optval = state->argv[state->optind + 1];
separate_optval = true;
}
if (state->short_option_offset == 0) {
/* We're done with this option. Adjust the array and position. */
shift_arg(argv, state->positional_offset++, state->optind++);
shift_arg(state->argv, state->positional_offset++, state->optind++);
if (separate_optval)
shift_arg(argv, state->positional_offset++, state->optind++);
shift_arg(state->argv, state->positional_offset++, state->optind++);
}
if (FLAGS_SET(option->flags, OPTION_STOPS_PARSING))
@@ -234,25 +230,25 @@ int option_parse(
return option->id;
}
char** option_parser_get_args(const OptionParser *state, int argc, char *argv[]) {
char** option_parser_get_args(const OptionParser *state) {
/* Returns positional args as a strv.
* If "--" was found, it has been moved before state->positional_offset.
* The array is only valid, i.e. clean without any options, after parsing
* has naturally finished. */
assert(state->optind > 0);
assert(state->optind == argc || state->parsing_stopped);
assert(state->positional_offset <= argc);
assert(state->optind == state->argc || state->parsing_stopped);
assert(state->positional_offset <= state->argc);
return argv + state->positional_offset;
return state->argv + state->positional_offset;
}
size_t option_parser_get_n_args(const OptionParser *state, int argc) {
size_t option_parser_get_n_args(const OptionParser *state) {
assert(state->optind > 0);
assert(state->optind == argc || state->parsing_stopped);
assert(state->positional_offset <= argc);
assert(state->optind == state->argc || state->parsing_stopped);
assert(state->positional_offset <= state->argc);
return argc - state->positional_offset;
return state->argc - state->positional_offset;
}
int _option_parser_get_help_table(
+10 -7
View File
@@ -68,6 +68,10 @@ extern const Option __start_SYSTEMD_OPTIONS[];
extern const Option __stop_SYSTEMD_OPTIONS[];
typedef struct OptionParser {
/* Those two should stay first so that it's possible to initialize the struct as { argc, argv }. */
int argc; /* The original argc. */
char **argv; /* The argv array, possibly reordered. */
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.
@@ -81,23 +85,22 @@ int option_parse(
const Option options[],
const Option options_end[],
OptionParser *state,
int argc, char *argv[],
const Option **ret_option,
const char **ret_arg);
/* Iterate over options. */
#define FOREACH_OPTION_FULL(parser, opt, argc, argv, ret_o, ret_a, on_error) \
for (int opt; (opt = option_parse(ALIGN_PTR(__start_SYSTEMD_OPTIONS), __stop_SYSTEMD_OPTIONS, parser, argc, argv, ret_o, ret_a)) != 0; ) \
#define FOREACH_OPTION_FULL(parser, opt, ret_o, ret_a, on_error) \
for (int opt; (opt = option_parse(ALIGN_PTR(__start_SYSTEMD_OPTIONS), __stop_SYSTEMD_OPTIONS, parser, ret_o, ret_a)) != 0; ) \
if (opt < 0) { \
on_error; \
break; \
} else
#define FOREACH_OPTION(parser, opt, argc, argv, ret_a, on_error) \
FOREACH_OPTION_FULL(parser, opt, argc, argv, /* ret_o= */ NULL, ret_a, on_error)
#define FOREACH_OPTION(parser, opt, ret_a, on_error) \
FOREACH_OPTION_FULL(parser, opt, /* ret_o= */ NULL, ret_a, on_error)
char** option_parser_get_args(const OptionParser *state, int argc, char *argv[]);
size_t option_parser_get_n_args(const OptionParser *state, int argc);
char** option_parser_get_args(const OptionParser *state);
size_t option_parser_get_n_args(const OptionParser *state);
int _option_parser_get_help_table(
const Option options[],
+9 -9
View File
@@ -31,10 +31,10 @@ static void test_option_parse_one(
for (const Entry *e = entries; e && (e->long_code || e->short_code != 0); e++)
n_entries++;
OptionParser state = {};
OptionParser state = { argc, argv };
const Option *opt;
const char *arg;
for (int c; (c = option_parse(options, options + n_options, &state, argc, argv, &opt, &arg)) != 0; ) {
for (int c; (c = option_parse(options, options + n_options, &state, &opt, &arg)) != 0; ) {
ASSERT_OK(c);
ASSERT_NOT_NULL(opt);
@@ -54,11 +54,11 @@ static void test_option_parse_one(
ASSERT_EQ(i, n_entries);
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
ASSERT_TRUE(strv_equal(args, remaining));
ASSERT_STREQ(argv[0], saved_argv0);
ASSERT_EQ(option_parser_get_n_args(&state, argc), strv_length(remaining));
ASSERT_EQ(option_parser_get_n_args(&state), strv_length(remaining));
}
static void test_option_invalid_one(
@@ -77,11 +77,11 @@ static void test_option_invalid_one(
for (const Option *o = options; o->short_code != 0 || o->long_code; o++)
n_options++;
OptionParser state = {};
OptionParser state = { argc, argv };
const Option *opt;
const char *arg;
int c = option_parse(options, options + n_options, &state, argc, argv, &opt, &arg);
int c = option_parse(options, options + n_options, &state, &opt, &arg);
ASSERT_ERROR(c, EINVAL);
}
@@ -691,11 +691,11 @@ static void test_macros_parse_one(
for (const Entry *e = entries; e && (e->long_code || e->short_code != 0); e++)
n_entries++;
OptionParser state = {};
OptionParser state = { argc, argv };
const Option *opt;
const char *arg;
FOREACH_OPTION_FULL(&state, c, argc, argv, &opt, &arg, ASSERT_TRUE(false)) {
FOREACH_OPTION_FULL(&state, c, &opt, &arg, ASSERT_TRUE(false)) {
log_debug("%c %s: %s=%s",
opt->short_code != 0 ? opt->short_code : ' ',
opt->long_code ?: "",
@@ -751,7 +751,7 @@ static void test_macros_parse_one(
ASSERT_EQ(i, n_entries);
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
ASSERT_TRUE(strv_equal(args, remaining));
ASSERT_STREQ(argv[0], saved_argv0);
}
+3 -3
View File
@@ -94,10 +94,10 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -112,7 +112,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
if (option_parser_get_n_args(&state, argc) > 0)
if (option_parser_get_n_args(&state) > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
return 1;
+3 -3
View File
@@ -62,10 +62,10 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -88,7 +88,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
if (strv_length(args) != 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),