Skip to content

Commit 5af693a

Browse files
committed
[Feature] Adding variadic positional arguments
1 parent 1291c24 commit 5af693a

File tree

30 files changed

+1195
-23
lines changed

30 files changed

+1195
-23
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111
- Printing subcommand list on command execution that cannot be executed directly.
12+
- Support for variadic positional arguments, allowing multiple values for a single positional argument.
1213

1314
### Changed
1415
- Change `ARGUS_RELEASE` into `ARGUS_DEBUG`

Justfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ examples:
165165
basic_usage) desc="Basic flags, string, and integer options" ;; \
166166
subcommands) desc="Git-like subcommands implementation" ;; \
167167
positional_args) desc="Handling positional arguments" ;; \
168+
variadic_positional) desc="Handling variadic positional arguments" ;; \
168169
custom_handlers) desc="Creating custom option handlers" ;; \
169170
advanced_options) desc="Dependencies, conflicts, and exclusive groups" ;; \
170171
nested_commands) desc="Complex nested subcommands (like git or docker)" ;; \

argus.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ EXPORTS
3838
free_map_int_handler
3939
free_map_float_handler
4040
free_map_bool_handler
41+
variadic_string_handler
42+
variadic_int_handler
43+
variadic_float_handler
44+
free_variadic_string_handler
4145
range_validator
4246
length_validator
4347
count_validator

docs/docs/api-reference/overview.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ POSITIONAL_STRING(name, ...) // String positional
7676
POSITIONAL_INT(name, ...) // Integer positional
7777
POSITIONAL_BOOL(name, ...) // Boolean positional
7878
POSITIONAL_FLOAT(name, ...) // Float positional
79+
80+
// Variadic positionals (accept multiple values)
81+
POSITIONAL_MANY_STRING(name, ...) // Variadic string positional
82+
POSITIONAL_MANY_INT(name, ...) // Variadic integer positional
83+
POSITIONAL_MANY_FLOAT(name, ...) // Variadic float positional
7984
```
8085
8186
### Structure Macros

docs/docs/appendices/cheat-sheet.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,14 @@ OPTION_MAP_BOOL('f', "features", HELP("Feature flags"))
5454
## // Positional Arguments
5555

5656
```c
57+
// Single positionals
5758
POSITIONAL_STRING("input", HELP("Input file"))
5859
POSITIONAL_INT("count", HELP("Count"), FLAGS(FLAG_OPTIONAL))
60+
61+
// Variadic positionals (accept multiple values)
62+
POSITIONAL_MANY_STRING("files", HELP("Files to process"))
63+
POSITIONAL_MANY_INT("numbers", HELP("Numbers to calculate"))
64+
POSITIONAL_MANY_FLOAT("values", HELP("Decimal values"))
5965
```
6066
6167
## // Option Modifiers
@@ -208,6 +214,9 @@ int main(int argc, char **argv)
208214
# Positional
209215
program input.txt output.txt
210216
217+
# Variadic positionals
218+
program file1.c file2.c file3.c ...
219+
211220
# Stop parsing
212221
program --option -- --not-an-option
213222
```

