source: mainline/uspace/lib/c/generic/strtol.c@ 6ff23ff

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

Fix block comment formatting (ccheck).

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