ack/lang/cem/libcc/stdio/doscan.c
1994-06-24 14:02:31 +00:00

298 lines
5.8 KiB
C

/* $Id$ */
#include <stdio.h>
#include <ctype.h>
#include <varargs.h>
union ptr_union {
char *chr_p;
unsigned short *ushort_p;
unsigned int *uint_p;
unsigned long *ulong_p;
#ifndef NOFLOAT
float *float_p;
double *double_p;
#endif
};
static char Xtable[128];
/*
* the routine that does the job
*/
_doscanf (iop, format, ap)
register FILE *iop;
char *format; /* the format control string */
va_list ap;
{
int done = 0; /* number of items done */
int base; /* conversion base */
long val; /* an integer value */
int sign; /* sign flag */
int do_assign; /* assignment suppression flag */
unsigned width; /* width of field */
int widflag; /* width was specified */
int longflag; /* true if long */
int shortflag; /* true if short */
int done_some; /* true if we have seen some data */
int reverse; /* reverse the checking in [...] */
int kind;
register int ic;
#ifndef NOFLOAT
extern double atof();
int dotseen;
int expseen;
char buffer[128];
#endif
ic = getc(iop);
if (ic == EOF) {
done = EOF;
goto quit;
}
while (1) {
if (isspace(*format)) {
while (isspace (*format))
++format; /* skip whitespace */
while (isspace (ic)) ic = getc(iop);
}
if (!*format)
goto all_done; /* end of format */
if (ic < 0)
goto quit; /* seen an error */
if (*format != '%') {
if (ic != *format)
goto all_done;
++format;
ic = getc(iop);
continue;
}
++format;
do_assign = 1;
if (*format == '*') {
++format;
do_assign = 0;
}
if (isdigit (*format)) {
widflag = 1;
for (width = 0; isdigit (*format);)
width = width * 10 + *format++ - '0';
} else
widflag = 0; /* no width spec */
if (longflag = (*format == 'L' || *format == 'l'))
++format;
else if (shortflag = (*format == 'H' || *format == 'h'))
++format;
if (isupper(*format)) {
kind = tolower(*format);
longflag = 1;
}
else kind = *format;
if (kind != 'c')
while (isspace (ic))
ic = getc(iop);
done_some = 0; /* nothing yet */
switch (kind) {
case 'o':
base = 8;
goto decimal;
case 'u':
case 'd':
base = 10;
goto decimal;
case 'x':
base = 16;
if (((!widflag) || width >= 2) && ic == '0') {
ic = getc(iop);
if (tolower (ic) == 'x') {
width -= 2;
done_some = 1;
ic = getc(iop);
} else {
ungetc(ic, iop);
ic = '0';
}
}
decimal:
val = 0L; /* our result value */
sign = 0; /* assume positive */
if (!widflag)
width = 0xffff; /* very wide */
if (width && ic == '+')
ic = getc(iop);
else if (width && ic == '-') {
sign = 1;
ic = getc(iop);
}
while (width--) {
if (isdigit (ic) && ic - '0' < base)
ic -= '0';
else if (base == 16 && tolower (ic) >= 'a' && tolower (ic) <= 'f')
ic = 10 + tolower (ic) - 'a';
else
break;
val = val * base + ic;
ic = getc(iop);
done_some = 1;
}
if (do_assign) {
if (sign)
val = -val;
if (longflag)
*va_arg(ap, unsigned long *) = (unsigned long) val;
else if (shortflag)
*va_arg(ap, unsigned short *) = (unsigned short) val;
else
*va_arg(ap, unsigned *) = (unsigned) val;
}
if (done_some) {
if (do_assign) ++done;
}
else
goto all_done;
break;
case 'c':
if (!widflag)
width = 1;
{ register char *p;
if (do_assign)
p = va_arg(ap, char *);
while (width-- && ic >= 0) {
if (do_assign)
*p++ = (char) ic;
ic = getc(iop);
done_some = 1;
}
}
if (do_assign) {
if (done_some)
++done;
}
break;
case 's':
if (!widflag)
width = 0xffff;
{ register char *p;
if (do_assign)
p = va_arg(ap, char *);
while (width-- && !isspace (ic) && ic > 0) {
if (do_assign)
*p++ = (char) ic;
ic = getc(iop);
done_some = 1;
}
if (do_assign) /* terminate the string */
*p = '\0';
}
if (done_some) {
if (do_assign)
++done;
}
else
goto all_done;
break;
case '[':
if (!widflag)
width = 0xffff;
if ( *(++format) == '^' ) {
reverse = 1;
format++;
} else
reverse = 0;
{ register char *c;
for (c = Xtable; c < &Xtable[128]; c++) *c = 0;
}
while (*format && *format != ']') {
Xtable[*format++] = 1;
}
if (!*format)
goto quit;
{ register char *p;
if (do_assign)
p = va_arg(ap, char *);
while (width-- && !isspace (ic) && ic > 0 &&
(Xtable[ic] ^ reverse)) {
if (do_assign)
*p++ = (char) ic;
ic = getc(iop);
done_some = 1;
}
if (do_assign) /* terminate the string */
*p = '\0';
}
if (done_some) {
if (do_assign)
++done;
}
else
goto all_done;
break;
#ifndef NOFLOAT:
case 'e':
case 'f': {
register char *c = buffer;
if (!widflag) width = 127;
if (width >= 128) width = 127;
if (width && (ic == '+' || ic == '-')) {
*c++ = ic;
width--;
ic = getc(iop);
}
while (isdigit(ic) && width) {
width--;
*c++ = ic;
ic = getc(iop);
}
if (ic == '.' && width) {
width--;
*c++ = ic;
ic = getc(iop);
}
while (isdigit(ic) && width) {
width--;
*c++ = ic;
ic = getc(iop);
}
if (width && (ic == 'e' || ic == 'E')) {
width--;
*c++ = ic;
ic = getc(iop);
if (width && (ic == '+' || ic == '-')) {
width--;
*c++ = ic;
ic = getc(iop);
}
}
while (isdigit(ic) && width) {
width--;
*c++ = ic;
ic = getc(iop);
}
if (c == buffer) goto all_done;
*c = 0;
if (do_assign) {
done++;
if (longflag)
*va_arg(ap, double *) = atof(buffer);
else
*va_arg(ap, float *) = atof(buffer);
}
}
break;
#endif
} /* end switch */
++format;
}
all_done:
if (ic >= 0)
ungetc(ic, iop);
quit:
return done;
}