1 /*
2  * Copyright (C) 2018-2022 Intel Corporation.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <types.h>
8 #include <rtl.h>
9 
10 /*
11  * Convert a string to a long integer - decimal support only.
12  */
strtol_deci(const char * nptr)13 int64_t strtol_deci(const char *nptr)
14 {
15 	const char *s = nptr;
16 	char c;
17 	uint64_t acc, cutoff, cutlim;
18 	int32_t neg = 0, any;
19 	uint64_t base = 10UL;
20 
21 	/*
22 	 * Skip white space and pick up leading +/- sign if any.
23 	 */
24 	do {
25 		c = *s;
26 		s++;
27 	} while (is_space(c));
28 
29 	if (c == '-') {
30 		neg = 1;
31 		c = *s;
32 		s++;
33 	} else if (c == '+') {
34 		c = *s;
35 		s++;
36 	} else {
37 		/* No sign character. */
38 	}
39 
40 	/*
41 	 * Compute the cutoff value between legal numbers and illegal
42 	 * numbers.  That is the largest legal value, divided by the
43 	 * base.  An input number that is greater than this value, if
44 	 * followed by a legal input character, is too big.  One that
45 	 * is equal to this value may be valid or not; the limit
46 	 * between valid and invalid numbers is then based on the last
47 	 * digit.  For instance, if the range for longs is
48 	 * [-2147483648..2147483647] and the input base is 10,
49 	 * cutoff will be set to 214748364 and cutlim to either
50 	 * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
51 	 * a value > 214748364, or equal but the next digit is > 7 (or 8),
52 	 * the number is too big, and we will return a range error.
53 	 *
54 	 * Set any if any `digits' consumed; make it negative to indicate
55 	 * overflow.
56 	 */
57 	cutoff = (neg != 0) ? LONG_MIN : LONG_MAX;
58 	cutlim = cutoff % base;
59 	cutoff /= base;
60 	acc = 0UL;
61 	any = 0;
62 
63 	while ((c >= '0') && (c <= '9')) {
64 		c -= '0';
65 		if ((acc > cutoff) ||
66 			((acc == cutoff) && ((uint64_t)c > cutlim))) {
67 			any = -1;
68 			break;
69 		} else {
70 			acc *= base;
71 			acc += (uint64_t)c;
72 		}
73 
74 		c = *s;
75 		s++;
76 	}
77 
78 	if (any < 0) {
79 		acc = (neg != 0) ? LONG_MIN : LONG_MAX;
80 	} else if (neg != 0) {
81 		acc = ~acc + 1UL;
82 	} else {
83 		/* There is no overflow and no leading '-' exists. In such case
84 		 * acc already holds the right number. No action required. */
85 	}
86 	return (long)acc;
87 }
88