From fd56c5124ad7ea8c40b64eaf007e34a08fe77b55 Mon Sep 17 00:00:00 2001 From: Connor Date: Thu, 18 Aug 2016 19:46:44 -0700 Subject: [PATCH 1/9] --- kyaa.h | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ kyaa.md | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 kyaa.h create mode 100644 kyaa.md diff --git a/kyaa.h b/kyaa.h new file mode 100644 index 0000000..a90b4a9 --- /dev/null +++ b/kyaa.h @@ -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); \ + } \ + diff --git a/kyaa.md b/kyaa.md new file mode 100644 index 0000000..b198cd0 --- /dev/null +++ b/kyaa.md @@ -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. \ No newline at end of file From 8c8b5d313af9dd4969c2e2bdebf4ade54fc692a7 Mon Sep 17 00:00:00 2001 From: Connor Date: Thu, 18 Aug 2016 19:50:10 -0700 Subject: [PATCH 2/9] --- kyaa.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/kyaa.md b/kyaa.md index b198cd0..69a91ea 100644 --- a/kyaa.md +++ b/kyaa.md @@ -17,7 +17,7 @@ standard library headers: 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: +iterate over the arguments with `KYAA_LOOP`: ```c int main(int argc, char *argv[]) { @@ -29,8 +29,8 @@ int main(int argc, char *argv[]) { } ``` -use KYAA\_BEGIN and KYAA\_END to begin parsing arguments. -put unrelated code above KYAA\_BEGIN or below KYAA\_END, but not within: +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 { @@ -43,8 +43,8 @@ put unrelated code above KYAA\_BEGIN or below KYAA\_END, but not within: } ``` -use KYAA\_FLAG for defining flags that don't take an argument, -and KYAA\_FLAG\_ARG or KYAA\_FLAG\_LONG for flags that do: +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; @@ -78,8 +78,8 @@ and KYAA\_FLAG\_ARG or KYAA\_FLAG\_LONG for flags that do: 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: +kyaa handles `-h` and `--help` for printing help text. +additional help text may be defined using `KYAA_HELP`: ```c KYAA_LOOP { @@ -93,25 +93,25 @@ additional help text may be defined using KYAA\_HELP: } ``` -kyaa interprets an argument of "-" as a request to enable reading from stdin: -kyaa\_read\_stdin is set to true. +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 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 prints error messages to `stderr`, and help text to `stdout`. -KYAA\_ITER may be redefined to avoid name collisions. +`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. \ No newline at end of file +* 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. \ No newline at end of file From 121dd444292c77a9120ebc2960725b515e793722 Mon Sep 17 00:00:00 2001 From: Connor Date: Thu, 18 Aug 2016 19:55:00 -0700 Subject: [PATCH 3/9] --- kyaa.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/kyaa.md b/kyaa.md index 69a91ea..b176916 100644 --- a/kyaa.md +++ b/kyaa.md @@ -7,15 +7,16 @@ super hacky macro hacks for parsing arguments in C. C99 or greater. standard library headers: -* errno.h -* stdbool.h -* stdio.h -* string.h +* `errno.h` +* `stdbool.h` +* `stdio.h` +* `string.h` +* `stdlib.h` ## tutorial/API -ensure argc and argv are defined. -kyaa doesn't actually care if it's in main() or not. +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`: From 7f5dd0453e23437296123dd9c8e07f285618505e Mon Sep 17 00:00:00 2001 From: Connor Date: Thu, 18 Aug 2016 19:56:33 -0700 Subject: [PATCH 4/9] --- kyaa.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kyaa.md b/kyaa.md index b176916..190eba6 100644 --- a/kyaa.md +++ b/kyaa.md @@ -115,4 +115,4 @@ kyaa prints error messages to `stderr`, and help text to `stdout`. * 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. \ No newline at end of file +* move `KYAA_FLAG_LONG` to `kyaa_extend.h` or something; write similar macros. \ No newline at end of file From 259475bfe6ee5d4b1e56f814952798f878eee5df Mon Sep 17 00:00:00 2001 From: Connor Date: Thu, 18 Aug 2016 20:04:48 -0700 Subject: [PATCH 5/9] --- kyaa.h | 2 -- kyaa.md | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/kyaa.h b/kyaa.h index a90b4a9..ff8afcd 100644 --- a/kyaa.h +++ b/kyaa.h @@ -21,11 +21,9 @@ 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; \ diff --git a/kyaa.md b/kyaa.md index 190eba6..9750ce0 100644 --- a/kyaa.md +++ b/kyaa.md @@ -10,8 +10,8 @@ standard library headers: * `errno.h` * `stdbool.h` * `stdio.h` -* `string.h` * `stdlib.h` +* `string.h` ## tutorial/API @@ -115,4 +115,5 @@ kyaa prints error messages to `stderr`, and help text to `stdout`. * support `--var=42` argument style * rename overlapping things, e.g. `KYAA_FLAG` vs `kyaa_flag`, `KYAA_FLAG_ARG` vs `kyaa_flag_arg`, etc. +* replace `strtol` with something more user-friendly (thereby don't require `stdlib.h` or `errno.h`) * move `KYAA_FLAG_LONG` to `kyaa_extend.h` or something; write similar macros. \ No newline at end of file From 82010d49d8aa1394189512b42ee57bb3e2eab615 Mon Sep 17 00:00:00 2001 From: Connor Date: Fri, 19 Aug 2016 02:11:10 -0700 Subject: [PATCH 6/9] --- kyaa.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kyaa.md b/kyaa.md index 9750ce0..b271f4a 100644 --- a/kyaa.md +++ b/kyaa.md @@ -105,7 +105,7 @@ arguments may be passed in three ways, consider: 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 uses `continue` for handling arguments. kyaa prints error messages to `stderr`, and help text to `stdout`. @@ -114,6 +114,7 @@ kyaa prints error messages to `stderr`, and help text to `stdout`. ## TODO * support `--var=42` argument style -* rename overlapping things, e.g. `KYAA_FLAG` vs `kyaa_flag`, `KYAA_FLAG_ARG` vs `kyaa_flag_arg`, etc. -* replace `strtol` with something more user-friendly (thereby don't require `stdlib.h` or `errno.h`) -* move `KYAA_FLAG_LONG` to `kyaa_extend.h` or something; write similar macros. \ No newline at end of file +* fix overlapping names, e.g. `KYAA_FLAG` vs `kyaa_flag`, `KYAA_FLAG_ARG` vs `kyaa_flag_arg`, etc. +* replace `strtol` with something more user-friendly (removes `stdlib.h` and `errno.h` requirements) +* move `KYAA_FLAG_LONG` to `kyaa_extend.h` or something; write similar macros. +* maybe pass `argc`/`argv` manually? \ No newline at end of file From f361a02852a04dc9e551305602c8e187bc602f95 Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Fri, 31 Mar 2017 06:29:13 +0000 Subject: [PATCH 7/9] . --- kyaa.h | 13 +--- kyaa.md | 48 ++++++++++-- kyaa_extra.h | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+), 18 deletions(-) create mode 100644 kyaa_extra.h diff --git a/kyaa.h b/kyaa.h index ff8afcd..5efdda8 100644 --- a/kyaa.h +++ b/kyaa.h @@ -16,8 +16,8 @@ #endif #define KYAA_SETUP \ - /* dumb sanity checks */ \ - if (argc <= 0 || argv == NULL || argv[0] == NULL || argv[0][0] == '\0') { \ + /* sanity checks */ \ + if (argc <= 0 || argv == NULL || argv[0] == NULL) { \ fprintf(stderr, "You've met with a terrible fate.\n"); \ return KYAA_ERROR; \ } \ @@ -104,15 +104,6 @@ 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) { \ diff --git a/kyaa.md b/kyaa.md index b271f4a..b68ae09 100644 --- a/kyaa.md +++ b/kyaa.md @@ -7,13 +7,11 @@ super hacky macro hacks for parsing arguments in C. C99 or greater. standard library headers: -* `errno.h` * `stdbool.h` * `stdio.h` -* `stdlib.h` * `string.h` -## tutorial/API +## tutorial ensure `argc` and `argv` are defined. kyaa doesn't actually care if it's in `main` or not. @@ -23,7 +21,7 @@ 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. + // KYAA_ITER, kyaa_name, kyaa_read_stdin, and kyaa_flag are exposed here. // [other code goes here] } return 0; @@ -115,6 +113,42 @@ kyaa prints error messages to `stderr`, and help text to `stdout`. * support `--var=42` argument style * fix overlapping names, e.g. `KYAA_FLAG` vs `kyaa_flag`, `KYAA_FLAG_ARG` vs `kyaa_flag_arg`, etc. -* replace `strtol` with something more user-friendly (removes `stdlib.h` and `errno.h` requirements) -* move `KYAA_FLAG_LONG` to `kyaa_extend.h` or something; write similar macros. -* maybe pass `argc`/`argv` manually? \ No newline at end of file +* maybe pass `argc`/`argv` manually? + +## API + +### kyaa.h + +``` +KYAA_OKAY DEFINE int +KYAA_ERROR DEFINE int +KYAA_ITER DEFINE int +KYAA_SETUP MACRO +KYAA_LOOP MACRO +KYAA_BEGIN MACRO +KYAA_END MACRO +KYAA_DESCRIBE MACRO +KYAA_FLAG MACRO +KYAA_FLAG_ARG MACRO +KYAA_HELP MACRO + +kyaa_name char * +kyaa_read_stdin bool +kyaa_flag char +kyaa_keep_parsing bool +kyaa_parse_next bool +kyaa_arg char * +kyaa_no_more bool +kyaa_helping bool +kyaa_any bool +kyaa_flag_arg char * +``` + +### kyaa\_extra.h + +``` +KYAA_FLAG_LONG MACRO + +char *kyaa_skip_spaces(char *str) +const char *kyaa_str_to_long(char *str, long *p_out) +``` diff --git a/kyaa_extra.h b/kyaa_extra.h new file mode 100644 index 0000000..75dba86 --- /dev/null +++ b/kyaa_extra.h @@ -0,0 +1,208 @@ +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; + char c; + while ((c = *str++) != '\0') { + 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; + char c; + while ((c = *str++) != '\0') { + 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; + char c; + while ((c = *str++) != '\0') { + 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; + char c; + while ((c = *str++) != '\0') { + 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"; + + // TODO: comment on how we go towards negatives instead of positives + 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"; + + // 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; \ + } \ + From 2f145eb87997e5ab16897e17a806aa47f6570f01 Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Sun, 2 Apr 2017 02:23:01 +0000 Subject: [PATCH 8/9] . --- kyaa.md | 66 ++++++++++++++++++++++++++++------------------------ kyaa_extra.h | 16 ++++++------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/kyaa.md b/kyaa.md index b68ae09..dc9dba7 100644 --- a/kyaa.md +++ b/kyaa.md @@ -11,6 +11,9 @@ standard library headers: * `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. @@ -119,36 +122,39 @@ kyaa prints error messages to `stderr`, and help text to `stdout`. ### kyaa.h -``` -KYAA_OKAY DEFINE int -KYAA_ERROR DEFINE int -KYAA_ITER DEFINE int -KYAA_SETUP MACRO -KYAA_LOOP MACRO -KYAA_BEGIN MACRO -KYAA_END MACRO -KYAA_DESCRIBE MACRO -KYAA_FLAG MACRO -KYAA_FLAG_ARG MACRO -KYAA_HELP MACRO - -kyaa_name char * -kyaa_read_stdin bool -kyaa_flag char -kyaa_keep_parsing bool -kyaa_parse_next bool -kyaa_arg char * -kyaa_no_more bool -kyaa_helping bool -kyaa_any bool -kyaa_flag_arg char * -``` +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 -``` -KYAA_FLAG_LONG MACRO - -char *kyaa_skip_spaces(char *str) -const char *kyaa_str_to_long(char *str, long *p_out) -``` +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_extra.h b/kyaa_extra.h index 75dba86..0724326 100644 --- a/kyaa_extra.h +++ b/kyaa_extra.h @@ -17,8 +17,7 @@ static char *kyaa_skip_spaces(char *str) { static const char *kyaa__base_2(char **p_str, long *p_out) { char *str = *p_str; long out = *p_out; - char c; - while ((c = *str++) != '\0') { + for (char c; (c = *str) != '\0'; str++) { switch (c) { case '0': case '1': { long digit = (long)(c - '0'); @@ -46,8 +45,7 @@ exit: static const char *kyaa__base_8(char **p_str, long *p_out) { char *str = *p_str; long out = *p_out; - char c; - while ((c = *str++) != '\0') { + 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': { @@ -75,8 +73,7 @@ exit: static const char *kyaa__base_10(char **p_str, long *p_out) { char *str = *p_str; long out = *p_out; - char c; - while ((c = *str++) != '\0') { + 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': { @@ -103,8 +100,7 @@ exit: static const char *kyaa__base_16(char **p_str, long *p_out) { char *str = *p_str; long out = *p_out; - char c; - while ((c = *str++) != '\0') { + 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': { @@ -177,7 +173,8 @@ static const char *kyaa_str_to_long(char *str, long *p_out) { if (*str == '\0') return "no number given"; - // TODO: comment on how we go towards negatives instead of positives + // 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; @@ -191,6 +188,7 @@ static const char *kyaa_str_to_long(char *str, long *p_out) { 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; From 327b8ef28c5d5adba5b6ee14015c70e4670e6db0 Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Sun, 2 Apr 2017 02:24:19 +0000 Subject: [PATCH 9/9] . --- kyaa.h | 4 ++-- kyaa_extra.h | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/kyaa.h b/kyaa.h index 5efdda8..2f825a3 100644 --- a/kyaa.h +++ b/kyaa.h @@ -1,6 +1,6 @@ /* kyaa.h - macro hacks for handling main() arguments - license: public domain or whatever. - documentation is kept separate in kyaa.md + This is free and unencumbered software released into the public domain. + Refer to kyaa.md for documentation. */ #pragma once diff --git a/kyaa_extra.h b/kyaa_extra.h index 0724326..77869f4 100644 --- a/kyaa_extra.h +++ b/kyaa_extra.h @@ -1,3 +1,8 @@ +/* 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') {