super hacky macro hacks for parsing arguments in C
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

kyaa_extra.h 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /* kyaa_extra.h - extensions to kyaa for parsing arguments.
  2. This is free and unencumbered software released into the public domain.
  3. For more information, please refer to <http://unlicense.org>
  4. */
  5. #ifndef KYAA_EXTRA
  6. #define KYAA_EXTRA
  7. static const char *kyaa_skip_spaces(const char *str) {
  8. /* iterates str to first non-space character according to the C locale */
  9. while (*str != '\0') {
  10. switch (*str) {
  11. case ' ':
  12. case '\f':
  13. case '\n':
  14. case '\r':
  15. case '\t':
  16. case '\v': { str++; } break;
  17. default: return str;
  18. }
  19. }
  20. return str;
  21. }
  22. static const char *kyaa__base_2(const char **p_str, long *p_out) {
  23. const char *str = *p_str;
  24. long out = *p_out;
  25. for (char c; (c = *str) != '\0'; str++) {
  26. switch (c) {
  27. case '0': case '1': {
  28. long digit = (long)(c - '0');
  29. if (out < (LONG_MIN + digit) / 2) {
  30. return "out of range for long integer";
  31. }
  32. out = out * 2 - digit;
  33. } break;
  34. case '2': case '3': case '4': case '5':
  35. case '6': case '7': case '8': case '9':
  36. case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  37. case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  38. case '.': return "invalid character for base 2 integer";
  39. default: goto exit;
  40. }
  41. }
  42. exit:
  43. *p_str = str;
  44. *p_out = out;
  45. return NULL;
  46. }
  47. static const char *kyaa__base_8(const char **p_str, long *p_out) {
  48. const char *str = *p_str;
  49. long out = *p_out;
  50. for (char c; (c = *str) != '\0'; str++) {
  51. switch (c) {
  52. case '0': case '1': case '2': case '3':
  53. case '4': case '5': case '6': case '7': {
  54. long digit = (long)(c - '0');
  55. if (out < (LONG_MIN + digit) / 8) {
  56. return "out of range for long integer";
  57. }
  58. out = out * 8 - digit;
  59. } break;
  60. case '8': case '9':
  61. case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  62. case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  63. case '.': return "invalid character for base 8 integer";
  64. default: goto exit;
  65. }
  66. }
  67. exit:
  68. *p_str = str;
  69. *p_out = out;
  70. return NULL;
  71. }
  72. static const char *kyaa__base_10(const char **p_str, long *p_out) {
  73. const char *str = *p_str;
  74. long out = *p_out;
  75. for (char c; (c = *str) != '\0'; str++) {
  76. switch (c) {
  77. case '0': case '1': case '2': case '3': case '4':
  78. case '5': case '6': case '7': case '8': case '9': {
  79. long digit = (long)(c - '0');
  80. if (out < (LONG_MIN + digit) / 10) {
  81. return "out of range for long integer";
  82. }
  83. out = out * 10 - digit;
  84. } break;
  85. case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  86. case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  87. case '.': return "invalid character for base 10 integer";
  88. default: goto exit;
  89. }
  90. }
  91. exit:
  92. *p_str = str;
  93. *p_out = out;
  94. return NULL;
  95. }
  96. static const char *kyaa__base_16(const char **p_str, long *p_out) {
  97. const char *str = *p_str;
  98. long out = *p_out;
  99. for (char c; (c = *str) != '\0'; str++) {
  100. switch (c) {
  101. case '0': case '1': case '2': case '3': case '4':
  102. case '5': case '6': case '7': case '8': case '9': {
  103. long digit = (long)(c - '0');
  104. if (out < (LONG_MIN + digit) / 16) {
  105. return "out of range for long integer";
  106. }
  107. out = out * 16 - digit;
  108. } break;
  109. case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': {
  110. long digit = (long)(c - 'A') + 10;
  111. if (out < (LONG_MIN + digit) / 16) {
  112. return "out of range for long integer";
  113. }
  114. out = out * 16 - digit;
  115. } break;
  116. case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': {
  117. long digit = (long)(c - 'a') + 10;
  118. if (out < (LONG_MIN + digit) / 16) {
  119. return "out of range for long integer";
  120. }
  121. out = out * 16 - digit;
  122. } break;
  123. case '.': return "invalid character for base 16 integer";
  124. default: goto exit;
  125. }
  126. }
  127. exit:
  128. *p_str = str;
  129. *p_out = out;
  130. return NULL;
  131. }
  132. static const char *kyaa_str_to_long(const char *str, long *p_out) {
  133. /* returns error message or NULL */
  134. long out = 0;
  135. int base = 10;
  136. bool negated = false;
  137. str = kyaa_skip_spaces(str);
  138. switch (*str) {
  139. case '-': { negated = true; str++; } break;
  140. case '+': { str++; } break;
  141. default: break;
  142. }
  143. switch (*str) {
  144. case '#': { base = 10; str++; } break;
  145. case '$': { base = 8; str++; } break;
  146. case '%': { base = 2; str++; } break;
  147. case '0': { base = -1; str++; } break;
  148. default: break;
  149. }
  150. if (base == -1) {
  151. switch (*str) {
  152. case '\0': { *p_out = 0; } return NULL;
  153. case 'b': { base = 2; str++; } break;
  154. case 'h': { base = 16; str++; } break;
  155. case 'o': { base = 8; str++; } break;
  156. case 'x': { base = 16; str++; } break;
  157. default: { base = 8; } break;
  158. }
  159. }
  160. if (*str == '\0') return "no number given";
  161. // NOTE: we actually subtract each digit from the result instead of summing.
  162. // this lets us represent LONG_MIN without overflowing.
  163. const char *err;
  164. switch (base) {
  165. case 2: { err = kyaa__base_2( &str, &out); } break;
  166. case 8: { err = kyaa__base_8( &str, &out); } break;
  167. case 10: { err = kyaa__base_10(&str, &out); } break;
  168. case 16: { err = kyaa__base_16(&str, &out); } break;
  169. default: return "internal error";
  170. }
  171. if (err != NULL) return err;
  172. str = kyaa_skip_spaces(str);
  173. if (*str != '\0') return "unexpected character";
  174. // NOTE: out is negative here; see above comment.
  175. // assuming two's complement
  176. if (!negated && out == LONG_MIN) return "out of range for long integer";
  177. *p_out = negated ? out : -out;
  178. return NULL;
  179. }
  180. #define KYAA_FLAG_LONG(c, name, description) \
  181. KYAA_FLAG_ARG(c, name, description) \
  182. long kyaa_long_value; \
  183. const char *err = kyaa_str_to_long(kyaa_etc, &kyaa_long_value); \
  184. if (err) { \
  185. KYAA_ERR("%s\n", err); \
  186. return KYAA_FAIL; \
  187. } \
  188. #endif /* KYAA_EXTRA */