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

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

Implement the full suite of standard string-to-int conversion functions in libc.

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