[comp.os.minix] New ANSI library routines

nfs@notecnirp.Princeton.EDU (Norbert Schlenker) (01/10/90)

There are bugs in the posted atoi() and atol() routines.  Most importantly,
they consider characters less than '0' to be negative digits (e.g. '/' is
treated as -1).  They also don't handle '+' signs in front of numbers,
which ANSI says they should.

I enclose my routines, which don't have these problems.

The multiplications in each routine have been replaced by shifts.  On my
80386, this atoi() is slightly slower than a version which multiplies.  On
machines with relatively slower multipliers (e.g. 8088's and 68000's), the
shifts should be a big win.  Atol() is slightly faster with shifts, even on
the 80386.

ANSI also says that atoi() and atol() are somewhat special cases of strtol(),
with error checking turned off.  I enclose copies of ANSI compliant strtol()
and strtoul() - they are much slower than atoi() and atol() [about a factor
of 2 relative to atol()].

Norbert

-----------------------------  Cut here --------------------------------
#!/bin/sh
echo x - atoi.c
sed '/^X/s///' > atoi.c << '/'
X/* atoi.c						ANSI 4.10.1.2
X *	int atoi(const char *nptr);
X *
X *	Converts a numeric string in base 10 to an integer.
X */
X
X#include <lib.h>
X#include <ctype.h>
X#include <stdlib.h>
X
X#ifdef atoi
X#undef atoi
X#endif
X
XPUBLIC int atoi(nptr)
Xregister _CONST char *nptr;
X{
X  register int c;
X  int result = 0;
X  int negative = 0;
X
X  while ((c = *nptr) && isspace(c))	/* skip leading white space */
X	++nptr;
X
X  if (c == '+' || c == '-') {		/* handle signs */
X	negative = (c == '-');
X	++nptr;
X  }
X
X  while ((c = *nptr++ - '0') >= 0 && c < 10)
X	result = (result << 1) + (result << 3) + c;
X
X  return (negative) ? 0 - result : result;
X}
/
echo x - atol.c
sed '/^X/s///' > atol.c << '/'
X/* atol.c						ANSI 4.10.1.3
X *	long int atol(const char *nptr);
X *
X *	Converts a numeric string in base 10 to a long integer.
X */
X
X#include <lib.h>
X#include <ctype.h>
X#include <stdlib.h>
X
X#ifdef atol
X#undef atol
X#endif
X
XPUBLIC long int atol(nptr)
Xregister _CONST char *nptr;
X{
X  register int c;
X  long int result = 0;
X  int negative = 0;
X
X  while ((c = *nptr) && isspace(c))	/* skip leading white space */
X	++nptr;
X
X  if (c == '+' || c == '-') {		/* handle signs */
X	negative = (c == '-');
X	++nptr;
X  }
X
X  while ((c = *nptr++ - '0') >= 0 && c < 10)
X	result = (result << 1) + (result << 3) + c;
X
X  return (negative) ? 0L - result : result;
X}
/
echo x - strtol.c
sed '/^X/s///' > strtol.c << '/'
X/* strtol.c						ANSI 4.10.1.5
X *	long int strtol(const char *nptr, char **endptr, int base);
X *
X *	Converts a numeric string, in various bases, to a long integer.
X */
X
X#include <lib.h>
X#include <ctype.h>
X#include <errno.h>
X#include <limits.h>
X#include <stdlib.h>
X
X#ifdef strtol
X#undef strtol
X#endif
X
XPUBLIC long int strtol(nptr, endptr, base)
X_CONST char *nptr;
Xchar **endptr;
Xint base;
X{
X  register int c;
X  long int result = 0L;
X  long int limit;
X  int negative = 0;
X  int overflow = 0;
X  int saw_a_digit = 0;			/* it's not a number without a digit */
X
X  if (endptr != (char **) NULL)		/* set up default final pointer */
X	*endptr = nptr;
X
X  while ((c = *nptr) && isspace(c))	/* skip leading white space */
X	++nptr;
X
X  if (c == '+' || c == '-') {		/* handle signs */
X	negative = (c == '-');
X	++nptr;
X  }
X
X  if (base == 0) {			/* determine base if unknown */
X	base = 10;
X	if (*nptr == '0') {
X		base = 8;
X		++nptr;
X		if ((c = *nptr) == 'x' || c == 'X') {
X			base = 16;
X			++nptr;
X		}
X	}
X  }
X  else
X  if (base == 16 && *nptr == '0') {	/* discard 0x/0X prefix if hex */
X	++nptr;
X	if ((c = *nptr == 'x') || c == 'X')
X		++nptr;
X  }
X
X  limit = LONG_MAX / base;		/* ensure no overflow */
X
X  --nptr;				/* convert the number */
X  while ((c = *++nptr) != 0) {
X	if (isdigit(c))
X		c -= '0';
X	else
X		c -= isupper(c) ? ('A' - 10) : ('a' - 10);
X	if (c < 0 || c >= base)
X		break;
X	saw_a_digit = 1;
X	if (result > limit)
X		overflow = 1;
X	if (!overflow) {
X		result *= base;
X		if (c > LONG_MAX - result)
X			overflow = 1;
X		else	
X			result += c;
X	}
X  }
X  if (!saw_a_digit)
X	return 0;
X
X  if (negative && !overflow)
X	result = 0L - result;
X  if (overflow) {
X	errno = ERANGE;
X	if (negative)
X		result = LONG_MIN;
X	else
X		result = LONG_MAX;
X  }
X
X  if (endptr != (char **) NULL)		/* record good final pointer */
X	*endptr = nptr;
X  return result;
X}
/
echo x - strtoul.c
sed '/^X/s///' > strtoul.c << '/'
X/* strtoul.c						ANSI 4.10.1.6
X *	unsigned long int strtoul(const char *nptr, char **endptr, int base);
X *
X *	Converts a numeric string, in various bases, to an unsigned long.
X */
X
X#include <lib.h>
X#include <ctype.h>
X#include <errno.h>
X#include <limits.h>
X#include <stdlib.h>
X
X#ifdef strtoul
X#undef strtoul
X#endif
X
XPUBLIC unsigned long int strtoul(nptr, endptr, base)
X_CONST char *nptr;
Xchar **endptr;
Xint base;
X{
X  register int c;
X  unsigned long int result = 0L;
X  unsigned long int limit;
X  int negative = 0;
X  int overflow = 0;
X  int saw_a_digit = 0;			/* it's not a number without a digit */
X
X  if (endptr != (char **) NULL)		/* set up default final pointer */
X	*endptr = nptr;
X
X  while ((c = *nptr) && isspace(c))	/* skip leading white space */
X	++nptr;
X
X  if (c == '+' || c == '-') {		/* handle signs */
X	negative = (c == '-');
X	++nptr;
X  }
X
X  if (base == 0) {			/* determine base if unknown */
X	base = 10;
X	if (*nptr == '0') {
X		base = 8;
X		++nptr;
X		if ((c = *nptr) == 'x' || c == 'X') {
X			base = 16;
X			++nptr;
X		}
X	}
X  }
X  else
X  if (base == 16 && *nptr == '0') {	/* discard 0x/0X prefix if hex */
X	++nptr;
X	if ((c = *nptr == 'x') || c == 'X')
X		++nptr;
X  }
X
X  limit = ULONG_MAX / base;		/* ensure no overflow */
X
X  --nptr;				/* convert the number */
X  while ((c = *++nptr) != 0) {
X	if (isdigit(c))
X		c -= '0';
X	else
X		c -= isupper(c) ? ('A' - 10) : ('a' - 10);
X	if (c < 0 || c >= base)
X		break;
X	saw_a_digit = 1;
X	if (result > limit)
X		overflow = 1;
X	if (!overflow) {
X		result = base * result;
X		if (c > ULONG_MAX - result)
X			overflow = 1;
X		else	
X			result += c;
X	}
X  }
X  if (!saw_a_digit)
X	return 0;
X
X  if (negative && !overflow)	/* BIZARRE, but ANSI says we should do this! */
X	result = 0L - result;
X  if (overflow) {
X	errno = ERANGE;
X	result = ULONG_MAX;
X  }
X
X  if (endptr != (char **) NULL)		/* record good final pointer */
X	*endptr = nptr;
X  return result;
X}
/