mirror of
https://github.com/cc65/cc65.git
synced 2024-10-31 04:04:49 +00:00
120 lines
2.8 KiB
C
120 lines
2.8 KiB
C
|
#include <limits.h>
|
||
|
#include <ctype.h>
|
||
|
#include <errno.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
|
||
|
|
||
|
unsigned long __fastcall__ strtoul (const char* nptr, char** endptr, int base)
|
||
|
/* Convert a string to a long unsigned int */
|
||
|
{
|
||
|
register const char* S = nptr;
|
||
|
unsigned long Val = 0;
|
||
|
unsigned char Minus = 0;
|
||
|
unsigned char Ovf = 0;
|
||
|
unsigned CvtCount = 0;
|
||
|
unsigned char DigitVal;
|
||
|
unsigned long MaxVal;
|
||
|
unsigned char MaxDigit;
|
||
|
|
||
|
|
||
|
/* Skip white space */
|
||
|
while (isspace (*S)) {
|
||
|
++S;
|
||
|
}
|
||
|
|
||
|
/* Check for leading + or - sign */
|
||
|
switch (*S) {
|
||
|
case '-':
|
||
|
Minus = 1;
|
||
|
/* FALLTHROUGH */
|
||
|
case '+':
|
||
|
++S;
|
||
|
}
|
||
|
|
||
|
/* If base is zero, we may have a 0 or 0x prefix. If base is 16, we may
|
||
|
* have a 0x prefix.
|
||
|
*/
|
||
|
if (base == 0) {
|
||
|
if (*S == '0') {
|
||
|
++S;
|
||
|
if (*S == 'x' || *S == 'X') {
|
||
|
++S;
|
||
|
base = 16;
|
||
|
} else {
|
||
|
base = 8;
|
||
|
}
|
||
|
} else {
|
||
|
base = 10;
|
||
|
}
|
||
|
} else if (base == 16 && *S == '0' && (S[1] == 'x' || S[1] == 'X')) {
|
||
|
S += 2;
|
||
|
}
|
||
|
|
||
|
/* Determine the maximum valid number and (if the number is equal to this
|
||
|
* value) the maximum valid digit.
|
||
|
*/
|
||
|
MaxDigit = ULONG_MAX % base;
|
||
|
MaxVal = ULONG_MAX / base;
|
||
|
|
||
|
/* Convert the number */
|
||
|
while (1) {
|
||
|
|
||
|
/* Convert the digit into a numeric value */
|
||
|
if (isdigit (*S)) {
|
||
|
DigitVal = *S - '0';
|
||
|
} else if (isupper (*S)) {
|
||
|
DigitVal = *S - ('A' - 10);
|
||
|
} else if (islower (*S)) {
|
||
|
DigitVal = *S - ('a' - 10);
|
||
|
} else {
|
||
|
/* Unknown character */
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Don't accept a character that doesn't match base */
|
||
|
if (DigitVal >= base) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Don't accept anything that makes the final value invalid */
|
||
|
if (Val > MaxVal || (Val == MaxVal && DigitVal > MaxDigit)) {
|
||
|
Ovf = 1;
|
||
|
}
|
||
|
|
||
|
/* Calculate the next value if digit is not invalid */
|
||
|
if (Ovf == 0) {
|
||
|
Val = (Val * base) + DigitVal;
|
||
|
++CvtCount;
|
||
|
}
|
||
|
|
||
|
/* Next character from input */
|
||
|
++S;
|
||
|
}
|
||
|
|
||
|
/* Store the end pointer. If no conversion was performed, the value of
|
||
|
* nptr is returned in endptr.
|
||
|
*/
|
||
|
if (endptr) {
|
||
|
if (CvtCount > 0) {
|
||
|
*endptr = (char*) S - 1;
|
||
|
} else {
|
||
|
*endptr = (char*) nptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Handle overflow */
|
||
|
if (Ovf) {
|
||
|
errno = ERANGE;
|
||
|
return ULONG_MAX;
|
||
|
}
|
||
|
|
||
|
/* Return the result */
|
||
|
if (Minus) {
|
||
|
return (unsigned long) -(long)Val;
|
||
|
} else {
|
||
|
return Val;
|
||
|
}
|
||
|
}
|
||
|
|