first version
This commit is contained in:
24
LICENSE
Normal file
24
LICENSE
Normal file
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <https://unlicense.org/>
|
||||
207
README.md
Normal file
207
README.md
Normal file
@@ -0,0 +1,207 @@
|
||||
## 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.
|
||||
|
||||
581
args.h
Normal file
581
args.h
Normal file
@@ -0,0 +1,581 @@
|
||||
#ifndef ARGS_HEADER
|
||||
/*
|
||||
args.h - v1
|
||||
Krzosa Karol - 2025
|
||||
|
||||
## 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.
|
||||
|
||||
*/
|
||||
#define ARGS_HEADER
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
|
||||
typedef enum {
|
||||
ARGS_TYPE_STRING,
|
||||
ARGS_TYPE_INT,
|
||||
} args_type_t;
|
||||
|
||||
typedef struct args_desc_t args_desc_t;
|
||||
struct args_desc_t {
|
||||
const char *name; // for example: '--filename', skipping '-' sets the argument as positional
|
||||
const char *alt_name; // usually the short name: '-f', invalid for positional
|
||||
const char *default_value; // default value parameter will contain if no value was provided on cmd
|
||||
const char *store_value; // turns processing into special flag syntax, no argument following flag is expected
|
||||
// ? - 1 or 0
|
||||
// * - 0 or more
|
||||
// + - 1 or more
|
||||
// 1 - exactly one
|
||||
// 2 - exactly two
|
||||
// 3 - ...
|
||||
const char *nargs; // how many arguments this command uses
|
||||
const char *help; // the text describing what the command does in the help string
|
||||
args_type_t type; // you can use this to add some additional checking for arguments
|
||||
|
||||
// internally used data, user shouldn't modify directly
|
||||
int match_count;
|
||||
const char **values;
|
||||
int nargs_parsed;
|
||||
bool nargs_required;
|
||||
};
|
||||
|
||||
typedef struct args_t args_t;
|
||||
struct args_t {
|
||||
const char *program;
|
||||
const char *description;
|
||||
const char *epilogue;
|
||||
|
||||
// internall used data, user shouldn't modify directly
|
||||
args_desc_t *desc;
|
||||
int errors;
|
||||
};
|
||||
|
||||
int args_parse(args_t *args, int argc, char **argv);
|
||||
void args_add(args_t *args, args_desc_t desc);
|
||||
const char **args_get(args_t *args, const char *name);
|
||||
void args_print_help(args_t *args);
|
||||
void args_free(args_t *args);
|
||||
|
||||
#ifdef ARGS_IMPLEMENTATION
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef ARGS_MALLOC
|
||||
#define ARGS_MALLOC(s) malloc(s)
|
||||
#endif
|
||||
|
||||
#ifndef ARGS_FREE
|
||||
#define ARGS_FREE(p) free(p)
|
||||
#endif
|
||||
|
||||
#ifndef ARGS_PRINTF
|
||||
#define ARGS_PRINTF(...) fprintf(stderr, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define ARGS_REPORT(args, ...) (args->errors++, ARGS_PRINTF(__VA_ARGS__))
|
||||
|
||||
// excerpt from Sean Barett's stb_ds.h, simple dynamic array
|
||||
typedef struct {
|
||||
size_t length;
|
||||
size_t capacity;
|
||||
} args__array_header;
|
||||
|
||||
#define args__header(t) ((args__array_header *)(t) - 1)
|
||||
#define args__arrgrow(a, b, c) ((a) = args__arrgrowf_wrapper((a), sizeof *(a), (b), (c)))
|
||||
#define args__arrcap(a) ((a) ? args__header(a)->capacity : 0)
|
||||
#define args__arrmaybegrow(a, n) ((!(a) || args__header(a)->length + (n) > args__header(a)->capacity) ? (args__arrgrow(a, n, 0), 0) : 0)
|
||||
#define args_alen(a) ((a) ? (int)args__header(a)->length : 0)
|
||||
#define args_slen(x) (int)(sizeof((x)) / sizeof((x)[0]))
|
||||
#define args_aput(a, v) (args__arrmaybegrow(a, 1), (a)[args__header(a)->length++] = (v))
|
||||
|
||||
static void *args__arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap) {
|
||||
args__array_header temp = {0}; // force debugging
|
||||
size_t min_len = args_alen(a) + addlen;
|
||||
(void)sizeof(temp);
|
||||
|
||||
// compute the minimum capacity needed
|
||||
if (min_len > min_cap)
|
||||
min_cap = min_len;
|
||||
|
||||
if (min_cap <= args__arrcap(a))
|
||||
return a;
|
||||
|
||||
// increase needed capacity to guarantee O(1) amortized
|
||||
if (min_cap < 2 * args__arrcap(a)) {
|
||||
min_cap = 2 * args__arrcap(a);
|
||||
} else if (min_cap < 4) {
|
||||
min_cap = 4;
|
||||
}
|
||||
|
||||
void *b = ARGS_MALLOC(elemsize * min_cap + sizeof(args__array_header));
|
||||
b = (char *)b + sizeof(args__array_header);
|
||||
if (a) {
|
||||
memcpy(b, a, args_alen(a) * elemsize);
|
||||
args__header(b)->length = args_alen(a);
|
||||
ARGS_FREE(args__header(a));
|
||||
} else {
|
||||
args__header(b)->length = 0;
|
||||
}
|
||||
args__header(b)->capacity = min_cap;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
template<class T> static T * args__arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) {
|
||||
return (T*)args__arrgrowf((void *)a, elemsize, addlen, min_cap);
|
||||
}
|
||||
#else
|
||||
#define args__arrgrowf_wrapper args__arrgrowf
|
||||
#endif
|
||||
|
||||
static bool args_streq(const char *a, const char *b) {
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
|
||||
static args_desc_t *args_get_desc(args_desc_t *desc, const char *name) {
|
||||
for (args_desc_t *it = desc; it < desc + args_alen(desc); it += 1) {
|
||||
if (args_streq(it->name, name) || (it->alt_name &&args_streq(it->alt_name, name))) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static args_desc_t *args_get_positional(args_desc_t *desc) {
|
||||
for (args_desc_t *it = desc; it < desc + args_alen(desc); it += 1) {
|
||||
if (it->name[0] != '-') return it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char **args_get(args_t *args, const char *name) {
|
||||
args_desc_t *i = args_get_desc(args->desc, name);
|
||||
if (i) return i->values;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void args_add(args_t *args, args_desc_t desc) {
|
||||
args_aput(args->desc, desc);
|
||||
}
|
||||
|
||||
#define ARGS__MAX(x,y) ((x)>(y)?(x):(y))
|
||||
void args_print_help(args_t *args) {
|
||||
ARGS_PRINTF("usage: %s [options]", args->program);
|
||||
args_desc_t *a = args_get_positional(args->desc);
|
||||
if (a) {
|
||||
ARGS_PRINTF(" %s", a->name);
|
||||
}
|
||||
ARGS_PRINTF(" ... \n");
|
||||
if (args->description) {
|
||||
ARGS_PRINTF("\n%s\n", args->description);
|
||||
}
|
||||
|
||||
ARGS_PRINTF("\noptions:\n");
|
||||
int name_len = 0;
|
||||
int alt_name_len = 0;
|
||||
for (args_desc_t *it = args->desc; it < args->desc + args_alen(args->desc); it += 1) {
|
||||
name_len = ARGS__MAX(strlen(it->name), name_len);
|
||||
if (it->alt_name) {
|
||||
alt_name_len = ARGS__MAX(strlen(it->alt_name), alt_name_len);
|
||||
}
|
||||
}
|
||||
for (args_desc_t *it = args->desc; it < args->desc + args_alen(args->desc); it += 1) {
|
||||
ARGS_PRINTF(" %-*s", name_len + 1, it->name);
|
||||
if (it->alt_name) {
|
||||
ARGS_PRINTF(" %-*s", alt_name_len + 1, it->alt_name);
|
||||
} else {
|
||||
ARGS_PRINTF(" %-*s", alt_name_len +1, " ");
|
||||
}
|
||||
|
||||
bool print_arg = false;
|
||||
bool print_tbc = false;
|
||||
if (args_streq(it->nargs, "+") || args_streq(it->nargs, "*") || it->nargs_parsed > 1) {
|
||||
print_arg = true;
|
||||
print_tbc = true;
|
||||
}
|
||||
if (args_streq(it->nargs, "?") || it->nargs_parsed == 1) {
|
||||
print_arg = true;
|
||||
}
|
||||
if (it->store_value) {
|
||||
print_arg = false;
|
||||
print_tbc = false;
|
||||
}
|
||||
if (print_arg) {
|
||||
ARGS_PRINTF(" <arg>");
|
||||
} else {
|
||||
ARGS_PRINTF(" ");
|
||||
}
|
||||
if (print_tbc) {
|
||||
ARGS_PRINTF(" ...");
|
||||
} else {
|
||||
ARGS_PRINTF(" ");
|
||||
}
|
||||
|
||||
if (it->help) {
|
||||
ARGS_PRINTF(" %s", it->help);
|
||||
}
|
||||
ARGS_PRINTF("\n");
|
||||
}
|
||||
|
||||
if (args->epilogue) {
|
||||
ARGS_PRINTF("\n%s\n", args->epilogue);
|
||||
}
|
||||
}
|
||||
|
||||
void args_free(args_t *args) {
|
||||
for (int i = 0; i < args_alen(args->desc); i += 1) {
|
||||
ARGS_FREE(args__header(args->desc[i].values));
|
||||
}
|
||||
ARGS_FREE(args__header(args->desc));
|
||||
}
|
||||
|
||||
int args_parse(args_t *args, int argc, char **argv) {
|
||||
int pos_count = 0;
|
||||
for (args_desc_t *it = args->desc; it < args->desc + args_alen(args->desc); it += 1) {
|
||||
if (it->alt_name && it->alt_name[0] != '-') {
|
||||
ARGS_REPORT(args, "invalid alt_name for flag: '%s', always should begin with '-'\n", it->alt_name);
|
||||
}
|
||||
if (it->nargs == NULL) {
|
||||
it->nargs = "?";
|
||||
} else {
|
||||
bool special = args_streq(it->nargs, "*") || args_streq(it->nargs, "+") || args_streq(it->nargs, "?");
|
||||
if (!special) {
|
||||
bool digit = true;
|
||||
for (int i = 0; it->nargs[i]; i += 1) {
|
||||
if (!isdigit(it->nargs[i])) {
|
||||
digit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!digit) {
|
||||
ARGS_REPORT(args, "invalid nargs: '%s', expected digit or '*', '+', '?'\n", it->nargs);
|
||||
} else {
|
||||
it->nargs_parsed = atoi(it->nargs);
|
||||
it->nargs_required = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (it->name[0] != '-') {
|
||||
pos_count += 1;
|
||||
if (pos_count > 1) {
|
||||
ARGS_REPORT(args, "more then one positional argument: '%s'\n", it->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args->errors) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc;) {
|
||||
const char *curr = argv[i++];
|
||||
if (curr[0] != '-') {
|
||||
args_desc_t *it = args_get_positional(args->desc);
|
||||
if (!it) {
|
||||
ARGS_REPORT(args, "unrecognized argument: '%s'\n", curr);
|
||||
continue;
|
||||
}
|
||||
|
||||
it->match_count += 1;
|
||||
args_aput(it->values, curr);
|
||||
continue;
|
||||
}
|
||||
|
||||
args_desc_t *it = args_get_desc(args->desc, curr);
|
||||
if (it == NULL) {
|
||||
ARGS_REPORT(args, "unrecognized argument: '%s'\n", curr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it->store_value) {
|
||||
args_aput(it->values, it->store_value);
|
||||
it->match_count += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
int end = i;
|
||||
if (it->nargs_required) end += it->nargs_parsed;
|
||||
if (args_streq(it->nargs, "?")) end += 1;
|
||||
if (args_streq(it->nargs, "+")) end += argc;
|
||||
if (args_streq(it->nargs, "*")) end += argc;
|
||||
for (;i < end && i < argc;) {
|
||||
if (argv[i] && argv[i][0] == '-') {
|
||||
break;
|
||||
}
|
||||
count += 1;
|
||||
it->match_count += 1;
|
||||
args_aput(it->values, argv[i]);
|
||||
i += 1;
|
||||
}
|
||||
if (count == 0) {
|
||||
ARGS_REPORT(args, "expected a value to follow after argument: '%s'\n", curr);
|
||||
}
|
||||
}
|
||||
|
||||
for (args_desc_t *it = args->desc; it < args->desc + args_alen(args->desc); it += 1) {
|
||||
if (args_streq(it->nargs, "?") && it->match_count > 1) {
|
||||
ARGS_REPORT(args, "expected argument: '%s', to appear 1 or 0 times instead of %d times\n", it->name, it->match_count);
|
||||
}
|
||||
if (args_streq(it->nargs, "+") && it->match_count == 0) {
|
||||
ARGS_REPORT(args, "required argument: '%s'\n", it->name);
|
||||
}
|
||||
if (it->nargs_required && it->match_count != it->nargs_parsed) {
|
||||
ARGS_REPORT(args, "expected %d arguments: '%s', got %d arguments\n", it->nargs_parsed, it->name, it->match_count);
|
||||
}
|
||||
if (it->default_value && it->match_count == 0) {
|
||||
args_aput(it->values, it->default_value);
|
||||
}
|
||||
if (it->type == ARGS_TYPE_INT) {
|
||||
for (int i = 0; i < args_alen(it->values); i += 1) {
|
||||
const char *val = it->values[i];
|
||||
for (int idx = 0; val[idx]; idx += 1) {
|
||||
char c = val[idx];
|
||||
if (!isdigit(c)) {
|
||||
ARGS_REPORT(args, "failed to interpret: '%s' as integer, non integer value: '%s'\n", it->name, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
args_aput(it->values, NULL);
|
||||
}
|
||||
end:;
|
||||
return args->errors;
|
||||
}
|
||||
#endif // ARGS_IMPLEMENTATION
|
||||
#endif // ARGS_HEADER
|
||||
/*
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <https://unlicense.org/>
|
||||
*/
|
||||
Reference in New Issue
Block a user