num.c - ltk - GUI toolkit for X11 (WIP)
HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
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 }