1 /* 2 * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1992 5 * The Regents of the University of California. All rights reserved. 6 * 7 * By using this file, you agree to the terms and conditions set 8 * forth in the LICENSE file which can be found at the top level of 9 * the sendmail distribution. 10 */ 11 12 #include <sm/gen.h> 13 SM_IDSTR(id, "@(#)$Id: strto.c,v 1.18 2001/12/30 04:59:37 gshapiro Exp $") 14 15 #include <sys/param.h> 16 #include <sys/types.h> 17 #include <stdlib.h> 18 #include <ctype.h> 19 #include <errno.h> 20 #include <sm/limits.h> 21 #include <sm/conf.h> 22 #include <sm/string.h> 23 24 /* 25 ** SM_STRTOLL -- Convert a string to a (signed) long long integer. 26 ** 27 ** Ignores `locale' stuff. Assumes that the upper and lower case 28 ** alphabets and digits are each contiguous. 29 ** 30 ** Parameters: 31 ** nptr -- string containing number 32 ** endptr -- location of first invalid character 33 ** base -- numeric base that 'nptr' number is based in 34 ** 35 ** Returns: 36 ** Failure: on underflow LLONG_MIN is returned; on overflow 37 ** LLONG_MAX is returned and errno is set. 38 ** When 'endptr' == '\0' then the entire string 'nptr' 39 ** was valid. 40 ** Success: returns the converted number 41 */ 42 43 LONGLONG_T 44 sm_strtoll(nptr, endptr, base) 45 const char *nptr; 46 char **endptr; 47 register int base; 48 { 49 register bool neg; 50 register const char *s; 51 register LONGLONG_T acc, cutoff; 52 register int c; 53 register int any, cutlim; 54 55 /* 56 ** Skip white space and pick up leading +/- sign if any. 57 ** If base is 0, allow 0x for hex and 0 for octal, else 58 ** assume decimal; if base is already 16, allow 0x. 59 */ 60 61 s = nptr; 62 do 63 { 64 c = (unsigned char) *s++; 65 } while (isascii(c) && isspace(c)); 66 if (c == '-') 67 { 68 neg = true; 69 c = *s++; 70 } 71 else 72 { 73 neg = false; 74 if (c == '+') 75 c = *s++; 76 } 77 if ((base == 0 || base == 16) && 78 c == '0' && (*s == 'x' || *s == 'X')) 79 { 80 c = s[1]; 81 s += 2; 82 base = 16; 83 } 84 if (base == 0) 85 base = c == '0' ? 8 : 10; 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-long's is 95 ** [-9223372036854775808..9223372036854775807] and the input base 96 ** is 10, cutoff will be set to 922337203685477580 and cutlim to 97 ** either 7 (!neg) or 8 (neg), 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 106 cutoff = neg ? LLONG_MIN : LLONG_MAX; 107 cutlim = cutoff % base; 108 cutoff /= base; 109 if (neg) 110 { 111 if (cutlim > 0) 112 { 113 cutlim -= base; 114 cutoff += 1; 115 } 116 cutlim = -cutlim; 117 } 118 for (acc = 0, any = 0;; c = (unsigned char) *s++) 119 { 120 if (isascii(c) && isdigit(c)) 121 c -= '0'; 122 else if (isascii(c) && isalpha(c)) 123 c -= isupper(c) ? 'A' - 10 : 'a' - 10; 124 else 125 break; 126 if (c >= base) 127 break; 128 if (any < 0) 129 continue; 130 if (neg) 131 { 132 if (acc < cutoff || (acc == cutoff && c > cutlim)) 133 { 134 any = -1; 135 acc = LLONG_MIN; 136 errno = ERANGE; 137 } 138 else 139 { 140 any = 1; 141 acc *= base; 142 acc -= c; 143 } 144 } 145 else 146 { 147 if (acc > cutoff || (acc == cutoff && c > cutlim)) 148 { 149 any = -1; 150 acc = LLONG_MAX; 151 errno = ERANGE; 152 } 153 else 154 { 155 any = 1; 156 acc *= base; 157 acc += c; 158 } 159 } 160 } 161 if (endptr != 0) 162 *endptr = (char *) (any ? s - 1 : nptr); 163 return acc; 164 } 165 166 /* 167 ** SM_STRTOULL -- Convert a string to an unsigned long long integer. 168 ** 169 ** Ignores `locale' stuff. Assumes that the upper and lower case 170 ** alphabets and digits are each contiguous. 171 ** 172 ** Parameters: 173 ** nptr -- string containing (unsigned) number 174 ** endptr -- location of first invalid character 175 ** base -- numeric base that 'nptr' number is based in 176 ** 177 ** Returns: 178 ** Failure: on overflow ULLONG_MAX is returned and errno is set. 179 ** When 'endptr' == '\0' then the entire string 'nptr' 180 ** was valid. 181 ** Success: returns the converted number 182 */ 183 184 ULONGLONG_T 185 sm_strtoull(nptr, endptr, base) 186 const char *nptr; 187 char **endptr; 188 register int base; 189 { 190 register const char *s; 191 register ULONGLONG_T acc, cutoff; 192 register int c; 193 register bool neg; 194 register int any, cutlim; 195 196 /* See sm_strtoll for comments as to the logic used. */ 197 s = nptr; 198 do 199 { 200 c = (unsigned char) *s++; 201 } while (isascii(c) && isspace(c)); 202 neg = (c == '-'); 203 if (neg) 204 { 205 c = *s++; 206 } 207 else 208 { 209 if (c == '+') 210 c = *s++; 211 } 212 if ((base == 0 || base == 16) && 213 c == '0' && (*s == 'x' || *s == 'X')) 214 { 215 c = s[1]; 216 s += 2; 217 base = 16; 218 } 219 if (base == 0) 220 base = c == '0' ? 8 : 10; 221 222 cutoff = ULLONG_MAX / (ULONGLONG_T)base; 223 cutlim = ULLONG_MAX % (ULONGLONG_T)base; 224 for (acc = 0, any = 0;; c = (unsigned char) *s++) 225 { 226 if (isascii(c) && isdigit(c)) 227 c -= '0'; 228 else if (isascii(c) && isalpha(c)) 229 c -= isupper(c) ? 'A' - 10 : 'a' - 10; 230 else 231 break; 232 if (c >= base) 233 break; 234 if (any < 0) 235 continue; 236 if (acc > cutoff || (acc == cutoff && c > cutlim)) 237 { 238 any = -1; 239 acc = ULLONG_MAX; 240 errno = ERANGE; 241 } 242 else 243 { 244 any = 1; 245 acc *= (ULONGLONG_T)base; 246 acc += c; 247 } 248 } 249 if (neg && any > 0) 250 acc = -((LONGLONG_T) acc); 251 if (endptr != 0) 252 *endptr = (char *) (any ? s - 1 : nptr); 253 return acc; 254 } 255