110 lines
7.2 KiB
C
110 lines
7.2 KiB
C
#ifndef _C_ARGH_H
|
|
#define _C_ARGH_H
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define ARGH_PARSE \
|
|
if (strcmp(argv[argc - 1], "-h") == 0 || \
|
|
strcmp(argv[argc - 1], "--help") == 0) { \
|
|
fprintf(stderr, "usage: %s\n", USAGE); \
|
|
exit(EXIT_FAILURE); \
|
|
} \
|
|
bool __attribute__((__unused__)) argh_used = true, \
|
|
argh_repeat = false, \
|
|
argh_val = false; \
|
|
char __attribute__((__unused__)) *ARGH_ARGV[argc], \
|
|
*argh_name; \
|
|
size_t __attribute__((__unused__)) ARGH_ARGC = 0, \
|
|
argh_diff = 0, \
|
|
argh_offset = 0; \
|
|
while (argh_offset < argc)
|
|
|
|
// check for a flag which is either present or not
|
|
#define ARGH_BOOL(short_name, long_name) \
|
|
/* ARGH_VAL cannot be used with ARGH_BOOL */ \
|
|
(argh_name = NULL, \
|
|
/* short name exact match */ \
|
|
(( 0 == strcmp(argv[argh_offset], short_name) \
|
|
/* short name prefix match, for specify multiple short names with a \
|
|
single param, -xyz instead -x -y -z. this mutates argv for the \
|
|
next pass, -xyz to -yz */ \
|
|
|| ((0 == strncmp(argv[argh_offset], short_name, 2) && \
|
|
strlen(argv[argh_offset]) > 2 && \
|
|
(argv[argh_offset]++, argv[argh_offset][0] = '-', \
|
|
argh_repeat = true))) \
|
|
/* long name exact match */ \
|
|
|| 0 == strcmp(argv[argh_offset], long_name)) \
|
|
/* set argh_used to avoid adding to ARGH_ARGV, return true */ \
|
|
? argh_used = true \
|
|
/* return false */ \
|
|
: false))
|
|
|
|
// check for a flag which if present is followed by a value
|
|
#define ARGH_FLAG(short_name, long_name) \
|
|
/* set argh_name so we can print it later as an error if no value is next \
|
|
in argv */ \
|
|
(argh_name = long_name, \
|
|
/* maybe set arg_diff=strlen(short_name) in case flag is \
|
|
"--nameVALUE" not "--name VALUE" */ \
|
|
( (argh_diff = strlen(argv[argh_offset]) != strlen(short_name) ? \
|
|
strlen(short_name) : 0, \
|
|
/* short name prefix match, if argh_diff == 0 then this is an exact \
|
|
match */ \
|
|
0 == strncmp(argv[argh_offset], short_name, strlen(short_name))) \
|
|
/* maybe set arg_diff=strlen(long_name) in case flag is "--nameVALUE"\
|
|
not "--name VALUE" */ \
|
|
|| (argh_diff = strlen(argv[argh_offset]) != strlen(long_name) ? \
|
|
strlen(long_name) : 0, \
|
|
/* long name prefix match, if argh_diff == 0 then this is an exact \
|
|
match */ \
|
|
0 == strncmp(argv[argh_offset], long_name, strlen(long_name)))) \
|
|
/* set argh_used to avoid adding to ARGH_ARGV, return true */ \
|
|
? argh_used = true \
|
|
/* return false */ \
|
|
: false)
|
|
|
|
// get value for the current flag
|
|
#define ARGH_VAL() \
|
|
/* assert can only be used with ARGH_FLAG */ \
|
|
((argh_name == NULL) \
|
|
? (fprintf(stderr, "fatal: ARGH_VAL can only be used with ARGH_FLAG\n"), \
|
|
exit(EXIT_FAILURE), NULL) \
|
|
/* if argh_diff, use suffix of current argv instead of next argv */ \
|
|
: (argh_diff \
|
|
? (argv[argh_offset] + argh_diff) \
|
|
/* else use next argv as long as it exists and does not start with \
|
|
"-" */ \
|
|
: ((argh_offset < argc - 1 && \
|
|
strlen(argv[argh_offset + 1]) > 0 && \
|
|
argv[argh_offset + 1][0] != '-') \
|
|
/* set argh_val to offset argv one additional space, return val */ \
|
|
? (argh_val = true, argv[argh_offset + 1]) \
|
|
: (fprintf(stderr, "fatal: needs value %s\n", argh_name), \
|
|
exit(EXIT_FAILURE), NULL))))
|
|
|
|
#define ARGH_NEXT() \
|
|
/* reset argh name, which is set by ARGH_FLAG */ \
|
|
argh_name = NULL; \
|
|
/* if not argh_used, save as a positional argument for later, then reset \
|
|
argh_used */ \
|
|
if (!argh_used) \
|
|
ARGH_ARGV[ARGH_ARGC++] = argv[argh_offset]; \
|
|
argh_used = false; \
|
|
/* if argh_val, offset argv an additional space because of ARGH_VAL, then \
|
|
reset argh_val */ \
|
|
if (argh_val) \
|
|
argh_offset++; \
|
|
argh_val = false; \
|
|
/* if not argh_repeat, offset argv, then reset */ \
|
|
if (!argh_repeat) \
|
|
argh_offset++; \
|
|
argh_repeat = false; \
|
|
/* check if we are done with all of argv and break is so */ \
|
|
if (argh_offset == argc) \
|
|
break;
|
|
|
|
#endif /* _C_ARGH_H */
|