#ifdef M_RCSID
#ifndef lint
static char rcsID[] = "$Id: vfscanf.c 1.27 1995/09/20 19:07:52 ant Exp $";
#endif
#endif
#include <mks.h>
#include <ctype.h>
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#ifdef __FLOAT__
#include <math.h>
#endif
#define CONVTYPE 1
#define STAR 2
#define PERCENT 3
#define NUMBER 4
#define MODCONVL 5
#define NSCAN 6
#define BRACKET 7
#define MODCONVH 8
#define BASE16 16
#define BASE10 10
#define BASE8 8
#define NOBASE 0
#define SIGNED 1
#define UNSIGNED 0
#define CBUFSIZ 100
struct lexlist {
char name;
char type;
};
static struct lexlist *lexp;
static struct lexlist lexlist[] ={
'*', STAR,
'%', PERCENT,
'l', MODCONVL,
'h', MODCONVH,
'n', NSCAN,
'[', BRACKET,
'd', CONVTYPE,
'S', CONVTYPE,
's', CONVTYPE,
'u', CONVTYPE,
'c', CONVTYPE,
'x', CONVTYPE,
'o', CONVTYPE,
'0', NUMBER,
'1', NUMBER,
'2', NUMBER,
'3', NUMBER,
'4', NUMBER,
'5', NUMBER,
'6', NUMBER,
'7', NUMBER,
'8', NUMBER,
'9', NUMBER,
'i', CONVTYPE,
'f', CONVTYPE,
'e', CONVTYPE,
'g', CONVTYPE,
0, 0
};
static int scan(int, const char *, const char *);
static int gettoken(void);
static void whitespace(void);
static int match(const char *, char *);
static long unsigned getnum(int, int, int);
static int getin(void);
static void unget(int);
#ifdef __FLOAT__
static double lstrtod(void);
#endif
static int ungot;
static FILE *fpin;
static int pflag;
static int width;
static const char *fmtptr;
static int charcnt;
static int from;
static int gfail;
int
mks_vfscanf(FILE *pfin, const char *fmt, va_list ap)
{
int nitems;
int ltoken;
int c;
int modconv;
int suppression;
long unsigned number;
ungot = EOF;
fpin = pfin;
fmtptr = fmt;
from = 'X';
nitems = 0;
charcnt = 0;
for (;;) {
if (from == 'X') {
pflag = 0;
modconv = 0;
suppression = 0;
width = 0;
}
ltoken = gettoken();
switch (ltoken) {
case 0:
goto retitems;
case MODCONVL:
case MODCONVH:
switch (from) {
case 'A':
case 'D':
case 'P':
from = 'E';
modconv = ltoken;
break;
default:
from = 'X';
break;
}
break;
case CONVTYPE:
switch (from) {
int intassign;
case 'E':
case 'P':
case 'D':
case 'A':
from = 'X';
intassign = 1;
pflag = 0;
switch (lexp->name) {
case 'd':
number = getnum(BASE10, width, SIGNED);
if (gfail)
goto retitems;
break;
case 'u':
number = getnum(BASE10, width, UNSIGNED);
if (gfail)
goto retitems;
break;
case 'x':
number = getnum(BASE16, width, SIGNED);
if (gfail)
goto retitems;
break;
case 'o':
number = getnum(BASE8, width, SIGNED);
if (gfail)
goto retitems;
break;
case 'i':
number = getnum(NOBASE, width, SIGNED);
if (gfail)
goto retitems;
break;
case 'c':
case 'S':
case 's': {
int gotitem = 0;
char *str;
if (!suppression)
str = va_arg(ap, char *);
if (lexp->name != 'c')
whitespace();
for (;;) {
c = getin();
if (lexp->name != 'c' && isspace(c)) {
unget(c);
break;
}
if (c == EOF) {
if(!gotitem)
goto retitems;
break;
}
gotitem = 1;
if (!suppression)
*str++ = c;
if (width) {
if (--width == 0)
break;
}
}
if (!suppression && lexp->name != 'c')
*str = '\0';
intassign = 0;
break;
}
#ifdef __FLOAT__
case 'f':
case 'g':
case 'e': {
double fresult;
fresult = lstrtod();
if(gfail)
goto retitems;
if(suppression)
break;
if (modconv == MODCONVL)
*(double *)va_arg(ap, double *) = fresult;
else
*(float *)va_arg(ap, float *) = (float)fresult;
}
#else
case 'f':
case 'g':
case 'e':
#endif
default:
intassign = 0;
break;
}
if (suppression)
break;
else
nitems++;
if (intassign == 0)
break;
switch (modconv) {
case MODCONVH:
*(short *)va_arg(ap, short *) = (short)number;
break;
case MODCONVL:
*(long *)va_arg(ap, long *) = (long)number;
break;
default:
*(int *)va_arg(ap, int *) = (int)number;
break;
}
break;
default:
from = 'X';
break;
}
break;
case STAR:
if (from == 'P') {
from = 'A';
suppression = 1;
} else {
from = 'X';
}
break;
case PERCENT:
if (from == 'P') {
from = 'X';
pflag = 0;
c = getin();
if (c != '%')
goto retitems;
} else {
from = 'X';
}
break;
case NUMBER:
if (from == 'P' || from == 'A') {
from = 'D';
} else {
from = 'X';
}
break;
case NSCAN:
if (from == 'P') {
pflag = 0;
if (!suppression) {
*(int *)va_arg(ap, int *) = charcnt;
}
}
from = 'X';
break;
case BRACKET:
switch (from) {
case 'A':
case 'D':
case 'P': {
char *ptr;
pflag = 0;
if (width == 0)
width = INT_MAX;
ptr = suppression ? NULL : va_arg(ap, char *);
if (match(fmtptr, ptr) && !feof(fpin)
&& !suppression)
nitems++;
while (*fmtptr++ != ']')
;
break;
}
default:
break;
}
from = 'X';
break;
default:
c = *(fmtptr-1);
if (c == ' ' || c == '\t' || c == '\n' || c == '\f')
whitespace();
else {
c = getin();
if (c != *(fmtptr-1))
goto retitems;
}
from = 'X';
break;
}
}
retitems:
if (ungot != EOF) {
ungetc(ungot, fpin);
ungot = EOF;
}
return nitems==0 ? EOF : nitems;
}
static int
gettoken()
{
char c;
if (*fmtptr == 0)
return 0;
c = *fmtptr++;
if (pflag) {
for(lexp=lexlist; lexp->name != 0; lexp++) {
if (c == lexp->name) {
if (lexp->type == NUMBER) {
width = (int) strtol(fmtptr-1, (char **)0, BASE10);
while (*fmtptr >= '0' && *fmtptr <= '9')
fmtptr++;
} else if (c == 'c') {
width = 1;
}
return lexp->type;
}
}
return -1;
}
if (c == '%') {
pflag = 1;
from = 'P';
return gettoken();
}
return -1;
}
static void
whitespace()
{
register int c;
do {
c = getin();
} while (isspace(c));
unget(c);
}
static int
scan(int ch, const char *str, const char *estr)
{
for (; str < estr; ++str)
if (*str == ch)
return 1;
return 0;
}
static int
match(const char *str, char *outstr)
{
int complement;
int i;
char start, end;
int c;
const char *bscan, *escan;
if (*str == '^') {
complement = 1;
str++;
} else
complement = 0;
start = *str++;
end = 0;
if (*str == '-') {
if (str[2] == ']')
end = str[1];
}
if (start > end) {
bscan = str - 1;
while (*str++ != ']')
;
escan = str - 1;
for (i=0; i<width; i++) {
if ((c = getin()) == EOF)
return 0;
if (!scan(c, bscan, escan) ^ complement)
break;
if (outstr != NULL)
*outstr++ = c;
}
} else {
for (i=0; i<width; i++) {
c = getin();
if (complement) {
if (c >= start && c <= end)
break;
else if (outstr != NULL)
*outstr++ = c;
} else {
if (c < start || c > end)
break;
else if (outstr != NULL)
*outstr++ = c;
}
}
}
if (i < width)
unget(c);
if (outstr != NULL)
*outstr = '\0';
return (i > 1);
}
static long unsigned
getnum(int base, int width, int sign)
{
char *s;
char cbuf[CBUFSIZ];
int w;
register int c;
int neg;
long ret;
gfail = 0;
whitespace();
if (width == 0)
width = sizeof cbuf;
neg = 0;
if (sign) {
c = getin();
if (c == '+' || c == '-')
neg = c=='-' ? 1 : 0;
else
unget(c);
}
if (base == 0) {
base = 10;
c = getin();
if (c == '0') {
base = 8;
c = getin();
if (c == 'X' || c == 'x')
base = 16;
else
unget(c);
} else
unget(c);
}
if (base == 10) {
w = 0;
s = cbuf;
while (w < width && w < sizeof cbuf) {
c = getin();
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
*s++ = c;
w++;
continue;
default:
unget(c);
w = width;
break;
}
}
*s = '\0';
ret = strtol(cbuf, (char **)0, 10);
goto retn;
}
if (base == 8) {
w = 0;
s = cbuf;
while (w < width && w < sizeof cbuf) {
c = getin();
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
*s++ = c;
w++;
continue;
default:
unget(c);
w = width;
break;
}
}
*s = '\0';
ret = strtol(cbuf, (char **)0, 8);
goto retn;
}
if (base == 16) {
w = 0;
s = cbuf;
while (w < width && w < sizeof cbuf) {
c = getin();
c = toupper(c);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
*s++ = c;
w++;
continue;
default:
unget(c);
w = width;
break;
}
}
*s = '\0';
ret = strtol(cbuf, (char **)0, 16);
goto retn;
}
gfail = -1;
retn:
if (*cbuf == '\0')
gfail = -1;
if (neg)
ret = -ret;
return ret;
}
#ifdef __FLOAT__
static double
lstrtod()
{
int slen;
int neg, eneg;
char cbuf[CBUFSIZ];
register int c;
register char *sp, *s1, *s2, *s3;
double total, exp, tens;
neg = eneg = 1;
gfail = 0;
whitespace();
c = getin();
if (c == '-' || c == '+')
if (c == '-') {
neg = -1;
c = getin();
}
sp = s1 = cbuf;
while (c >= '0' && c <= '9') {
*sp++ = c;
c = getin();
}
s2 = sp;
if (c == '.') {
c = getin();
while (c >= '0' && c <= '9') {
*sp++ = c;
c = getin();
}
}
s3 = sp;
if (c == 'e' || c == 'E') {
c = getin();
if (c == '-' || c == '+')
if (c == '-') {
eneg = -1;
c = getin();
}
while (c >= '0' && c <= '9') {
*sp++ = c;
c = getin();
}
}
*sp = '\0';
if (s1 == s2 && s2 == s3) {
gfail = -1;
return 0.0;
}
unget(c);
total = 0.0;
tens = 1.0;
for (sp=s2-1; sp >= s1; sp--) {
total += (*sp -'0') * tens;
tens *= 10.0;
}
tens = .1;
for (sp=s2; sp < s3; sp++) {
total += (*sp - '0') * tens;
tens /= 10.0;
}
total *= (double)neg;
exp = 0.0;
tens = 1.0;
if ((slen = strlen(s3)) > 0) {
sp = s3 + slen - 1;
for ( ; sp >= s3; sp--) {
exp += (*sp - '0') * tens;
tens *= 10.0;
}
}
*sp = '\0';
exp *= (double)eneg;
total *= pow(10.0, exp);
return total;
}
#endif
static int
getin()
{
int c;
if (ungot != EOF) {
c = ungot;
ungot = EOF;
} else
c = getc(fpin);
charcnt++;
return c;
}
static void
unget(int c)
{
ungot = c;
charcnt--;
}