]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
shared/options: add new helper option_parser_get_arg
authorZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Wed, 29 Apr 2026 10:20:58 +0000 (12:20 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Wed, 29 Apr 2026 13:28:05 +0000 (15:28 +0200)
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.

src/cryptenroll/cryptenroll.c
src/growfs/growfs.c
src/nspawn/nspawn.c
src/shared/options.c
src/shared/options.h
src/test/test-options.c

index 5d0c782689392306ebbb904bbc900fc1d1cb8435..f7e7ff121804a478c5b2af0c4b518d17020a10d2 100644 (file)
@@ -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
index 3e9eb678bf038b6a78cc3d7776050deacf82e84c..efb94e37650530db277827671d47f370f1a179f9 100644 (file)
@@ -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;
 }
 
index e4e0359ce6d79e9170f81fbce896a848ac534f65..6c9c1050c692140e9ce782b76ef7c1477a9d183d 100644 (file)
@@ -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.");
index 01684fc1fced5170499681eaec6ef9ca035761c7..85ab3155bf867fba5367ab90b88991b5fccd85f5 100644 (file)
@@ -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 |
index a171f5f6a43f74704e890cf25bcd2e3d5ff483e0..5803eb120ef6775a223be25c14c9b715a8a2cc93 100644 (file)
@@ -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(
index 04fddc1c347009c2227662e2c898f65f5b43e182..efa3a73d69edd97ba0dc645c8eb3333178550f27 100644 (file)
@@ -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);
                                 }