GNU text utilities
[gnulib.git] / lib / strtol.c
1 /* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB.  If
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA.  */
18
19 #include <ctype.h>
20 #include <errno.h>
21
22 #if HAVE_LIMITS_H
23 #include <limits.h>
24 #endif
25 #ifndef ULONG_MAX
26 #define LONG_MAX (~(1 << (sizeof (long) * 8 - 1)))
27 #define LONG_MIN (-LONG_MAX-1)
28 #define ULONG_MAX ((unsigned long) ~(unsigned long) 0)
29 #endif
30
31 #if STDC_HEADERS
32 #include <stddef.h>
33 #include <stdlib.h>
34 #else
35 #define NULL 0
36 extern int errno;
37 #endif
38
39 #ifndef UNSIGNED
40 #define UNSIGNED        0
41 #endif
42
43 /* Convert NPTR to an `unsigned long int' or `long int' in base BASE.
44    If BASE is 0 the base is determined by the presence of a leading
45    zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal.
46    If BASE is < 2 or > 36, it is reset to 10.
47    If ENDPTR is not NULL, a pointer to the character after the last
48    one converted is stored in *ENDPTR.  */
49 #if     UNSIGNED
50 unsigned long int
51 #define strtol  strtoul
52 #else
53 long int
54 #endif
55 strtol (nptr, endptr, base)
56      const char *nptr;
57      char **endptr;
58      int base;
59 {
60   int negative;
61   register unsigned long int cutoff;
62   register unsigned int cutlim;
63   register unsigned long int i;
64   register const char *s;
65   register unsigned char c;
66   const char *save;
67   int overflow;
68
69   if (base < 0 || base == 1 || base > 36)
70     base = 10;
71
72   s = nptr;
73
74   /* Skip white space.  */
75   while (isspace (*s))
76     ++s;
77   if (*s == '\0')
78     goto noconv;
79
80   /* Check for a sign.  */
81   if (*s == '-')
82     {
83       negative = 1;
84       ++s;
85     }
86   else if (*s == '+')
87     {
88       negative = 0;
89       ++s;
90     }
91   else
92     negative = 0;
93
94   if (base == 16 && s[0] == '0' && toupper (s[1]) == 'X')
95     s += 2;
96
97   /* If BASE is zero, figure it out ourselves.  */
98   if (base == 0)
99     {
100       if (*s == '0')
101         {
102           if (toupper (s[1]) == 'X')
103             {
104               s += 2;
105               base = 16;
106             }
107           else
108             base = 8;
109         }
110       else
111         base = 10;
112     }
113
114   /* Save the pointer so we can check later if anything happened.  */
115   save = s;
116
117   cutoff = ULONG_MAX / (unsigned long int) base;
118   cutlim = ULONG_MAX % (unsigned long int) base;
119
120   overflow = 0;
121   i = 0;
122   for (c = *s; c != '\0'; c = *++s)
123     {
124       if (isdigit (c))
125         c -= '0';
126       else if (isalpha (c))
127         c = toupper (c) - 'A' + 10;
128       else
129         break;
130       if (c >= base)
131         break;
132       /* Check for overflow.  */
133       if (i > cutoff || (i == cutoff && c > cutlim))
134         overflow = 1;
135       else
136         {
137           i *= (unsigned long int) base;
138           i += c;
139         }
140     }
141
142   /* Check if anything actually happened.  */
143   if (s == save)
144     goto noconv;
145
146   /* Store in ENDPTR the address of one character
147      past the last character we converted.  */
148   if (endptr != NULL)
149     *endptr = (char *) s;
150
151 #if     !UNSIGNED
152   /* Check for a value that is within the range of
153      `unsigned long int', but outside the range of `long int'.  */
154   if (i > (negative ?
155            -(unsigned long int) LONG_MIN : (unsigned long int) LONG_MAX))
156     overflow = 1;
157 #endif
158
159   if (overflow)
160     {
161       errno = ERANGE;
162 #if     UNSIGNED
163       return ULONG_MAX;
164 #else
165       return negative ? LONG_MIN : LONG_MAX;
166 #endif
167     }
168
169   /* Return the result of the appropriate sign.  */
170   return (negative ? -i : i);
171
172 noconv:;
173   /* There was no number to convert.  */
174   if (endptr != NULL)
175     *endptr = (char *) nptr;
176   return 0L;
177 }