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