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)
:type
is a non-bool supported type, name is a valid C identifier which is used to access the positional argument in theARGS_T
struct.NAMED(type, name, short)
:type
is 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.short
must be a character literal and specifies the short form of the name, accessed by-short
NAMED_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,PARSE
returns 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_name
field. - 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
a
andb
are both longs then-ab 10 20
assigns the value10
toa
and20
tob
) - 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 ifa
is a known flag but no others are,-abcd
setsa
totrue
and leaves-bcd
for future consideration. It also means that-100
is 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
extra
field andnum_extra
is set appropriately.extra
is 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