Decentralized CLI argument framework.
This library lets each translation unit declare and register arguments near the code that owns them, instead of maintaining a single global argument table. Registration is constructor-driven and therefore complete before calling args_parse.
Correctness depends on constructor attribute support (GCC/Clang) or .CRT$XCU sections (MSVC) executing registration hooks before main.
#ifndef ARGS_H_
#define ARGS_H_
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#ifndef __cplusplus
#define ARGUMENT(name) \
ARG_DECLARE(name); \
_ARGS_CONSTRUCTOR(_arg_register_##name) \
{ \
_args_register(ARG(name)); \
} \
ARG_DECLARE(name)
#else
#define ARGUMENT(name) \
ARG_EXTERN(name); \
_ARGS_CONSTRUCTOR(_arg_register_##name) \
{ \
_args_register(ARG(name)); \
} \
ARG_DECLARE(name)
#endif
};
};
#define ARG_PARSER(name, strto, ARG_BASE, strto_t, dest_t, CAST, cond, err) \
static struct arg_callback parse_##name(const char *str, void *dest) \
{ \
errno = 0; \
char *end = NULL; \
strto_t val = strto(str, &end ARG_BASE); \
if (end == str || *end != '\0' || errno == ERANGE || (cond)) \
return ARG_INVALID((err) && *(err) ? (err) : ARG_ERR); \
*(dest_t *)dest = CAST val; \
return ARG_VALID(); \
}
#define ARG_BASE(N) , N
#ifndef ARG_ERR
#define ARG_ERR "Invalid value"
#endif
#define ARG_PARSE_L(name, base, dest_t, CAST, cond, err) \
ARG_PARSER(name, strtol, ARG_BASE(base), long, dest_t, CAST, cond, err)
#define ARG_PARSE_LL(name, base, dest_t, CAST, cond, err) \
ARG_PARSER(name, strtoll, ARG_BASE(base), long long, dest_t, CAST, \
cond, err)
#define ARG_PARSE_UL(name, base, dest_t, CAST, cond, err) \
ARG_PARSER(name, strtoul, ARG_BASE(base), unsigned long, dest_t, CAST, \
cond, err)
#define ARG_PARSE_ULL(name, base, dest_t, CAST, cond, err) \
ARG_PARSER(name, strtoull, ARG_BASE(base), unsigned long long, dest_t, \
CAST, cond, err)
#define ARG_PARSE_F(name, dest_t, CAST, cond, err) \
ARG_PARSER(name, strtof, , float, dest_t, CAST, cond, err)
#define ARG_PARSE_D(name, dest_t, CAST, cond, err) \
ARG_PARSER(name, strtod, , double, dest_t, CAST, cond, err)
};
#define ARG_INVALID(msg) ((struct arg_callback){ .error = msg })
#define ARG_VALID() ((struct arg_callback){ .error = NULL })
};
#ifdef ARGS_EXTERN_ARGR
#endif
const char *req, const char *opt);
};
#define ARG(name) &_arg_##name
#define ARG_DECLARE(name) struct argument _arg_##name
#define ARG_EXTERN(name) extern struct argument _arg_##name
#define ARG_DEPENDS(relation_phase, ...) \
._.deps_phase = relation_phase, \
._.deps = (struct argument *[]){ __VA_ARGS__, NULL }, \
._.deps_n = sizeof((struct argument *[]){ __VA_ARGS__ }) / \
sizeof(struct argument *)
#define ARG_CONFLICTS(relation_phase, ...) \
._.cons_phase = relation_phase, \
._.cons = (struct argument *[]){ __VA_ARGS__, NULL }, \
._.cons_n = sizeof((struct argument *[]){ __VA_ARGS__ }) / \
sizeof(struct argument *)
#define ARG_SUBSETS(...) \
._.subs = (struct argument *[]){ __VA_ARGS__, NULL }, \
._.subs_n = sizeof((struct argument *[]){ __VA_ARGS__ }) / \
sizeof(struct argument *)
#define ARG_SUBSTRINGS(...) \
._.subs_strs = ((const char *[]){ __VA_ARGS__, NULL })
#define ARG_SUBPASS ((const char *)-1)
};
#define ARG_ORDER_FIRST ((struct argument *)-1)
#define ARG_ORDER_AFTER(arg) (arg)
struct _args_internal {
struct argument *next_args;
struct argument *next_help;
struct argument *next_validate;
struct argument *next_action;
struct argument **deps;
size_t deps_n;
struct argument **cons;
size_t cons_n;
struct argument **subs;
const char **subs_strs;
size_t subs_n;
size_t help_len;
bool valid;
};
struct _args_internal _;
};
#ifdef __cplusplus
#define _ARGS_CONSTRUCTOR(f) \
static void f(void); \
struct f##_t_ { \
f##_t_(void) \
{ \
f(); \
} \
}; \
static f##_t_ f##_; \
static void f(void)
#elif defined(_MSC_VER) && !defined(__clang__)
#pragma section(".CRT$XCU", read)
#define _ARGS_CONSTRUCTOR2_(f, p) \
static void f(void); \
__declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
__pragma(comment(linker, "/include:" p #f "_")) static void f(void)
#ifdef _WIN64
#define _ARGS_CONSTRUCTOR(f) _ARGS_CONSTRUCTOR2_(f, "")
#else
#define _ARGS_CONSTRUCTOR(f) _ARGS_CONSTRUCTOR2_(f, "_")
#endif
#else
#define _ARGS_CONSTRUCTOR(f) \
static void f(void) __attribute__((constructor)); \
static void f(void)
#endif
#endif
#if defined(ARGS_IMPLEMENTATION) && !defined(_ARGS_IMPLEMENTED)
#define _ARGS_IMPLEMENTED
#ifndef ARGS_STR_PREPAD
#define ARGS_STR_PREPAD (2)
#elif ARGS_STR_PREPAD < 0
#error ARGS_STR_PREPAD cannot be negative
#endif
#ifndef ARGS_PARAM_OFFSET
#define ARGS_PARAM_OFFSET (1)
#elif ARGS_PARAM_OFFSET < 1
#error ARGS_PARAM_OFFSET must be at least 1 for proper formatting
#endif
#ifndef ARGS_HELP_OFFSET
#define ARGS_HELP_OFFSET (4)
#elif ARGS_HELP_OFFSET < 1
#error ARGS_HELP_OFFSET must be at least 1 for proper formatting
#endif
#ifndef ARGS_PRINT_H
#define ARGS_PRINT_H 0
#else
#ifndef PRINT_H_
#error "Include print.h before including args.h"
#endif
#undef ARGS_PRINT_H
#define ARGS_PRINT_H 0
#endif
#ifndef args_po
#define args_po(...) printf(__VA_ARGS__)
#endif
#ifndef args_pe
#if ARGS_PRINT_H
#define args_pe perr
#else
#define args_pe(...) fprintf(stderr, __VA_ARGS__)
#endif
#endif
#ifndef NDEBUG
#ifndef args_pd
#if ARGS_PRINT_H
#define args_pd pdev
#else
#define args_pd(...) fprintf(stderr, __VA_ARGS__)
#endif
#endif
#else
#undef args_pd
#define args_pd(...)
#endif
#ifndef args_pi
#if ARGS_PRINT_H
#define args_pi(arg) perr("Internal error for %s\n", arg_str(arg))
#else
#define args_pi(arg) args_pe("Internal error for %s\n", arg_str(arg))
#endif
#endif
#ifndef args_abort
#if ARGS_PRINT_H
#define args_abort pabort
#else
#define args_abort() abort()
#endif
#endif
#ifndef ARGS_IMPLICIT_SETS
#define ARGS_IMPLICIT_SETS (64)
#elif ARGS_IMPLICIT_SETS < 1
#error ARGS_IMPLICIT_SETS must be at least 1 for defined behavior
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define for_each_arg(a, list) \
for (struct argument *a = (list); a; a = (a)->_.next_##list)
#define for_each_rel(a, rel, var) \
for (size_t var##i = 0; var##i < (a)->_.rel##_n; var##i++) \
for (struct argument *var = (a)->_.rel[var##i]; var; var = NULL)
static size_t args_num;
static size_t longest;
#ifndef ARG_STR_MAX_CALLS
#define ARG_STR_MAX_CALLS (2)
#endif
#ifndef ARG_STR_BUF_SIZE
#define ARG_STR_BUF_SIZE (BUFSIZ)
#endif
static const char *arg_str(
const struct argument *a)
{
if (!a)
return "<null-arg>";
static char buf[ARG_STR_MAX_CALLS][ARG_STR_BUF_SIZE];
static size_t i = 0;
snprintf(buf[i],
sizeof(buf[i]),
"-%c, --%s", a->
opt, a->
lopt);
snprintf(buf[i],
sizeof(buf[i]),
"-%c ", a->
opt);
snprintf(buf[i],
sizeof(buf[i]),
" --%s", a->
lopt);
else
return "<invalid-arg>";
i = 1 - i;
return buf[1 - i];
}
static void arg_set_new(
struct argument *a)
{
static size_t sets_n = 0;
args_pd(
"ARGS_IMPLICIT_SETS exceeded, try increasing it\n");
}
a->
set = &sets[sets_n++];
}
{
if (!a) {
args_pd(
"Cannot register %s\n", arg_str(a));
}
args_pd(
"%s must have an option\n", arg_str(a));
}
if (a->_.valid) {
args_pd(
"%s has internals pre-set\n", arg_str(a));
}
args_pd(
"%s requires parameter but has no .param\n",
arg_str(a));
}
args_pd(
"%s has .param but has no .parse_callback\n",
arg_str(a));
}
args_pd(
"%s has .validate_phase but has no .validate_callback\n",
arg_str(a));
}
args_pd(
"%s has .action_phase but has no .action_callback\n",
arg_str(a));
}
args_pd(
"%s has no dependencies, conflicts, or validator\n",
arg_str(a));
}
bool needs_set = false;
needs_set = true;
needs_set = true;
needs_set = true;
if (a->_.deps || a->_.cons || a->_.subs)
needs_set = true;
if (needs_set)
arg_set_new(a);
}
size_t ndeps = 0;
size_t ncons = 0;
size_t nsubs = 0;
if (!a->_.deps) {
if (a->_.deps_n > 0) {
args_pd(
"%s has deps_n=%zu but deps=NULL\n", arg_str(a),
a->_.deps_n);
args_pd(
"Add dependencies using ARG_DEPENDS()\n");
}
args_pd(
"%s has relation phase but no dependencies\n",
arg_str(a));
}
goto arg_no_deps;
}
while (a->_.deps[ndeps])
ndeps++;
if (ndeps != a->_.deps_n) {
args_pd(
"%s deps_n=%zu but actual is %zu\n", arg_str(a),
a->_.deps_n, ndeps);
args_pd(
"Add dependencies using ARG_DEPENDS()\n");
}
for_each_rel(a, deps, dep) {
if (!dep) {
args_pd(
"%s NULL deps[%zu]\n", arg_str(a), depi);
}
if (dep == a) {
args_pd(
"%s depends on itself\n", arg_str(a));
}
if (!dep->set)
arg_set_new(dep);
}
arg_no_deps:
if (!a->_.cons) {
if (a->_.cons_n > 0) {
args_pd(
"%s cons_n=%zu but cons=NULL\n", arg_str(a),
a->_.cons_n);
args_pd(
"Add conflicts using ARG_CONFLICTS()\n");
}
args_pd(
"%s has relation phase but no conflicts\n",
arg_str(a));
}
goto arg_no_cons;
}
while (a->_.cons[ncons])
ncons++;
if (ncons != a->_.cons_n) {
args_pd(
"%s cons_n=%zu but actual is %zu\n", arg_str(a),
a->_.cons_n, ncons);
args_pd(
"Add conflicts using ARG_CONFLICTS()\n");
}
for_each_rel(a, cons, con) {
if (!con) {
args_pd(
"%s NULL cons[%zu]\n", arg_str(a), coni);
}
if (con == a) {
args_pd(
"%s conflicts itself\n", arg_str(a));
}
if (!con->set)
arg_set_new(con);
for_each_rel(a, deps, dep) {
if (dep != con)
continue;
args_pd(
"%s both depends and conflicts %s\n",
arg_str(a), arg_str(con));
}
}
arg_no_cons:
if (!a->_.subs) {
if (a->_.subs_n > 0) {
args_pd(
"%s subs_n=%zu but subs=NULL\n", arg_str(a),
a->_.subs_n);
args_pd(
"Specify subsets using ARG_SUBSETS()\n");
}
if (a->_.subs_strs) {
args_pd(
"%s has subs_strs but no subsets\n",
arg_str(a));
}
goto arg_no_subs;
}
while (a->_.subs[nsubs])
nsubs++;
if (nsubs != a->_.subs_n) {
args_pd(
"%s subs_n=%zu but actual is %zu\n", arg_str(a),
a->_.subs_n, nsubs);
args_pd(
"Specify subset args using ARG_SUBSETS()\n");
}
if (a->_.subs_strs) {
size_t nsstrs = 0;
while (a->_.subs_strs[nsstrs])
nsstrs++;
if (nsstrs != a->_.subs_n) {
args_pd(
"%s subs_n=%zu but subs_strs has %zu entries\n",
arg_str(a), a->_.subs_n, nsstrs);
args_pd(
"Both lists must be the same size\n");
}
}
for_each_rel(a, subs, sub) {
if (sub == a) {
args_pd(
"%s subsets itself\n", arg_str(a));
}
if (!sub->set)
arg_set_new(sub);
(!a->_.subs_strs || a->_.subs_strs[subi] ==
ARG_SUBPASS)) {
args_pd(
"%s requires param but superset %s might not and has no custom string\n",
arg_str(sub), arg_str(a));
}
arg_set_new(a);
for_each_rel(a, cons, con) {
if (con == sub) {
args_pd(
"%s both supersets and conflicts %s\n",
arg_str(a), arg_str(sub));
}
}
for_each_rel(sub, deps, dep) {
if (dep == a) {
args_pd(
"%s supersets %s but also depends on it\n",
arg_str(a), arg_str(sub));
}
}
}
arg_no_subs:
for_each_arg(c, args) {
if ((a->
opt && c->opt && a->
opt == c->opt) ||
(a->
lopt && c->lopt && strcmp(a->
lopt, c->lopt) == 0)) {
args_pd(
"%s same opts as %s\n", arg_str(a), arg_str(c));
}
}
#define args_insert(list) \
do { \
a->_.next_##list = list; \
list = a; \
} while (0);
args_insert(args);
args_insert(validate);
args_insert(action);
#undef args_insert
size_t check = len;
if (check >= ARG_STR_BUF_SIZE) {
args_pd(
"%s combined opt, lopt, help string too long: %zu chars\n",
arg_str(a), check);
}
a->_.help_len = len;
if (len > longest)
longest = len;
a->_.valid = true;
args_num++;
}
static bool arg_process(
struct argument *a,
const char *str)
{
if (!a->_.valid) {
args_pd(
"%s has internals pre-set\n", arg_str(a));
args_pd(
"Please register arguments using ARGUMENT()\n");
}
args_pe(
"Argument %s specified multiple times\n", arg_str(a));
return false;
}
return false;
}
}
for_each_rel(a, deps, dep) {
if (!*dep->set) {
args_pe(
"%s requires %s to be set first\n",
arg_str(a), arg_str(dep));
return false;
}
}
}
for_each_rel(a, cons, con) {
if (*con->set) {
args_pe(
"%s conflicts with %s\n", arg_str(a),
arg_str(con));
return false;
}
}
}
for_each_rel(a, subs, sub) {
if (*sub->set)
continue;
const char *sub_str = str;
if (a->_.subs_strs && a->_.subs_strs[subi] &&
sub_str = a->_.subs_strs[subi];
if (!arg_process(sub, sub_str))
return false;
}
return true;
}
static bool arg_option(const char *token)
{
if (!token || token[0] != '-' || token[1] == '\0')
return false;
if (strcmp(token, "--") == 0)
return true;
if (token[1] == '-') {
const char *name = token + 2;
const char *eq = strchr(name, '=');
size_t name_len = eq ? (size_t)(eq - name) : strlen(name);
for_each_arg(c, args) {
if (c->lopt && strncmp(c->lopt, name, name_len) == 0 &&
c->lopt[name_len] == '\0')
return true;
}
return false;
}
for_each_arg(c, args) {
if (c->opt && c->opt == token[1])
return true;
}
return false;
}
static bool arg_parse_lopt(int *i)
{
char *name = arg + 2;
char *value = strchr(name, '=');
size_t name_len = value ? (size_t)(value - name) : strlen(name);
if (value)
value++;
for_each_arg(c, args) {
if (c->lopt && strncmp(c->lopt, name, name_len) == 0 &&
c->lopt[name_len] == '\0') {
a = c;
break;
}
}
if (!a) {
args_pe(
"Unknown: --%.*s\n", (
int)name_len, name);
return false;
}
const char *str = NULL;
if (value) {
str = value;
}
else if (*i + 1 <
argr.c) {
} else {
return false;
}
if (value)
str = value;
else if (*i + 1 <
argr.c && !arg_option(
argr.v[*i + 1]))
} else {
if (value) {
return false;
}
}
return arg_process(a, str);
}
static bool arg_parse_opt(int *i)
{
for (size_t j = 1; arg[j]; j++) {
for_each_arg(c, args) {
a = c;
break;
}
}
if (!a) {
return false;
}
const char *str = NULL;
if (arg[j + 1]) {
str = arg + j + 1;
j = strlen(arg);
}
else if (*i + 1 <
argr.c) {
} else {
return false;
}
if (arg[j + 1]) {
str = arg + j + 1;
j = strlen(arg);
}
}
if (!arg_process(a, str))
return false;
}
return true;
}
#define args_vaorder(list) \
do { \
for_each_arg(a, list) { \
struct argument *order = a->list##_order; \
if (order == NULL || order == (struct argument *)-1) \
continue; \
\
bool found = false; \
for_each_arg(check, list) { \
if (check == order) { \
found = true; \
break; \
} \
} \
\
if (!found) { \
args_pd("%s has invalid argument in " #list \
"_order\n", \
arg_str(a)); \
args_pi(a); \
args_abort(); \
} \
} \
} while (0)
#define args_reorder(list) \
do { \
struct argument *ordered = NULL; \
struct argument *unordered = list; \
list = NULL; \
\
struct argument **pp = &unordered; \
while (*pp) { \
struct argument *a = *pp; \
if (a->list##_order == (struct argument *)-1) { \
*pp = a->_.next_##list; \
a->_.next_##list = ordered; \
ordered = a; \
} else { \
pp = &(*pp)->_.next_##list; \
} \
} \
\
bool changed = true; \
while (unordered && changed) { \
changed = false; \
pp = &unordered; \
while (*pp) { \
struct argument *a = *pp; \
struct argument *ord = a->list##_order; \
bool can_place = false; \
struct argument **insert_pos = NULL; \
\
if (ord == NULL) { \
can_place = true; \
if (!ordered) { \
insert_pos = &ordered; \
} else { \
struct argument *cur = \
ordered; \
while (cur->_.next_##list) \
cur = cur->_.next_##list; \
insert_pos = \
&cur->_.next_##list; \
} \
} else { \
struct argument **pord = &ordered; \
while (*pord) { \
if (*pord == ord) { \
can_place = true; \
insert_pos = \
&(*pord)->_ \
.next_##list; \
break; \
} \
pord = &(*pord)->_.next_##list; \
} \
} \
\
if (can_place && insert_pos) { \
*pp = a->_.next_##list; \
a->_.next_##list = *insert_pos; \
*insert_pos = a; \
changed = true; \
} else { \
pp = &(*pp)->_.next_##list; \
} \
} \
} \
\
if (unordered) { \
if (!ordered) { \
ordered = unordered; \
} else { \
struct argument *cur = ordered; \
while (cur->_.next_##list) \
cur = cur->_.next_##list; \
cur->_.next_##list = unordered; \
} \
} \
\
list = ordered; \
} while (0)
{
args_vaorder(validate);
args_vaorder(action);
#undef args_vaorder
args_reorder(validate);
args_reorder(action);
#undef args_reorder
bool success = true;
for (
int i = 1; i <
argr.c; i++) {
if (strcmp(arg, "--") == 0)
break;
if (arg[0] != '-')
continue;
if (arg[1] == '\0')
continue;
if (arg[1] == '-') {
if (!arg_parse_lopt(&i))
success = false;
} else {
if (!arg_parse_opt(&i))
success = false;
}
}
return success;
}
{
bool any_invalid = false;
for_each_arg(a, validate) {
if (!a->_.valid) {
args_pd(
"%s has internals pre-set\n", arg_str(a));
args_pd(
"Please register arguments using ARGUMENT()\n");
}
bool any_conflict_set = false;
for_each_rel(a, cons, con) {
if (*con->set) {
any_conflict_set = true;
break;
}
}
if (!any_conflict_set) {
args_pe(
"Missing required argument: %s\n",
arg_str(a));
a->_.valid = false;
any_invalid = true;
}
}
bool should_check_deps = false;
switch (a->_.deps_phase) {
break;
should_check_deps = true;
break;
should_check_deps = *a->
set;
break;
should_check_deps = !*a->
set;
break;
default:
args_pd(
"Unknown dependency relation phase in %s\n",
arg_str(a));
break;
}
if (should_check_deps) {
for_each_rel(a, deps, dep) {
if (*dep->set)
continue;
args_pe(
"%s requires %s to be set\n",
arg_str(a), arg_str(dep));
any_invalid = true;
}
}
bool should_check_cons = false;
switch (a->_.cons_phase) {
break;
should_check_cons = true;
break;
should_check_cons = *a->
set;
break;
should_check_cons = !*a->
set;
break;
default:
args_pd(
"Unknown conflict relation phase in %s\n",
arg_str(a));
break;
}
if (should_check_cons) {
for_each_rel(a, cons, con) {
if (!*con->set)
continue;
args_pe(
"%s conflicts with %s\n", arg_str(a),
arg_str(con));
any_invalid = true;
}
}
continue;
bool should_validate = false;
should_validate = true;
break;
should_validate = *a->
set;
break;
should_validate = !*a->
set;
break;
default:
args_pd(
"Unknown .validate_phase in %s\n", arg_str(a));
}
if (should_validate) {
any_invalid = true;
a->_.valid = false;
}
}
}
return !any_invalid;
}
{
for_each_arg(a, action) {
continue;
bool should_act = false;
should_act = true;
break;
break;
break;
default:
args_pd(
"Unknown .action_phase in %s\n", arg_str(a));
}
if (should_act)
}
}
static void arg_help_print(
const struct argument *a)
{
return;
}
size_t pad = off > a->_.help_len ? off - a->_.help_len : 1;
bool first = true;
const char *phelp = a->
help;
while (*phelp) {
const char *nl = strchr(phelp, '\n');
size_t line = nl ? (size_t)(nl - phelp) : strlen(phelp);
if (first) {
args_po(
"%*s%.*s", (
int)pad,
"", (
int)line, phelp);
first = false;
} else {
args_po(
"\n%*s%.*s", (
int)off,
"", (
int)line, phelp);
}
if (!nl)
break;
phelp = nl + 1;
}
}
const char *req, const char *opt)
{
args_po(
"%s%s%s", usage, bin, hint);
bool first = true;
for_each_arg(a, help) {
continue;
if (first) {
first = false;
}
arg_help_print(a);
}
first = true;
for_each_arg(a, help) {
continue;
if (first) {
first = false;
}
arg_help_print(a);
}
}
#undef for_each_arg
#undef for_each_rel
#endif
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