URI:
       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 }