Takes up to 1024 command line arguments and parses them.
The names functions in this file are generated by macro CCLAP_PREFIXED(.),
which is defined by default to add the prefix cclap_. It is however, possible
to include the cclap.h file multiple times, redefining CCLAP_PREFIXED to a
different prefix each time.
It returns a value of type ARGS_T *, which is defined to be
CCLAP_PREFIXED(args_t) *.
The PARSE and ARGS_T macros are undefined within this file; in order to use
this function, you must use the expanded value, as governed by CCLAP_PREFIXED
The pointer returned by PARSE should be passed to ARGS_DESTROY
(CCLAP_PREFIXED(args_destroy)), which will deallocate all memory allocated by
it. All pointers are heap allocated using malloc. Ownership of the pointers
can be transferred out of the ARGS_T struct without double free by setting
the field in the struct to NULL.
The fields of ARGS_T are
char *process_name: the first argument in argvchar **extras: all unprocessed arguments and all arguments following--. This array isNULL-terminated.size_t num_extras: the number of unprocessed arguments- additional fields governed by the value of the CCLAP_ARGS macro when this header file is included (further information below).
When this file is included, the CCLAP_ARGS macro should consist of 0 or more whitespace separated invocations to the following macros:
POSITIONAL(type, name):typeis a non-bool supported type, name is a valid C identifier which is used to access the positional argument in theARGS_Tstruct.NAMED(type, name, short):typeis a supported type, name is a valid C identifier as above; in this case, name is also used to specify the option in the arguments, as--name. If this option is not a flag, the following argument is used as the value.shortmust be a character literal and specifies the short form of the name, accessed by-shortNAMED_LONG(type, name): as above, but defining an argument without a short nameNAMED_SHORT(type, name, short): as above, but defining an argument without a long name. The name field is still used to access the field in the struct, however.
The currently supported type values are:
char *: the option is a string and should be taken as-is.long *: this specifies that the option should be parsed as a number. If an argument is expected to be a number but is not,PARSEreturns NULL. Because every argument is optional, this is a pointer.bool: this specifies that the option is a flag. This type cannot be used for positional arguments and does not take the next argument. The corresponding field in the struct is set to true if the flag appears in the arguments and false otherwise.
All values are optional, meaning that it is the caller's job to verify that
non-bool fields of the ARGS_T struct as non-NULL before using them.
PARSE will return NULL in one of three cases:
- an argument (positional or named) which expected a number got an argument that could not fully be parsed as a number
- a non-bool named option was present without a corresponding argument to read as its value.
- an internal allocation fails
Arg parsing proceeds as follows:
- The first argument is placed into the
process_namefield. - Named arguments are parsed next:
- if an argument consisting entirely of
--is encountered, all remaining remaining arguments are dumped into extras. - when a
--is encountered, this is parsed as a long name, which grabs the the next argument as its value if it is recognized and its not a flag. If it isn't recognized, it is skipped for now. - when a
-is encountered not followed by a second one, this is treated as a SEQUENCE of short names.- Recognized flags are set to
true - Recognized non-flag options take their values from the following
arguments, in order (so if
aandbare both longs then-ab 10 20assigns the value10toaand20tob) - Unrecognized characters are concatenated together and left to be parsed
together as a positional argument, or an extra argument, with a
-prefix. This means that ifais a known flag but no others are,-abcdsetsatotrueand leaves-bcdfor future consideration. It also means that-100is correctly left untouched as long as no digits are defined to be shortnames, so positional arguments can be negative numbers.
- Recognized flags are set to
- if an argument consisting entirely of
- Left over arguments from named parsing are read into positional arguments
in order. If there are fewer left over arguments than positionals,
the tail is left missing (
NULL). - Any arguments not parsed as named or positional are also placed into the
extrafield andnum_extrais set appropriately.extrais alsoNULL-terminated.
An example usage is:
#include <stdio.h>
#include <assert.h>
#define CCLAP_ARGS \
POSITIONAL(long *, test_num) \
NAMED(char *, test_str, 't', "Invokes baz.") \
NAMED(bool, test_flag_1, 'f', "Tells foo to be bar.") \
NAMED(bool, test_flag_2, 'g') \
NAMED(bool, help, 'h', "Prints this description.")
#include "cclap.h"
int main(int argc, char *argv[]) {
cclap_args_t *args = cclap_parse(argc, argv);
assert(args);
if (args->help) {
cclap_fprint_descriptions(stdout);
return 0;
}
if (args->test_num) {
printf("test_num = %ld\n", *args->test_num);
}
if (args->test_str) {
printf("--test_str (alias -t) = `%s`\n", args->test_str);
}
printf("--test_flag_1 (alias -f) = %d\n", args->test_flag_1);
printf("--test_flag_2 (alias -g) = %d\n", args->test_flag_2);
if (args->num_extra > 0) {
printf("extras (%lu):\n", args->num_extra);
for (size_t i = 0; i < args->num_extra; i++) {
printf(" %s\n", args->extra[i]);
}
}
}>>> ./example --help
Available options are:
[positional]: long
--test_str, -t: string | Invokes baz.
--test_flag_1, -f: flag | Tells foo to be bar.
--test_flag_2, -g: flag
--help, -h: flag | Prints this description.
>>> ./example --test_str hello
--test_str (alias -t) = `hello`
--test_flag_1 (alias -f) = 0
--test_flag_2 (alias -g) = 0
>>> ./example -tf hello -503
test_num = -503
--test_str (alias -t) = `hello`
--test_flag_1 (alias -f) = 1
--test_flag_2 (alias -g) = 0
>>> ./example -tfg hello
--test_str (alias -t) = `hello`
--test_flag_1 (alias -f) = 1
--test_flag_2 (alias -g) = 1
>>> ./example -- -h 100 --test_str hello
--test_flag_1 (alias -f) = 0
--test_flag_2 (alias -g) = 0
extras [4]:
-h
100
--test_str
hello