Formerly stdlib/stdlib.h.~12~
[kopensolaris-gnu/glibc.git] / stdlib / 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 <ansidecl.h>
20 #include <ctype.h>
21 #include <limits.h>
22 #include <stddef.h>
23 #include <stdlib.h>
24
25
26 #ifndef UNSIGNED
27 #define UNSIGNED        0
28 #endif
29
30 /* Convert NPTR to an `unsigned long int' or `long int' in base BASE.
31    If BASE is 0 the base is determined by the presence of a leading
32    zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal.
33    If BASE is < 2 or > 36, it is reset to 10.
34    If ENDPTR is not NULL, a pointer to the character after the last
35    one converted is stored in *ENDPTR.  */
36 #if     UNSIGNED
37 unsigned long int
38 #define strtol  strtoul
39 #else
40 long int
41 #endif
42 DEFUN(strtol, (nptr, endptr, base),
43       CONST char *nptr AND char **endptr AND int base)
44 {
45   int negative;
46   register unsigned long int cutoff;
47   register unsigned int cutlim;
48   register unsigned long int i;
49   register CONST char *s;
50   register unsigned char c;
51   CONST char *save;
52   int overflow;
53
54   if (base < 0 || base == 1 || base > 36)
55     base = 10;
56
57   s = nptr;
58
59   /* Skip white space.  */
60   while (isspace(*s))
61     ++s;
62   if (*s == '\0')
63     goto noconv;
64
65   /* Check for a sign.  */
66   if (*s == '-')
67     {
68       negative = 1;
69       ++s;
70     }
71   else if (*s == '+')
72     {
73       negative = 0;
74       ++s;
75     }
76   else
77     negative = 0;
78
79   if (base == 16 && s[0] == '0' && toupper(s[1]) == 'X')
80     s += 2;
81
82   /* If BASE is zero, figure it out ourselves.  */
83   if (base == 0)
84     if (*s == '0')
85       {
86         if (toupper(s[1]) == 'X')
87           {
88             s += 2;
89             base = 16;
90           }
91         else
92           base = 8;
93       }
94     else
95       base = 10;
96
97   /* Save the pointer so we can check later if anything happened.  */
98   save = s;
99
100   cutoff = ULONG_MAX / (unsigned long int) base;
101   cutlim = ULONG_MAX % (unsigned long int) base;
102
103   overflow = 0;
104   i = 0;
105   for (c = *s; c != '\0'; c = *++s)
106     {
107       if (isdigit(c))
108         c -= '0';
109       else if (isalpha(c))
110         c = toupper(c) - 'A' + 10;
111       else
112         break;
113       if (c >= base)
114         break;
115       /* Check for overflow.  */
116       if (i > cutoff || (i == cutoff && c > cutlim))
117         overflow = 1;
118       else
119         {
120           i *= (unsigned long int) base;
121           i += c;
122         }
123     }
124
125   /* Check if anything actually happened.  */
126   if (s == save)
127     goto noconv;
128
129   /* Store in ENDPTR the address of one character
130      past the last character we converted.  */
131   if (endptr != NULL)
132     *endptr = (char *) s;
133
134 #if     !UNSIGNED
135   /* Check for a value that is within the range of
136      `unsigned long int', but outside the range of `long int'.  */
137   if (i > (negative ?
138            - (unsigned long int) LONG_MIN : (unsigned long int) LONG_MAX))
139     overflow = 1;
140 #endif
141
142   if (overflow)
143     {
144       errno = ERANGE;
145 #if     UNSIGNED
146       return ULONG_MAX;
147 #else
148       return negative ? LONG_MIN : LONG_MAX;
149 #endif
150     }
151
152   /* Return the result of the appropriate sign.  */
153   return (negative ? - i : i);
154
155  noconv:
156   /* There was no number to convert.  */
157   if (endptr != NULL)
158     *endptr = (char *) nptr;
159   return 0L;
160 }