diff --git a/kyaa.h b/kyaa.h index 5b58b9b..5efdda8 100644 --- a/kyaa.h +++ b/kyaa.h @@ -16,7 +16,7 @@ #endif #define KYAA_SETUP \ - /* dumb sanity checks */ \ + /* 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_extra.h b/kyaa_extra.h new file mode 100644 index 0000000..420dd67 --- /dev/null +++ b/kyaa_extra.h @@ -0,0 +1,204 @@ +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"; + + // 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; \ + } \ + diff --git a/resynth.c b/resynth.c index 70e6fa9..c454596 100644 --- a/resynth.c +++ b/resynth.c @@ -11,7 +11,7 @@ If not, visit to obtain one. */ -#include // for argument parsing with strtol (kyaa.h) +#include #include // for log (neglog_cauchy) used in pixel diff. calculations #include // we're targetting C11 anyway, may as well use it #include // for uint8_t for pixel data @@ -47,6 +47,7 @@ // for command-line argument parsing #include "kyaa.h" +#include "kyaa_extra.h" // convenience macros. hopefully these names don't interfere // with any defined in the standard library headers on any system.