1/* Convert string representation of a number into an integer value. 2 Copyright (C) 1991,92,94,95,96,97,98,99,2000,2001 Free Software Foundation, Inc. 3 This file is part of the GNU C Library. 4 5 The GNU C Library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 The GNU C Library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with the GNU C Library; if not, write to the Free 17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 18 02111-1307 USA. */ 19 20#include <config.h> 21 22#if !defined (HAVE_STRTOL) 23 24#include <chartypes.h> 25#include <errno.h> 26 27#ifndef errno 28extern int errno; 29#endif 30 31#ifndef __set_errno 32# define __set_errno(Val) errno = (Val) 33#endif 34 35#ifdef HAVE_LIMITS_H 36# include <limits.h> 37#endif 38 39#include <typemax.h> 40 41#include <stdc.h> 42#include <bashansi.h> 43 44#ifndef NULL 45# define NULL 0 46#endif 47 48/* Nonzero if we are defining `strtoul' or `strtoull', operating on 49 unsigned integers. */ 50#ifndef UNSIGNED 51# define UNSIGNED 0 52# define INT LONG int 53#else 54# define INT unsigned LONG int 55#endif 56 57#if UNSIGNED 58# ifdef QUAD 59# define strtol strtoull 60# else 61# define strtol strtoul 62# endif 63#else 64# ifdef QUAD 65# define strtol strtoll 66# endif 67#endif 68 69/* If QUAD is defined, we are defining `strtoll' or `strtoull', 70 operating on `long long ints. */ 71 72#ifdef QUAD 73# define LONG long long 74# define STRTOL_LONG_MIN LLONG_MIN 75# define STRTOL_LONG_MAX LLONG_MAX 76# define STRTOL_ULONG_MAX ULLONG_MAX 77#else /* !QUAD */ 78# define LONG long 79# define STRTOL_LONG_MIN LONG_MIN 80# define STRTOL_LONG_MAX LONG_MAX 81# define STRTOL_ULONG_MAX ULONG_MAX 82#endif 83 84/* Convert NPTR to an `unsigned long int' or `long int' in base BASE. 85 If BASE is 0 the base is determined by the presence of a leading 86 zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal. 87 If BASE is < 2 or > 36, it is no longer reset to 10; EINVAL is returned. 88 If ENDPTR is not NULL, a pointer to the character after the last 89 one converted is stored in *ENDPTR. */ 90 91INT 92strtol (nptr, endptr, base) 93 const char *nptr; 94 char **endptr; 95 int base; 96{ 97 int negative; 98 register unsigned LONG int cutoff; 99 register unsigned int cutlim; 100 register unsigned LONG int i; 101 register const char *s; 102 register unsigned char c; 103 const char *save, *end; 104 int overflow; 105 106 if (base < 0 || base == 1 || base > 36) 107 { 108 __set_errno (EINVAL); 109 return 0; 110 } 111 112 save = s = nptr; 113 114 /* Skip white space. */ 115 while (ISSPACE ((unsigned char)*s)) 116 ++s; 117 if (*s == '\0') 118 goto noconv; 119 120 /* Check for a sign. */ 121 if (*s == '-' || *s == '+') 122 { 123 negative = (*s == '-'); 124 ++s; 125 } 126 else 127 negative = 0; 128 129 /* Recognize number prefix and if BASE is zero, figure it out ourselves. */ 130 if (*s == '0') 131 { 132 if ((base == 0 || base == 16) && TOUPPER ((unsigned char) s[1]) == 'X') 133 { 134 s += 2; 135 base = 16; 136 } 137 else if (base == 0) 138 base = 8; 139 } 140 else if (base == 0) 141 base = 10; 142 143 /* Save the pointer so we can check later if anything happened. */ 144 save = s; 145 146 end = NULL; 147 148 cutoff = STRTOL_ULONG_MAX / (unsigned LONG int) base; 149 cutlim = STRTOL_ULONG_MAX % (unsigned LONG int) base; 150 151 overflow = 0; 152 i = 0; 153 c = *s; 154 if (sizeof (long int) != sizeof (LONG int)) 155 { 156 unsigned long int j = 0; 157 unsigned long int jmax = ULONG_MAX / base; 158 159 for (;c != '\0'; c = *++s) 160 { 161 if (s == end) 162 break; 163 if (DIGIT (c)) 164 c -= '0'; 165 else if (ISALPHA (c)) 166 c = TOUPPER (c) - 'A' + 10; 167 else 168 break; 169 170 if ((int) c >= base) 171 break; 172 /* Note that we never can have an overflow. */ 173 else if (j >= jmax) 174 { 175 /* We have an overflow. Now use the long representation. */ 176 i = (unsigned LONG int) j; 177 goto use_long; 178 } 179 else 180 j = j * (unsigned long int) base + c; 181 } 182 183 i = (unsigned LONG int) j; 184 } 185 else 186 for (;c != '\0'; c = *++s) 187 { 188 if (s == end) 189 break; 190 if (DIGIT (c)) 191 c -= '0'; 192 else if (ISALPHA (c)) 193 c = TOUPPER (c) - 'A' + 10; 194 else 195 break; 196 if ((int) c >= base) 197 break; 198 /* Check for overflow. */ 199 if (i > cutoff || (i == cutoff && c > cutlim)) 200 overflow = 1; 201 else 202 { 203 use_long: 204 i *= (unsigned LONG int) base; 205 i += c; 206 } 207 } 208 209 /* Check if anything actually happened. */ 210 if (s == save) 211 goto noconv; 212 213 /* Store in ENDPTR the address of one character 214 past the last character we converted. */ 215 if (endptr != NULL) 216 *endptr = (char *) s; 217 218#if !UNSIGNED 219 /* Check for a value that is within the range of 220 `unsigned LONG int', but outside the range of `LONG int'. */ 221 if (overflow == 0 222 && i > (negative 223 ? -((unsigned LONG int) (STRTOL_LONG_MIN + 1)) + 1 224 : (unsigned LONG int) STRTOL_LONG_MAX)) 225 overflow = 1; 226#endif 227 228 if (overflow) 229 { 230 __set_errno (ERANGE); 231#if UNSIGNED 232 return STRTOL_ULONG_MAX; 233#else 234 return negative ? STRTOL_LONG_MIN : STRTOL_LONG_MAX; 235#endif 236 } 237 238 /* Return the result of the appropriate sign. */ 239 return negative ? -i : i; 240 241noconv: 242 /* We must handle a special case here: the base is 0 or 16 and the 243 first two characters are '0' and 'x', but the rest are no 244 hexadecimal digits. This is no error case. We return 0 and 245 ENDPTR points to the `x`. */ 246 if (endptr != NULL) 247 { 248 if (save - nptr >= 2 && TOUPPER ((unsigned char) save[-1]) == 'X' && save[-2] == '0') 249 *endptr = (char *) &save[-1]; 250 else 251 /* There was no number to convert. */ 252 *endptr = (char *) nptr; 253 } 254 255 return 0L; 256} 257 258#endif /* !HAVE_STRTOL */ 259