source: mainline/common/strtol.c@ e2b417f

Last change on this file since e2b417f was fdfb24e, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 20 months ago

Deduplicate string related functions

  • Property mode set to 100644
File size: 13.1 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#include <str.h>
47
48// FIXME: The original HelenOS functions return EOVERFLOW instead
49// of ERANGE. It's a pointless distinction from standard functions,
50// so we should change that. Beware the callers though.
51
52// TODO: more unit tests
53
54static inline int _digit_value(int c)
55{
56 if (isdigit(c)) {
57 return c - '0';
58 } else if (islower(c)) {
59 return c - 'a' + 10;
60 } else if (isupper(c)) {
61 return c - 'A' + 10;
62 }
63 return INT_MAX;
64}
65
66/*
67 * FIXME: workaround for GCC "optimizing" the overflow check
68 * into soft-emulated 128b multiplication using `__multi3`,
69 * which we don't currently implement.
70 */
71__attribute__((noinline)) static uintmax_t _max_value(int base)
72{
73 return UINTMAX_MAX / base;
74}
75
76static inline int _prefixbase(const char *restrict *nptrptr, bool nonstd)
77{
78 const char *nptr = *nptrptr;
79
80 if (nptr[0] != '0')
81 return 10;
82
83 if (nptr[1] == 'x' || nptr[1] == 'X') {
84 if (_digit_value(nptr[2]) < 16) {
85 *nptrptr += 2;
86 return 16;
87 }
88 }
89
90 if (nonstd) {
91 switch (nptr[1]) {
92 case 'b':
93 case 'B':
94 if (_digit_value(nptr[2]) < 2) {
95 *nptrptr += 2;
96 return 2;
97 }
98 break;
99 case 'o':
100 case 'O':
101 if (_digit_value(nptr[2]) < 8) {
102 *nptrptr += 2;
103 return 8;
104 }
105 break;
106 case 'd':
107 case 'D':
108 case 't':
109 case 'T':
110 if (_digit_value(nptr[2]) < 10) {
111 *nptrptr += 2;
112 return 10;
113 }
114 break;
115 }
116 }
117
118 return 8;
119}
120
121static inline uintmax_t _strtoumax(
122 const char *restrict nptr, char **restrict endptr, int base,
123 bool *restrict sgn, errno_t *err, bool nonstd)
124{
125 assert(nptr != NULL);
126 assert(sgn != NULL);
127
128 const char *first = nptr;
129
130 /* Skip leading whitespace. */
131
132 while (isspace(*nptr)) {
133 nptr++;
134 }
135
136 /* Parse sign, if any. */
137
138 switch (*nptr) {
139 case '-':
140 *sgn = true;
141 nptr++;
142 break;
143 case '+':
144 nptr++;
145 break;
146 }
147
148 /* Figure out the base. */
149
150 if (base == 0)
151 base = _prefixbase(&nptr, nonstd);
152
153 if (base == 16 && !nonstd) {
154 /*
155 * Standard strto* functions allow hexadecimal prefix to be
156 * present when base is explicitly set to 16.
157 * Our nonstandard str_* functions don't allow it.
158 * I don't know if that is intended, just matching the original
159 * functionality here.
160 */
161
162 if (nptr[0] == '0' && (nptr[1] == 'x' || nptr[1] == 'X') &&
163 _digit_value(nptr[2]) < base)
164 nptr += 2;
165 }
166
167 if (base < 2 || base > 36) {
168 *err = EINVAL;
169 return 0;
170 }
171
172 /* Must be at least one digit. */
173
174 if (_digit_value(*nptr) >= base) {
175 /* No digits on input. */
176 if (endptr != NULL)
177 *endptr = (char *) first;
178 return 0;
179 }
180
181 /* Read the value. */
182
183 uintmax_t result = 0;
184 uintmax_t max = _max_value(base);
185 int digit;
186
187 while (digit = _digit_value(*nptr), digit < base) {
188 if (result > max ||
189 __builtin_add_overflow(result * base, digit, &result)) {
190
191 *err = nonstd ? EOVERFLOW : ERANGE;
192 result = UINTMAX_MAX;
193 break;
194 }
195
196 nptr++;
197 }
198
199 /* Set endptr. */
200
201 if (endptr != NULL) {
202 /*
203 * Move the pointer to the end of the number,
204 * in case it isn't there already.
205 * This can happen when the number has legal formatting,
206 * but is out of range of the target type.
207 */
208 while (_digit_value(*nptr) < base) {
209 nptr++;
210 }
211
212 *endptr = (char *) nptr;
213 }
214
215 return result;
216}
217
218static inline intmax_t _strtosigned(const char *nptr, char **endptr, int base,
219 intmax_t min, intmax_t max, errno_t *err, bool nonstd)
220{
221 bool sgn = false;
222 uintmax_t number = _strtoumax(nptr, endptr, base, &sgn, err, nonstd);
223
224 if (number > (uintmax_t) max) {
225 if (sgn && (number - 1 == (uintmax_t) max)) {
226 return min;
227 }
228
229 *err = nonstd ? EOVERFLOW : ERANGE;
230 return (sgn ? min : max);
231 }
232
233 return (sgn ? -number : number);
234}
235
236static inline uintmax_t _strtounsigned(const char *nptr, char **endptr, int base,
237 uintmax_t max, errno_t *err, bool nonstd)
238{
239 bool sgn = false;
240 uintmax_t number = _strtoumax(nptr, endptr, base, &sgn, err, nonstd);
241
242 if (nonstd && sgn) {
243 /* Do not allow negative values */
244 *err = EINVAL;
245 return 0;
246 }
247
248 if (number > max) {
249 *err = nonstd ? EOVERFLOW : ERANGE;
250 return max;
251 }
252
253 return (sgn ? -number : number);
254}
255
256/** Convert initial part of string to long int according to given base.
257 * The number may begin with an arbitrary number of whitespaces followed by
258 * optional sign (`+' or `-'). If the base is 0 or 16, the prefix `0x' may be
259 * inserted and the number will be taken as hexadecimal one. If the base is 0
260 * and the number begin with a zero, number will be taken as octal one (as with
261 * base 8). Otherwise the base 0 is taken as decimal.
262 *
263 * @param nptr Pointer to string.
264 * @param[out] endptr If not NULL, function stores here pointer to the first
265 * invalid character.
266 * @param base Zero or number between 2 and 36 inclusive.
267 * @return Result of conversion.
268 */
269long strtol(const char *nptr, char **endptr, int base)
270{
271#if !__STDC_HOSTED__
272 errno_t errno;
273#endif
274
275 return _strtosigned(nptr, endptr, base, LONG_MIN, LONG_MAX, &errno, false);
276}
277
278/** Convert initial part of string to unsigned long according to given base.
279 * The number may begin with an arbitrary number of whitespaces followed by
280 * optional sign (`+' or `-'). If the base is 0 or 16, the prefix `0x' may be
281 * inserted and the number will be taken as hexadecimal one. If the base is 0
282 * and the number begin with a zero, number will be taken as octal one (as with
283 * base 8). Otherwise the base 0 is taken as decimal.
284 *
285 * @param nptr Pointer to string.
286 * @param[out] endptr If not NULL, function stores here pointer to the first
287 * invalid character
288 * @param base Zero or number between 2 and 36 inclusive.
289 * @return Result of conversion.
290 */
291unsigned long strtoul(const char *nptr, char **endptr, int base)
292{
293#if !__STDC_HOSTED__
294 errno_t errno;
295#endif
296
297 return _strtounsigned(nptr, endptr, base, ULONG_MAX, &errno, false);
298}
299
300long long strtoll(const char *nptr, char **endptr, int base)
301{
302#if !__STDC_HOSTED__
303 errno_t errno;
304#endif
305
306 return _strtosigned(nptr, endptr, base, LLONG_MIN, LLONG_MAX, &errno, false);
307}
308
309unsigned long long strtoull(const char *nptr, char **endptr, int base)
310{
311#if !__STDC_HOSTED__
312 errno_t errno;
313#endif
314
315 return _strtounsigned(nptr, endptr, base, ULLONG_MAX, &errno, false);
316}
317
318intmax_t strtoimax(const char *nptr, char **endptr, int base)
319{
320#if !__STDC_HOSTED__
321 errno_t errno;
322#endif
323
324 return _strtosigned(nptr, endptr, base, INTMAX_MIN, INTMAX_MAX, &errno, false);
325}
326
327uintmax_t strtoumax(const char *nptr, char **endptr, int base)
328{
329#if !__STDC_HOSTED__
330 errno_t errno;
331#endif
332
333 return _strtounsigned(nptr, endptr, base, UINTMAX_MAX, &errno, false);
334}
335
336int atoi(const char *nptr)
337{
338 return (int)strtol(nptr, NULL, 10);
339}
340
341long atol(const char *nptr)
342{
343 return strtol(nptr, NULL, 10);
344}
345
346long long atoll(const char *nptr)
347{
348 return strtoll(nptr, NULL, 10);
349}
350
351/** Convert string to uint8_t.
352 *
353 * @param nptr Pointer to string.
354 * @param endptr If not NULL, pointer to the first invalid character
355 * is stored here.
356 * @param base Zero or number between 2 and 36 inclusive.
357 * @param strict Do not allow any trailing characters.
358 * @param result Result of the conversion.
359 *
360 * @return EOK if conversion was successful.
361 *
362 */
363errno_t str_uint8_t(const char *nptr, const char **endptr, unsigned int base,
364 bool strict, uint8_t *result)
365{
366 assert(result != NULL);
367
368 errno_t rc = EOK;
369 char *lendptr = (char *) nptr;
370
371 uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT8_MAX, &rc, true);
372
373 if (endptr)
374 *endptr = lendptr;
375
376 if (rc != EOK)
377 return rc;
378
379 if (strict && *lendptr != '\0')
380 return EINVAL;
381
382 *result = r;
383 return EOK;
384}
385
386/** Convert string to uint16_t.
387 *
388 * @param nptr Pointer to string.
389 * @param endptr If not NULL, pointer to the first invalid character
390 * is stored here.
391 * @param base Zero or number between 2 and 36 inclusive.
392 * @param strict Do not allow any trailing characters.
393 * @param result Result of the conversion.
394 *
395 * @return EOK if conversion was successful.
396 *
397 */
398errno_t str_uint16_t(const char *nptr, const char **endptr, unsigned int base,
399 bool strict, uint16_t *result)
400{
401 assert(result != NULL);
402
403 errno_t rc = EOK;
404 char *lendptr = (char *) nptr;
405
406 uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT16_MAX, &rc, true);
407
408 if (endptr)
409 *endptr = lendptr;
410
411 if (rc != EOK)
412 return rc;
413
414 if (strict && *lendptr != '\0')
415 return EINVAL;
416
417 *result = r;
418 return EOK;
419}
420
421/** Convert string to uint32_t.
422 *
423 * @param nptr Pointer to string.
424 * @param endptr If not NULL, pointer to the first invalid character
425 * is stored here.
426 * @param base Zero or number between 2 and 36 inclusive.
427 * @param strict Do not allow any trailing characters.
428 * @param result Result of the conversion.
429 *
430 * @return EOK if conversion was successful.
431 *
432 */
433errno_t str_uint32_t(const char *nptr, const char **endptr, unsigned int base,
434 bool strict, uint32_t *result)
435{
436 assert(result != NULL);
437
438 errno_t rc = EOK;
439 char *lendptr = (char *) nptr;
440
441 uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT32_MAX, &rc, true);
442
443 if (endptr)
444 *endptr = lendptr;
445
446 if (rc != EOK)
447 return rc;
448
449 if (strict && *lendptr != '\0')
450 return EINVAL;
451
452 *result = r;
453 return EOK;
454}
455
456/** Convert string to uint64_t.
457 *
458 * @param nptr Pointer to string.
459 * @param endptr If not NULL, pointer to the first invalid character
460 * is stored here.
461 * @param base Zero or number between 2 and 36 inclusive.
462 * @param strict Do not allow any trailing characters.
463 * @param result Result of the conversion.
464 *
465 * @return EOK if conversion was successful.
466 *
467 */
468errno_t str_uint64_t(const char *nptr, const char **endptr, unsigned int base,
469 bool strict, uint64_t *result)
470{
471 assert(result != NULL);
472
473 errno_t rc = EOK;
474 char *lendptr = (char *) nptr;
475
476 uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT64_MAX, &rc, true);
477
478 if (endptr)
479 *endptr = lendptr;
480
481 if (rc != EOK)
482 return rc;
483
484 if (strict && *lendptr != '\0')
485 return EINVAL;
486
487 *result = r;
488 return EOK;
489}
490
491/** Convert string to int64_t.
492 *
493 * @param nptr Pointer to string.
494 * @param endptr If not NULL, pointer to the first invalid character
495 * is stored here.
496 * @param base Zero or number between 2 and 36 inclusive.
497 * @param strict Do not allow any trailing characters.
498 * @param result Result of the conversion.
499 *
500 * @return EOK if conversion was successful.
501 *
502 */
503errno_t str_int64_t(const char *nptr, const char **endptr, unsigned int base,
504 bool strict, int64_t *result)
505{
506 assert(result != NULL);
507
508 errno_t rc = EOK;
509 char *lendptr = (char *) nptr;
510
511 intmax_t r = _strtosigned(nptr, &lendptr, base, INT64_MIN, INT64_MAX, &rc, true);
512
513 if (endptr)
514 *endptr = lendptr;
515
516 if (rc != EOK)
517 return rc;
518
519 if (strict && *lendptr != '\0')
520 return EINVAL;
521
522 *result = r;
523 return EOK;
524}
525
526/** Convert string to size_t.
527 *
528 * @param nptr Pointer to string.
529 * @param endptr If not NULL, pointer to the first invalid character
530 * is stored here.
531 * @param base Zero or number between 2 and 36 inclusive.
532 * @param strict Do not allow any trailing characters.
533 * @param result Result of the conversion.
534 *
535 * @return EOK if conversion was successful.
536 *
537 */
538errno_t str_size_t(const char *nptr, const char **endptr, unsigned int base,
539 bool strict, size_t *result)
540{
541 assert(result != NULL);
542
543 errno_t rc = EOK;
544 char *lendptr = (char *) nptr;
545
546 uintmax_t r = _strtounsigned(nptr, &lendptr, base, SIZE_MAX, &rc, true);
547
548 if (endptr)
549 *endptr = lendptr;
550
551 if (rc != EOK)
552 return rc;
553
554 if (strict && *lendptr != '\0')
555 return EINVAL;
556
557 *result = r;
558 return EOK;
559}
560
561/** @}
562 */
Note: See TracBrowser for help on using the repository browser.