diff --git a/README.md b/README.md index dd3d912..31f6c44 100644 --- a/README.md +++ b/README.md @@ -129,11 +129,7 @@ the batch script exploits [a UAC bypass found by Tyranid.](https://tyranidslair. ### kyaa -really *really* hacky argument-parsing macros for C programs. - -contains [some ad-hoc documentation.](/kyaa/kyaa.md) - -This is free and unencumbered software released into the public domain. +[*moved to its own repo*](/git/notwa/kyaa) ### lsca diff --git a/kyaa/kyaa.h b/kyaa/kyaa.h deleted file mode 100644 index 2f825a3..0000000 --- a/kyaa/kyaa.h +++ /dev/null @@ -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); \ - } \ - diff --git a/kyaa/kyaa.md b/kyaa/kyaa.md deleted file mode 100644 index dc9dba7..0000000 --- a/kyaa/kyaa.md +++ /dev/null @@ -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 diff --git a/kyaa/kyaa_extra.h b/kyaa/kyaa_extra.h deleted file mode 100644 index 77869f4..0000000 --- a/kyaa/kyaa_extra.h +++ /dev/null @@ -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; \ - } \ -