34#ifndef FLAG_LIST_INIT_CAP
35#define FLAG_LIST_INIT_CAP 8
45bool *
flag_bool(
const char *name,
bool def,
const char *desc);
46uint64_t *
flag_uint64(
const char *name, uint64_t def,
const char *desc);
47size_t *
flag_size(
const char *name, uint64_t def,
const char *desc);
48char **
flag_str(
const char *name,
const char *def,
const char *desc);
60#ifdef FLAG_IMPLEMENTATION
71static_assert(COUNT_FLAG_TYPES == 5,
"Exhaustive Flag_Value definition");
84 FLAG_ERROR_INVALID_NUMBER,
85 FLAG_ERROR_INTEGER_OVERFLOW,
86 FLAG_ERROR_INVALID_SIZE_SUFFIX,
103 Flag flags[FLAGS_CAP];
106 Flag_Error flag_error;
107 char *flag_error_name;
109 const char *program_name;
115static Flag_Context flag_global_context;
117Flag *flag_new(Flag_Type type,
const char *name,
const char *desc)
119 Flag_Context *c = &flag_global_context;
121 assert(c->flags_count < FLAGS_CAP);
122 Flag *flag = &c->flags[c->flags_count++];
123 memset(flag, 0,
sizeof(*flag));
126 flag->name = (
char*) name;
127 flag->desc = (
char*) desc;
133 Flag *flag = (Flag*) ((
char*) val - offsetof(Flag, val));
137bool *
flag_bool(
const char *name,
bool def,
const char *desc)
139 Flag *flag = flag_new(FLAG_BOOL, name, desc);
140 flag->def.as_bool = def;
141 flag->val.as_bool = def;
142 return &flag->val.as_bool;
145uint64_t *
flag_uint64(
const char *name, uint64_t def,
const char *desc)
147 Flag *flag = flag_new(FLAG_UINT64, name, desc);
148 flag->val.as_uint64 = def;
149 flag->def.as_uint64 = def;
150 return &flag->val.as_uint64;
153size_t *
flag_size(
const char *name, uint64_t def,
const char *desc)
155 Flag *flag = flag_new(FLAG_SIZE, name, desc);
156 flag->val.as_size = def;
157 flag->def.as_size = def;
158 return &flag->val.as_size;
161char **
flag_str(
const char *name,
const char *def,
const char *desc)
163 Flag *flag = flag_new(FLAG_STR, name, desc);
164 flag->val.as_str = (
char*) def;
165 flag->def.as_str = (
char*) def;
166 return &flag->val.as_str;
171 Flag *flag = flag_new(FLAG_LIST, name, desc);
172 return &flag->val.as_list;
175static void flag_list_append(
Flag_List *list,
char *item)
179 list->
items = (
const char**)realloc(list->
items, new_capacity*
sizeof(*list->
items));
186static char *flag_shift_args(
int *argc,
char ***argv)
189 char *result = **argv;
197 return flag_global_context.rest_argc;
202 return flag_global_context.rest_argv;
205const char *flag_program_name(
void)
207 return flag_global_context.program_name;
212 Flag_Context *c = &flag_global_context;
214 if (c->program_name == NULL) {
215 c->program_name = flag_shift_args(&argc, &argv);
219 char *flag = flag_shift_args(&argc, &argv);
223 c->rest_argc = argc + 1;
224 c->rest_argv = argv - 1;
228 if (strcmp(flag,
"--") == 0) {
229#ifdef FLAG_PUSH_DASH_DASH_BACK
231 c->rest_argc = argc + 1;
232 c->rest_argv = argv - 1;
244 char *equals = strchr(flag,
'=');
245 if (equals != NULL) {
253 for (
size_t i = 0; i < c->flags_count; ++i) {
254 if (strcmp(c->flags[i].name, flag) == 0) {
255 static_assert(COUNT_FLAG_TYPES == 5,
"Exhaustive flag type parsing");
256 switch (c->flags[i].type) {
259 c->flag_error = FLAG_ERROR_NO_VALUE;
260 c->flag_error_name = flag;
264 char *arg = flag_shift_args(&argc, &argv);
265 flag_list_append(&c->flags[i].val.as_list, arg);
270 c->flags[i].val.as_bool =
true;
276 if (equals == NULL) {
278 c->flag_error = FLAG_ERROR_NO_VALUE;
279 c->flag_error_name = flag;
282 arg = flag_shift_args(&argc, &argv);
287 c->flags[i].val.as_str = arg;
293 if (equals == NULL) {
295 c->flag_error = FLAG_ERROR_NO_VALUE;
296 c->flag_error_name = flag;
299 arg = flag_shift_args(&argc, &argv);
304 static_assert(
sizeof(
unsigned long long int) ==
sizeof(uint64_t),
"The original author designed this for x86_64 machine with the compiler that expects unsigned long long int and uint64_t to be the same thing, so they could use strtoull() function to parse it. Please adjust this code for your case and maybe even send the patch to upstream to make it work on a wider range of environments.");
308 unsigned long long int result = strtoull(arg, &endptr, 10);
310 if (*endptr !=
'\0') {
311 c->flag_error = FLAG_ERROR_INVALID_NUMBER;
312 c->flag_error_name = flag;
316 if (result == ULLONG_MAX && errno == ERANGE) {
317 c->flag_error = FLAG_ERROR_INTEGER_OVERFLOW;
318 c->flag_error_name = flag;
322 c->flags[i].val.as_uint64 = result;
328 if (equals == NULL) {
330 c->flag_error = FLAG_ERROR_NO_VALUE;
331 c->flag_error_name = flag;
334 arg = flag_shift_args(&argc, &argv);
339 static_assert(
sizeof(
unsigned long long int) ==
sizeof(size_t),
"The original author designed this for x86_64 machine with the compiler that expects unsigned long long int and size_t to be the same thing, so they could use strtoull() function to parse it. Please adjust this code for your case and maybe even send the patch to upstream to make it work on a wider range of environments.");
343 unsigned long long int result = strtoull(arg, &endptr, 10);
351 if (strcmp(endptr,
"K") == 0) {
353 }
else if (strcmp(endptr,
"M") == 0) {
355 }
else if (strcmp(endptr,
"G") == 0) {
356 result *= 1024*1024*1024;
357 }
else if (strcmp(endptr,
"") != 0) {
358 c->flag_error = FLAG_ERROR_INVALID_SIZE_SUFFIX;
359 c->flag_error_name = flag;
364 if (result == ULLONG_MAX && errno == ERANGE) {
365 c->flag_error = FLAG_ERROR_INTEGER_OVERFLOW;
366 c->flag_error_name = flag;
370 c->flags[i].val.as_size = result;
374 case COUNT_FLAG_TYPES:
376 assert(0 &&
"unreachable");
386 c->flag_error = FLAG_ERROR_UNKNOWN;
387 c->flag_error_name = flag;
399 Flag_Context *c = &flag_global_context;
400 for (
size_t i = 0; i < c->flags_count; ++i) {
401 Flag *flag = &c->flags[i];
403 static_assert(COUNT_FLAG_TYPES == 5,
"Exhaustive flag type defaults printing");
404 switch (c->flags[i].type) {
406 fprintf(stream,
" -%s <str> ... -%s <str> ...\n", flag->name, flag->name);
407 fprintf(stream,
" %s\n", flag->desc);
410 fprintf(stream,
" -%s\n", flag->name);
411 fprintf(stream,
" %s\n", flag->desc);
412 if (flag->def.as_bool) {
413 fprintf(stream,
" Default: %s\n", flag->def.as_bool ?
"true" :
"false");
417 fprintf(stream,
" -%s <int>\n", flag->name);
418 fprintf(stream,
" %s\n", flag->desc);
419 fprintf(stream,
" Default: %" PRIu64
"\n", flag->def.as_uint64);
422 fprintf(stream,
" -%s <int>\n", flag->name);
423 fprintf(stream,
" %s\n", flag->desc);
424 fprintf(stream,
" Default: %zu\n", flag->def.as_size);
427 fprintf(stream,
" -%s <str>\n", flag->name);
428 fprintf(stream,
" %s\n", flag->desc);
429 if (flag->def.as_str) {
430 fprintf(stream,
" Default: %s\n", flag->def.as_str);
434 assert(0 &&
"unreachable");
442 Flag_Context *c = &flag_global_context;
443 static_assert(COUNT_FLAG_ERRORS == 6,
"Exhaustive flag error printing");
444 switch (c->flag_error) {
447 fprintf(stream,
"Operation Failed Successfully! Please tell the developer of this software that they don't know what they are doing! :)");
449 case FLAG_ERROR_UNKNOWN:
450 fprintf(stream,
"ERROR: -%s: unknown flag\n", c->flag_error_name);
452 case FLAG_ERROR_NO_VALUE:
453 fprintf(stream,
"ERROR: -%s: no value provided\n", c->flag_error_name);
455 case FLAG_ERROR_INVALID_NUMBER:
456 fprintf(stream,
"ERROR: -%s: invalid number\n", c->flag_error_name);
458 case FLAG_ERROR_INTEGER_OVERFLOW:
459 fprintf(stream,
"ERROR: -%s: integer overflow\n", c->flag_error_name);
461 case FLAG_ERROR_INVALID_SIZE_SUFFIX:
462 fprintf(stream,
"ERROR: -%s: invalid size suffix\n", c->flag_error_name);
464 case COUNT_FLAG_ERRORS:
466 assert(0 &&
"unreachable");
void flag_print_error(FILE *stream)
uint64_t * flag_uint64(const char *name, uint64_t def, const char *desc)
char * flag_name(void *val)
char ** flag_str(const char *name, const char *def, const char *desc)
#define FLAG_LIST_INIT_CAP
Definition flag.h:35
char ** flag_rest_argv(void)
bool flag_parse(int argc, char **argv)
size_t * flag_size(const char *name, uint64_t def, const char *desc)
void flag_print_options(FILE *stream)
Flag_List * flag_list(const char *name, const char *desc)
bool * flag_bool(const char *name, bool def, const char *desc)
size_t capacity
Definition flag.h:41
const char ** items
Definition flag.h:39
size_t count
Definition flag.h:40