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