source: mainline/uspace/lib/c/generic/strtol.c@ dd0502ae

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since dd0502ae was 0260034, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 6 years ago

Fix and test additional corner cases in strtol()

  • Property mode set to 100644
File size: 7.9 KB
RevLine 
[d39c46e0]1/*
2 * Copyright (c) 2005 Martin Decky
3 * Copyright (c) 2008 Jiri Svoboda
4 * Copyright (c) 2011 Martin Sucha
5 * Copyright (c) 2011 Oleg Romanenko
6 * Copyright (c) 2011 Jiri Zarevucky
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * - Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * - The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/** @addtogroup libc
34 * @{
35 */
36/** @file
37 */
38
[55092672]39#include <assert.h>
40#include <ctype.h>
41#include <errno.h>
[d39c46e0]42#include <inttypes.h>
43#include <limits.h>
[55092672]44#include <stdbool.h>
45#include <stdlib.h>
[d39c46e0]46
47// TODO: unit tests
48
49static inline int _digit_value(int c)
50{
51 if (isdigit(c)) {
52 return c - '0';
53 } else if (islower(c)) {
54 return c - 'a' + 10;
55 } else if (isupper(c)) {
56 return c - 'A' + 10;
57 }
58 return INT_MAX;
59}
60
[7c3fb9b]61/*
62 * FIXME: workaround for GCC "optimizing" the overflow check
[17c14273]63 * into soft-emulated 128b multiplication using `__multi3`,
64 * which we don't currently implement.
65 */
66__attribute__((noinline)) static uintmax_t _max_value(int base)
67{
68 return UINTMAX_MAX / base;
69}
70
[31a566b]71static inline int _prefixbase(const char *restrict *nptrptr, bool nonstandard_prefixes)
72{
73 const char *nptr = *nptrptr;
74
75 if (nptr[0] != '0')
76 return 10;
77
78 if (nptr[1] == 'x' || nptr[1] == 'X') {
[0260034]79 if (_digit_value(nptr[2]) < 16) {
80 *nptrptr += 2;
81 return 16;
82 }
[31a566b]83 }
84
85 if (nonstandard_prefixes) {
86 switch (nptr[1]) {
87 case 'b':
88 case 'B':
[0260034]89 if (_digit_value(nptr[2]) < 2) {
90 *nptrptr += 2;
91 return 2;
92 }
93 break;
[31a566b]94 case 'o':
95 case 'O':
[0260034]96 if (_digit_value(nptr[2]) < 8) {
97 *nptrptr += 2;
98 return 8;
99 }
100 break;
[31a566b]101 case 'd':
102 case 'D':
103 case 't':
104 case 'T':
[0260034]105 if (_digit_value(nptr[2]) < 10) {
106 *nptrptr += 2;
107 return 10;
108 }
109 break;
[31a566b]110 }
111 }
112
113 return 8;
114}
115
[d39c46e0]116static inline uintmax_t _strtoumax(
117 const char *restrict nptr, char **restrict endptr, int base,
[31a566b]118 bool *restrict sgn, errno_t *err, bool nonstandard_prefixes)
[d39c46e0]119{
120 assert(nptr != NULL);
121 assert(sgn != NULL);
122
[0260034]123 const char *first = nptr;
124
[d39c46e0]125 /* Skip leading whitespace. */
126
127 while (isspace(*nptr)) {
128 nptr++;
129 }
130
131 /* Parse sign, if any. */
132
133 switch (*nptr) {
134 case '-':
135 *sgn = true;
136 nptr++;
137 break;
138 case '+':
139 nptr++;
140 break;
141 }
142
143 /* Figure out the base. */
144
[31a566b]145 if (base == 0)
146 base = _prefixbase(&nptr, nonstandard_prefixes);
147
148 if (base == 16 && !nonstandard_prefixes) {
149 /*
150 * Standard strto* functions allow hexadecimal prefix to be
151 * present when base is explicitly set to 16.
152 * Our nonstandard str_* functions don't allow it.
[0260034]153 * I don't know if that is intended, just matching the original
154 * functionality here.
[31a566b]155 */
156
[0260034]157 if (nptr[0] == '0' && (nptr[1] == 'x' || nptr[1] == 'X') &&
158 _digit_value(nptr[2]) < base)
[d39c46e0]159 nptr += 2;
[31a566b]160 }
161
162 if (base < 2 || base > 36) {
163 *err = EINVAL;
[d39c46e0]164 return 0;
165 }
166
[0260034]167 /* Must be at least one digit. */
168
169 if (_digit_value(*nptr) >= base) {
170 /* No digits on input. */
171 if (endptr != NULL)
172 *endptr = (char *) first;
173 return 0;
174 }
175
176 /* Read the value. */
[d39c46e0]177
178 uintmax_t result = 0;
[17c14273]179 uintmax_t max = _max_value(base);
[d39c46e0]180 int digit;
181
182 while (digit = _digit_value(*nptr), digit < base) {
[17c14273]183 if (result > max ||
184 __builtin_add_overflow(result * base, digit, &result)) {
[d39c46e0]185
[31a566b]186 *err = ERANGE;
[d39c46e0]187 result = UINTMAX_MAX;
188 break;
189 }
190
191 nptr++;
192 }
193
194 /* Set endptr. */
195
196 if (endptr != NULL) {
[7c3fb9b]197 /*
198 * Move the pointer to the end of the number,
[d39c46e0]199 * in case it isn't there already.
[31a566b]200 * This can happen when the number has legal formatting,
201 * but is out of range of the target type.
[d39c46e0]202 */
203 while (_digit_value(*nptr) < base) {
204 nptr++;
205 }
206
207 *endptr = (char *) nptr;
208 }
209
210 return result;
211}
212
213static inline intmax_t _strtosigned(const char *nptr, char **endptr, int base,
[31a566b]214 intmax_t min, intmax_t max, errno_t *err, bool nonstandard_prefixes)
[d39c46e0]215{
216 bool sgn = false;
[31a566b]217 uintmax_t number = _strtoumax(nptr, endptr, base, &sgn, err,
218 nonstandard_prefixes);
[d39c46e0]219
220 if (number > (uintmax_t) max) {
221 if (sgn && (number - 1 == (uintmax_t) max)) {
222 return min;
223 }
224
[31a566b]225 *err = ERANGE;
[d39c46e0]226 return (sgn ? min : max);
227 }
228
229 return (sgn ? -number : number);
230}
231
232static inline uintmax_t _strtounsigned(const char *nptr, char **endptr, int base,
[31a566b]233 uintmax_t max, errno_t *err, bool nonstandard_prefixes)
[d39c46e0]234{
235 bool sgn = false;
[31a566b]236 uintmax_t number = _strtoumax(nptr, endptr, base, &sgn, err,
237 nonstandard_prefixes);
[d39c46e0]238
239 if (number > max) {
[31a566b]240 *err = ERANGE;
[d39c46e0]241 return max;
242 }
243
[31a566b]244 return (sgn ? -number : number);
[d39c46e0]245}
246
247/** Convert initial part of string to long int according to given base.
248 * The number may begin with an arbitrary number of whitespaces followed by
249 * optional sign (`+' or `-'). If the base is 0 or 16, the prefix `0x' may be
250 * inserted and the number will be taken as hexadecimal one. If the base is 0
251 * and the number begin with a zero, number will be taken as octal one (as with
252 * base 8). Otherwise the base 0 is taken as decimal.
253 *
254 * @param nptr Pointer to string.
255 * @param[out] endptr If not NULL, function stores here pointer to the first
256 * invalid character.
257 * @param base Zero or number between 2 and 36 inclusive.
258 * @return Result of conversion.
259 */
260long strtol(const char *nptr, char **endptr, int base)
261{
[31a566b]262 return _strtosigned(nptr, endptr, base, LONG_MIN, LONG_MAX, &errno, false);
[d39c46e0]263}
264
265/** Convert initial part of string to unsigned long according to given base.
266 * The number may begin with an arbitrary number of whitespaces followed by
267 * optional sign (`+' or `-'). If the base is 0 or 16, the prefix `0x' may be
268 * inserted and the number will be taken as hexadecimal one. If the base is 0
269 * and the number begin with a zero, number will be taken as octal one (as with
270 * base 8). Otherwise the base 0 is taken as decimal.
271 *
272 * @param nptr Pointer to string.
273 * @param[out] endptr If not NULL, function stores here pointer to the first
274 * invalid character
275 * @param base Zero or number between 2 and 36 inclusive.
276 * @return Result of conversion.
277 */
278unsigned long strtoul(const char *nptr, char **endptr, int base)
279{
[31a566b]280 return _strtounsigned(nptr, endptr, base, ULONG_MAX, &errno, false);
[d39c46e0]281}
282
283long long strtoll(const char *nptr, char **endptr, int base)
284{
[31a566b]285 return _strtosigned(nptr, endptr, base, LLONG_MIN, LLONG_MAX, &errno, false);
[d39c46e0]286}
287
288unsigned long long strtoull(const char *nptr, char **endptr, int base)
289{
[31a566b]290 return _strtounsigned(nptr, endptr, base, ULLONG_MAX, &errno, false);
[d39c46e0]291}
292
293intmax_t strtoimax(const char *nptr, char **endptr, int base)
294{
[31a566b]295 return _strtosigned(nptr, endptr, base, INTMAX_MIN, INTMAX_MAX, &errno, false);
[d39c46e0]296}
297
298uintmax_t strtoumax(const char *nptr, char **endptr, int base)
299{
[31a566b]300 return _strtounsigned(nptr, endptr, base, UINTMAX_MAX, &errno, false);
[d39c46e0]301}
302
303int atoi(const char *nptr)
304{
305 return (int)strtol(nptr, NULL, 10);
306}
307
308long atol(const char *nptr)
309{
310 return strtol(nptr, NULL, 10);
311}
312
313long long atoll(const char *nptr)
314{
315 return strtoll(nptr, NULL, 10);
316}
317
318/** @}
319 */
Note: See TracBrowser for help on using the repository browser.