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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ab6edb6 was 55092672, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Clean up libposix stdio.h and stdlib.h a bit.

  • Property mode set to 100644
File size: 6.6 KB
Line 
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
39#include <assert.h>
40#include <ctype.h>
41#include <errno.h>
42#include <inttypes.h>
43#include <limits.h>
44#include <stdbool.h>
45#include <stdlib.h>
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
61/*
62 * FIXME: workaround for GCC "optimizing" the overflow check
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
71static inline uintmax_t _strtoumax(
72 const char *restrict nptr, char **restrict endptr, int base,
73 bool *restrict sgn)
74{
75 assert(nptr != NULL);
76 assert(sgn != NULL);
77
78 /* Skip leading whitespace. */
79
80 while (isspace(*nptr)) {
81 nptr++;
82 }
83
84 /* Parse sign, if any. */
85
86 switch (*nptr) {
87 case '-':
88 *sgn = true;
89 nptr++;
90 break;
91 case '+':
92 nptr++;
93 break;
94 }
95
96 /* Figure out the base. */
97
98 if (base == 0) {
99 if (*nptr == '0') {
100 if (tolower(nptr[1]) == 'x') {
101 /* 0x... is hex. */
102 base = 16;
103 nptr += 2;
104 } else {
105 /* 0... is octal. */
106 base = 8;
107 }
108 } else {
109 /* Anything else is decimal by default. */
110 base = 10;
111 }
112 } else if (base == 16) {
113 /* Allow hex number to be prefixed with "0x". */
114 if (nptr[0] == '0' && tolower(nptr[1]) == 'x') {
115 nptr += 2;
116 }
117 } else if (base < 0 || base == 1 || base > 36) {
118 errno = EINVAL;
119 return 0;
120 }
121
122 /* Read the value. */
123
124 uintmax_t result = 0;
125 uintmax_t max = _max_value(base);
126 int digit;
127
128 while (digit = _digit_value(*nptr), digit < base) {
129
130 if (result > max ||
131 __builtin_add_overflow(result * base, digit, &result)) {
132
133 errno = ERANGE;
134 result = UINTMAX_MAX;
135 break;
136 }
137
138 nptr++;
139 }
140
141 /* Set endptr. */
142
143 if (endptr != NULL) {
144 /*
145 * Move the pointer to the end of the number,
146 * in case it isn't there already.
147 */
148 while (_digit_value(*nptr) < base) {
149 nptr++;
150 }
151
152 *endptr = (char *) nptr;
153 }
154
155 return result;
156}
157
158static inline intmax_t _strtosigned(const char *nptr, char **endptr, int base,
159 intmax_t min, intmax_t max)
160{
161 bool sgn = false;
162 uintmax_t number = _strtoumax(nptr, endptr, base, &sgn);
163
164 if (number > (uintmax_t) max) {
165 if (sgn && (number - 1 == (uintmax_t) max)) {
166 return min;
167 }
168
169 errno = ERANGE;
170 return (sgn ? min : max);
171 }
172
173 return (sgn ? -number : number);
174}
175
176static inline uintmax_t _strtounsigned(const char *nptr, char **endptr, int base,
177 uintmax_t max)
178{
179 bool sgn = false;
180 uintmax_t number = _strtoumax(nptr, endptr, base, &sgn);
181
182 if (sgn) {
183 if (number == 0) {
184 return 0;
185 } else {
186 errno = ERANGE;
187 return max;
188 }
189 }
190
191 if (number > max) {
192 errno = ERANGE;
193 return max;
194 }
195
196 return number;
197}
198
199/** Convert initial part of string to long int according to given base.
200 * The number may begin with an arbitrary number of whitespaces followed by
201 * optional sign (`+' or `-'). If the base is 0 or 16, the prefix `0x' may be
202 * inserted and the number will be taken as hexadecimal one. If the base is 0
203 * and the number begin with a zero, number will be taken as octal one (as with
204 * base 8). Otherwise the base 0 is taken as decimal.
205 *
206 * @param nptr Pointer to string.
207 * @param[out] endptr If not NULL, function stores here pointer to the first
208 * invalid character.
209 * @param base Zero or number between 2 and 36 inclusive.
210 * @return Result of conversion.
211 */
212long strtol(const char *nptr, char **endptr, int base)
213{
214 return _strtosigned(nptr, endptr, base, LONG_MIN, LONG_MAX);
215}
216
217/** Convert initial part of string to unsigned long according to given base.
218 * The number may begin with an arbitrary number of whitespaces followed by
219 * optional sign (`+' or `-'). If the base is 0 or 16, the prefix `0x' may be
220 * inserted and the number will be taken as hexadecimal one. If the base is 0
221 * and the number begin with a zero, number will be taken as octal one (as with
222 * base 8). Otherwise the base 0 is taken as decimal.
223 *
224 * @param nptr Pointer to string.
225 * @param[out] endptr If not NULL, function stores here pointer to the first
226 * invalid character
227 * @param base Zero or number between 2 and 36 inclusive.
228 * @return Result of conversion.
229 */
230unsigned long strtoul(const char *nptr, char **endptr, int base)
231{
232 return _strtounsigned(nptr, endptr, base, ULONG_MAX);
233}
234
235long long strtoll(const char *nptr, char **endptr, int base)
236{
237 return _strtosigned(nptr, endptr, base, LLONG_MIN, LLONG_MAX);
238}
239
240unsigned long long strtoull(const char *nptr, char **endptr, int base)
241{
242 return _strtounsigned(nptr, endptr, base, ULLONG_MAX);
243}
244
245intmax_t strtoimax(const char *nptr, char **endptr, int base)
246{
247 return _strtosigned(nptr, endptr, base, INTMAX_MIN, INTMAX_MAX);
248}
249
250uintmax_t strtoumax(const char *nptr, char **endptr, int base)
251{
252 return _strtounsigned(nptr, endptr, base, UINTMAX_MAX);
253}
254
255int atoi(const char *nptr)
256{
257 return (int)strtol(nptr, NULL, 10);
258}
259
260long atol(const char *nptr)
261{
262 return strtol(nptr, NULL, 10);
263}
264
265long long atoll(const char *nptr)
266{
267 return strtoll(nptr, NULL, 10);
268}
269
270/** @}
271 */
Note: See TracBrowser for help on using the repository browser.