]> git.ipfire.org Git - thirdparty/glibc.git/blame - stdlib/strtol.c
(categories_write): Fix two bugs with handling string arrays. If writing a locale...
[thirdparty/glibc.git] / stdlib / strtol.c
CommitLineData
28f540f4
RM
1/* Copyright (C) 1991, 1992, 1994, 1995 Free Software Foundation, Inc.
2
3This file is part of the GNU C Library.
4
5The GNU C Library is free software; you can redistribute it and/or
6modify it under the terms of the GNU Library General Public License as
7published by the Free Software Foundation; either version 2 of the
8License, or (at your option) any later version.
9
10The GNU C Library is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13Library General Public License for more details.
14
15You should have received a copy of the GNU Library General Public
16License along with the GNU C Library; see the file COPYING.LIB. If
17not, write to the Free Software Foundation, Inc., 675 Mass Ave,
18Cambridge, MA 02139, USA. */
19
20#include <ctype.h>
21#include <limits.h>
22#include <stddef.h>
23#include <stdlib.h>
24#include <errno.h>
f0bf9cb9 25#include "../locale/localeinfo.h"
28f540f4 26
f0bf9cb9
RM
27
28/* Nonzero if we are defining `strtoul' or `strtouq', operating on
29 unsigned integers. */
30#ifndef UNSIGNED
28f540f4 31#define UNSIGNED 0
f0bf9cb9
RM
32#define INT LONG int
33#else
34#define strtol strtoul
35#define INT unsigned LONG int
28f540f4
RM
36#endif
37
38/* If QUAD is defined, we are defining `strtoq' or `strtouq',
39 operating on `long long int's. */
40#ifdef QUAD
41#if UNSIGNED
42#define strtoul strtouq
43#else
44#define strtol strtoq
45#endif
46#define LONG long long
47#undef LONG_MIN
48#define LONG_MIN LONG_LONG_MIN
49#undef LONG_MAX
50#define LONG_MAX LONG_LONG_MAX
51#undef ULONG_MAX
52#define ULONG_MAX ULONG_LONG_MAX
53#if __GNUC__ == 2 && __GNUC_MINOR__ < 7
54/* Work around gcc bug with using this constant. */
55static const unsigned long long int maxquad = ULONG_LONG_MAX;
56#undef ULONG_MAX
57#define ULONG_MAX maxquad
58#endif
59#else
60#define LONG long
61#endif
62
f0bf9cb9
RM
63
64#define INTERNAL(x) INTERNAL1(x)
65#define INTERNAL1(x) __##x##_internal
66
67/* This file defines a function to check for correct grouping. */
68#include "grouping.h"
69
70
28f540f4
RM
71/* Convert NPTR to an `unsigned long int' or `long int' in base BASE.
72 If BASE is 0 the base is determined by the presence of a leading
73 zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal.
74 If BASE is < 2 or > 36, it is reset to 10.
75 If ENDPTR is not NULL, a pointer to the character after the last
76 one converted is stored in *ENDPTR. */
f0bf9cb9
RM
77
78INT
79INTERNAL (strtol) (nptr, endptr, base, group)
28f540f4
RM
80 const char *nptr;
81 char **endptr;
82 int base;
f0bf9cb9 83 int group;
28f540f4
RM
84{
85 int negative;
86 register unsigned LONG int cutoff;
87 register unsigned int cutlim;
88 register unsigned LONG int i;
89 register const char *s;
90 register unsigned char c;
f0bf9cb9 91 const char *save, *end;
28f540f4
RM
92 int overflow;
93
f0bf9cb9
RM
94 /* The thousands character of the current locale. */
95 wchar_t thousands;
96 /* The numeric grouping specification of the current locale,
97 in the format described in <locale.h>. */
98 const char *grouping;
99
100 if (group)
101 {
102 grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
103 if (*grouping <= 0 || *grouping == CHAR_MAX)
104 grouping = NULL;
105 else
106 {
107 /* Figure out the thousands separator character. */
108 if (mbtowc (&thousands, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP),
109 strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0)
110 thousands = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
111 if (thousands == L'\0')
112 grouping = NULL;
113 }
114 }
115 else
116 grouping = NULL;
117
118
28f540f4
RM
119 if (base < 0 || base == 1 || base > 36)
120 base = 10;
121
122 s = nptr;
123
124 /* Skip white space. */
125 while (isspace (*s))
126 ++s;
127 if (*s == '\0')
128 goto noconv;
129
130 /* Check for a sign. */
131 if (*s == '-')
132 {
133 negative = 1;
134 ++s;
135 }
136 else if (*s == '+')
137 {
138 negative = 0;
139 ++s;
140 }
141 else
142 negative = 0;
143
144 if (base == 16 && s[0] == '0' && toupper (s[1]) == 'X')
145 s += 2;
146
147 /* If BASE is zero, figure it out ourselves. */
148 if (base == 0)
149 if (*s == '0')
150 {
151 if (toupper (s[1]) == 'X')
152 {
153 s += 2;
154 base = 16;
155 }
156 else
157 base = 8;
158 }
159 else
160 base = 10;
161
162 /* Save the pointer so we can check later if anything happened. */
163 save = s;
164
f0bf9cb9
RM
165 if (group)
166 {
167 /* Find the end of the digit string and check its grouping. */
168 end = s;
169 for (c = *end; c != '\0'; c = *++end)
170 if (c != thousands && !isdigit (c) &&
171 (!isalpha (c) || toupper (c) - 'A' + 10 >= base))
172 break;
173 if (*s == thousands)
174 end = s;
175 else
176 end = correctly_grouped_prefix (s, end, thousands, grouping);
177 }
be69ea41
RM
178 else
179 end = NULL;
f0bf9cb9 180
28f540f4
RM
181 cutoff = ULONG_MAX / (unsigned LONG int) base;
182 cutlim = ULONG_MAX % (unsigned LONG int) base;
183
184 overflow = 0;
185 i = 0;
186 for (c = *s; c != '\0'; c = *++s)
187 {
be69ea41 188 if (s == end)
f0bf9cb9 189 break;
28f540f4
RM
190 if (isdigit (c))
191 c -= '0';
192 else if (isalpha (c))
193 c = toupper (c) - 'A' + 10;
194 else
195 break;
196 if (c >= base)
197 break;
198 /* Check for overflow. */
199 if (i > cutoff || (i == cutoff && c > cutlim))
200 overflow = 1;
201 else
202 {
203 i *= (unsigned LONG int) base;
204 i += c;
205 }
206 }
207
208 /* Check if anything actually happened. */
209 if (s == save)
210 goto noconv;
211
212 /* Store in ENDPTR the address of one character
213 past the last character we converted. */
214 if (endptr != NULL)
215 *endptr = (char *) s;
216
217#if !UNSIGNED
218 /* Check for a value that is within the range of
219 `unsigned LONG int', but outside the range of `LONG int'. */
220 if (i > (negative ?
221 -(unsigned LONG int) LONG_MIN : (unsigned LONG int) LONG_MAX))
222 overflow = 1;
223#endif
224
225 if (overflow)
226 {
227 errno = ERANGE;
228#if UNSIGNED
229 return ULONG_MAX;
230#else
231 return negative ? LONG_MIN : LONG_MAX;
232#endif
233 }
234
235 /* Return the result of the appropriate sign. */
236 return (negative ? -i : i);
237
238noconv:
239 /* There was no number to convert. */
240 if (endptr != NULL)
241 *endptr = (char *) nptr;
242 return 0L;
243}
f0bf9cb9
RM
244\f
245/* External user entry point. */
246
247weak_symbol (strtol)
248
249INT
250strtol (nptr, endptr, base)
251 const char *nptr;
252 char **endptr;
253 int base;
254{
255 return INTERNAL (strtol) (nptr, endptr, base, 0);
256}