Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_FS_1_19910112
-
c/old/lib/scanf.c
There are 9 other files named scanf.c in the archive. Click here to see a list.
/* KLH 14-Nov-85 fixed 2 bugs:
* %s wasn't handled right (_cst not initialized); changed to avoid _cst.
* Was doing arg++ instead of arg-- to get next arg pointer.
*/
/*
** scanf.c - formatted input
** Bill Palmer / Stanford University / November 1984
** partially based on printf.c, by David Eppstein
**
** references: Kernighan and Ritchie
** Unix Programmer's Manual, section 3S
**
** Variable arguments are handled as follows:
**
** Each function calls a handler routine to do the common work,
** with a pointer to the first of the variable arguments. Since
** arguments appear in reverse order on the stack, each argument
** can be found by referencing and auto-decrementing the passed
** argument pointer.
**
** This is not guaranteed to work for any compiler other than
** KCC. A more general scheme is to increment by the difference
** between successive args, rather than merely subtracting one,
** but even that will not always work.
*/
entry scanf, fscanf, sscanf, _scanf; /* comment out for 4.2 compiler */
#include <stdio.h>
#include <ctype.h>
#define SHORT 0
#define INT 1
#define LONG 2
#define FLOAT 0
#define MAXDIGS 100
#define SPACE 01
#define STOP 02
char *_gcs();
char _cst[128];
/* ------------------------- */
/* formatted input */
/* ------------------------- */
/* ------------------------- */
/* read from stdin */
/* ------------------------- */
static scanf(control, arg)
char *control;
{
return(_scanf(stdin, control, &arg));
}
/* ------------------------- */
/* read from stream */
/* ------------------------- */
static fscanf(iop, control, arg)
FILE *iop;
char *control;
{
return(_scanf(iop, control, &arg));
}
/* ------------------------- */
/* read from string */
/* ------------------------- */
static sscanf(source, control, arg)
char *source, *control;
{
FILE _strbuf;
_strbuf._flag = _IOREAD|_IOSTRG;
_strbuf._ptr = _strbuf._base = source;
_strbuf._cnt = 0;
while (*source++)
_strbuf._cnt++;
/* _strbuf._bufsiz = _strbuf._cnt; */
return (_scanf(&_strbuf, control, &arg));
}
_scanf(iop, control, arg)
FILE *iop; /* file ptr to work with */
char *control; /* ptr to conversion ctl string */
int **arg; /* ptr to arguments */
{
int ch, /* last character read from fmt arg */
ch2, /* char read from stream */
matches, /* number of matched items */
width, /* width of field */
type, /* type of num. being read */
eodata, /* flag for eof reached */
**valptr; /* pointer to return arg */
matches = 0; /* haven't seen anything yet */
eodata = 0;
for (;;) switch (ch = *control++) { /* loop through fmt str. */
case '\0': /* null-termination */
return(matches); /* return num. of matches */
case '%': /* conversion spec. */
if ((ch = *control++) == '%') /* %% ? */
goto defalt; /* yes, treat as ordinary char */
if (ch == '*') { /* assignment suppression? */
ch = *control++; /* get next character */
valptr = 0; /* point at nothing (no return var) */
} else
valptr = arg--; /* nope, point at return var */
width = 0;
type = INT; /* default to reading an int */
while (isdigit(ch)) { /* read over field width, if any */
width = width * 10 + ch - '0';
ch = *control++; /* get next char in format str. */
}
if (!width) width = MAXDIGS; /* make width large if none read */
if (ch == 'l') {
type = LONG;
ch = *control++;
} else if (ch == 'h') {
type = SHORT;
ch = *control++;
break;
} else if (ch == '[')
control = _gcs(control);
if (isupper(ch)) {
ch = tolower(ch); /* make lowercase */
type = LONG; /* flag greater precision */
}
if (ch == '\0')
return -1;
if (_readnum(valptr, ch, width, type, iop, &eodata) && valptr)
matches++; /* successful assignment */
if (eodata)
return(matches ? matches : -1);
break;
case ' ':
case '\n':
case '\t':
while ((ch2=getc(iop))==' ' || ch2=='\t' || ch2=='\n')
; /* skip whitespace */
if (ch2 != EOF) /* if not eof */
ungetc(ch2,iop); /* put back character */
break;
defalt: /* losing c */
default:
ch2 = getc(iop);
if (ch != ch2) { /* if no match */
if (ch2 == EOF)
return -1;
ungetc(ch2,iop); /* put back char */
return(matches);
} /* do nothing if match */
} /* end of switch (ch = *control++) */
}
static _readnum(valptr, ch, width, size, iop, eodata)
int **valptr, *eodata;
FILE *iop;
{
extern double atof();
long accval;
int radix, expnot, sign, type, c, digits;
char number[MAXDIGS];
char *np; /* pointer to number[] */
if (ch == 'c' || ch == 's' || ch == '[')
return(_rdstr(valptr ? *(char **)valptr : (char *)NULL, ch, width, iop, eodata));
accval = 0;
digits = 0; /* digit count */
type = INT;
if (ch == 'e' || ch == 'f')
type = FLOAT;
radix = 10;
if (ch == 'o')
radix = 8;
else if (ch == 'x')
radix = 16;
np = number; /* point at buffer for accum. text */
expnot = 0; /* no exponential notation yet */
sign = 0; /* sign of 0 = + */
/* okay, now we have parsed %3lf in format arg */
while ((c = getc(iop)) == ' ' || c == '\t' || c == '\n')
; /* skip whitespace */
if (c == '+') { /* plus sign? */
width--; /* count off one char */
c = getc(iop); /* get next one */
} else if (c == '-') {
sign++; /* signal we are negative */
width--; /* count off one space */
*np++ = c; /* save char in buffer */
c = getc(iop); /* get next one */
}
for ( ; --width >= 0; *np++ = c, c = getc(iop)) {
if (isdigit(c) ||
(radix == 16 && ((c >= 'a' && c <= 'f')||(c >= 'A' && c <= 'F')))) {
accval *= radix; /* multiply by current radix */
digits++; /* bump digit count */
if (isdigit(c))
accval += (c - '0');
else if (c >= 'a' && c <= 'f')
accval += (c - ('a'-10));
else if (c >= 'A' && c <= 'F')
accval += (c - ('A'-10));
continue;
} else if (c == '.') { /* maybe a decimal point */
if (type == INT || radix != 10)
break; /* don't allow '.' here */
digits++; /* count another digit */
continue;
} else if ((c == 'e' || c == 'E') && !expnot) { /* exp. notation */
if (type == INT || radix != 10 || !digits)
break; /* well, maybe not */
expnot++; /* flag we have exp. notation */
*np++ = c; /* copy char to atof() buffer */
if ((c = getc(iop)) != '+' && c != '-'
&& (c < '0' || c > '9'))
break; /* wasn't a valid char after */
/* the 'e', so quit scanning */
} else
break; /* can't parse further, quit now */
} /* for (; --width >= 0 ...) */
if (sign) accval = -accval; /* change sign if necessary */
if (c != EOF) {
ungetc(c, iop); /* put back last char read */
*eodata = 0; /* didn't hit end of data */
} else *eodata = 1; /* did hit end of data */
if (valptr==NULL || np == number || (sign && np == number+1)) return(0);
*np++ = '\0'; /* null terminate string */
switch ((type<<4) | size) {
case (FLOAT<<4) | SHORT:
case (FLOAT<<4) | INT:
**(float **)valptr = atof(number);
break;
case (FLOAT<<4) | LONG:
**(double **)valptr = atof(number);
break;
case (INT<<4) | SHORT:
**(short **)valptr = accval;
break;
case (INT<<4) | INT:
**(int **)valptr = accval;
break;
case (INT<<4) | LONG:
**(long **)valptr = accval;
break;
}
return(1); /* signal success */
}
static _rdstr(valptr, ch, width, iop, eodata)
char *valptr;
FILE *iop;
int *eodata;
{
int c; /* character place holder */
char *sptr; /* save valptr */
int nost; /* no string processing flag */
*eodata = 0;
sptr = valptr;
if (ch =='c' && width == MAXDIGS)
width = 1;
nost = 0;
if (ch == 's') /* should we do a string */
{
do c = getc(iop);
while(c==' ' || c=='\t' || c=='\n'); /* Ignore wsp */
while (c != EOF && c != ' ' && c != '\t' && c != '\n')
{ if (valptr)
*valptr++ = c;
if (--width <= 0)
break;
c = getc(iop);
}
goto done; /* Done with %s handling */
}
while (_cst[c = getc(iop)] & nost)
if (c == EOF)
break;
nost = SPACE;
if (ch == 'c')
nost = 0;
else if (ch == '[')
nost = STOP;
while (c != EOF && (_cst[c] & nost) == 0) {
if (valptr)
*valptr++ = c;
if (--width <= 0)
break;
c = getc(iop);
}
done:
if (c != EOF) {
if (width > 0)
ungetc(c, iop);
*eodata = 0;
} else
*eodata = 1;
if (valptr && valptr != sptr) {
if (ch != 'c')
*valptr++ = '\0';
return (1);
}
return (0);
}
static char *_gcs(st)
char *st;
{
int c, hat = 0;
for (c = 0; c < sizeof _cst; c++)
_cst[c] = '\0';
_cst['\011'] = _cst['\n'] = _cst[' '] = SPACE;
if (*st == '^')
hat++, st++;
for (c = 0; c < sizeof _cst; c++)
if (hat)
_cst[c] &= ~STOP;
else
_cst[c] |= STOP;
while (((c = *st++)&0177) != ']') {
if (hat)
_cst[c++] |= STOP;
else
_cst[c++] &= ~STOP;
if (!c)
return --st;
}
return st;
}