174 lines
4.0 KiB
C
174 lines
4.0 KiB
C
/*
|
|
* Copyright (C) 2011 Rick_1995@xda-developers.com
|
|
*
|
|
* Notes:
|
|
* The primary objective of this implementation was minimal size.
|
|
* Note: Assumes char layout 0-9.*A-Z.*a-z for ordinals values.
|
|
* There are a couple of compile-time options below.
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
/* OPTIONS */
|
|
/*****************************************************************************/
|
|
|
|
/* Set if we want strtod to set errno appropriately. */
|
|
/* NOTE: Implies _STRTO_ENDPTR below */
|
|
#define _STRTO_ERRNO 0
|
|
|
|
/* Set if we want support for the endptr arg. */
|
|
/* Implied by _STRTO_ERRNO. */
|
|
#define _STRTO_ENDPTR 1
|
|
|
|
/*****************************************************************************/
|
|
/* Don't change anything that follows. */
|
|
/*****************************************************************************/
|
|
|
|
#if _STRTO_ERRNO
|
|
#undef _STRTO_ENDPTR
|
|
#define _STRTO_ENDPTR 1
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Are there actually an machines where this might fail? */
|
|
#if 'A' > 'a'
|
|
#error ordering assumption violated : 'A' > 'a'
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
|
|
#if _STRTO_ERRNO
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
/*
|
|
* This is the main work fuction which handles both strtol (uflag = 0) and
|
|
* strtoul (uflag = 1).
|
|
*/
|
|
|
|
unsigned long _strto_l(const char *str, char **endptr, int base, int uflag)
|
|
{
|
|
unsigned long number = 0;
|
|
unsigned long cutoff;
|
|
char *pos = (char *) str;
|
|
#if _STRTO_ENDPTR
|
|
char *fail_char = (char *) str;
|
|
#endif
|
|
int digit, cutoff_digit;
|
|
int negative;
|
|
|
|
while (isspace(*pos)) { /* skip leading whitespace */
|
|
++pos;
|
|
}
|
|
|
|
/* handle optional sign */
|
|
negative = 0;
|
|
switch(*pos) {
|
|
case '-': negative = 1; /* fall through to increment pos */
|
|
case '+': ++pos;
|
|
}
|
|
|
|
if ((base == 16) && (*pos == '0')) { /* handle option prefix */
|
|
++pos;
|
|
#if _STRTO_ENDPTR
|
|
fail_char = pos;
|
|
#endif
|
|
if ((*pos == 'x') || (*pos == 'X')) {
|
|
++pos;
|
|
}
|
|
}
|
|
|
|
if (base == 0) { /* dynamic base */
|
|
base = 10; /* default is 10 */
|
|
if (*pos == '0') {
|
|
++pos;
|
|
base -= 2; /* now base is 8 (or 16) */
|
|
#if _STRTO_ENDPTR
|
|
fail_char = pos;
|
|
#endif
|
|
if ((*pos == 'x') || (*pos == 'X')) {
|
|
base += 8; /* base is 16 */
|
|
++pos;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((base < 2) || (base > 36)) { /* illegal base */
|
|
goto DONE;
|
|
}
|
|
|
|
cutoff = ULONG_MAX / base;
|
|
cutoff_digit = ULONG_MAX - cutoff * base;
|
|
|
|
while (1) {
|
|
digit = 40;
|
|
if ((*pos >= '0') && (*pos <= '9')) {
|
|
digit = (*pos - '0');
|
|
} else if (*pos >= 'a') {
|
|
digit = (*pos - 'a' + 10);
|
|
} else if (*pos >= 'A') {
|
|
digit = (*pos - 'A' + 10);
|
|
} else break;
|
|
|
|
if (digit >= base) {
|
|
break;
|
|
}
|
|
|
|
++pos;
|
|
#if _STRTO_ENDPTR
|
|
fail_char = pos;
|
|
#endif
|
|
|
|
/* adjust number, with overflow check */
|
|
if ((number > cutoff)
|
|
|| ((number == cutoff) && (digit > cutoff_digit))) {
|
|
number = ULONG_MAX;
|
|
if (uflag) {
|
|
negative = 0; /* since unsigned returns ULONG_MAX */
|
|
}
|
|
#if _STRTO_ERRNO
|
|
errno = ERANGE;
|
|
#endif
|
|
} else {
|
|
number = number * base + digit;
|
|
}
|
|
|
|
}
|
|
|
|
DONE:
|
|
#if _STRTO_ENDPTR
|
|
if (endptr) {
|
|
*endptr = fail_char;
|
|
}
|
|
#endif
|
|
|
|
if (negative) {
|
|
if (!uflag && (number > ((unsigned long)(-(1+LONG_MIN)))+1)) {
|
|
#if _STRTO_ERRNO
|
|
errno = ERANGE;
|
|
#endif
|
|
return (unsigned long) LONG_MIN;
|
|
}
|
|
return (unsigned long)(-((long)number));
|
|
} else {
|
|
if (!uflag && (number > (unsigned long) LONG_MAX)) {
|
|
#if _STRTO_ERRNO
|
|
errno = ERANGE;
|
|
#endif
|
|
return LONG_MAX;
|
|
}
|
|
return number;
|
|
}
|
|
}
|
|
|
|
unsigned long strtoul(const char *str, char **endptr, int base)
|
|
{
|
|
return _strto_l(str, endptr, base, 1);
|
|
}
|
|
|
|
long strtol(const char *str, char **endptr, int base)
|
|
{
|
|
return _strto_l(str, endptr, base, 0);
|
|
} |