Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel (at) haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.haxx.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  ***************************************************************************/
     22 
     23 #include <errno.h>
     24 #include "curl_setup.h"
     25 
     26 #include "strtoofft.h"
     27 
     28 /*
     29  * NOTE:
     30  *
     31  * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we
     32  * could use in case strtoll() doesn't exist...  See
     33  * https://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html
     34  */
     35 
     36 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
     37 #  ifdef HAVE_STRTOLL
     38 #    define strtooff strtoll
     39 #  else
     40 #    if defined(_MSC_VER) && (_MSC_VER >= 1300) && (_INTEGRAL_MAX_BITS >= 64)
     41 #      if defined(_SAL_VERSION)
     42          _Check_return_ _CRTIMP __int64 __cdecl _strtoi64(
     43              _In_z_ const char *_String,
     44              _Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix);
     45 #      else
     46          _CRTIMP __int64 __cdecl _strtoi64(const char *_String,
     47                                            char **_EndPtr, int _Radix);
     48 #      endif
     49 #      define strtooff _strtoi64
     50 #    else
     51 #      define PRIVATE_STRTOOFF 1
     52 #    endif
     53 #  endif
     54 #else
     55 #  define strtooff strtol
     56 #endif
     57 
     58 #ifdef PRIVATE_STRTOOFF
     59 
     60 /* Range tests can be used for alphanum decoding if characters are consecutive,
     61    like in ASCII. Else an array is scanned. Determine this condition now. */
     62 
     63 #if('9' - '0') != 9 || ('Z' - 'A') != 25 || ('z' - 'a') != 25
     64 
     65 #define NO_RANGE_TEST
     66 
     67 static const char valchars[] =
     68             "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
     69 #endif
     70 
     71 static int get_char(char c, int base);
     72 
     73 /**
     74  * Custom version of the strtooff function.  This extracts a curl_off_t
     75  * value from the given input string and returns it.
     76  */
     77 static curl_off_t strtooff(const char *nptr, char **endptr, int base)
     78 {
     79   char *end;
     80   int is_negative = 0;
     81   int overflow;
     82   int i;
     83   curl_off_t value = 0;
     84   curl_off_t newval;
     85 
     86   /* Skip leading whitespace. */
     87   end = (char *)nptr;
     88   while(ISSPACE(end[0])) {
     89     end++;
     90   }
     91 
     92   /* Handle the sign, if any. */
     93   if(end[0] == '-') {
     94     is_negative = 1;
     95     end++;
     96   }
     97   else if(end[0] == '+') {
     98     end++;
     99   }
    100   else if(end[0] == '\0') {
    101     /* We had nothing but perhaps some whitespace -- there was no number. */
    102     if(endptr) {
    103       *endptr = end;
    104     }
    105     return 0;
    106   }
    107 
    108   /* Handle special beginnings, if present and allowed. */
    109   if(end[0] == '0' && end[1] == 'x') {
    110     if(base == 16 || base == 0) {
    111       end += 2;
    112       base = 16;
    113     }
    114   }
    115   else if(end[0] == '0') {
    116     if(base == 8 || base == 0) {
    117       end++;
    118       base = 8;
    119     }
    120   }
    121 
    122   /* Matching strtol, if the base is 0 and it doesn't look like
    123    * the number is octal or hex, we assume it's base 10.
    124    */
    125   if(base == 0) {
    126     base = 10;
    127   }
    128 
    129   /* Loop handling digits. */
    130   value = 0;
    131   overflow = 0;
    132   for(i = get_char(end[0], base);
    133       i != -1;
    134       end++, i = get_char(end[0], base)) {
    135     newval = base * value + i;
    136     if(newval < value) {
    137       /* We've overflowed. */
    138       overflow = 1;
    139       break;
    140     }
    141     else
    142       value = newval;
    143   }
    144 
    145   if(!overflow) {
    146     if(is_negative) {
    147       /* Fix the sign. */
    148       value *= -1;
    149     }
    150   }
    151   else {
    152     if(is_negative)
    153       value = CURL_OFF_T_MIN;
    154     else
    155       value = CURL_OFF_T_MAX;
    156 
    157     errno = ERANGE;
    158   }
    159 
    160   if(endptr)
    161     *endptr = end;
    162 
    163   return value;
    164 }
    165 
    166 /**
    167  * Returns the value of c in the given base, or -1 if c cannot
    168  * be interpreted properly in that base (i.e., is out of range,
    169  * is a null, etc.).
    170  *
    171  * @param c     the character to interpret according to base
    172  * @param base  the base in which to interpret c
    173  *
    174  * @return  the value of c in base, or -1 if c isn't in range
    175  */
    176 static int get_char(char c, int base)
    177 {
    178 #ifndef NO_RANGE_TEST
    179   int value = -1;
    180   if(c <= '9' && c >= '0') {
    181     value = c - '0';
    182   }
    183   else if(c <= 'Z' && c >= 'A') {
    184     value = c - 'A' + 10;
    185   }
    186   else if(c <= 'z' && c >= 'a') {
    187     value = c - 'a' + 10;
    188   }
    189 #else
    190   const char *cp;
    191   int value;
    192 
    193   cp = memchr(valchars, c, 10 + 26 + 26);
    194 
    195   if(!cp)
    196     return -1;
    197 
    198   value = cp - valchars;
    199 
    200   if(value >= 10 + 26)
    201     value -= 26;                /* Lowercase. */
    202 #endif
    203 
    204   if(value >= base) {
    205     value = -1;
    206   }
    207 
    208   return value;
    209 }
    210 #endif  /* Only present if we need strtoll, but don't have it. */
    211 
    212 /*
    213  * Parse a *positive* up to 64 bit number written in ascii.
    214  */
    215 CURLofft curlx_strtoofft(const char *str, char **endp, int base,
    216                          curl_off_t *num)
    217 {
    218   char *end;
    219   curl_off_t number;
    220   errno = 0;
    221   *num = 0; /* clear by default */
    222 
    223   DEBUGASSERT(str);
    224 
    225   while(*str && ISSPACE(*str))
    226     str++;
    227   if('-' == *str) {
    228     if(endp)
    229       *endp = (char *)str; /* didn't actually move */
    230     return CURL_OFFT_INVAL; /* nothing parsed */
    231   }
    232   number = strtooff(str, &end, base);
    233   if(endp)
    234     *endp = end;
    235   if(errno == ERANGE)
    236     /* overflow/underflow */
    237     return CURL_OFFT_FLOW;
    238   else if(str == end)
    239     /* nothing parsed */
    240     return CURL_OFFT_INVAL;
    241 
    242   *num = number;
    243   return CURL_OFFT_OK;
    244 }
    245