1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 John Baldwin <jhb@FreeBSD.org>
5 * Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <atf-c.h>
30 #include <errno.h>
31 #include <libutil.h>
32 #include <stdint.h>
33
34 static void
require_success(const char * str,int64_t exp_val)35 require_success(const char *str, int64_t exp_val)
36 {
37 int64_t val;
38
39 ATF_REQUIRE_MSG(expand_number(str, &val) == 0,
40 "Failed to parse '%s': %m", str);
41 ATF_REQUIRE_MSG(val == exp_val,
42 "String '%s' parsed as %jd instead of expected %jd", str,
43 (intmax_t)val, (intmax_t)exp_val);
44 }
45
46 static void
require_error(const char * str,int exp_errno)47 require_error(const char *str, int exp_errno)
48 {
49 int64_t val;
50
51 ATF_REQUIRE_MSG(expand_number(str, &val) == -1,
52 "String '%s' parsed as %jd instead of error", str, (intmax_t)val);
53 ATF_REQUIRE_MSG(errno == exp_errno,
54 "String '%s' failed with %d instead of expected %d", str, errno,
55 exp_errno);
56 }
57
58 ATF_TC_WITHOUT_HEAD(expand_number__ok);
ATF_TC_BODY(expand_number__ok,tp)59 ATF_TC_BODY(expand_number__ok, tp)
60 {
61 /* Bare numbers. */
62 require_success("-0", 0);
63 require_success(" 0", 0);
64 require_success("+0", 0);
65 require_success("-1", -1);
66 require_success(" 1", 1);
67 require_success("+1", 1);
68 require_success("-10", -10);
69 require_success(" 10", 10);
70 require_success("+10", 10);
71
72 /* Uppercase suffixes. */
73 require_success("1B", 1);
74 require_success("1K", 1LL << 10);
75 require_success("1M", 1LL << 20);
76 require_success("1G", 1LL << 30);
77 require_success("1T", 1LL << 40);
78 require_success("1P", 1LL << 50);
79 require_success("1E", 1LL << 60);
80
81 /* Lowercase suffixes. */
82 require_success("2b", 2);
83 require_success("2k", 2LL << 10);
84 require_success("2m", 2LL << 20);
85 require_success("2g", 2LL << 30);
86 require_success("2t", 2LL << 40);
87 require_success("2p", 2LL << 50);
88 require_success("2e", 2LL << 60);
89
90 /* Suffixes with a trailing 'b'. */
91 require_success("3KB", 3LL << 10);
92 require_success("3MB", 3LL << 20);
93 require_success("3GB", 3LL << 30);
94 require_success("3TB", 3LL << 40);
95 require_success("3PB", 3LL << 50);
96 require_success("3EB", 3LL << 60);
97
98 /* Negative numbers. */
99 require_success("-1", -1);
100 require_success("-10", -10);
101 require_success("-1B", -1);
102 require_success("-1K", -(1LL << 10));
103 require_success("-1M", -(1LL << 20));
104 require_success("-1G", -(1LL << 30));
105 require_success("-1T", -(1LL << 40));
106 require_success("-1P", -(1LL << 50));
107 require_success("-1E", -(1LL << 60));
108 require_success("-2b", -2);
109 require_success("-2k", -(2LL << 10));
110 require_success("-2m", -(2LL << 20));
111 require_success("-2g", -(2LL << 30));
112 require_success("-2t", -(2LL << 40));
113 require_success("-2p", -(2LL << 50));
114 require_success("-2e", -(2LL << 60));
115 require_success("-3KB", -(3LL << 10));
116 require_success("-3MB", -(3LL << 20));
117 require_success("-3GB", -(3LL << 30));
118 require_success("-3TB", -(3LL << 40));
119 require_success("-3PB", -(3LL << 50));
120 require_success("-3EB", -(3LL << 60));
121
122 /* Maximum values. */
123 require_success("7E", 7LL << 60);
124 require_success("8191P", 8191LL << 50);
125 require_success("8388607T", 8388607LL << 40);
126 require_success("8589934591G", 8589934591LL << 30);
127 require_success("8796093022207M", 8796093022207LL << 20);
128 require_success("9007199254740991K", 9007199254740991LL << 10);
129 require_success("9223372036854775807", INT64_MAX);
130
131 /* Minimum values. */
132 require_success("-7E", -(7LL << 60));
133 require_success("-8191P", -(8191LL << 50));
134 require_success("-8388607T", -(8388607LL << 40));
135 require_success("-8589934591G", -(8589934591LL << 30));
136 require_success("-8796093022207M", -(8796093022207LL << 20));
137 require_success("-9007199254740991K", -(9007199254740991LL << 10));
138 require_success("-9223372036854775808", INT64_MIN);
139 }
140
141 ATF_TC_WITHOUT_HEAD(expand_number__bad);
ATF_TC_BODY(expand_number__bad,tp)142 ATF_TC_BODY(expand_number__bad, tp)
143 {
144 /* No digits. */
145 require_error("", EINVAL);
146 require_error("b", EINVAL);
147 require_error("k", EINVAL);
148 require_error("m", EINVAL);
149 require_error("g", EINVAL);
150 require_error("t", EINVAL);
151 require_error("p", EINVAL);
152 require_error("e", EINVAL);
153 require_error("-", EINVAL);
154 require_error("-b", EINVAL);
155 require_error("-k", EINVAL);
156 require_error("-m", EINVAL);
157 require_error("-g", EINVAL);
158 require_error("-t", EINVAL);
159 require_error("-p", EINVAL);
160 require_error("-e", EINVAL);
161
162 require_error("not_a_number", EINVAL);
163
164 /* Invalid suffixes. */
165 require_error("1a", EINVAL);
166 require_error("1c", EINVAL);
167 require_error("1d", EINVAL);
168 require_error("1f", EINVAL);
169 require_error("1h", EINVAL);
170 require_error("1i", EINVAL);
171 require_error("1j", EINVAL);
172 require_error("1l", EINVAL);
173 require_error("1n", EINVAL);
174 require_error("1o", EINVAL);
175 require_error("1q", EINVAL);
176 require_error("1r", EINVAL);
177 require_error("1s", EINVAL);
178 require_error("1u", EINVAL);
179 require_error("1v", EINVAL);
180 require_error("1w", EINVAL);
181 require_error("1x", EINVAL);
182 require_error("1y", EINVAL);
183 require_error("1z", EINVAL);
184
185 /* Trailing garbage. */
186 require_error("1K foo", EINVAL);
187 require_error("1Mfoo", EINVAL);
188
189 /* Overflow. */
190 require_error("8E", ERANGE);
191 require_error("8192P", ERANGE);
192 require_error("8388608T", ERANGE);
193 require_error("8589934592G", ERANGE);
194 require_error("8796093022208M", ERANGE);
195 require_error("9007199254740992K", ERANGE);
196 require_error("9223372036854775808", ERANGE);
197
198 /* Multiple signs */
199 require_error("--1", EINVAL);
200 require_error("-+1", EINVAL);
201 require_error("+-1", EINVAL);
202 require_error("++1", EINVAL);
203
204 /* Whitespace after the sign */
205 require_error(" - 1", EINVAL);
206 require_error(" + 1", EINVAL);
207 }
208
209 ATF_TC_WITHOUT_HEAD(expand_unsigned);
ATF_TC_BODY(expand_unsigned,tp)210 ATF_TC_BODY(expand_unsigned, tp)
211 {
212 static struct tc {
213 const char *str;
214 uint64_t num;
215 int error;
216 } tcs[] = {
217 { "0", 0, 0 },
218 { "+0", 0, 0 },
219 { "-0", 0, 0 },
220 { "1", 1, 0 },
221 { "+1", 1, 0 },
222 { "-1", 0, ERANGE },
223 { "18446744073709551615", UINT64_MAX, 0 },
224 { "+18446744073709551615", UINT64_MAX, 0 },
225 { "-18446744073709551615", 0, ERANGE },
226 { 0 },
227 };
228 struct tc *tc;
229 uint64_t num;
230 int error, ret;
231
232 for (tc = tcs; tc->str != NULL; tc++) {
233 ret = expand_number(tc->str, &num);
234 error = errno;
235 if (tc->error == 0) {
236 ATF_REQUIRE_EQ_MSG(0, ret,
237 "%s ret = %d", tc->str, ret);
238 ATF_REQUIRE_EQ_MSG(tc->num, num,
239 "%s num = %ju", tc->str, (uintmax_t)num);
240 } else {
241 ATF_REQUIRE_EQ_MSG(-1, ret,
242 "%s ret = %d", tc->str, ret);
243 ATF_REQUIRE_EQ_MSG(tc->error, error,
244 "%s errno = %d", tc->str, error);
245 }
246 }
247 }
248
249 ATF_TC_WITHOUT_HEAD(expand_generic);
ATF_TC_BODY(expand_generic,tp)250 ATF_TC_BODY(expand_generic, tp)
251 {
252 uint64_t uint64;
253 int64_t int64;
254 #ifdef __LP64__
255 size_t size;
256 #endif
257 off_t off;
258
259 ATF_REQUIRE_EQ(0, expand_number("18446744073709551615", &uint64));
260 ATF_REQUIRE_EQ(UINT64_MAX, uint64);
261 ATF_REQUIRE_EQ(-1, expand_number("-1", &uint64));
262 ATF_REQUIRE_EQ(ERANGE, errno);
263
264 ATF_REQUIRE_EQ(0, expand_number("9223372036854775807", &int64));
265 ATF_REQUIRE_EQ(INT64_MAX, int64);
266 ATF_REQUIRE_EQ(-1, expand_number("9223372036854775808", &int64));
267 ATF_REQUIRE_EQ(ERANGE, errno);
268 ATF_REQUIRE_EQ(0, expand_number("-9223372036854775808", &int64));
269 ATF_REQUIRE_EQ(INT64_MIN, int64);
270
271 #ifdef __LP64__
272 ATF_REQUIRE_EQ(0, expand_number("18446744073709551615", &size));
273 ATF_REQUIRE_EQ(UINT64_MAX, size);
274 ATF_REQUIRE_EQ(-1, expand_number("-1", &size));
275 ATF_REQUIRE_EQ(ERANGE, errno);
276 #endif
277
278 ATF_REQUIRE_EQ(0, expand_number("9223372036854775807", &off));
279 ATF_REQUIRE_EQ(INT64_MAX, off);
280 ATF_REQUIRE_EQ(-1, expand_number("9223372036854775808", &off));
281 ATF_REQUIRE_EQ(ERANGE, errno);
282 ATF_REQUIRE_EQ(0, expand_number("-9223372036854775808", &off));
283 ATF_REQUIRE_EQ(INT64_MIN, off);
284 }
285
ATF_TP_ADD_TCS(tp)286 ATF_TP_ADD_TCS(tp)
287 {
288 ATF_TP_ADD_TC(tp, expand_number__ok);
289 ATF_TP_ADD_TC(tp, expand_number__bad);
290 ATF_TP_ADD_TC(tp, expand_unsigned);
291 ATF_TP_ADD_TC(tp, expand_generic);
292
293 return (atf_no_error());
294 }
295