rockbox/firmware/libc/strtoul.c
Solomon Peachy 481cc70fe0 libc: Add an implementation of strtol/strtoul and export it via plugins
These were lifted from the lua plugin.

sdl, doom, puzzles updated to use the exported version

todo: lua, maybe?
also: convert uses of atoi [back] to strtol

Change-Id: I5a1ebbe8d8c99349e594ab9bbbce474e7645b4e9
2025-12-06 09:04:36 -05:00

57 lines
1.3 KiB
C

#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
#include <gcc_extensions.h>
unsigned long int strtoul(const char *ptr, char **endptr, int base)
{
int neg = 0, overflow = 0;
unsigned long int v=0;
const char* orig;
const char* nptr=ptr;
while(UNLIKELY(isspace(*nptr))) ++nptr;
if (*nptr == '-') { neg=1; nptr++; }
else if (*nptr == '+') ++nptr;
orig=nptr;
if (base==16 && nptr[0]=='0') goto skip0x;
if (base) {
register unsigned int b=base-2;
if (UNLIKELY(b>34)) { errno=EINVAL; return 0; }
} else {
if (*nptr=='0') {
base=8;
skip0x:
if ((nptr[1]=='x'||nptr[1]=='X') && isxdigit(nptr[2])) {
nptr+=2;
base=16;
}
} else
base=10;
}
while(LIKELY(*nptr)) {
register unsigned char c=*nptr;
c=(c>='a'?c-'a'+10:c>='A'?c-'A'+10:c<='9'?c-'0':0xff);
if (UNLIKELY(c>=base)) break; /* out of base */
{
register unsigned long x=(v&0xff)*base+c;
register unsigned long w=(v>>8)*base+(x>>8);
if (w>(ULONG_MAX>>8)) overflow=1;
v=(w<<8)+(x&0xff);
}
++nptr;
}
if (UNLIKELY(nptr==orig)) { /* no conversion done */
nptr=ptr;
errno=EINVAL;
v=0;
}
if (endptr) *endptr=(char *)nptr;
if (overflow) {
errno=ERANGE;
return ULONG_MAX;
}
return (neg?-v:v);
}