1227614Sluigi/* $OpenBSD: strtol.c,v 1.4 2022/01/08 06:49:41 guenther Exp $ */
2249659Sluigi
3241719Sluigi/*-
4227614Sluigi * Copyright (c) 1990 The Regents of the University of California.
5227614Sluigi * All rights reserved.
6227614Sluigi *
7228276Sluigi * Redistribution and use in source and binary forms, with or without
8228276Sluigi * modification, are permitted provided that the following conditions
9228276Sluigi * are met:
10228276Sluigi * 1. Redistributions of source code must retain the above copyright
11227614Sluigi *    notice, this list of conditions and the following disclaimer.
12241719Sluigi * 2. Redistributions in binary form must reproduce the above copyright
13227614Sluigi *    notice, this list of conditions and the following disclaimer in the
14227614Sluigi *    documentation and/or other materials provided with the distribution.
15227614Sluigi * 3. Neither the name of the University nor the names of its contributors
16227614Sluigi *    may be used to endorse or promote products derived from this software
17227614Sluigi *    without specific prior written permission.
18227614Sluigi *
19227614Sluigi * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20227614Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21227614Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22227614Sluigi * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23227614Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24227614Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25227614Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26227614Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27227614Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28227614Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29227614Sluigi * SUCH DAMAGE.
30227614Sluigi */
31227614Sluigi
32227614Sluigi#include <sys/types.h>
33227614Sluigi#include <limits.h>
34227614Sluigi
35227614Sluigi#include "util.h"
36231594Sluigi
37250052Sluigi/*
38238812Sluigi * Convert a string to a long integer.
39238812Sluigi *
40238812Sluigi * Ignores `locale' stuff.  Assumes that the upper and lower case
41231594Sluigi * alphabets and digits are each contiguous.
42251139Sluigi */
43231594Sluigilong
44231594Sluigi_dl_strtol(const char *nptr, char **endptr, int base)
45231594Sluigi{
46250052Sluigi	const char *s;
47232238Sluigi	long acc, cutoff;
48250052Sluigi	int c;
49250052Sluigi	int neg, any, cutlim;
50251139Sluigi
51231594Sluigi	/*
52231594Sluigi	 * Skip white space and pick up leading +/- sign if any.
53231594Sluigi	 * If base is 0, allow 0x for hex and 0 for octal, else
54238812Sluigi	 * assume decimal; if base is already 16, allow 0x.
55238812Sluigi	 */
56238812Sluigi	s = nptr;
57238812Sluigi	do {
58238812Sluigi		c = (unsigned char) *s++;
59238812Sluigi	} while (c <= ' ' || c >= 0x7f);
60241719Sluigi	if (c == '-') {
61241719Sluigi		neg = 1;
62238812Sluigi		c = *s++;
63241719Sluigi	} else {
64238812Sluigi		neg = 0;
65238812Sluigi		if (c == '+')
66238831Sluigi			c = *s++;
67231594Sluigi	}
68251139Sluigi	if ((base == 0 || base == 16) && c == '0' &&
69238812Sluigi	    (*s == 'x' || *s == 'X') && ((s[1] >= '0' && s[1] <= '9') ||
70238812Sluigi	    (s[1] >= 'A' && s[1] <= 'F') || (s[1] >= 'a' && s[1] <= 'f'))) {
71238812Sluigi		c = s[1];
72250052Sluigi		s += 2;
73241719Sluigi		base = 16;
74238812Sluigi	}
75238812Sluigi	if (base == 0)
76238812Sluigi		base = c == '0' ? 8 : 10;
77238812Sluigi
78238812Sluigi	/*
79238812Sluigi	 * Compute the cutoff value between legal numbers and illegal
80238812Sluigi	 * numbers.  That is the largest legal value, divided by the
81238812Sluigi	 * base.  An input number that is greater than this value, if
82250052Sluigi	 * followed by a legal input character, is too big.  One that
83231594Sluigi	 * is equal to this value may be valid or not; the limit
84231594Sluigi	 * between valid and invalid numbers is then based on the last
85250052Sluigi	 * digit.  For instance, if the range for longs is
86250052Sluigi	 * [-2147483648..2147483647] and the input base is 10,
87227614Sluigi	 * cutoff will be set to 214748364 and cutlim to either
88230058Sluigi	 * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
89230058Sluigi	 * a value > 214748364, or equal but the next digit is > 7 (or 8),
90230058Sluigi	 * the number is too big, and we will return a range error.
91227614Sluigi	 *
92230058Sluigi	 * Set any if any `digits' consumed; make it negative to indicate
93230058Sluigi	 * overflow.
94230058Sluigi	 */
95227614Sluigi	cutoff = neg ? LONG_MIN : LONG_MAX;
96241719Sluigi	cutlim = cutoff % base;
97241719Sluigi	cutoff /= base;
98241719Sluigi	if (neg) {
99241719Sluigi		if (cutlim > 0) {
100241719Sluigi			cutlim -= base;
101241719Sluigi			cutoff += 1;
102241719Sluigi		}
103241719Sluigi		cutlim = -cutlim;
104241719Sluigi	}
105241719Sluigi	for (acc = 0, any = 0;; c = (unsigned char) *s++) {
106241719Sluigi		if (c >= '0' && c <= '9')
107241719Sluigi			c -= '0';
108241719Sluigi		else if (c >= 'A' && c <= 'Z')
109227614Sluigi			c -= 'A' - 10;
110251139Sluigi		else if (c >= 'a' && c <= 'z')
111251139Sluigi			c -= 'a' - 10;
112251139Sluigi		else
113227614Sluigi			break;
114227614Sluigi		if (c >= base)
115232238Sluigi			break;
116232238Sluigi		if (any < 0)
117227614Sluigi			continue;
118232238Sluigi		if (neg) {
119232238Sluigi			if (acc < cutoff || (acc == cutoff && c > cutlim)) {
120232238Sluigi				any = -1;
121232238Sluigi				acc = LONG_MIN;
122232238Sluigi			} else {
123232238Sluigi				any = 1;
124231594Sluigi				acc *= base;
125227614Sluigi				acc -= c;
126227614Sluigi			}
127231594Sluigi		} else {
128227614Sluigi			if (acc > cutoff || (acc == cutoff && c > cutlim)) {
129227614Sluigi				any = -1;
130245579Sluigi				acc = LONG_MAX;
131245579Sluigi			} else {
132245579Sluigi				any = 1;
133245579Sluigi				acc *= base;
134227614Sluigi				acc += c;
135227614Sluigi			}
136227614Sluigi		}
137227614Sluigi	}
138227614Sluigi	if (endptr != 0)
139230572Sluigi		*endptr = (char *) (any ? s - 1 : nptr);
140234174Sluigi	return (acc);
141227614Sluigi}
142227614Sluigi