Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_FS_1_19910112
-
c/lib/gen/atoi.c
There are 8 other files named atoi.c in the archive. Click here to see a list.
/*
** ATOI - convert strings to numbers
**
** (c) Copyright Ken Harrenstien 1989
** Re-written from original version,
** (c) Copyright 1987 by Ian Macky, SRI International
**
** Note this implementation relies on a non-ANSI <ctype.h> facility,
** namely toint(c) which converts a character to its numerical value
** using the same base system as ANSI's strtol().
*/
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <float.h>
#include <errno.h> /* For ERANGE */
#include <math.h> /* For HUGE_VAL */
#if __STDC__
#define CONST const
#else
#define CONST
#endif
#define LONG_BIT (sizeof(long)*CHAR_BIT) /* # bits in a long int */
#define SIGN ((unsigned long)1<<(LONG_BIT-1)) /* Sign bit */
#define MAXPOSLONG LONG_MAX
#define ulong unsigned long /* Convenient abbrev */
static long conv_const(), conv_oct(), conv_dec(), conv_hex(), conv_base();
double (atof)(str) /* Note parens in case macro */
CONST char *str;
{
return strtod(str, (char **) NULL);
}
int (atoi)(str) /* Note parens in case macro */
CONST char *str;
{
return (int) atol(str);
}
long (atol)(str) /* Note parens in case macro */
CONST char *str;
{
while (isspace(*str)) str++; /* Skip leading whitespace */
switch (*str) {
case '-': str++;
return - conv_dec(&str, (int *)NULL); /* Negate result */
case '+': str++;
default:
return conv_dec(&str, (int *)NULL);
}
}
/* STRTOD - Parse a string into a double value
**
** This code is one of the few good candidates for an assembly-language
** substitute.
*/
double strtod(nptr, endptr)
CONST char *nptr;
char **endptr;
{
register CONST char *cp = nptr;
register int c;
double value = 0.0; /* the actual value */
int negative = 0; /* negative number? */
int exponent = 0; /* exponent... */
double divisor;
int expsign;
int ovfl = 0;
CONST char *savptr, *str;
while (isspace(*cp)) cp++; /* skip leading whitespace */
switch (*cp) {
case '-': negative = 1; /* leading - means negative # */
case '+': cp++; /* - falls into. skip the sign */
}
savptr = cp; /* Remember where we started */
/* First do whole-number part. We use floating arithmetic to avoid
** the possibility of integer overflow. Slower, but safer.
*/
for (c = *cp; isdigit(c); c = *++cp) {
value = (value * 10.0) + (c - '0');
if (value && value < 1.0) { /* If exponent wrapped around, */
ovfl++; /* we overflowed. */
value = 1.0;
}
}
/* Now do fractional part if one was specified */
if (c == '.') {
divisor = 1.0; /* Place-value for post-. digits */
while (isdigit(*++cp)) {
if ((divisor *= 10.0) > 1.0) /* Until get too low, */
value += (*cp - '0') / divisor; /* add fraction digits in */
else if (!value) ovfl++; /* Underflowed */
}
c = *cp;
}
if (cp != savptr) { /* Was anything scanned? */
str = cp; /* Yes, update returned ptr now */
if (*cp == 'e' || *cp == 'E') { /* Look for exponent */
expsign = (c = *++cp); /* Get possible exponent sign */
if (c == '-' || c == '+')
c = *++cp;
if (isdigit(c)) {
exponent = c - '0';
while (isdigit(*++cp)) {
exponent = exponent*10 + (*cp-'0');
if (exponent >= (INT_MAX/10))
ovfl++, value = (expsign=='-' ? 0.0 : 1.0);
}
/* EXTREMELY dumb method of scaling value by exponent */
/* Fix this up later!! */
if (!ovfl) {
double pv;
if (expsign == '-')
while (--exponent >= 0) {
pv = value; /* Remember so can */
if ((value /= 10.0) > pv) { /* chk for underflow */
ovfl++;
value = 0;
break;
}
}
else
while (!ovfl && --exponent >= 0) {
pv = value;
if ((value *= 10.0) < pv) { /* Chk for overflow */
ovfl++;
value = 1.0;
break;
}
}
}
str = cp; /* Won, update returned ptr again */
}
}
}
if (endptr) *endptr = (char *)str; /* save pointer to after number */
if (ovfl) { /* If overflowed, */
errno = ERANGE;
value = value ? HUGE_VAL : 0.0; /* Return either big val or 0 */
}
return (negative) ? -value : value; /* return the double value */
}
/* STRTOL - Parse string as long integer value.
** This uses a special flag to share code with strtoul().
*/
#define STRTOL_UNSIGNF 010000 /* Use an unlikely bit */
long (strtol)(nptr, endptr, base)
CONST char *nptr;
char **endptr;
int base;
{
register char *str = (char *)nptr;
long v;
int neg = 0; /* Flag set if '-' seen */
int ovfl = 0; /* Flag set if overflow detected */
int unsignf; /* Flag set if returning unsigned long */
char *sptr;
while (isspace(*str)) str++; /* skip leading whitespace */
switch (*str) { /* check for leading sign char */
case '-': neg++; /* yes, negative #. fall into '+' */
case '+': str++; /* skip the sign character */
}
sptr = str; /* Remember current loc */
if (unsignf = (base & STRTOL_UNSIGNF))
base &= ~STRTOL_UNSIGNF; /* Ensure flag taken out */
switch (base) {
case 0: v = conv_const(&sptr, &ovfl); break;
case 8: v = conv_oct(&sptr, &ovfl); break;
case 10:v = conv_dec(&sptr, &ovfl); break;
case 16: /* Check for hex prefix 0X */
if (*sptr == '0' && (*++sptr == 'x' || *sptr == 'X'))
str = ++sptr; /* Update so err if nothing follows */
v = conv_hex(&sptr, &ovfl);
break;
default:
if (base <= 36) { v = conv_base(&sptr, &ovfl, base); break; }
case 1: /* Illegal base */
v = 0;
break;
}
/* Now see if anything was parsed, and return appropriate ptr */
if (endptr)
*endptr = (sptr == str) /* If pointer hasn't changed, */
? nptr /* then return original ptr! */
: sptr; /* Else use updated ptr */
/* Now handle overflow if any.
** If we're returning a long, it's also an overflow if the sign bit
** is set prior to negation test.
*/
if (ovfl || ((v & SIGN) && !unsignf)) {
errno = ERANGE;
return unsignf ? ULONG_MAX
: ((neg) ? LONG_MIN : LONG_MAX);
}
return (neg) ? -v : v; /* Negate number if needed */
}
/* STRTOUL - Parse string as unsigned long value.
** Note flag trickery to share code with strtol().
*/
unsigned long (strtoul)(str, ptr, base) /* Parens in case macro */
CONST char *str;
char **ptr;
int base;
{
return strtol(str, ptr, base | STRTOL_UNSIGNF);
}
/* CONV_CONST(ptr, retf) - Parse string as C-style integer constant.
**
** This routine and all of the CONV_* routines handle their args in
** the same way:
** ptr - points to char ptr to string; updated by call.
** retf - if not NULL, points to overflow flag. This flag is
** incremented if overflow is detected.
**
** There is an ambiguity for the case of "0x" where we could
** treat it as any of:
** (1) Erroneous hex syntax (as it is for C compiler)
** (2) Valid hex syntax, value 0
** (3) Octal constant 0 with pointer left at "x"
**
** Currently we opt for case 3 since that is the strictest interpretation
** of the dpANS. Also, note that for base 16, the strtol() code fails
** if only "0x" is seen; this likewise conforms to a strict interpretation.
*/
static long
conv_const(str, retf)
char **str;
int *retf;
{
register char *cp = *str;
if (*cp != '0') /* Octal/Hex prefix? */
return conv_dec(str, retf); /* Nope, use decimal */
if (*++cp == 'x' || *cp == 'X') { /* Check for hex */
if (!isxdigit(*++cp)) { /* Next digit valid hex? */
++(*str); /* Nope, do case 3 and return 0 */
return 0;
}
*str = cp; /* Win, update pointer! */
return conv_hex(str, retf); /* Use hex */
}
*str = cp;
return conv_oct(str, retf); /* Use octal */
}
/* CONV_OCT - convert octal string */
static long
conv_oct(ptr, retf)
char **ptr;
int *retf;
{
register char *cp = *ptr;
register ulong v = 0;
register int c;
for (c = *cp; isodigit(c); c = *++cp) {
if ((v & (07 << (LONG_BIT-3))) && retf) (*retf)++;
v = (v << 3) + c - '0';
}
*ptr = cp;
return v;
}
/* CONV_HEX - convert hexadecimal string */
static long
conv_hex(ptr, retf)
char **ptr;
int *retf;
{
register char *cp = *ptr;
register ulong v = 0;
register int c;
for (c = *cp; isxdigit(c); c = *++cp) {
if ((v & (017 << (LONG_BIT-4))) && retf) (*retf)++;
v = (v << 4) + toint(c);
}
*ptr = cp;
return v;
}
/* CONV_DEC - convert decimal string */
static long
conv_dec(ptr, retf)
char **ptr;
int *retf;
{
register char *cp = *ptr;
register long v = 0;
register int c;
for (c = *cp; isdigit(c); c = *++cp) {
if (v < ((MAXPOSLONG-9)/10)) /* If can't overflow, */
v = v*10 + c - '0'; /* do it fast */
else { /* Can, so use slow */
do { /* unsigned mult loop */
ulong pv = v;
v = ((ulong)v)*10 + c - '0';
if ((ulong)v/10 != pv) /* See if divide restores v */
if (retf) (*retf)++; /* If not, we overflowed */
} while (isdigit(c = *++cp));
break; /* Done, leave outer loop */
}
}
*ptr = cp;
return v;
}
/* CONV_BASE - Convert string of arbitrary base (2-36 inclusive)
** There is one additional arg -- the base to use.
*/
static long
conv_base(ptr, retf, base)
char **ptr;
int *retf;
int base;
{
register char *cp = *ptr;
register long v = 0;
long ovtest = LONG_MAX/base;
register int c;
for (c = *cp; isalnum(c); c = *++cp) {
c = toint(c); /* Convert to number */
if (c >= base) break; /* digit out of range for base? */
if (v < ovtest)
v = v * base + c; /* accumulate the digit */
else {
ulong pv;
v = (ulong)v * base + c;
if ((ulong)v/base != pv)
if (retf) (*retf)++; /* Overflowed */
}
}
*ptr = cp;
return v;
}