/* * Copyright (C) 2000 Manuel Novoa III * * 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 errno set 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 any 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 unsigned long long _strto_ll(const char *str, char **endptr, int base, int uflag); #if L_strto_ll /* * This is the main work fuction which handles both strtol (uflag = 0) and * strtoul (uflag = 1). */ unsigned long long _strto_ll(const char *str, char **endptr, int base, int uflag) { unsigned long long number = 0; unsigned long 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_digit = ULONG_LONG_MAX % base; cutoff = ULONG_LONG_MAX / 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_LONG_MAX; if (uflag) { negative = 0; /* since unsigned returns ULONG_LONG_MAX */ } #if _STRTO_ERRNO __set_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 long)(-(1+LONG_LONG_MIN)))+1)) { #if _STRTO_ERRNO __set_errno(ERANGE); #endif return (unsigned long long) LONG_LONG_MIN; } return (unsigned long long)(-((long long)number)); } else { if (!uflag && (number > (unsigned long long) LONG_LONG_MAX)) { #if _STRTO_ERRNO __set_errno(ERANGE); #endif return LONG_LONG_MAX; } return number; } } #endif #if L_strtoull unsigned long long strtoull(const char *str, char **endptr, int base) { return _strto_ll(str, endptr, base, 1); } #endif #if L_strtoll long long strtoll(const char *str, char **endptr, int base) { return _strto_ll(str, endptr, base, 0); } #endif