File: parsenum.h

package info (click to toggle)
python-scrypt 0.9.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 832 kB
  • sloc: ansic: 6,290; python: 733; sh: 99; makefile: 5
file content (197 lines) | stat: -rw-r--r-- 6,494 bytes parent folder | download | duplicates (4)
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_ */