Skip to content

Commit 8dd07b7

Browse files
committed
option-parser: Add repeating positional arguments
1 parent 3bea938 commit 8dd07b7

File tree

3 files changed

+86
-2
lines changed

3 files changed

+86
-2
lines changed

include/option-parser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct wv_option {
2727
const char* default_;
2828
const char* positional;
2929
bool is_subcommand;
30+
bool is_repeating;
3031
};
3132

3233
struct wv_option_value {
@@ -64,4 +65,7 @@ const char* option_parser_get_value(const struct option_parser* self,
6465
const char* option_parser_get_value_no_default(const struct option_parser* self,
6566
const char* name);
6667

68+
const char* option_parser_get_value_with_offset(const struct option_parser* self,
69+
const char* name, int index);
70+
6771
void option_parser_print_cmd_summary(const char* summary, FILE* stream);

src/option-parser.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022 Andri Yngvason
2+
* Copyright (c) 2022 - 2024 Andri Yngvason
33
*
44
* Permission to use, copy, modify, and/or distribute this software for any
55
* purpose with or without fee is hereby granted, provided that the above
@@ -97,10 +97,15 @@ void option_parser_print_usage(struct option_parser* self, FILE* stream)
9797
print_string_tolower(stream, self->name);
9898
fprintf(stream, "]");
9999
int optional_paren_count = 0;
100+
int end = self->n_opts;
100101
for (int i = 0; i < self->n_opts; ++i) {
101102
const struct wv_option* opt = &self->options[i];
102103
if (!opt->positional)
103104
continue;
105+
if (opt->is_repeating) {
106+
end = i;
107+
break;
108+
}
104109
const char* open = "<";
105110
const char* close = ">";
106111
if (opt->default_) {
@@ -116,6 +121,15 @@ void option_parser_print_usage(struct option_parser* self, FILE* stream)
116121
}
117122
for (int i = 0; i < optional_paren_count; ++i)
118123
fprintf(stream, "]");
124+
125+
for (int i = end; i < self->n_opts; ++i) {
126+
const struct wv_option* opt = &self->options[i];
127+
if (!opt->positional)
128+
continue;
129+
assert(opt->is_repeating);
130+
131+
fprintf(stream, " [%s...]", opt->positional);
132+
}
119133
}
120134

121135
int option_parser_print_arguments(struct option_parser* self, FILE* stream)
@@ -301,7 +315,8 @@ static int parse_positional_arg(struct option_parser* self, char argc,
301315
if (append_value(self, opt, argv[i]) < 0)
302316
return -1;
303317

304-
self->position += 1;
318+
if (!opt->is_repeating)
319+
self->position += 1;
305320

306321
return opt->is_subcommand ? 0 : 1;
307322
}
@@ -394,6 +409,30 @@ const char* option_parser_get_value(const struct option_parser* self,
394409
return NULL;
395410
}
396411

412+
const char* option_parser_get_value_with_offset(const struct option_parser* self,
413+
const char* name, int index)
414+
{
415+
const struct wv_option* opt;
416+
417+
for (int i = 0; i < self->n_values; ++i) {
418+
const struct wv_option_value* value = &self->values[i];
419+
opt = value->option;
420+
421+
if (!opt->positional || !opt->is_repeating)
422+
continue;
423+
424+
if (strcmp(opt->positional, name) != 0)
425+
continue;
426+
427+
if (index-- > 0)
428+
continue;
429+
430+
return value->value;
431+
}
432+
433+
return NULL;
434+
}
435+
397436
void option_parser_print_cmd_summary(const char* summary, FILE* stream)
398437
{
399438
struct table_printer printer;

test/option-parser-test.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,46 @@ static int test_defaults_overridden(void)
289289
return 0;
290290
}
291291

292+
static int test_repeating_positional_option(void)
293+
{
294+
static const struct wv_option options[] = {
295+
{ .positional = "first" },
296+
{ .positional = "second", .is_repeating = true },
297+
{ 'a', "option-a", NULL, "Description of a" },
298+
{ },
299+
};
300+
301+
struct option_parser parser;
302+
option_parser_init(&parser, options);
303+
304+
const char* argv[] = {
305+
"executable",
306+
"non-repeating",
307+
"one",
308+
"-a",
309+
"two",
310+
"three",
311+
};
312+
313+
ASSERT_INT_EQ(0, option_parser_parse(&parser, ARRAY_SIZE(argv), argv));
314+
315+
ASSERT_STR_EQ("non-repeating", option_parser_get_value(&parser,
316+
"first"));
317+
ASSERT_STR_EQ("one", option_parser_get_value_with_offset(&parser,
318+
"second", 0));
319+
ASSERT_STR_EQ("two", option_parser_get_value_with_offset(&parser,
320+
"second", 1));
321+
ASSERT_STR_EQ("three", option_parser_get_value_with_offset(&parser,
322+
"second", 2));
323+
ASSERT_FALSE(option_parser_get_value_with_offset(&parser, "second", 3));
324+
325+
ASSERT_TRUE(option_parser_get_value(&parser, "a"));
326+
327+
ASSERT_INT_EQ(0, parser.remaining_argc);
328+
329+
return 0;
330+
}
331+
292332
int main()
293333
{
294334
int r = 0;
@@ -310,5 +350,6 @@ int main()
310350
RUN_TEST(test_subcommand_with_arguments);
311351
RUN_TEST(test_defaults_not_set);
312352
RUN_TEST(test_defaults_overridden);
353+
RUN_TEST(test_repeating_positional_option);
313354
return r;
314355
}

0 commit comments

Comments
 (0)