strtol.c revision 235785
166458Sdfr/*-
266458Sdfr * Copyright (c) 1990, 1993
366458Sdfr *	The Regents of the University of California.  All rights reserved.
4139790Simp *
566458Sdfr * Copyright (c) 2011 The FreeBSD Foundation
666458Sdfr * All rights reserved.
766458Sdfr * Portions of this software were developed by David Chisnall
866458Sdfr * under sponsorship from the FreeBSD Foundation.
966458Sdfr *
1066458Sdfr * Redistribution and use in source and binary forms, with or without
1166458Sdfr * modification, are permitted provided that the following conditions
1266458Sdfr * are met:
1366458Sdfr * 1. Redistributions of source code must retain the above copyright
1466458Sdfr *    notice, this list of conditions and the following disclaimer.
1566458Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1666458Sdfr *    notice, this list of conditions and the following disclaimer in the
1766458Sdfr *    documentation and/or other materials provided with the distribution.
1866458Sdfr * 4. Neither the name of the University nor the names of its contributors
1966458Sdfr *    may be used to endorse or promote products derived from this software
2066458Sdfr *    without specific prior written permission.
2166458Sdfr *
2266458Sdfr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2366458Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2466458Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2566458Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2666458Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2766458Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2866458Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2966458Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3066458Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3166458Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3266458Sdfr * SUCH DAMAGE.
3366458Sdfr */
3466458Sdfr
35113941Skan#if defined(LIBC_SCCS) && !defined(lint)
36113941Skanstatic char sccsid[] = "@(#)strtol.c	8.1 (Berkeley) 6/4/93";
3766458Sdfr#endif /* LIBC_SCCS and not lint */
38114678Skan#include <sys/cdefs.h>
39114678Skan__FBSDID("$FreeBSD: stable/9/lib/libc/stdlib/strtol.c 235785 2012-05-22 14:40:39Z theraven $");
40114678Skan
41114678Skan#include <limits.h>
42114678Skan#include <ctype.h>
43114678Skan#include <errno.h>
44114678Skan#include <stdlib.h>
45114678Skan#include "xlocale_private.h"
46114678Skan
47115164Skan
48115164Skan/*
49113941Skan * Convert a string to a long integer.
50113941Skan *
5166458Sdfr * Assumes that the upper and lower case
52217145Stijl * alphabets and digits are each contiguous.
5366458Sdfr */
54217145Stijllong
55113941Skanstrtol_l(const char * __restrict nptr, char ** __restrict endptr, int base,
56113941Skan		locale_t locale)
5766458Sdfr{
58217145Stijl	const char *s;
59113941Skan	unsigned long acc;
60113941Skan	char c;
6166458Sdfr	unsigned long cutoff;
62217145Stijl	int neg, any, cutlim;
63217145Stijl	FIX_LOCALE(locale);
64217145Stijl
65113941Skan	/*
66217145Stijl	 * Skip white space and pick up leading +/- sign if any.
6767488Sobrien	 * If base is 0, allow 0x for hex and 0 for octal, else
68113941Skan	 * assume decimal; if base is already 16, allow 0x.
69113941Skan	 */
70113941Skan	s = nptr;
7167488Sobrien	do {
72113941Skan		c = *s++;
7366458Sdfr	} while (isspace_l((unsigned char)c, locale));
74113941Skan	if (c == '-') {
7566458Sdfr		neg = 1;
76113941Skan		c = *s++;
77113941Skan	} else {
7881720Sache		neg = 0;
7967488Sobrien		if (c == '+')
80113941Skan			c = *s++;
81113941Skan	}
82113941Skan	if ((base == 0 || base == 16) &&
8366458Sdfr	    c == '0' && (*s == 'x' || *s == 'X') &&
84113941Skan	    ((s[1] >= '0' && s[1] <= '9') ||
85113941Skan	    (s[1] >= 'A' && s[1] <= 'F') ||
8666458Sdfr	    (s[1] >= 'a' && s[1] <= 'f'))) {
87149337Sstefanf		c = s[1];
88149337Sstefanf		s += 2;
89149337Sstefanf		base = 16;
90113941Skan	}
91	if (base == 0)
92		base = c == '0' ? 8 : 10;
93	acc = any = 0;
94	if (base < 2 || base > 36)
95		goto noconv;
96
97	/*
98	 * Compute the cutoff value between legal numbers and illegal
99	 * numbers.  That is the largest legal value, divided by the
100	 * base.  An input number that is greater than this value, if
101	 * followed by a legal input character, is too big.  One that
102	 * is equal to this value may be valid or not; the limit
103	 * between valid and invalid numbers is then based on the last
104	 * digit.  For instance, if the range for longs is
105	 * [-2147483648..2147483647] and the input base is 10,
106	 * cutoff will be set to 214748364 and cutlim to either
107	 * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
108	 * a value > 214748364, or equal but the next digit is > 7 (or 8),
109	 * the number is too big, and we will return a range error.
110	 *
111	 * Set 'any' if any `digits' consumed; make it negative to indicate
112	 * overflow.
113	 */
114	cutoff = neg ? (unsigned long)-(LONG_MIN + LONG_MAX) + LONG_MAX
115	    : LONG_MAX;
116	cutlim = cutoff % base;
117	cutoff /= base;
118	for ( ; ; c = *s++) {
119		if (c >= '0' && c <= '9')
120			c -= '0';
121		else if (c >= 'A' && c <= 'Z')
122			c -= 'A' - 10;
123		else if (c >= 'a' && c <= 'z')
124			c -= 'a' - 10;
125		else
126			break;
127		if (c >= base)
128			break;
129		if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
130			any = -1;
131		else {
132			any = 1;
133			acc *= base;
134			acc += c;
135		}
136	}
137	if (any < 0) {
138		acc = neg ? LONG_MIN : LONG_MAX;
139		errno = ERANGE;
140	} else if (!any) {
141noconv:
142		errno = EINVAL;
143	} else if (neg)
144		acc = -acc;
145	if (endptr != NULL)
146		*endptr = (char *)(any ? s - 1 : nptr);
147	return (acc);
148}
149long
150strtol(const char * __restrict nptr, char ** __restrict endptr, int base)
151{
152	return strtol_l(nptr, endptr, base, __get_locale());
153}
154long double
155strtold(const char * __restrict nptr, char ** __restrict endptr)
156{
157	return strtold_l(nptr, endptr, __get_locale());
158}
159