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