This commit is contained in:
commit
fd56c5124a
2 changed files with 241 additions and 0 deletions
124
kyaa.h
Normal file
124
kyaa.h
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/* kyaa.h - macro hacks for handling main() arguments
|
||||||
|
license: public domain or whatever.
|
||||||
|
documentation is kept separate in kyaa.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef KYAA_OKAY
|
||||||
|
#define KYAA_OKAY 0
|
||||||
|
#endif
|
||||||
|
#ifndef KYAA_ERROR
|
||||||
|
#define KYAA_ERROR 1
|
||||||
|
#endif
|
||||||
|
#ifndef KYAA_ITER
|
||||||
|
#define KYAA_ITER i
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define KYAA_SETUP \
|
||||||
|
/* dumb sanity checks */ \
|
||||||
|
if (argc <= 0 || argv == NULL || argv[0] == NULL || argv[0][0] == '\0') { \
|
||||||
|
fprintf(stderr, "You've met with a terrible fate.\n"); \
|
||||||
|
return KYAA_ERROR; \
|
||||||
|
} \
|
||||||
|
/* read-only */ \
|
||||||
|
char *kyaa_name = argv[0]; \
|
||||||
|
bool kyaa_read_stdin = false; \
|
||||||
|
char kyaa_flag = '\0'; \
|
||||||
|
/* internal */ \
|
||||||
|
bool kyaa_keep_parsing = true; \
|
||||||
|
bool kyaa_parse_next = false; \
|
||||||
|
|
||||||
|
#define KYAA_LOOP \
|
||||||
|
KYAA_SETUP \
|
||||||
|
for (int KYAA_ITER = 1; KYAA_ITER < argc; KYAA_ITER++) \
|
||||||
|
|
||||||
|
#define KYAA_BEGIN \
|
||||||
|
char *kyaa_arg = argv[KYAA_ITER]; \
|
||||||
|
if (kyaa_keep_parsing && (kyaa_parse_next || kyaa_arg[0] == '-')) { \
|
||||||
|
if (!kyaa_parse_next && kyaa_arg[1] == '-' && kyaa_arg[2] == '\0') { \
|
||||||
|
kyaa_keep_parsing = false; \
|
||||||
|
continue; \
|
||||||
|
} \
|
||||||
|
if (!kyaa_parse_next && kyaa_arg[1] == '\0') { \
|
||||||
|
kyaa_read_stdin = true; \
|
||||||
|
} else { \
|
||||||
|
/* case: kyaa_parse_next: arg is at least 1 char initialized. */ \
|
||||||
|
/* case: !kyaa_parse_next: arg is at least 3 chars initialized. */ \
|
||||||
|
char *kyaa_etc = kyaa_parse_next ? kyaa_arg : kyaa_arg + 2; \
|
||||||
|
bool kyaa_no_more = false; \
|
||||||
|
bool kyaa_helping = false; \
|
||||||
|
bool kyaa_any = false; \
|
||||||
|
if (!kyaa_parse_next && kyaa_arg[1] != '-') { \
|
||||||
|
kyaa_flag = kyaa_arg[1]; \
|
||||||
|
kyaa_no_more = kyaa_arg[2] == '\0'; \
|
||||||
|
} \
|
||||||
|
if (kyaa_flag == 'h' || !strcmp(kyaa_arg, "--help")) { \
|
||||||
|
printf("usage:\n"); \
|
||||||
|
kyaa_helping = true; \
|
||||||
|
} \
|
||||||
|
if (0) { \
|
||||||
|
|
||||||
|
#define KYAA_END \
|
||||||
|
} \
|
||||||
|
if (!kyaa_any && !kyaa_helping) { \
|
||||||
|
if (kyaa_flag) { \
|
||||||
|
fprintf(stderr, "unknown flag: -%c\n", kyaa_flag); \
|
||||||
|
} else { \
|
||||||
|
fprintf(stderr, "unknown flag: %s\n", kyaa_arg); \
|
||||||
|
} \
|
||||||
|
return KYAA_ERROR; \
|
||||||
|
} \
|
||||||
|
if (kyaa_helping) { \
|
||||||
|
return KYAA_OKAY; \
|
||||||
|
} \
|
||||||
|
kyaa_parse_next = false; \
|
||||||
|
kyaa_flag = '\0'; \
|
||||||
|
continue; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
|
||||||
|
#define KYAA_DESCRIBE(c, name, description) \
|
||||||
|
printf(" -%c --%s\n%s\n", c, name, description) \
|
||||||
|
|
||||||
|
#define KYAA_FLAG(c, name, description) \
|
||||||
|
} \
|
||||||
|
if (kyaa_helping) { \
|
||||||
|
KYAA_DESCRIBE(c, name, description); \
|
||||||
|
} else if (kyaa_flag == c || !strcmp(kyaa_arg, "--"name)) { \
|
||||||
|
kyaa_flag = c; \
|
||||||
|
kyaa_any = true; \
|
||||||
|
|
||||||
|
#define KYAA_FLAG_ARG(c, name, description) \
|
||||||
|
} \
|
||||||
|
if (kyaa_helping) { \
|
||||||
|
KYAA_DESCRIBE(c, name, description); \
|
||||||
|
} else if (kyaa_flag == c || !strcmp(kyaa_arg, "--"name)) { \
|
||||||
|
if (kyaa_no_more || kyaa_flag != c) { \
|
||||||
|
kyaa_parse_next = true; \
|
||||||
|
if (KYAA_ITER + 1 == argc || argv[KYAA_ITER + 1][0] == '\0') { \
|
||||||
|
fprintf(stderr, "expected an argument for --%s (-%c)\n", name, c); \
|
||||||
|
return KYAA_ERROR; \
|
||||||
|
} \
|
||||||
|
kyaa_flag = c; \
|
||||||
|
continue; \
|
||||||
|
} \
|
||||||
|
kyaa_flag = c; \
|
||||||
|
kyaa_any = true; \
|
||||||
|
|
||||||
|
#define KYAA_FLAG_LONG(c, name, description) \
|
||||||
|
KYAA_FLAG_ARG(c, name, description) \
|
||||||
|
errno = 0; \
|
||||||
|
long kyaa_flag_arg = strtol(kyaa_etc, NULL, 0); \
|
||||||
|
if (errno) { \
|
||||||
|
perror(NULL); \
|
||||||
|
return KYAA_ERROR; \
|
||||||
|
} \
|
||||||
|
|
||||||
|
#define KYAA_HELP(description) \
|
||||||
|
} \
|
||||||
|
if (kyaa_helping) { \
|
||||||
|
if (description != NULL) { \
|
||||||
|
printf("%s\n", description); \
|
||||||
|
} \
|
||||||
|
|
117
kyaa.md
Normal file
117
kyaa.md
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
# kyaa
|
||||||
|
|
||||||
|
super hacky macro hacks for parsing arguments in C.
|
||||||
|
|
||||||
|
## prerequisites
|
||||||
|
|
||||||
|
C99 or greater.
|
||||||
|
|
||||||
|
standard library headers:
|
||||||
|
* errno.h
|
||||||
|
* stdbool.h
|
||||||
|
* stdio.h
|
||||||
|
* string.h
|
||||||
|
|
||||||
|
## tutorial/API
|
||||||
|
|
||||||
|
ensure argc and argv are defined.
|
||||||
|
kyaa doesn't actually care if it's in main() or not.
|
||||||
|
|
||||||
|
iterate over the arguments with KYAA\_LOOP:
|
||||||
|
|
||||||
|
```c
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
KYAA_LOOP {
|
||||||
|
// i, kyaa_name, kyaa_read_stdin, and kyaa_flag are exposed here.
|
||||||
|
// [other code goes here]
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
use KYAA\_BEGIN and KYAA\_END to begin parsing arguments.
|
||||||
|
put unrelated code above KYAA\_BEGIN or below KYAA\_END, but not within:
|
||||||
|
|
||||||
|
```c
|
||||||
|
KYAA_LOOP {
|
||||||
|
// [other code goes here]
|
||||||
|
KYAA_BEGIN
|
||||||
|
// kyaa_arg and kyaa_etc are exposed here.
|
||||||
|
// [kyaa code goes here]
|
||||||
|
KYAA_END
|
||||||
|
// [other code goes here]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
use KYAA\_FLAG for defining flags that don't take an argument,
|
||||||
|
and KYAA\_FLAG\_ARG or KYAA\_FLAG\_LONG for flags that do:
|
||||||
|
|
||||||
|
```c
|
||||||
|
bool use_feature = false;
|
||||||
|
char *log_fn = "logs.txt";
|
||||||
|
long my_var = 0;
|
||||||
|
KYAA_LOOP {
|
||||||
|
// [other code goes here]
|
||||||
|
KYAA_BEGIN
|
||||||
|
|
||||||
|
// arguments: short flag, long flag, help description
|
||||||
|
KYAA_FLAG("-x", "--enable-feature",
|
||||||
|
" enable some feature")
|
||||||
|
use_feature = true;
|
||||||
|
|
||||||
|
// same arguments, but kyaa_etc contains the relevant string.
|
||||||
|
KYAA_FLAG_ARG("-l", "--log-file",
|
||||||
|
" use a given filename for the log file")
|
||||||
|
log_fn = kyaa_etc;
|
||||||
|
|
||||||
|
// same arguments, kyaa_etc is set, but kyaa_flag_arg is also set.
|
||||||
|
KYAA_FLAG_LONG("-v", "--var",
|
||||||
|
" set an integer variable\n"
|
||||||
|
" default: 0")
|
||||||
|
my_var = kyaa_flag_arg;
|
||||||
|
|
||||||
|
KYAA_END
|
||||||
|
// [other code goes here]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
kyaa secretly wraps flag handling in if/else statements with {} blocks.
|
||||||
|
do not confuse it for a switch/case method.
|
||||||
|
|
||||||
|
kyaa handles -h and --help for printing help text.
|
||||||
|
additional help text may be defined using KYAA\_HELP:
|
||||||
|
|
||||||
|
```c
|
||||||
|
KYAA_LOOP {
|
||||||
|
KYAA_BEGIN
|
||||||
|
KYAA_HELP(
|
||||||
|
" {files...}\n"
|
||||||
|
" do things with files.")
|
||||||
|
KYAA_END
|
||||||
|
|
||||||
|
do_stuff(kyaa_arg);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
kyaa interprets an argument of "-" as a request to enable reading from stdin:
|
||||||
|
kyaa\_read\_stdin is set to true.
|
||||||
|
|
||||||
|
arguments may be passed in three ways, consider:
|
||||||
|
* `-v42`
|
||||||
|
* `-v 42`
|
||||||
|
* `--var 42`
|
||||||
|
|
||||||
|
kyaa returns KYAA\_OKAY when -h or --help is given,
|
||||||
|
and KYAA\_ERROR in the event of invalid flags, missing arguments,
|
||||||
|
or invalid numbers (KYAA\_FLAG\_LONG).
|
||||||
|
kyaa uses continue for handling arguments.
|
||||||
|
|
||||||
|
kyaa prints error messages to stderr, and help text to stdout.
|
||||||
|
|
||||||
|
KYAA\_ITER may be redefined to avoid name collisions.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
* support `--var=42` argument style
|
||||||
|
* rename overlapping things, e.g. KYAA\_FLAG vs kyaa\_flag, KYAA\_FLAG\_ARG vs kyaa\_flag\_arg, etc.
|
||||||
|
* move KYAA\_FLAG\_ARG to `kyaa_extend.h` or something; write similar macros.
|
Loading…
Add table
Reference in a new issue