Home | History | Annotate | Download | only in StdLib
      1 /*  $NetBSD: strtoimax.c,v 1.4 2005/11/29 03:12:00 christos Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 1992, 1993
      5  *  The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 #include  <LibConfig.h>
     32 #include <sys/EfiCdefs.h>
     33 #if defined(LIBC_SCCS) && !defined(lint)
     34 #if 0
     35 static char sccsid[] = "from: @(#)strtoq.c  8.1 (Berkeley) 6/4/93";
     36 #else
     37 __RCSID("$NetBSD: strtoimax.c,v 1.4 2005/11/29 03:12:00 christos Exp $");
     38 #endif
     39 #endif /* LIBC_SCCS and not lint */
     40 
     41 #include "namespace.h"
     42 
     43 #include <assert.h>
     44 #include <ctype.h>
     45 #include <errno.h>
     46 #include <inttypes.h>
     47 #include <stddef.h>
     48 
     49 #ifdef __weak_alias
     50 __weak_alias(strtoimax, _strtoimax)
     51 #endif
     52 
     53 /*
     54  * Convert a string to an intmax_t.
     55  *
     56  * Ignores `locale' stuff.  Assumes that the upper and lower case
     57  * alphabets and digits are each contiguous.
     58  */
     59 intmax_t
     60 _strtoimax(const char *nptr, char **endptr, int base)
     61 {
     62   const char *s;
     63   intmax_t acc, cutoff;
     64   int c;
     65   int neg, any, cutlim;
     66 
     67   _DIAGASSERT(nptr != NULL);
     68   /* endptr may be NULL */
     69 
     70 #ifdef __GNUC__
     71   /* This outrageous construct just to shut up a GCC warning. */
     72   (void) &acc; (void) &cutoff;
     73 #endif
     74 
     75   /*
     76    * Skip white space and pick up leading +/- sign if any.
     77    * If base is 0, allow 0x for hex and 0 for octal, else
     78    * assume decimal; if base is already 16, allow 0x.
     79    */
     80   s = nptr;
     81   do {
     82     c = (unsigned char) *s++;
     83   } while (isspace(c));
     84   if (c == '-') {
     85     neg = 1;
     86     c = *s++;
     87   } else {
     88     neg = 0;
     89     if (c == '+')
     90       c = *s++;
     91   }
     92   if ((base == 0 || base == 16) &&
     93       c == '0' && (*s == 'x' || *s == 'X')) {
     94     c = s[1];
     95     s += 2;
     96     base = 16;
     97   }
     98   if (base == 0)
     99     base = c == '0' ? 8 : 10;
    100 
    101   /*
    102    * Compute the cutoff value between legal numbers and illegal
    103    * numbers.  That is the largest legal value, divided by the
    104    * base.  An input number that is greater than this value, if
    105    * followed by a legal input character, is too big.  One that
    106    * is equal to this value may be valid or not; the limit
    107    * between valid and invalid numbers is then based on the last
    108    * digit.  For instance, if the range for intmax_t is
    109    * [-9223372036854775808..9223372036854775807] and the input base
    110    * is 10, cutoff will be set to 922337203685477580 and cutlim to
    111    * either 7 (neg==0) or 8 (neg==1), meaning that if we have
    112    * accumulated a value > 922337203685477580, or equal but the
    113    * next digit is > 7 (or 8), the number is too big, and we will
    114    * return a range error.
    115    *
    116    * Set any if any `digits' consumed; make it negative to indicate
    117    * overflow.
    118    */
    119   cutoff = neg ? INTMAX_MIN : INTMAX_MAX;
    120   cutlim = (int)(cutoff % base);
    121   cutoff /= base;
    122   if (neg) {
    123     if (cutlim > 0) {
    124       cutlim -= base;
    125       cutoff += 1;
    126     }
    127     cutlim = -cutlim;
    128   }
    129   for (acc = 0, any = 0;; c = (unsigned char) *s++) {
    130     if (isdigit(c))
    131       c -= '0';
    132     else if (isalpha(c))
    133       c -= isupper(c) ? 'A' - 10 : 'a' - 10;
    134     else
    135       break;
    136     if (c >= base)
    137       break;
    138     if (any < 0)
    139       continue;
    140     if (neg) {
    141       if (acc < cutoff || (acc == cutoff && c > cutlim)) {
    142         any = -1;
    143         acc = INTMAX_MIN;
    144         errno = ERANGE;
    145       } else {
    146         any = 1;
    147         acc *= base;
    148         acc -= c;
    149       }
    150     } else {
    151       if (acc > cutoff || (acc == cutoff && c > cutlim)) {
    152         any = -1;
    153         acc = INTMAX_MAX;
    154         errno = ERANGE;
    155       } else {
    156         any = 1;
    157         acc *= base;
    158         acc += c;
    159       }
    160     }
    161   }
    162   if (endptr != 0)
    163     *endptr = (char *)(any ? s - 1 : nptr);
    164     //*endptr = __UNCONST(any ? s - 1 : nptr);
    165   return (acc);
    166 }
    167