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