aboutsummaryrefslogtreecommitdiff
path: root/libbb/xatol.c
blob: 82250a7bb5f1796633d1829c63945ca8a01a9ca5 (plain)
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
198
199
200
201
202
203
204
205
206
207
208
209
210
/* vi: set sw=4 ts=4: */
/*
 * ascii-to-numbers implementations for busybox
 *
 * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
 *
 * Licensed under GPLv2, see file LICENSE in this tarball for details.
 */

#include "libbb.h"

unsigned long long xatoull(const char *numstr)
{
	unsigned long long r;
	int old_errno;
	char *e;
	if ((*numstr == '-') || (isspace)(*numstr))
		bb_error_msg_and_die("invalid number '%s'", numstr);
	old_errno = errno;
	errno = 0;
	r = strtoull(numstr, &e, 10);
	if (errno || (numstr == e) || *e)
		/* Error / no digits / illegal trailing chars */
		bb_error_msg_and_die("invalid number '%s'", numstr);
	/* No error. So restore errno. */
	errno = old_errno;
	return r;
}

unsigned long xstrtoul_range_sfx(const char *numstr, int base,
		unsigned long lower,
		unsigned long upper,
		const struct suffix_mult *suffixes)
{
	unsigned long r;
	int old_errno;
	char *e;

	/* Disallow '-' and any leading whitespace.  Speed isn't critical here
	 * since we're parsing commandline args.  So make sure we get the
	 * actual isspace function rather than a lnumstrer macro implementaion. */
	if ((*numstr == '-') || (isspace)(*numstr))
		goto inval;

	/* Since this is a lib function, we're not allowed to reset errno to 0.
	 * Doing so could break an app that is deferring checking of errno.
	 * So, save the old value so that we can restore it if successful. */
	old_errno = errno;
	errno = 0;
	r = strtoul(numstr, &e, base);
	/* Do the initial validity check.  Note: The standards do not
	 * guarantee that errno is set if no digits were found.  So we
	 * must test for this explicitly. */
	if (errno || (numstr == e))
		goto inval; /* error / no digits / illegal trailing chars */

	errno = old_errno;	/* Ok.  So restore errno. */

	/* Do optional suffix parsing.  Allow 'empty' suffix tables.
	 * Note that we also allow nul suffixes with associated multipliers,
	 * to allow for scaling of the numstr by some default multiplier. */
	if (suffixes) {
		while (suffixes->suffix) {
			if (strcmp(suffixes->suffix, e) == 0) {
				if (ULONG_MAX / suffixes->mult < r)
					goto range; /* overflow! */
				++e;
				r *= suffixes->mult;
				break;
			}
			++suffixes;
		}
	}

	/* Note: trailing space is an error.
	   It would be easy enough to allow though if desired. */
	if (*e)
		goto inval;
	/* Finally, check for range limits. */
	if (r >= lower && r <= upper)
		return r;
 range:
	bb_error_msg_and_die("number %s is not in %lu..%lu range",
		numstr, lower, upper);
 inval:
	bb_error_msg_and_die("invalid number '%s'", numstr);
}

unsigned long xstrtoul_range(const char *numstr, int base,
		unsigned long lower,
		unsigned long upper)
{
	return xstrtoul_range_sfx(numstr, base, lower, upper, NULL);
}

unsigned long xstrtoul(const char *numstr, int base)
{
	return xstrtoul_range_sfx(numstr, base, 0, ULONG_MAX, NULL);
}

unsigned long xatoul_range_sfx(const char *numstr,
		unsigned long lower,
		unsigned long upper,
		const struct suffix_mult *suffixes)
{
	return xstrtoul_range_sfx(numstr, 10, lower, upper, suffixes);
}

unsigned long xatoul_sfx(const char *numstr,
		const struct suffix_mult *suffixes)
{
	return xstrtoul_range_sfx(numstr, 10, 0, ULONG_MAX, suffixes);
}

unsigned long xatoul_range(const char *numstr,
		unsigned long lower,
		unsigned long upper)
{
	return xstrtol_range_sfx(numstr, 10, lower, upper, NULL);
}

unsigned long xatoul(const char *numstr)
{
	return xatoul_sfx(numstr, NULL);
}

/* Signed ones */

long xstrtol_range_sfx(const char *numstr, int base,
		long lower,
		long upper,
		const struct suffix_mult *suffixes)
{
	unsigned long u = LONG_MAX;
	long r;
	const char *p = numstr;

	if ((p[0] == '-') && (p[1] != '+')) {
		++p;
		++u;	/* two's complement */
	}

	r = xstrtoul_range_sfx(p, base, 0, u, suffixes);

	if (*numstr == '-') {
		r = -r;
	}

	if (r < lower || r > upper) {
		bb_error_msg_and_die("number %s is not in %ld..%ld range",
				numstr, lower, upper);
	}

	return r;
}

long xstrtol_range(const char *numstr, int base, long lower, long upper)
{
	return xstrtol_range_sfx(numstr, base, lower, upper, NULL);
}

long xatol_range_sfx(const char *numstr,
		long lower,
		long upper,
		const struct suffix_mult *suffixes)
{
	return xstrtol_range_sfx(numstr, 10, lower, upper, suffixes);
}

long xatol_range(const char *numstr, long lower, long upper)
{
	return xstrtol_range_sfx(numstr, 10, lower, upper, NULL);
}

long xatol_sfx(const char *numstr, const struct suffix_mult *suffixes)
{
	return xstrtol_range_sfx(numstr, 10, LONG_MIN, LONG_MAX, suffixes);
}

long xatol(const char *numstr)
{
	return xstrtol_range_sfx(numstr, 10, LONG_MIN, LONG_MAX, NULL);
}

/* Others */

unsigned xatou(const char *numstr)
{
	return xatoul_range(numstr, 0, UINT_MAX);
}

int xatoi(const char *numstr)
{
	return xatol_range(numstr, INT_MIN, INT_MAX);
}

int xatoi_u(const char *numstr)
{
	return xatoul_range(numstr, 0, INT_MAX);
}

uint32_t xatou32(const char *numstr)
{
	return xatoul_range(numstr, 0, 0xffffffff);
}

uint16_t xatou16(const char *numstr)
{
	return xatoul_range(numstr, 0, 0xffff);
}