/* 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 <stddef.h>
#include <stdlib.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)))

long int strtol(nptr, endptr, base)
register const char *nptr;
char **endptr;
int base;
{
  register short c;
  long result = 0L;
  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;

  /* be careful with a preprocessor and signs!! */
  limit = (long)LONG_MIN/(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 0 /* Amylaar: non-numeric characters should not be accepted. */
	if (isdigit(c))
		digit = c - '0';
	else
		digit = c - (isupper(c) ? 'A' : 'a') + 10;
	if (digit < 0 || digit >= base)
		break;
#else
	if (isdigit(c)) {
		digit = c - '0';
		if (digit < 0 || digit >= base)
			break;
	} else {
		digit = c - (isupper(c) ? 'A' : 'a') + 10;
		if (digit < 10 || digit >= base)
			break;
	}
#endif
	if (0 == (overflow &= 1)) {	/* valid digit
					   - some conversion performed */
	    if ((result < limit) ||
		(digit >
		 ((result = _BASEMUL(base, shift, result)) - LONG_MIN))) {
		result = LONG_MIN;
		overflow = 1;
		
	    }
	    else
		result -= digit;
	}
  }

  if (!negative) {
      if (LONG_MIN == result) {
	  result += 1;		/* this is -(LONG_MAX) */
	  overflow = 1;		/* we may get LONG_MIN without overflow */
      }
      result = 0L - result;
  }

  if (overflow > 0) {
	errno = ERANGE;
  }

  if ((endptr != NULL) && (overflow >= 0)) 	/* move *endptr if some */
      *endptr = (char *) nptr;                  /* digits were accepted */
  return result;
}