docs/docs/features/collections.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,75 @@ ARGUS_OPTIONS(
178178
</TabItem>
179179
</Tabs>
180180

181+
## // Variadic Positional
182+
183+
Accept multiple values for positional arguments, similar to array options but for positional parameters:
184+
185+
<Tabs>
186+
<TabItem value="variadic-types" label="Variadic Types" default>
187+
188+
```c
189+
ARGUS_OPTIONS(
190+
options,
191+
HELP_OPTION(),
192+
193+
// Multiple string values
194+
POSITIONAL_MANY_STRING("files", HELP("Input files to process"),
195+
HINT("FILE..."),
196+
FLAGS(FLAG_UNIQUE | FLAG_SORTED)),
197+
198+
// Multiple integer values
199+
POSITIONAL_MANY_INT("numbers", HELP("Numbers to process"),
200+
HINT("NUM..."),
201+
VALIDATOR(V_COUNT(1, 10))),
202+
203+
// Multiple float values
204+
POSITIONAL_MANY_FLOAT("values", HELP("Float values"),
205+
HINT("VAL...")),
206+
)
207+
```
208+
209+
**Supported types:**
210+
- `POSITIONAL_MANY_STRING` - Multiple text values
211+
- `POSITIONAL_MANY_INT` - Multiple integer values
212+
- `POSITIONAL_MANY_FLOAT` - Multiple decimal values
213+
214+
**Important restrictions:**
215+
- Only one `POSITIONAL_MANY` argument allowed per command
216+
- Must be placed after all regular positional arguments
217+
- Cannot mix with optional positional arguments
218+
- Cannot be used with subcommands at the same level
219+
220+
</TabItem>
221+
<TabItem value="variadic-usage" label="Usage Examples">
222+
223+
```bash
224+
# Process multiple files
225+
./program file1.c file2.c file3.c
226+
227+
# Process numbers
228+
./program 1 2 3 4 5
229+
230+
# No arguments (if optional)
231+
./program
232+
233+
# With other options
234+
./program --verbose file1.txt file2.txt
235+
```
236+
237+
**Key characteristics:**
238+
- Accepts zero or more values
239+
- Values are collected into an array
240+
- Supports same flags as array options (`FLAG_SORTED`, `FLAG_UNIQUE`)
241+
- Can use validation like `V_COUNT()` to enforce limits
242+
243+
**Access methods:**
244+
- Same as array collections: `argus_get().as_array`, `argus_count()`, `argus_array_it()`
245+
- See [Accessing Values](../fundamentals/accessing-values#variadic-positional) for detailed examples
246+
247+
</TabItem>
248+
</Tabs>
249+
181250
## // Accessing Collection Values
182251
183252
<Tabs>

docs/docs/fundamentals/accessing-values.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,114 @@ int main(int argc, char **argv)
361361
</TabItem>
362362
</Tabs>
363363

364+
## // Variadic Positional
365+
366+
Access variadic positional arguments the same way as array collections:
367+
368+
<Tabs>
369+
<TabItem value="variadic-direct" label="Direct Access" default>
370+
371+
```c
372+
ARGUS_OPTIONS(
373+
options,
374+
HELP_OPTION(),
375+
POSITIONAL_MANY_STRING("files", HELP("Input files to process"),
376+
HINT("FILE..."),
377+
FLAGS(FLAG_UNIQUE | FLAG_SORTED)),
378+
)
379+
380+
int main(int argc, char **argv)
381+
{
382+
argus_t argus = argus_init(options, "myapp", "1.0.0");
383+
argus_parse(&argus, argc, argv);
384+
385+
// Check if files were provided
386+
if (argus_is_set(&argus, "files")) {
387+
size_t count = argus_count(&argus, "files");
388+
argus_value_t *files = argus_get(&argus, "files").as_array;
389+
390+
printf("Processing %zu files:\n", count);
391+
for (size_t i = 0; i < count; i++) {
392+
printf(" %zu: %s\n", i + 1, files[i].as_string);
393+
}
394+
} else {
395+
printf("No files specified\n");
396+
}
397+
398+
argus_free(&argus);
399+
return 0;
400+
}
401+
```
402+
403+
</TabItem>
404+
<TabItem value="variadic-iterator" label="Iterator Access">
405+
406+
```c
407+
ARGUS_OPTIONS(
408+
options,
409+
HELP_OPTION(),
410+
POSITIONAL_MANY_STRING("files", HELP("Input files"), HINT("FILE...")),
411+
)
412+
413+
int main(int argc, char **argv)
414+
{
415+
argus_t argus = argus_init(options, "myapp", "1.0.0");
416+
argus_parse(&argus, argc, argv);
417+
418+
// Use iterator for clean traversal
419+
argus_array_it_t it = argus_array_it(&argus, "files");
420+
421+
printf("Files to process:\n");
422+
while (argus_array_next(&it)) {
423+
printf(" - %s\n", it.value.as_string);
424+
process_file(it.value.as_string);
425+
}
426+
427+
argus_free(&argus);
428+
return 0;
429+
}
430+
```
431+
432+
</TabItem>
433+
<TabItem value="variadic-helper" label="Helper Functions">
434+
435+
```c
436+
ARGUS_OPTIONS(
437+
options,
438+
HELP_OPTION(),
439+
POSITIONAL_MANY_INT("numbers", HELP("Numbers to process"), HINT("NUM...")),
440+
)
441+
442+
int main(int argc, char **argv)
443+
{
444+
argus_t argus = argus_init(options, "myapp", "1.0.0");
445+
argus_parse(&argus, argc, argv);
446+
447+
// Access specific elements by index
448+
int first = argus_array_get(&argus, "numbers", 0).as_int;
449+
int second = argus_array_get(&argus, "numbers", 1).as_int;
450+
451+
if (first) printf("First number: %d\n", first);
452+
if (second) printf("Second number: %d\n", second);
453+
454+
// Get total count
455+
size_t total = argus_count(&argus, "numbers");
456+
printf("Total numbers: %zu\n", total);
457+
458+
argus_free(&argus);
459+
return 0;
460+
}
461+
```
462+
463+
</TabItem>
464+
</Tabs>
465+
466+
**Important notes:**
467+
- Variadic positionals use the same access functions as array options
468+
- Always check `argus_is_set()` before accessing since they accept zero values
469+
- Use `argus_count()` to get the number of values provided
470+
- Iterator pattern is most efficient for processing all values
471+
364472
## // Subcommand Access
365473
366474
In subcommand context, access values using path notation or relative names:
@@ -576,6 +684,8 @@ FILE *f = fopen(output, "w"); // output might be NULL!
576684
| **Check if set** | `argus_is_set()` | `if (argus_is_set(&argus, "output"))` |
577685
| **Array elements** | `argus_array_get()` | `argus_array_get(&argus, "tags", 0)` |
578686
| **Array iteration** | `argus_array_it()` | `while (argus_array_next(&it))` |
687+
| **Variadic positional** | `argus_get().as_array` | `argus_get(&argus, "files").as_array` |
688+
| **Variadic iteration** | `argus_array_it()` | `while (argus_array_next(&it))` |
579689
| **Map lookup** | `argus_map_get()` | `argus_map_get(&argus, "env", "USER")` |
580690
| **Map iteration** | `argus_map_it()` | `while (argus_map_next(&it))` |
581691
| **Subcommand relative** | `argus_get()` | `argus_get(argus, "file").as_string` |

examples/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ example_programs = [
33
'basic_usage',
44
'subcommands',
55
'positional_args',
6+
'variadic_positional',
67
'custom_handlers',
78
'advanced_options',
89
'nested_commands',

examples/variadic_positional.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Multi-value positional arguments example for argus
3+
*
4+
* Demonstrates how to use positional arguments that can accept
5+
* multiple values, similar to how many command-line tools work (e.g., cp file1 file2 file3 dest)
6+
*/
7+
8+
#include "argus.h"
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
12+
ARGUS_OPTIONS(
13+
options,
14+
HELP_OPTION(),
15+
VERSION_OPTION(),
16+
17+
POSITIONAL_STRING(
18+
"command", HELP("Operation to perform"), HINT("CMD"),
19+
VALIDATOR(V_CHOICE_STR("build", "copy", "delete"))),
20+
21+
POSITIONAL_MANY_STRING(
22+
"files", HELP("Input files to process"), HINT("FILE..."),
23+
FLAGS(FLAG_UNIQUE | FLAG_SORTED)),
24+
)
25+
26+
int main(int argc, char **argv)
27+
{
28+
argus_t argus = argus_init(options, "many_args_example", "1.0.0");
29+
argus.description = "Example of multi-value positional arguments";
30+
31+
int status = argus_parse(&argus, argc, argv);
32+
if (status != ARGUS_SUCCESS)
33+
return status;
34+
35+
const char *command = argus_get(&argus, "command").as_string;
36+
37+
printf("Configuration:\n");
38+
printf(" Command: %s\n", command);
39+
40+
if (argus_is_set(&argus, "files"))
41+
{
42+
printf(" Files:\n");
43+
44+
argus_array_it_t it = argus_array_it(&argus, "files");
45+
while (argus_array_next(&it))
46+
printf(" - %s\n", it.value.as_string);
47+
}
48+
else
49+
printf(" Files: (none)\n");
50+
51+
argus_free(&argus);
52+
return 0;
53+
}

includes/argus/internal/callbacks/handlers.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ int map_bool_handler(argus_t *argus, argus_option_t *options, char *arg);
3535
*/
3636
int default_free(argus_option_t *option);
3737
int free_array_string_handler(argus_option_t *option);
38-
// int free_array_int_handler(argus_option_t *option);
39-
// int free_array_float_handler(argus_option_t *option);
38+
int free_variadic_string_handler(argus_option_t *option);
4039

4140
int free_map_string_handler(argus_option_t *option);
4241
int free_map_int_handler(argus_option_t *option);

0 commit comments

Comments
 (0)