Files
args.h/README.md
Krzosa Karol f54ed04e27 first version
2025-10-20 11:34:59 +02:00

208 lines
5.5 KiB
Markdown

## args.h
Single header command line argument parser for C and C++
loosely based on python's argparse module.
## Usage:
Define implementation macro before you include this file in *one* C or C++ file:
```c
#define ARGS_IMPLEMENTATION
```
Should look like this:
```c
#include ...
#include ...
#define ARGS_IMPLEMENTATION
#include "args.h"
```
This will expand the implementation into that file, in other files you can just
include the header without the macro.
```c
#include "args.h"
```
## Example:
```c
void example(int argc, char **argv) {
args_t args = {
.program = argv[0],
.description = "this program does very cool things on many files and outputs it somewhere",
.epilogue = "it also shows cool epilogue text in it's help",
};
args_add(&args, (args_desc_t){"filenames", .nargs = "+" });
args_add(&args, (args_desc_t){"--output", "-o" });
args_add(&args, (args_desc_t){"--force", "-f", .store_value = "1", .default_value = "0"});
args_add(&args, (args_desc_t){"--recursive", "-r", .store_value = "1", .default_value = "0"});
args_add(&args, (args_desc_t){"--help", "-h", .store_value = "1", .default_value = "0"});
args_parse(&args, argc, argv);
const char **filenames = args_get(&args, "filenames");
const char **output = args_get(&args, "--output");
int force = atoi(args_get(&args, "-f")[0]);
int recursive = atoi(args_get(&args, "--recursive")[0]);
int help = atoi(args_get(&args, "--help")[0]);
if (help) {
args_print_help(&args);
return;
}
// ..
for (int i = 0; filenames[i]; i += 1) {
// ..
}
}
```
Execute like this:
> app.exe file1 file2 file3 --force --output /folder/.
Help menu:
```
usage: ./a.out [options] filenames ...
this program does very cool things on many files and outputs it somewhere
options:
filenames <arg> ... list of filenames to process
--output -o <arg> path to the folder where output will be gathered
--force -f force the processing
--recursive -r apply recursively to files in current directory
--help -h show this help window
it also shows cool epilogue text in it's help
```
## How To's
1. Single positional argument
> program.exe argument
Specifying `.nargs = "<digit>"` enforces a strict number
of arguments, so supplying less or more will result in
an error.
```c
args_t args = {0};
args_add(&args, (args_desc_t){"argument", .nargs = "1"});
args_parse(&args, argc, argv);
const char *argument = args_get(&args, "argument")[0];
```
2. One or more positional arguments
> program.exe a b c
You need to set `.nargs = "+"` this will make it so that one
or more arguments are expected. Alternatively `.nargs = "*"`,
which makes it so that having even one argumenti is optional.
```c
args_t args = {0};
args_add(&args, (args_desc_t){"arguments", .nargs = "+"});
args_parse(&args, argc, argv);
const char **arguments = args_get(&args, "arguments");
```
3. Help menu, help flag
> program.exe --help
```
args_t args = {0};
args_add(&args, (args_desc_t){"--help", "-h", .store_value = "1", .default_value = "0"});
args_parse(&args, argc, argv);
int help = atoi(args_get(&args, "help")[0]);
if (help) {
args_print_help(&args);
exit(0);
}
```
4. Typechecked optional list of integers
> program.exe --list 1 2 3
> program.exe
```c
args_t args = {0};
args_add(&args, (args_desc_t){"--list", "-l", .type = ARGS_TYPE_INT, .nargs = "*"});
args_parse(&args, argc, argv);
const char **list = args_get(&args, "--list");
```
5. Custom logging
You can override the ARGS_PRINTF macro, at compile time
the library will use that. By default it uses fprintf(stderr)
```c
#define ARGS_PRINTF(...) fprintf(stdout, __VA_ARGS__)
#define ARGS_IMPLEMENTATION
#include "args.h"
```
6. User defined memory allocator
```c
#define ARGS_MALLOC(x) my_malloc(x)
#define ARGS_FREE(x) my_free(x)
#define ARGS_IMPLEMENTATION
#include "args.h"
```
7. C++
The library works in C++ but leverages special C syntax for aggregate types,
in later C++ the syntax might be simiar so no issues there but for older
versions you will either need to write some small wrapper functions or just
bare with initializing structures inline. For example:
```c
void args_add_bool(args_t *args, const char *name, const char *alt_name, const char *help) {
args_desc_t desc = {name, alt_name};
desc.help = help;
desc.store_value = "1";
desc.default_value = "0";
args_add(args, desc);
}
void args_add_nargs(args_t *args, const char *name, const char *alt_name, const char *nargs, const char *help) {
args_desc_t desc = {name, alt_name};
desc.nargs = nargs;
desc.help = help;
args_add(args, desc);
}
int main(int argc, char **argv) {
args_t args = {
.program = argv[0],
.description = "this program does very cool things on many files and outputs it somewhere",
.epilogue = "it also shows cool epilogue text in it's help",
};
args_add_nargs(&args, "filenames", NULL, "+", "list of filenames to process");
args_add_nargs(&args, "--output", "-o", "?", "path to the folder where output will be gathered");
args_add_bool(&args, "--force", "-f", "force the processing");
args_add_bool(&args, "--recursive", "-r", "apply recursively to files in current directory");
args_add_bool(&args, "--help", "-h", "show this help window");
args_parse(&args, argc, argv);
}
```
## License
Library is in public domain. See end of code file for license.