208 lines
5.5 KiB
Markdown
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.
|
|
|