ltk

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