
/* istrtoul.c */

#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>


/* Get value of DIGIT.  Does not support locale stuff. */

#ifdef __GNUC__
__inline__
#endif
static int
digit_value (int digit)
{
  digit -= '0';
  if (digit < 0)
    return -1;
  if (digit >= 10)
    {
      digit -= 'A' - ('9' + 1);
      if (digit >= 36)
	digit -= 'a' - 'A';
      if (digit < 10)
	return -1;
    }
  return digit;
}


/* Internal version of strtoul().  Same as strtoul() except it does not accept
   leading whitespace or sign. */

unsigned long 
__strtoul (const char *nptr, char **endptr, int base)
{
  unsigned long value, cutoff;
  int any, digit;
  const char *p;

  p = nptr;

  /* Process a prefix. */

  if (base == 0)
    {
      /* When BASE is zero, the base is determined by the format of the
	 number. */
      if (p[0] == '0')
	{
	  if (p[1] == 'x' || p[1] == 'X')
	    {
	      base = 16;
	      /* Skip the leading `0x' in hexadecimal numbers. */
	      p += 2;
	    }
	  else
	    /* For octal values, don't skip the leading zero yet.  The
	       number could be just a zero by itself. */
	    base = 8;
	}
      else
	base = 10;
    }
  else
    /* A leading `0x' is allowed for hexadecimal numbers. */
    if (base == 16 && p[0] == '0' && tolower (p[1]) == 'x')
      p += 2;

  /* Find the maximum value that is safe to multiply by BASE. */
  cutoff = (unsigned long) ULONG_MAX / base;

  /* Accumulate a value. */

  any = 0;
  value = 0;
  for ( ; ; p++)
    {
      digit = digit_value (*p);
      if (digit < 0 || digit >= base)
	break;
      if (any < 0 || value > cutoff)
	{
	  any = -1;
	  continue;
	}
      value *= base;
      if (value > ULONG_MAX - digit)
	{
	  any = -1;
	  continue;
	}
      value += digit;
      any = 1;
    }

  if (any < 0)
    {
      value = ULONG_MAX;
      errno = ERANGE;
    }

  if (endptr != NULL)
    *endptr = (char *) (any ? p : nptr);

  return value;
}
