num.c (8193B)
1 /* Note: strtonum taken from OpenBSD: */ 2 /* $OpenBSD: strtonum.c,v 1.8 2015/09/13 08:31:48 guenther Exp $ */ 3 /* 4 * Copyright (c) 2004 Ted Unangst and Todd Miller 5 * All rights reserved. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* Note: strtoll taken from OpenBSD, modified to only use base 10: */ 21 /* $OpenBSD: strtoll.c,v 1.10 2017/07/06 16:23:11 millert Exp $ */ 22 /* 23 * Copyright (c) 1992 The Regents of the University of California. 24 * All rights reserved. 25 * 26 * Redistribution and use in source and binary forms, with or without 27 * modification, are permitted provided that the following conditions 28 * are met: 29 * 1. Redistributions of source code must retain the above copyright 30 * notice, this list of conditions and the following disclaimer. 31 * 2. Redistributions in binary form must reproduce the above copyright 32 * notice, this list of conditions and the following disclaimer in the 33 * documentation and/or other materials provided with the distribution. 34 * 3. Neither the name of the University nor the names of its contributors 35 * may be used to endorse or promote products derived from this software 36 * without specific prior written permission. 37 * 38 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 39 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 41 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 42 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 43 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 44 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 45 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 46 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 47 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 * SUCH DAMAGE. 49 */ 50 51 #include <errno.h> 52 #include <limits.h> 53 #include <stdlib.h> 54 #include <sys/types.h> 55 #include <ctype.h> 56 57 /* FIXME: also use locale-independent version of isspace and isdigit? */ 58 59 /* 60 * Convert a string to a long long. 61 * 62 * Ignores `locale' stuff. Assumes that the upper and lower case 63 * alphabets and digits are each contiguous. 64 */ 65 static long long 66 ltk_strtoll(const char *nptr, char **endptr) { 67 const char *s; 68 long long acc, cutoff; 69 int c; 70 int neg, any, cutlim; 71 const int base = 10; 72 73 /* Skip white space and pick up leading +/- sign if any. */ 74 s = nptr; 75 do { 76 c = (unsigned char) *s++; 77 } while (isspace(c)); 78 if (c == '-') { 79 neg = 1; 80 c = *s++; 81 } else { 82 neg = 0; 83 if (c == '+') 84 c = *s++; 85 } 86 87 /* 88 * Compute the cutoff value between legal numbers and illegal 89 * numbers. That is the largest legal value, divided by the 90 * base. An input number that is greater than this value, if 91 * followed by a legal input character, is too big. One that 92 * is equal to this value may be valid or not; the limit 93 * between valid and invalid numbers is then based on the last 94 * digit. For instance, if the range for long longs is 95 * [-9223372036854775808..9223372036854775807] and the input base 96 * is 10, cutoff will be set to 922337203685477580 and cutlim to 97 * either 7 (neg==0) or 8 (neg==1), meaning that if we have 98 * accumulated a value > 922337203685477580, or equal but the 99 * next digit is > 7 (or 8), the number is too big, and we will 100 * return a range error. 101 * 102 * Set any if any `digits' consumed; make it negative to indicate 103 * overflow. 104 */ 105 cutoff = neg ? LLONG_MIN : LLONG_MAX; 106 cutlim = cutoff % base; 107 cutoff /= base; 108 if (neg) { 109 if (cutlim > 0) { 110 cutlim -= base; 111 cutoff += 1; 112 } 113 cutlim = -cutlim; 114 } 115 for (acc = 0, any = 0;; c = (unsigned char) *s++) { 116 if (isdigit(c)) 117 c -= '0'; 118 else 119 break; 120 if (c >= base) 121 break; 122 if (any < 0) 123 continue; 124 if (neg) { 125 if (acc < cutoff || (acc == cutoff && c > cutlim)) { 126 any = -1; 127 acc = LLONG_MIN; 128 errno = ERANGE; 129 } else { 130 any = 1; 131 acc *= base; 132 acc -= c; 133 } 134 } else { 135 if (acc > cutoff || (acc == cutoff && c > cutlim)) { 136 any = -1; 137 acc = LLONG_MAX; 138 errno = ERANGE; 139 } else { 140 any = 1; 141 acc *= base; 142 acc += c; 143 } 144 } 145 } 146 if (endptr != 0) 147 *endptr = (char *) (any ? s - 1 : nptr); 148 return (acc); 149 } 150 151 #define INVALID 1 152 #define TOOSMALL 2 153 #define TOOLARGE 3 154 155 long long 156 ltk_strtonum( 157 const char *numstr, long long minval, 158 long long maxval, const char **errstrp) { 159 long long ll = 0; 160 int error = 0; 161 char *ep; 162 struct errval { 163 const char *errstr; 164 int err; 165 } ev[4] = { 166 { NULL, 0 }, 167 { "invalid", EINVAL }, 168 { "too small", ERANGE }, 169 { "too large", ERANGE }, 170 }; 171 172 ev[0].err = errno; 173 errno = 0; 174 if (minval > maxval) { 175 error = INVALID; 176 } else { 177 ll = ltk_strtoll(numstr, &ep); 178 if (numstr == ep || *ep != '\0') 179 error = INVALID; 180 else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) 181 error = TOOSMALL; 182 else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) 183 error = TOOLARGE; 184 } 185 if (errstrp != NULL) 186 *errstrp = ev[error].errstr; 187 errno = ev[error].err; 188 if (error) 189 ll = 0; 190 191 return (ll); 192 } 193 194 /* Parses values of the form integer.max2digits i.e. floating-point values, 195 but with at most two digits after the decimal point. If there are more 196 digits after the decimal point, they are ignored (yeah, that should 197 probably be changed). The return value is the parsed value, multiplied 198 by 100 so it can be represented as an integer. minval and maxval also must 199 be multiplied by 100. If there is a decimal point, but no digits after 200 it, the decimal point is still removed and the number is interpreted 201 as if there was no decimal point. */ 202 long long 203 ltk_strtoscalednum( 204 const char *numstr, long long minval, long long maxval, 205 char **endptr, const char **errstrp) { 206 long long ll = 0; 207 int error = 0; 208 char *ep = NULL; 209 struct errval { 210 const char *errstr; 211 int err; 212 } ev[4] = { 213 { NULL, 0 }, 214 { "invalid", EINVAL }, 215 { "too small", ERANGE }, 216 { "too large", ERANGE }, 217 }; 218 219 ev[0].err = errno; 220 errno = 0; 221 if (minval > maxval) { 222 error = INVALID; 223 goto ret; 224 } 225 226 ll = ltk_strtoll(numstr, &ep); 227 if (numstr == ep) { 228 error = INVALID; 229 goto ret; 230 } else if ((ll == LLONG_MIN && errno == ERANGE)) { 231 error = TOOSMALL; 232 goto ret; 233 } else if ((ll == LLONG_MAX && errno == ERANGE)) { 234 error = TOOLARGE; 235 goto ret; 236 } 237 238 long long afterpoint = 0; 239 if (*ep == '.') { 240 ep++; 241 if (isdigit(*ep)) { 242 afterpoint += 10 * (*ep - '0'); 243 ep++; 244 if (isdigit(*ep)) { 245 afterpoint += *ep - '0'; 246 ep++; 247 } 248 } 249 /* FIXME: warn if there are any more digits */ 250 } 251 252 /* FIXME: decrease code duplication and check that this actually works */ 253 long long cutoff = ll < 0 ? LLONG_MIN : LLONG_MAX; 254 long long cutlim = cutoff % 100; 255 cutoff /= 100; 256 if (ll < 0) { 257 if (cutlim > 0) { 258 cutlim -= 100; 259 cutoff += 1; 260 } 261 cutlim = -cutlim; 262 } 263 if (ll < 0) { 264 if (ll < cutoff || (ll == cutoff && afterpoint > cutlim)) { 265 error = TOOSMALL; 266 goto ret; 267 } else { 268 ll *= 100; 269 ll -= afterpoint; 270 } 271 } else { 272 if (ll > cutoff || (ll == cutoff && afterpoint > cutlim)) { 273 error = TOOLARGE; 274 goto ret; 275 } else { 276 ll *= 100; 277 ll += afterpoint; 278 } 279 } 280 if (ll < minval) 281 error = TOOSMALL; 282 else if (ll > maxval) 283 error = TOOLARGE; 284 285 ret: 286 if (endptr != NULL) 287 *endptr = ep; 288 if (errstrp != NULL) 289 *errstrp = ev[error].errstr; 290 errno = ev[error].err; 291 if (error) 292 ll = 0; 293 294 return (ll); 295 }