parsenum.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. #ifndef PARSENUM_H_
  2. #define PARSENUM_H_
  3. #include <assert.h>
  4. #include <errno.h>
  5. #include <inttypes.h>
  6. #include <math.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. /* Handle compiler warnings about implicit variable conversion in PARSENUM. */
  10. #ifdef __clang__
  11. /* Disable clang warnings. */
  12. #define PARSENUM_PROLOGUE \
  13. _Pragma("clang diagnostic push") \
  14. _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") \
  15. _Pragma("clang diagnostic ignored \"-Wfloat-conversion\"") \
  16. _Pragma("clang diagnostic ignored \"-Wsign-conversion\"") \
  17. _Pragma("clang diagnostic ignored \"-Wshorten-64-to-32\"") \
  18. _Pragma("clang diagnostic ignored \"-Wconversion\"")
  19. /* Enable clang warnings for code outside of PARSENUM. */
  20. #define PARSENUM_EPILOGUE \
  21. _Pragma("clang diagnostic pop")
  22. /* Other compilers don't need any special handling */
  23. #else
  24. #define PARSENUM_PROLOGUE /* NOTHING */
  25. #define PARSENUM_EPILOGUE /* NOTHING */
  26. #endif /* !__clang__ */
  27. /* Print a message before assert(). */
  28. #define ASSERT_FAIL(s) \
  29. ( \
  30. fprintf(stderr, "Assertion failed: " s \
  31. ", function %s, file %s, line %d\n", \
  32. __func__, __FILE__, __LINE__), \
  33. abort() \
  34. )
  35. /**
  36. * PARSENUM(x, s, min, max):
  37. * Parse the string ${s} according to the type of the unsigned integer, signed
  38. * integer, or floating-point number variable ${x}. If the string consists of
  39. * optional whitespace followed by a number (and nothing else) and the numeric
  40. * interpretation of the number is between ${min} and ${max} inclusive, store
  41. * the value into ${x}, set errno to zero, and return zero. Otherwise, return
  42. * nonzero with an unspecified value of ${x} and errno set to EINVAL or ERANGE
  43. * as appropriate.
  44. *
  45. * For floating-point and unsigned integer variables ${x}, this can also be
  46. * invoked as PARSENUM(x, s), in which case the minimum and maximum values are
  47. * set to +/- infinity or the limits of the unsigned integer type.
  48. */
  49. #define PARSENUM2(x, s) \
  50. PARSENUM_EX4(x, s, 0, 0, "PARSENUM")
  51. #define PARSENUM4(x, s, min, max) \
  52. PARSENUM_EX6(x, s, min, max, 0, 0, "PARSENUM")
  53. /* Magic to select which version of PARSENUM to use. */
  54. #define PARSENUM(...) PARSENUM_(PARSENUM_COUNT(__VA_ARGS__))(__VA_ARGS__)
  55. #define PARSENUM_(N) PARSENUM__(N)
  56. #define PARSENUM__(N) PARSENUM ## N
  57. #define PARSENUM_COUNT(...) PARSENUM_COUNT_(__VA_ARGS__, 4, 3, 2, 1)
  58. #define PARSENUM_COUNT_(_1, _2, _3, _4, N, ...) N
  59. /**
  60. * PARSENUM_EX(x, s, min, max, base, trailing):
  61. * Parse the string ${s} according to the type of the unsigned integer or
  62. * signed integer variable ${x}, in the specified ${base}. If the string
  63. * consists of optional whitespace followed by a number (and nothing else) and
  64. * the numeric interpretation of the number is between ${min} and ${max}
  65. * inclusive, store the value into ${x}, set errno to zero, and return zero.
  66. * Otherwise, return nonzero with an unspecified value of ${x} and errno set
  67. * to EINVAL or ERANGE as appropriate. The check for trailing characters (and
  68. * EINVAL if they are found) is disabled if ${trailing} is non-zero.
  69. *
  70. * For an unsigned integer variable ${x}, this can also be invoked as
  71. * PARSENUM_EX(x, s, base), in which case the minimum and maximum values are
  72. * set to the limits of the unsigned integer type.
  73. *
  74. * For a floating-point variable ${x}, the ${base} must be 0.
  75. */
  76. #define PARSENUM_EX4(x, s, base, trailing, _define_name) \
  77. ( \
  78. PARSENUM_PROLOGUE \
  79. errno = 0, \
  80. (((*(x)) = 1, (*(x)) /= 2) > 0) ? \
  81. (((base) == 0) ? \
  82. ((*(x)) = parsenum_float((s), \
  83. (double)-INFINITY, \
  84. (double)INFINITY, (trailing))) : \
  85. (ASSERT_FAIL(_define_name " applied to" \
  86. " float with base != 0"), 1)) : \
  87. (((*(x)) = -1) > 0) ? \
  88. ((*(x)) = parsenum_unsigned((s), 0, (*(x)), \
  89. (*(x)), (base), (trailing))) : \
  90. (ASSERT_FAIL(_define_name " applied to signed" \
  91. " integer without specified bounds"), 1), \
  92. errno != 0 \
  93. PARSENUM_EPILOGUE \
  94. )
  95. #define PARSENUM_EX6(x, s, min, max, base, trailing, _define_name) \
  96. ( \
  97. PARSENUM_PROLOGUE \
  98. errno = 0, \
  99. (((*(x)) = 1, (*(x)) /= 2) > 0) ? \
  100. (((base) == 0) ? \
  101. ((*(x)) = parsenum_float((s), \
  102. (double)(min), (double)(max), \
  103. (trailing))) : \
  104. (ASSERT_FAIL(_define_name " applied to" \
  105. " float with base != 0"), 1)) : \
  106. (((*(x)) = -1) <= 0) ? \
  107. ((*(x)) = parsenum_signed((s), \
  108. ((*(x)) <= 0) ? (min) : 0, \
  109. ((*(x)) <= 0) ? (max) : 0, (base), \
  110. (trailing))) : \
  111. (((*(x)) = parsenum_unsigned((s), \
  112. ((min) <= 0) ? 0 : (min), \
  113. (uintmax_t)(max), (*(x)), (base), \
  114. (trailing))), \
  115. (((max) <= INTMAX_MAX) ? \
  116. (((intmax_t)(max) < 0) && (errno == 0)) ? \
  117. (errno = ERANGE) : \
  118. 0 : 0)), \
  119. errno != 0 \
  120. PARSENUM_EPILOGUE \
  121. )
  122. /* Magic to select which version of PARSENUM_EX to use. */
  123. #define PARSENUM_EX(...) \
  124. PARSENUM_EX_(PARSENUM_EX_COUNT(__VA_ARGS__))(__VA_ARGS__, "PARSENUM_EX")
  125. #define PARSENUM_EX_(N) PARSENUM_EX__(N)
  126. #define PARSENUM_EX__(N) PARSENUM_EX ## N
  127. #define PARSENUM_EX_COUNT(...) \
  128. PARSENUM_EX_COUNT_(__VA_ARGS__, 6, 5, 4, 3, 2, 1)
  129. #define PARSENUM_EX_COUNT_(_1, _2, _3, _4, _5, _6, N, ...) N
  130. /* Functions for performing the parsing and parameter checking. */
  131. static inline double
  132. parsenum_float(const char * s, double min, double max, int trailing)
  133. {
  134. char * eptr;
  135. double val;
  136. /* Sanity check. */
  137. assert(s != NULL);
  138. val = strtod(s, &eptr);
  139. if (eptr == s || (!trailing && (*eptr != '\0')))
  140. errno = EINVAL;
  141. else if ((val < min) || (val > max))
  142. errno = ERANGE;
  143. return (val);
  144. }
  145. static inline intmax_t
  146. parsenum_signed(const char * s, intmax_t min, intmax_t max, int base,
  147. int trailing)
  148. {
  149. char * eptr;
  150. intmax_t val;
  151. /* Sanity check. */
  152. assert(s != NULL);
  153. val = strtoimax(s, &eptr, base);
  154. if (eptr == s || (!trailing && (*eptr != '\0')))
  155. errno = EINVAL;
  156. else if ((val < min) || (val > max)) {
  157. errno = ERANGE;
  158. val = 0;
  159. }
  160. return (val);
  161. }
  162. static inline uintmax_t
  163. parsenum_unsigned(const char * s, uintmax_t min, uintmax_t max,
  164. uintmax_t typemax, int base, int trailing)
  165. {
  166. char * eptr;
  167. uintmax_t val;
  168. /* Sanity check. */
  169. assert(s != NULL);
  170. val = strtoumax(s, &eptr, base);
  171. if (eptr == s || (!trailing && (*eptr != '\0')))
  172. errno = EINVAL;
  173. else if ((val < min) || (val > max) || (val > typemax))
  174. errno = ERANGE;
  175. return (val);
  176. }
  177. #endif /* !PARSENUM_H_ */