remove kyaa
This commit is contained in:
parent
02f586f1cd
commit
9babd47832
|
@ -129,11 +129,7 @@ the batch script exploits [a UAC bypass found by Tyranid.](https://tyranidslair.
|
||||||
|
|
||||||
### kyaa
|
### kyaa
|
||||||
|
|
||||||
really *really* hacky argument-parsing macros for C programs.
|
[*moved to its own repo*](/git/notwa/kyaa)
|
||||||
|
|
||||||
contains [some ad-hoc documentation.](/kyaa/kyaa.md)
|
|
||||||
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
### lsca
|
### lsca
|
||||||
|
|
||||||
|
|
113
kyaa/kyaa.h
113
kyaa/kyaa.h
|
@ -1,113 +0,0 @@
|
||||||
/* kyaa.h - macro hacks for handling main() arguments
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
Refer to kyaa.md for documentation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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 \
|
|
||||||
/* sanity checks */ \
|
|
||||||
if (argc <= 0 || argv == NULL || argv[0] == NULL) { \
|
|
||||||
fprintf(stderr, "You've met with a terrible fate.\n"); \
|
|
||||||
return KYAA_ERROR; \
|
|
||||||
} \
|
|
||||||
char *kyaa_name = argv[0]; \
|
|
||||||
bool kyaa_read_stdin = false; \
|
|
||||||
char kyaa_flag = '\0'; \
|
|
||||||
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_HELP(description) \
|
|
||||||
} \
|
|
||||||
if (kyaa_helping) { \
|
|
||||||
if (description != NULL) { \
|
|
||||||
printf("%s\n", description); \
|
|
||||||
} \
|
|
||||||
|
|
160
kyaa/kyaa.md
160
kyaa/kyaa.md
|
@ -1,160 +0,0 @@
|
||||||
# kyaa
|
|
||||||
|
|
||||||
super hacky macro hacks for parsing arguments in C.
|
|
||||||
|
|
||||||
## prerequisites
|
|
||||||
|
|
||||||
C99 or greater.
|
|
||||||
|
|
||||||
standard library headers:
|
|
||||||
* `stdbool.h`
|
|
||||||
* `stdio.h`
|
|
||||||
* `string.h`
|
|
||||||
|
|
||||||
in addition, `kyaa_extra.h` requires `limits.h`,
|
|
||||||
or at least `LONG_MIN` to be defined.
|
|
||||||
|
|
||||||
## tutorial
|
|
||||||
|
|
||||||
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 {
|
|
||||||
// KYAA_ITER, 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
|
|
||||||
* fix overlapping names, e.g. `KYAA_FLAG` vs `kyaa_flag`, `KYAA_FLAG_ARG` vs `kyaa_flag_arg`, etc.
|
|
||||||
* maybe pass `argc`/`argv` manually?
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
### kyaa.h
|
|
||||||
|
|
||||||
return type or type | name | arguments or default value
|
|
||||||
--- | --- | ---
|
|
||||||
define | KYAA\_OKAY | 0
|
|
||||||
define | KYAA\_ERROR | 1
|
|
||||||
define | KYAA\_ITER | i
|
|
||||||
||
|
|
||||||
macro | KYAA\_SETUP | *none*
|
|
||||||
macro | KYAA\_LOOP | *none*
|
|
||||||
macro | KYAA\_BEGIN | *none*
|
|
||||||
macro | KYAA\_END | *none*
|
|
||||||
macro | KYAA\_DESCRIBE | c, name, description
|
|
||||||
macro | KYAA\_FLAG | c, name, description
|
|
||||||
macro | KYAA\_FLAG\_ARG | c, name, description
|
|
||||||
macro | KYAA\_HELP | description
|
|
||||||
||
|
|
||||||
`char *` | kyaa\_name | *n/a*
|
|
||||||
`bool` | kyaa\_read\_stdin | *n/a*
|
|
||||||
`char *` | kyaa\_arg | *n/a*
|
|
||||||
|| **internal use** ||
|
|
||||||
`char` | kyaa\_flag | *n/a*
|
|
||||||
`bool` | kyaa\_keep\_parsing | *n/a*
|
|
||||||
`bool` | kyaa\_parse\_next | *n/a*
|
|
||||||
`bool` | kyaa\_no\_more | *n/a*
|
|
||||||
`bool` | kyaa\_helping | *n/a*
|
|
||||||
`bool` | kyaa\_any | *n/a*
|
|
||||||
|
|
||||||
### kyaa\_extra.h
|
|
||||||
|
|
||||||
return value or type | name | arguments or default value
|
|
||||||
--- | --- | ---
|
|
||||||
macro | KYAA\_FLAG\_LONG | c, name, description
|
|
||||||
||
|
|
||||||
`char *` | kyaa\_flag\_arg | *n/a*
|
|
||||||
||
|
|
||||||
`char *` | kyaa\_skip\_spaces | `char *` str
|
|
||||||
`const char *` | kyaa\_str\_to\_long | `char *` str, `long *` p\_out
|
|
|
@ -1,211 +0,0 @@
|
||||||
/* kyaa_extra.h - extensions to kyaa for parsing arguments.
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
Refer to kyaa.md for documentation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static char *kyaa_skip_spaces(char *str) {
|
|
||||||
/* iterates str to first non-space character according to the C locale */
|
|
||||||
while (*str != '\0') {
|
|
||||||
switch (*str) {
|
|
||||||
case ' ':
|
|
||||||
case '\f':
|
|
||||||
case '\n':
|
|
||||||
case '\r':
|
|
||||||
case '\t':
|
|
||||||
case '\v': { str++; } break;
|
|
||||||
default: return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *kyaa__base_2(char **p_str, long *p_out) {
|
|
||||||
char *str = *p_str;
|
|
||||||
long out = *p_out;
|
|
||||||
for (char c; (c = *str) != '\0'; str++) {
|
|
||||||
switch (c) {
|
|
||||||
case '0': case '1': {
|
|
||||||
long digit = (long)(c - '0');
|
|
||||||
if (out < (LONG_MIN + digit) / 2) {
|
|
||||||
return "out of range for long integer";
|
|
||||||
}
|
|
||||||
out = out * 2 - digit;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case '2': case '3': case '4': case '5':
|
|
||||||
case '6': case '7': case '8': case '9':
|
|
||||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
|
||||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
|
||||||
case '.': return "invalid character for base 2 integer";
|
|
||||||
|
|
||||||
default: goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
*p_str = str;
|
|
||||||
*p_out = out;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *kyaa__base_8(char **p_str, long *p_out) {
|
|
||||||
char *str = *p_str;
|
|
||||||
long out = *p_out;
|
|
||||||
for (char c; (c = *str) != '\0'; str++) {
|
|
||||||
switch (c) {
|
|
||||||
case '0': case '1': case '2': case '3':
|
|
||||||
case '4': case '5': case '6': case '7': {
|
|
||||||
long digit = (long)(c - '0');
|
|
||||||
if (out < (LONG_MIN + digit) / 8) {
|
|
||||||
return "out of range for long integer";
|
|
||||||
}
|
|
||||||
out = out * 8 - digit;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case '8': case '9':
|
|
||||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
|
||||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
|
||||||
case '.': return "invalid character for base 8 integer";
|
|
||||||
|
|
||||||
default: goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
*p_str = str;
|
|
||||||
*p_out = out;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *kyaa__base_10(char **p_str, long *p_out) {
|
|
||||||
char *str = *p_str;
|
|
||||||
long out = *p_out;
|
|
||||||
for (char c; (c = *str) != '\0'; str++) {
|
|
||||||
switch (c) {
|
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
|
||||||
case '5': case '6': case '7': case '8': case '9': {
|
|
||||||
long digit = (long)(c - '0');
|
|
||||||
if (out < (LONG_MIN + digit) / 10) {
|
|
||||||
return "out of range for long integer";
|
|
||||||
}
|
|
||||||
out = out * 10 - digit;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
|
||||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
|
||||||
case '.': return "invalid character for base 10 integer";
|
|
||||||
|
|
||||||
default: goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
*p_str = str;
|
|
||||||
*p_out = out;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *kyaa__base_16(char **p_str, long *p_out) {
|
|
||||||
char *str = *p_str;
|
|
||||||
long out = *p_out;
|
|
||||||
for (char c; (c = *str) != '\0'; str++) {
|
|
||||||
switch (c) {
|
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
|
||||||
case '5': case '6': case '7': case '8': case '9': {
|
|
||||||
long digit = (long)(c - '0');
|
|
||||||
if (out < (LONG_MIN + digit) / 16) {
|
|
||||||
return "out of range for long integer";
|
|
||||||
}
|
|
||||||
out = out * 16 - digit;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': {
|
|
||||||
long digit = (long)(c - 'A') + 10;
|
|
||||||
if (out < (LONG_MIN + digit) / 16) {
|
|
||||||
return "out of range for long integer";
|
|
||||||
}
|
|
||||||
out = out * 16 - digit;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': {
|
|
||||||
long digit = (long)(c - 'a') + 10;
|
|
||||||
if (out < (LONG_MIN + digit) / 16) {
|
|
||||||
return "out of range for long integer";
|
|
||||||
}
|
|
||||||
out = out * 16 - digit;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case '.': return "invalid character for base 16 integer";
|
|
||||||
|
|
||||||
default: goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
*p_str = str;
|
|
||||||
*p_out = out;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *kyaa_str_to_long(char *str, long *p_out) {
|
|
||||||
/* returns error message or NULL */
|
|
||||||
long out = 0;
|
|
||||||
int base = 10;
|
|
||||||
bool negated = false;
|
|
||||||
|
|
||||||
str = kyaa_skip_spaces(str);
|
|
||||||
|
|
||||||
switch (*str) {
|
|
||||||
case '-': { negated = true; str++; } break;
|
|
||||||
case '+': { str++; } break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (*str) {
|
|
||||||
case '#': { base = 10; str++; } break;
|
|
||||||
case '$': { base = 8; str++; } break;
|
|
||||||
case '%': { base = 2; str++; } break;
|
|
||||||
case '0': { base = -1; str++; } break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (base == -1) {
|
|
||||||
switch (*str) {
|
|
||||||
case '\0': { *p_out = 0; } return NULL;
|
|
||||||
case 'b': { base = 2; str++; } break;
|
|
||||||
case 'h': { base = 16; str++; } break;
|
|
||||||
case 'o': { base = 8; str++; } break;
|
|
||||||
case 'x': { base = 16; str++; } break;
|
|
||||||
default: { base = 8; } break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*str == '\0') return "no number given";
|
|
||||||
|
|
||||||
// NOTE: we actually subtract each digit from the result instead of summing.
|
|
||||||
// this lets us represent LONG_MIN without overflowing.
|
|
||||||
const char *err;
|
|
||||||
switch (base) {
|
|
||||||
case 2: { err = kyaa__base_2( &str, &out); } break;
|
|
||||||
case 8: { err = kyaa__base_8( &str, &out); } break;
|
|
||||||
case 10: { err = kyaa__base_10(&str, &out); } break;
|
|
||||||
case 16: { err = kyaa__base_16(&str, &out); } break;
|
|
||||||
default: return "internal error";
|
|
||||||
}
|
|
||||||
if (err != NULL) return err;
|
|
||||||
|
|
||||||
str = kyaa_skip_spaces(str);
|
|
||||||
if (*str != '\0') return "unexpected character";
|
|
||||||
|
|
||||||
// NOTE: out is negative here; see above comment.
|
|
||||||
// assuming two's complement
|
|
||||||
if (!negated && out == LONG_MIN) return "out of range for long integer";
|
|
||||||
*p_out = negated ? out : -out;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define KYAA_FLAG_LONG(c, name, description) \
|
|
||||||
KYAA_FLAG_ARG(c, name, description) \
|
|
||||||
long kyaa_flag_arg; \
|
|
||||||
const char *err = kyaa_str_to_long(kyaa_etc, &kyaa_flag_arg); \
|
|
||||||
if (err) { \
|
|
||||||
fprintf(stderr, "%s\n", err); \
|
|
||||||
return KYAA_ERROR; \
|
|
||||||
} \
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user