146#define ARGUMENT(name) \
148 _ARGS_CONSTRUCTOR(_arg_register_##name) \
150 _args_register(ARG(name)); \
154#define ARGUMENT(name) \
156 _ARGS_CONSTRUCTOR(_arg_register_##name) \
158 _args_register(ARG(name)); \
222#define ARG_PARSER(name, strto, ARG_BASE, strto_t, dest_t, CAST, cond, err) \
223 static struct arg_callback parse_##name(const char *str, void *dest) \
227 strto_t val = strto(str, &end ARG_BASE); \
228 if (end == str || *end != '\0' || errno == ERANGE || (cond)) \
229 return ARG_INVALID((err) && *(err) ? (err) : ARG_ERR); \
230 *(dest_t *)dest = CAST val; \
231 return ARG_VALID(); \
241#define ARG_BASE(N) , N
248#define ARG_ERR "Invalid value"
259#define ARG_PARSE_L(name, base, dest_t, CAST, cond, err) \
260 ARG_PARSER(name, strtol, ARG_BASE(base), long, dest_t, CAST, cond, err)
266#define ARG_PARSE_LL(name, base, dest_t, CAST, cond, err) \
267 ARG_PARSER(name, strtoll, ARG_BASE(base), long long, dest_t, CAST, \
274#define ARG_PARSE_UL(name, base, dest_t, CAST, cond, err) \
275 ARG_PARSER(name, strtoul, ARG_BASE(base), unsigned long, dest_t, CAST, \
282#define ARG_PARSE_ULL(name, base, dest_t, CAST, cond, err) \
283 ARG_PARSER(name, strtoull, ARG_BASE(base), unsigned long long, dest_t, \
290#define ARG_PARSE_F(name, dest_t, CAST, cond, err) \
291 ARG_PARSER(name, strtof, , float, dest_t, CAST, cond, err)
297#define ARG_PARSE_D(name, dest_t, CAST, cond, err) \
298 ARG_PARSER(name, strtod, , double, dest_t, CAST, cond, err)
327#define ARG_INVALID(msg) ((struct arg_callback){ .error = msg })
333#define ARG_VALID() ((struct arg_callback){ .error = NULL })
346#ifdef ARGS_EXTERN_ARGR
389 const char *req,
const char *opt);
491#define ARG(name) &_arg_##name
509#define ARG_DECLARE(name) struct argument _arg_##name
533#define ARG_EXTERN(name) extern struct argument _arg_##name
559#define ARG_DEPENDS(relation_phase, ...) \
560 ._.deps_phase = relation_phase, \
561 ._.deps = (struct argument *[]){ __VA_ARGS__, NULL }, \
562 ._.deps_n = sizeof((struct argument *[]){ __VA_ARGS__ }) / \
563 sizeof(struct argument *)
585#define ARG_CONFLICTS(relation_phase, ...) \
586 ._.cons_phase = relation_phase, \
587 ._.cons = (struct argument *[]){ __VA_ARGS__, NULL }, \
588 ._.cons_n = sizeof((struct argument *[]){ __VA_ARGS__ }) / \
589 sizeof(struct argument *)
612#define ARG_SUBSETS(...) \
613 ._.subs = (struct argument *[]){ __VA_ARGS__, NULL }, \
614 ._.subs_n = sizeof((struct argument *[]){ __VA_ARGS__ }) / \
615 sizeof(struct argument *)
624#define ARG_SUBSTRINGS(...) \
625 ._.subs_strs = ((const char *[]){ __VA_ARGS__, NULL })
634#define ARG_SUBPASS ((const char *)-1)
663#define ARG_ORDER_FIRST ((struct argument *)-1)
683#define ARG_ORDER_AFTER(arg) (arg)
698struct _args_internal {
708 const char **subs_strs;
939 struct _args_internal _;
951void _args_register(
struct argument *);
954#define _ARGS_CONSTRUCTOR(f) \
955 static void f(void); \
962 static f##_t_ f##_; \
964#elif defined(_MSC_VER) && !defined(__clang__)
965#pragma section(".CRT$XCU", read)
966#define _ARGS_CONSTRUCTOR2_(f, p) \
967 static void f(void); \
968 __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
969 __pragma(comment(linker, "/include:" p #f "_")) static void f(void)
971#define _ARGS_CONSTRUCTOR(f) _ARGS_CONSTRUCTOR2_(f, "")
973#define _ARGS_CONSTRUCTOR(f) _ARGS_CONSTRUCTOR2_(f, "_")
981#define _ARGS_CONSTRUCTOR(f) \
982 static void f(void) __attribute__((constructor)); \
989#if defined(ARGS_IMPLEMENTATION) && !defined(_ARGS_IMPLEMENTED)
990#define _ARGS_IMPLEMENTED
1002#ifndef ARGS_STR_PREPAD
1007#define ARGS_STR_PREPAD (2)
1008#elif ARGS_STR_PREPAD < 0
1009#error ARGS_STR_PREPAD cannot be negative
1012#ifndef ARGS_PARAM_OFFSET
1017#define ARGS_PARAM_OFFSET (1)
1018#elif ARGS_PARAM_OFFSET < 1
1019#error ARGS_PARAM_OFFSET must be at least 1 for proper formatting
1022#ifndef ARGS_HELP_OFFSET
1027#define ARGS_HELP_OFFSET (4)
1028#elif ARGS_HELP_OFFSET < 1
1029#error ARGS_HELP_OFFSET must be at least 1 for proper formatting
1039#define ARGS_PRINT_H 0
1042#error "Include print.h before including args.h"
1045#define ARGS_PRINT_H 0
1057#define args_po(...) printf(__VA_ARGS__)
1073#define args_pe(...) fprintf(stderr, __VA_ARGS__)
1087#define args_pd(...) fprintf(stderr, __VA_ARGS__)
1097#define args_pi(arg) perr("Internal error for %s\n", arg_str(arg))
1103#define args_pi(arg) args_pe("Internal error for %s\n", arg_str(arg))
1109#define args_abort pabort
1115#define args_abort() abort()
1119#ifndef ARGS_IMPLICIT_SETS
1124#define ARGS_IMPLICIT_SETS (64)
1125#elif ARGS_IMPLICIT_SETS < 1
1126#error ARGS_IMPLICIT_SETS must be at least 1 for defined behavior
1147#define for_each_arg(a, list) \
1148 for (struct argument *a = (list); a; a = (a)->_.next_##list)
1150#define for_each_rel(a, rel, var) \
1151 for (size_t var##i = 0; var##i < (a)->_.rel##_n; var##i++) \
1152 for (struct argument *var = (a)->_.rel[var##i]; var; var = NULL)
1154static size_t args_num;
1155static size_t longest;
1157#ifndef ARG_STR_MAX_CALLS
1158#define ARG_STR_MAX_CALLS (2)
1161#ifndef ARG_STR_BUF_SIZE
1162#define ARG_STR_BUF_SIZE (BUFSIZ)
1165static const char *arg_str(
const struct argument *a)
1168 return "<null-arg>";
1170 static char buf[ARG_STR_MAX_CALLS][ARG_STR_BUF_SIZE];
1171 static size_t i = 0;
1174 snprintf(buf[i],
sizeof(buf[i]),
"-%c, --%s", a->
opt, a->
lopt);
1176 snprintf(buf[i],
sizeof(buf[i]),
"-%c ", a->
opt);
1178 snprintf(buf[i],
sizeof(buf[i]),
" --%s", a->
lopt);
1180 return "<invalid-arg>";
1186static void arg_set_new(
struct argument *a)
1189 static size_t sets_n = 0;
1192 args_pd(
"ARGS_IMPLICIT_SETS exceeded, try increasing it\n");
1197 a->
set = &sets[sets_n++];
1200void _args_register(
struct argument *a)
1203 args_pd(
"Cannot register %s\n", arg_str(a));
1209 args_pd(
"%s must have an option\n", arg_str(a));
1215 args_pd(
"%s has internals pre-set\n", arg_str(a));
1221 args_pd(
"%s requires parameter but has no .param\n",
1228 args_pd(
"%s has .param but has no .parse_callback\n",
1235 args_pd(
"%s has .validate_phase but has no .validate_callback\n",
1242 args_pd(
"%s has .action_phase but has no .action_callback\n",
1250 args_pd(
"%s has no dependencies, conflicts, or validator\n",
1257 bool needs_set =
false;
1265 if (a->_.deps || a->_.cons || a->_.subs)
1276 if (a->_.deps_n > 0) {
1277 args_pd(
"%s has deps_n=%zu but deps=NULL\n", arg_str(a),
1279 args_pd(
"Add dependencies using ARG_DEPENDS()\n");
1285 args_pd(
"%s has relation phase but no dependencies\n",
1294 while (a->_.deps[ndeps])
1297 if (ndeps != a->_.deps_n) {
1298 args_pd(
"%s deps_n=%zu but actual is %zu\n", arg_str(a),
1299 a->_.deps_n, ndeps);
1300 args_pd(
"Add dependencies using ARG_DEPENDS()\n");
1305 for_each_rel(a, deps, dep) {
1307 args_pd(
"%s NULL deps[%zu]\n", arg_str(a), depi);
1313 args_pd(
"%s depends on itself\n", arg_str(a));
1324 if (a->_.cons_n > 0) {
1325 args_pd(
"%s cons_n=%zu but cons=NULL\n", arg_str(a),
1327 args_pd(
"Add conflicts using ARG_CONFLICTS()\n");
1333 args_pd(
"%s has relation phase but no conflicts\n",
1342 while (a->_.cons[ncons])
1345 if (ncons != a->_.cons_n) {
1346 args_pd(
"%s cons_n=%zu but actual is %zu\n", arg_str(a),
1347 a->_.cons_n, ncons);
1348 args_pd(
"Add conflicts using ARG_CONFLICTS()\n");
1353 for_each_rel(a, cons, con) {
1355 args_pd(
"%s NULL cons[%zu]\n", arg_str(a), coni);
1361 args_pd(
"%s conflicts itself\n", arg_str(a));
1369 for_each_rel(a, deps, dep) {
1373 args_pd(
"%s both depends and conflicts %s\n",
1374 arg_str(a), arg_str(con));
1382 if (a->_.subs_n > 0) {
1383 args_pd(
"%s subs_n=%zu but subs=NULL\n", arg_str(a),
1385 args_pd(
"Specify subsets using ARG_SUBSETS()\n");
1390 if (a->_.subs_strs) {
1391 args_pd(
"%s has subs_strs but no subsets\n",
1400 while (a->_.subs[nsubs])
1403 if (nsubs != a->_.subs_n) {
1404 args_pd(
"%s subs_n=%zu but actual is %zu\n", arg_str(a),
1405 a->_.subs_n, nsubs);
1406 args_pd(
"Specify subset args using ARG_SUBSETS()\n");
1411 if (a->_.subs_strs) {
1413 while (a->_.subs_strs[nsstrs])
1416 if (nsstrs != a->_.subs_n) {
1417 args_pd(
"%s subs_n=%zu but subs_strs has %zu entries\n",
1418 arg_str(a), a->_.subs_n, nsstrs);
1419 args_pd(
"Both lists must be the same size\n");
1425 for_each_rel(a, subs, sub) {
1427 args_pd(
"%s subsets itself\n", arg_str(a));
1437 (!a->_.subs_strs || a->_.subs_strs[subi] ==
ARG_SUBPASS)) {
1438 args_pd(
"%s requires param but superset %s might not and has no custom string\n",
1439 arg_str(sub), arg_str(a));
1447 for_each_rel(a, cons, con) {
1449 args_pd(
"%s both supersets and conflicts %s\n",
1450 arg_str(a), arg_str(sub));
1456 for_each_rel(sub, deps, dep) {
1458 args_pd(
"%s supersets %s but also depends on it\n",
1459 arg_str(a), arg_str(sub));
1467 for_each_arg(c, args) {
1468 if ((a->
opt && c->opt && a->
opt == c->opt) ||
1469 (a->
lopt && c->lopt && strcmp(a->
lopt, c->lopt) == 0)) {
1470 args_pd(
"%s same opts as %s\n", arg_str(a), arg_str(c));
1476#define args_insert(list) \
1478 a->_.next_##list = list; \
1484 args_insert(validate);
1485 args_insert(action);
1494 if (check >= ARG_STR_BUF_SIZE) {
1495 args_pd(
"%s combined opt, lopt, help string too long: %zu chars\n",
1500 a->_.help_len = len;
1508static bool arg_process(
struct argument *a,
const char *str)
1511 args_pd(
"%s has internals pre-set\n", arg_str(a));
1512 args_pd(
"Please register arguments using ARGUMENT()\n");
1518 args_pe(
"Argument %s specified multiple times\n", arg_str(a));
1531 for_each_rel(a, deps, dep) {
1533 args_pe(
"%s requires %s to be set first\n",
1534 arg_str(a), arg_str(dep));
1541 for_each_rel(a, cons, con) {
1543 args_pe(
"%s conflicts with %s\n", arg_str(a),
1553 for_each_rel(a, subs, sub) {
1557 const char *sub_str = str;
1558 if (a->_.subs_strs && a->_.subs_strs[subi] &&
1560 sub_str = a->_.subs_strs[subi];
1562 if (!arg_process(sub, sub_str))
1569static bool arg_option(
const char *token)
1571 if (!token || token[0] !=
'-' || token[1] ==
'\0')
1574 if (strcmp(token,
"--") == 0)
1577 if (token[1] ==
'-') {
1578 const char *name = token + 2;
1579 const char *eq = strchr(name,
'=');
1580 size_t name_len = eq ? (size_t)(eq - name) : strlen(name);
1581 for_each_arg(c, args) {
1582 if (c->lopt && strncmp(c->lopt, name, name_len) == 0 &&
1583 c->lopt[name_len] ==
'\0')
1590 for_each_arg(c, args) {
1591 if (c->opt && c->opt == token[1])
1598static bool arg_parse_lopt(
int *i)
1600 char *arg =
argr.v[*i];
1601 char *name = arg + 2;
1602 char *value = strchr(name,
'=');
1603 size_t name_len = value ? (size_t)(value - name) : strlen(name);
1608 for_each_arg(c, args) {
1609 if (c->lopt && strncmp(c->lopt, name, name_len) == 0 &&
1610 c->lopt[name_len] ==
'\0') {
1617 args_pe(
"Unknown: --%.*s\n", (
int)name_len, name);
1621 const char *str = NULL;
1625 }
else if (*i + 1 <
argr.c) {
1626 str =
argr.v[++(*i)];
1634 else if (*i + 1 <
argr.c && !arg_option(
argr.v[*i + 1]))
1635 str =
argr.v[++(*i)];
1638 args_pe(
"--%s does not take a parameter\n", a->
lopt);
1643 return arg_process(a, str);
1646static bool arg_parse_opt(
int *i)
1648 char *arg =
argr.v[*i];
1649 for (
size_t j = 1; arg[j]; j++) {
1653 for_each_arg(c, args) {
1654 if (c->opt ==
opt) {
1665 const char *str = NULL;
1670 }
else if (*i + 1 <
argr.c) {
1671 str =
argr.v[++(*i)];
1683 if (!arg_process(a, str))
1689#define args_vaorder(list) \
1691 for_each_arg(a, list) { \
1692 struct argument *order = a->list##_order; \
1693 if (order == NULL || order == (struct argument *)-1) \
1696 bool found = false; \
1697 for_each_arg(check, list) { \
1698 if (check == order) { \
1705 args_pd("%s has invalid argument in " #list \
1714#define args_reorder(list) \
1716 struct argument *ordered = NULL; \
1717 struct argument *unordered = list; \
1720 struct argument **pp = &unordered; \
1722 struct argument *a = *pp; \
1723 if (a->list##_order == (struct argument *)-1) { \
1724 *pp = a->_.next_##list; \
1725 a->_.next_##list = ordered; \
1728 pp = &(*pp)->_.next_##list; \
1732 bool changed = true; \
1733 while (unordered && changed) { \
1737 struct argument *a = *pp; \
1738 struct argument *ord = a->list##_order; \
1739 bool can_place = false; \
1740 struct argument **insert_pos = NULL; \
1742 if (ord == NULL) { \
1745 insert_pos = &ordered; \
1747 struct argument *cur = \
1749 while (cur->_.next_##list) \
1750 cur = cur->_.next_##list; \
1752 &cur->_.next_##list; \
1755 struct argument **pord = &ordered; \
1757 if (*pord == ord) { \
1764 pord = &(*pord)->_.next_##list; \
1768 if (can_place && insert_pos) { \
1769 *pp = a->_.next_##list; \
1770 a->_.next_##list = *insert_pos; \
1774 pp = &(*pp)->_.next_##list; \
1781 ordered = unordered; \
1783 struct argument *cur = ordered; \
1784 while (cur->_.next_##list) \
1785 cur = cur->_.next_##list; \
1786 cur->_.next_##list = unordered; \
1799 args_vaorder(validate);
1800 args_vaorder(action);
1804 args_reorder(validate);
1805 args_reorder(action);
1808 bool success =
true;
1810 for (
int i = 1; i <
argr.c; i++) {
1811 char *arg =
argr.v[i];
1813 if (strcmp(arg,
"--") == 0)
1822 if (arg[1] ==
'-') {
1823 if (!arg_parse_lopt(&i))
1826 if (!arg_parse_opt(&i))
1836 bool any_invalid =
false;
1838 for_each_arg(a, validate) {
1840 args_pd(
"%s has internals pre-set\n", arg_str(a));
1841 args_pd(
"Please register arguments using ARGUMENT()\n");
1847 bool any_conflict_set =
false;
1848 for_each_rel(a, cons, con) {
1850 any_conflict_set =
true;
1854 if (!any_conflict_set) {
1855 args_pe(
"Missing required argument: %s\n",
1862 bool should_check_deps =
false;
1863 switch (a->_.deps_phase) {
1867 should_check_deps =
true;
1870 should_check_deps = *a->
set;
1873 should_check_deps = !*a->
set;
1876 args_pd(
"Unknown dependency relation phase in %s\n",
1882 if (should_check_deps) {
1883 for_each_rel(a, deps, dep) {
1886 args_pe(
"%s requires %s to be set\n",
1887 arg_str(a), arg_str(dep));
1892 bool should_check_cons =
false;
1893 switch (a->_.cons_phase) {
1897 should_check_cons =
true;
1900 should_check_cons = *a->
set;
1903 should_check_cons = !*a->
set;
1906 args_pd(
"Unknown conflict relation phase in %s\n",
1912 if (should_check_cons) {
1913 for_each_rel(a, cons, con) {
1916 args_pe(
"%s conflicts with %s\n", arg_str(a),
1925 bool should_validate =
false;
1928 should_validate =
true;
1931 should_validate = *a->
set;
1934 should_validate = !*a->
set;
1937 args_pd(
"Unknown .validate_phase in %s\n", arg_str(a));
1942 if (should_validate) {
1952 return !any_invalid;
1957 for_each_arg(a, action) {
1961 bool should_act =
false;
1967 should_act = *a->
set;
1970 should_act = !*a->
set;
1973 args_pd(
"Unknown .action_phase in %s\n", arg_str(a));
1983static void arg_help_print(
const struct argument *a)
1995 size_t pad = off > a->_.help_len ? off - a->_.help_len : 1;
1998 const char *phelp = a->
help;
2000 const char *nl = strchr(phelp,
'\n');
2001 size_t line = nl ? (size_t)(nl - phelp) : strlen(phelp);
2004 args_po(
"%*s%.*s", (
int)pad,
"", (
int)line, phelp);
2007 args_po(
"\n%*s%.*s", (
int)off,
"", (
int)line, phelp);
2018void args_help_print(
const char *usage,
const char *bin,
const char *hint,
2019 const char *req,
const char *opt)
2021 args_po(
"%s%s%s", usage, bin, hint);
2024 for_each_arg(a, help) {
2035 for_each_arg(a, help) {
arg_callback_phase
Execution policy for argument::validate_phase and argument::action_phase.
Definition args.h:436
@ ARG_CALLBACK_IF_SET
Definition args.h:438
@ ARG_CALLBACK_ALWAYS
Definition args.h:437
@ ARG_CALLBACK_IF_UNSET
Definition args.h:439
arg_requirement
Requirement policy for argument::arg_req.
Definition args.h:170
bool args_validate(void)
Validates argument schema rules and user-provided combinations.
void args_actions(void)
Executes action callbacks for arguments matching action phase rules.
bool args_parse(int argc, char *argv[])
Parses command line arguments and runs parse-phase relations.
arg_parameter
Parameter requirement for argument::param_req.
Definition args.h:185
@ ARG_REQUIRED
Definition args.h:172
@ ARG_OPTIONAL
Definition args.h:171
@ ARG_SOMETIME
Definition args.h:174
@ ARG_HIDDEN
Definition args.h:173
@ ARG_PARAM_REQUIRED
Definition args.h:187
@ ARG_PARAM_OPTIONAL
Definition args.h:188
@ ARG_PARAM_NONE
Definition args.h:186
void args_help_print(const char *usage, const char *bin, const char *hint, const char *req, const char *opt)
Prints generated CLI help output.
#define args_pe(...)
Error print.
Definition args.h:1073
struct args_raw argr
Global storage of raw argc/argv values.
#define args_pd(...)
Developer-only debug print.
Definition args.h:1087
#define args_pi(arg)
Internal error print, user-facing dev print.
Definition args.h:1103
#define ARGS_STR_PREPAD
Pre-padding for help text.
Definition args.h:1007
#define ARGS_HELP_OFFSET
Offset from longest argument for help text.
Definition args.h:1027
#define args_abort()
Abort function.
Definition args.h:1115
#define ARGS_IMPLICIT_SETS
Maximum implicit allocations of argument::set booleans.
Definition args.h:1124
#define ARGS_PARAM_OFFSET
Offset between argument and parameter in help text.
Definition args.h:1017
#define args_po(...)
Normal print.
Definition args.h:1057
arg_relation_phase
Relation evaluation phase used by ARG_DEPENDS and ARG_CONFLICTS.
Definition args.h:643
#define ARG_SUBPASS
Special marker meaning "forward parent string to subset parser".
Definition args.h:634
@ ARG_RELATION_PARSE
Definition args.h:644
@ ARG_RELATION_VALIDATE_SET
Definition args.h:646
@ ARG_RELATION_VALIDATE_UNSET
Definition args.h:647
@ ARG_RELATION_VALIDATE_ALWAYS
Definition args.h:645
Result object returned by parser/validator callbacks.
Definition args.h:315
const char * error
Diagnostic user-facing message for failures, NULL on success.
Definition args.h:319
Copy of the raw process argument vector.
Definition args.h:341
int c
Definition args.h:342
char ** v
Definition args.h:343
Declarative schema object for one CLI argument.
Definition args.h:727
const char * help
Help description for this argument.
Definition args.h:915
struct argument * validate_order
Ordering for validation.
Definition args.h:887
char opt
Short option character (e.g. 'o' -> -o).
Definition args.h:931
const char * lopt
Long option name (e.g. "output" -> --output).
Definition args.h:926
struct arg_callback(* parse_callback)(const char *str, void *dest)
Function invoked during args_parse.
Definition args.h:798
enum arg_callback_phase action_phase
Controls when argument::action_callback runs.
Definition args.h:876
struct arg_callback(* validate_callback)(void)
Function invoked during args_validate.
Definition args.h:826
bool * set
Tracks whether the user supplied this argument.
Definition args.h:736
void * dest
Destination pointer passed as dest to parser callbacks.
Definition args.h:741
struct argument * action_order
Ordering for action execution.
Definition args.h:894
enum arg_callback_phase validate_phase
Controls when argument::validate_callback runs.
Definition args.h:870
void(* action_callback)(void)
Function invoked during args_actions.
Definition args.h:848
enum arg_requirement arg_req
Specifies whether this argument is required, optional, or hidden.
Definition args.h:858
struct argument * help_order
Ordering for help display.
Definition args.h:901
const char * param
Parameter name for help display (e.g., "N" for a number).
Definition args.h:921
enum arg_parameter param_req
Specifies whether this argument takes a parameter and if it's required.
Definition args.h:864