From: Zbigniew Jędrzejewski-Szmek Date: Wed, 29 Apr 2026 10:20:58 +0000 (+0200) Subject: shared/options: add new helper option_parser_get_arg X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ab9acf8c8cf75906ebe0827c200f5bbd31f28580;p=thirdparty%2Fsystemd.git shared/options: add new helper option_parser_get_arg option_parser_next_arg() is renamed to option_parser_peek_next_arg() to match option_parser_consume_next_arg(). A new helper is added option_parser_get_arg(…, n). It is a common pattern to only need a single arg, and getting an array and extracting a single item from it is too verbose. --- diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c index 5d0c7826893..f7e7ff12180 100644 --- a/src/cryptenroll/cryptenroll.c +++ b/src/cryptenroll/cryptenroll.c @@ -31,7 +31,6 @@ #include "process-util.h" #include "string-table.h" #include "string-util.h" -#include "strv.h" #include "tpm2-pcr.h" #include "tpm2-util.h" @@ -579,14 +578,12 @@ static int parse_argv(int argc, char *argv[]) { break; } - char **args = option_parser_get_args(&opts); + if (option_parser_get_n_args(&opts) > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many arguments, refusing."); - if (strv_length(args) > 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Too many arguments, refusing."); - - if (args[0]) - r = parse_path_argument(args[0], false, &arg_node); + const char *arg = option_parser_get_arg(&opts, 0); + if (arg) + r = parse_path_argument(arg, false, &arg_node); else if (!wipe_requested()) r = determine_default_node(); else diff --git a/src/growfs/growfs.c b/src/growfs/growfs.c index 3e9eb678bf0..efb94e37650 100644 --- a/src/growfs/growfs.c +++ b/src/growfs/growfs.c @@ -181,8 +181,7 @@ static int parse_argv(int argc, char *argv[]) { "%s expects exactly one argument (the mount point).", program_invocation_short_name); - arg_target = option_parser_get_args(&opts)[0]; - + arg_target = option_parser_get_arg(&opts, 0); return 1; } diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index e4e0359ce6d..6c9c1050c69 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1420,7 +1420,7 @@ static int parse_argv(int argc, char *argv[]) { * the old container user functionality. To maintain backwards compatibility * with the space-separated form (--user NAME), if the next opts.arg does not look * like an option, interpret it as a user name. */ - const char *t = option_parser_next_arg(&opts); + const char *t = option_parser_peek_next_arg(&opts); if (t && t[0] != '-') { opts.arg = option_parser_consume_next_arg(&opts); log_warning("--user NAME is deprecated, use --uid=NAME instead."); diff --git a/src/shared/options.c b/src/shared/options.c index 01684fc1fce..85ab3155bf8 100644 --- a/src/shared/options.c +++ b/src/shared/options.c @@ -344,7 +344,7 @@ int option_parse( return r; } -char* option_parser_next_arg(const OptionParser *state) { +char* option_parser_peek_next_arg(const OptionParser *state) { /* Peek at the next argument, whatever it is (option or position arg). * May return NULL. */ @@ -360,7 +360,7 @@ char* option_parser_consume_next_arg(OptionParser *state) { * so we won't try to interpret it as an option. * May return NULL. */ - char *t = option_parser_next_arg(state); + char *t = option_parser_peek_next_arg(state); if (t) shift_arg(state->argv, state->positional_offset++, state->optind++); return t; @@ -388,6 +388,14 @@ size_t option_parser_get_n_args(const OptionParser *state) { return state->argc - state->positional_offset; } +char* option_parser_get_arg(const OptionParser *state, size_t i) { + assert(state->optind > 0); + assert(state->state == OPTION_PARSER_DONE); + assert(state->positional_offset <= state->argc); + + return (size_t) (state->argc - state->positional_offset) > i ? state->argv[state->positional_offset + i] : NULL; +} + char* option_get_synopsis(const Option *opt, const char *joiner, bool show_metavar) { assert(opt); assert(!(opt->flags & (OPTION_NAMESPACE_MARKER | diff --git a/src/shared/options.h b/src/shared/options.h index a171f5f6a43..5803eb120ef 100644 --- a/src/shared/options.h +++ b/src/shared/options.h @@ -224,11 +224,17 @@ int option_parse( break; \ } else -char* option_parser_next_arg(const OptionParser *state); +/* Those helpers are used *during* option parsing and allow looking at or taking the next item in + * the argv array, either an option or a positional parameter. */ +char* option_parser_peek_next_arg(const OptionParser *state); char* option_parser_consume_next_arg(OptionParser *state); +/* Those helpers are used *after* option parsing and return the positional arguments (and unparsed + * options in case option parsing was stopped early, e.g. via "--"). */ char** option_parser_get_args(const OptionParser *state); size_t option_parser_get_n_args(const OptionParser *state); +char* option_parser_get_arg(const OptionParser *state, size_t i); + char* option_get_synopsis(const Option *opt, const char *joiner, bool show_metavar); int _option_parser_get_help_table_full( diff --git a/src/test/test-options.c b/src/test/test-options.c index 04fddc1c347..efa3a73d69e 100644 --- a/src/test/test-options.c +++ b/src/test/test-options.c @@ -58,7 +58,12 @@ static void test_option_parse_one( ASSERT_TRUE(strv_equal(args, remaining)); ASSERT_STREQ(argv[0], saved_argv0); - ASSERT_EQ(option_parser_get_n_args(&opts), strv_length(remaining)); + size_t l = strv_length(remaining); + ASSERT_EQ(option_parser_get_n_args(&opts), l); + ASSERT_STREQ(option_parser_get_arg(&opts, 0), l > 0 ? remaining[0] : NULL); + ASSERT_STREQ(option_parser_get_arg(&opts, 1), l > 1 ? remaining[1] : NULL); + ASSERT_STREQ(option_parser_get_arg(&opts, 2), l > 2 ? remaining[2] : NULL); + ASSERT_STREQ(option_parser_get_arg(&opts, 3), l > 3 ? remaining[3] : NULL); } static void test_option_invalid_one( @@ -1331,7 +1336,7 @@ TEST(option_optional_arg_consume) { /* --user without arg: next arg is positional (doesn't start with -). * The option parser returns NULL for the arg. The caller would then - * use option_parser_next_arg/consume_next_arg to grab it. */ + * use option_parser_peek_next_arg/consume_next_arg to grab it. */ { char **argv = STRV_MAKE("arg0", "--user", "someuser", "pos1"); int argc = strv_length(argv); @@ -1341,7 +1346,7 @@ TEST(option_optional_arg_consume) { ASSERT_OK_POSITIVE(option_parse(options, options + 3, &opts)); ASSERT_STREQ(opts.opt->long_code, "user"); ASSERT_NULL(opts.arg); - ASSERT_STREQ(option_parser_next_arg(&opts), "someuser"); + ASSERT_STREQ(option_parser_peek_next_arg(&opts), "someuser"); ASSERT_STREQ(option_parser_consume_next_arg(&opts), "someuser"); ASSERT_EQ(option_parse(options, options + 3, &opts), 0); @@ -1361,7 +1366,7 @@ TEST(option_optional_arg_consume) { ASSERT_OK_POSITIVE(option_parse(options, options + 3, &opts)); ASSERT_STREQ(opts.opt->long_code, "user"); ASSERT_NULL(opts.arg); - ASSERT_NULL(option_parser_next_arg(&opts)); + ASSERT_NULL(option_parser_peek_next_arg(&opts)); ASSERT_NULL(option_parser_consume_next_arg(&opts)); ASSERT_EQ(option_parse(options, options + 3, &opts), 0); @@ -1381,12 +1386,12 @@ TEST(option_optional_arg_consume) { ASSERT_OK_POSITIVE(option_parse(options, options + 3, &opts)); ASSERT_STREQ(opts.opt->long_code, "user"); ASSERT_NULL(opts.arg); - ASSERT_STREQ(option_parser_next_arg(&opts), "-u"); + ASSERT_STREQ(option_parser_peek_next_arg(&opts), "-u"); ASSERT_OK_POSITIVE(option_parse(options, options + 3, &opts)); ASSERT_STREQ(opts.opt->long_code, "uid"); ASSERT_STREQ(opts.arg, "nobody"); - ASSERT_NULL(option_parser_next_arg(&opts)); + ASSERT_NULL(option_parser_peek_next_arg(&opts)); ASSERT_NULL(option_parser_consume_next_arg(&opts)); ASSERT_EQ(option_parse(options, options + 3, &opts), 0); @@ -1413,7 +1418,7 @@ TEST(option_optional_arg_consume) { const char *arg = opts.arg; if (!arg) { - const char *t = option_parser_next_arg(&opts); + const char *t = option_parser_peek_next_arg(&opts); if (t && t[0] != '-') arg = option_parser_consume_next_arg(&opts); }