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 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 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); 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); 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); 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); 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 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