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.