/* original from norbert schelenkar's stdio */
/* eff hacks	++jrb */
/* conversion to ansi spec -- mj */
/* 
 * Base can be anything between 2 and 36 or 0.
 * If not NULL then resulting *endptr points to the first
 * non-accepted character.
 */

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <stddef.h>

/* macro to avoid most frequent long muls on a lowly 68k */
#define _BASEMUL(B, SH, X) \
    ((0 != SH) ? \
       ((10 == (B)) ? ((((X) << (SH)) + (X)) << 1) : ((X) << (SH))) : \
       ((X) * (B))) 

unsigned long int strtoul(nptr, endptr, base)
register const char *nptr;
char **endptr;
int base;
{
  register short c;
  unsigned long result = 0L;
  unsigned long limit;
  short negative = 0;
  short overflow = -2;	/* if this stays negative then no
			 conversion was performed */
  short digit;
  int shift;

  if (endptr != NULL)
      *endptr = (char *)nptr;

  while ((c = *nptr) && isspace(c))	/* skip leading white space */
	nptr++;
  if ((c = *nptr) == '+' || c == '-') {	/* handle signs */
	negative = (c == '-');
	nptr++;
  }
  if (base == 0) {			/* determine base if unknown */
	if (*nptr == '0') {
		base = 8;
		nptr++;
		if ((c = *nptr) == 'x' || c == 'X') {
			base += base;
			nptr++;
		}
	}
	else
	        base = 10;
  }
  else
  if (base == 16 && *nptr == '0') {	/* discard 0x/0X prefix if hex */
	nptr++;
	if ((c = *nptr == 'x') || c == 'X')
		nptr++;
  }
  if (base < 2 || base > 36)
      return result;

  limit = ULONG_MAX/(unsigned long)base; 	/* ensure no overflow */
  shift = 0;
  switch (base) {
      case 32: shift++;
      case 16: shift++;
      case 8: shift++;
      case 4: case 10: shift++;
      case 2: shift++;
      default:;
  }
  
  nptr--;				/* convert the number */
  while (c = *++nptr) {
	if (isdigit(c))
		digit = c - '0';
	else
		digit = c - (isupper(c) ? 'A' : 'a') + 10;
	if (digit < 0 || digit >= base)
		break;
	overflow &= 1;		/* valid digit - some conversion performed */
	if (0 == (overflow &= 1)) {	/* valid digit
					   - some conversion performed */
	    if ((result > limit) ||
		(digit >
		 (ULONG_MAX - (result = _BASEMUL(base, shift, result))))) {
		result = ULONG_MAX;
		errno = ERANGE;
		overflow = 1;
		negative = 0;
	    }
	    else 
		result += digit;
	}
  }
  if (negative)				/* ANSI says we should do this! */
	result = 0L - result;

  if ((endptr != NULL) && (overflow >= 0)) 	/* move *endptr if some */
      *endptr = (char *) nptr;                  /* digits were accepted */
  return result;
}
