mirror of
https://git.planet-casio.com/Vhex-Kernel-Core/fxlibc.git
synced 2025-04-19 17:37:09 +02:00
96 lines
2.5 KiB
C
96 lines
2.5 KiB
C
#include "stdlib_p.h"
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
|
|
/* Parse an integer from a string. Base function for strtol, strtoul, strtoll,
|
|
and strtoull. This function:
|
|
-> Does not set errno, and instead return the potential error code. Setting
|
|
errno would prevent these functions from calling each other as they all
|
|
have different ranges, resulting in undue ERANGE.
|
|
-> Can parse into both long and long long, depending on what pointer is
|
|
non-NULL. */
|
|
int strto_int(char const * restrict ptr, char ** restrict endptr, int base,
|
|
long *outl, long long *outll, bool use_unsigned)
|
|
{
|
|
/* Save the value of ptr in endptr now in case the format is invalid */
|
|
if(endptr) *endptr = (char *)ptr;
|
|
|
|
/* Skip initial whitespace */
|
|
while(isspace(*ptr)) ptr++;
|
|
|
|
/* Accept a sign character */
|
|
bool negative = false;
|
|
if(*ptr == '-') negative = true;
|
|
if(*ptr == '-' || *ptr == '+') ptr++;
|
|
|
|
/* Use unsigned variables as only these have defined overflow */
|
|
unsigned long xl = 0;
|
|
unsigned long long xll = 0;
|
|
|
|
int errno_value = 0;
|
|
bool valid = false;
|
|
|
|
/* Read prefixes and determine base */
|
|
if((base == 0 || base == 16) && ptr[0]=='0' && tolower(ptr[1])=='x') {
|
|
ptr += 2;
|
|
base = 16;
|
|
}
|
|
else if(base == 0 && ptr[0] == '0') {
|
|
ptr++;
|
|
base = 8;
|
|
}
|
|
else if(base == 0) {
|
|
base = 10;
|
|
}
|
|
|
|
/* Read digits */
|
|
while(1) {
|
|
int v = -1;
|
|
if(isdigit(*ptr)) v = *ptr - '0';
|
|
if(islower(*ptr)) v = *ptr - 'a' + 10;
|
|
if(v == -1 || v >= base) break;
|
|
|
|
/* The value is valid as long as there is at least one digit */
|
|
valid = true;
|
|
|
|
/* (x = base*x + v) but with overflow checks */
|
|
|
|
/*
|
|
** TODO FIXME: There is a bug with overflows if the unsigned
|
|
** value cannot be represented but the signed value can (which
|
|
** is the case only for LONG_MIN and LLONG_MIN)
|
|
*/
|
|
if(outl) {
|
|
if(__builtin_umull_overflow(xl, base, &xl))
|
|
errno_value = ERANGE;
|
|
if(__builtin_uaddl_overflow(xl, v, &xl))
|
|
errno_value = ERANGE;
|
|
}
|
|
if(outll) {
|
|
if(__builtin_umulll_overflow(xll, base, &xll))
|
|
errno_value = ERANGE;
|
|
if(__builtin_uaddll_overflow(xll, v, &xll))
|
|
errno_value = ERANGE;
|
|
}
|
|
|
|
ptr++;
|
|
}
|
|
|
|
/* Handle the sign */
|
|
if(negative) {
|
|
/* Only -0 can be represented as unsigned */
|
|
if(outl && use_unsigned && xl != 0)
|
|
errno_value = ERANGE;
|
|
if(outll && use_unsigned && xll != 0)
|
|
errno_value = ERANGE;
|
|
|
|
xl = -xl;
|
|
xll = -xll;
|
|
}
|
|
|
|
if(outl) *outl = xl;
|
|
if(outll) *outll = xll;
|
|
if(endptr && valid) *endptr = (char *)ptr;
|
|
return errno_value;
|
|
}
|