ack/lang/cem/libcc.ansi/stdio/doscan.c

434 lines
9.4 KiB
C
Raw Normal View History

1989-05-30 13:34:25 +00:00
/*
* doscan.c - scan formatted input
*/
/* $Header$ */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include "loc_incl.h"
1989-06-26 10:37:05 +00:00
#define NUMLEN 512
1989-12-18 15:04:14 +00:00
#define NR_CHARS 256
1989-05-30 13:34:25 +00:00
1989-12-18 15:04:14 +00:00
static char Xtable[NR_CHARS];
1989-05-30 13:34:25 +00:00
static char inp_buf[NUMLEN];
/* Collect a number of characters which constitite an ordinal number.
* When the type is 'i', the base can be 8, 10, or 16, depending on the
* first 1 or 2 characters. This means that the base must be adjusted
* according to the format of the number. At the end of the function, base
* is then set to 0, so strtol() will get the right argument.
*/
static char *
o_collect(register int c, register FILE *stream, char type,
1989-12-18 15:04:14 +00:00
int width, int *basep)
1989-05-30 13:34:25 +00:00
{
register char *bufp = inp_buf;
1989-12-18 15:04:14 +00:00
register int base;
1989-05-30 13:34:25 +00:00
switch (type) {
case 'i': /* i means octal, decimal or hexadecimal */
case 'p':
case 'x':
1989-12-18 15:04:14 +00:00
case 'X': base = 16; break;
1989-05-30 13:34:25 +00:00
case 'd':
1989-12-18 15:04:14 +00:00
case 'u': base = 10; break;
case 'o': base = 8; break;
case 'b': base = 2; break;
1989-05-30 13:34:25 +00:00
}
if (c == '-' || c == '+') {
*bufp++ = c;
1989-12-18 15:04:14 +00:00
if (--width);
c = getc(stream);
1989-05-30 13:34:25 +00:00
}
1989-12-18 15:04:14 +00:00
if (width && c == '0' && base == 16) {
1989-05-30 13:34:25 +00:00
*bufp++ = c;
1989-12-18 15:04:14 +00:00
if (--width)
c = getc(stream);
1989-05-30 13:34:25 +00:00
if (c != 'x' && c != 'X') {
1989-12-18 15:04:14 +00:00
if (type == 'i') base = 8;
1989-05-30 13:34:25 +00:00
}
else if (width) {
*bufp++ = c;
1989-12-18 15:04:14 +00:00
if (--width)
c = getc(stream);
1989-05-30 13:34:25 +00:00
}
}
1989-12-18 15:04:14 +00:00
else if (type == 'i') base = 10;
1989-05-30 13:34:25 +00:00
while (width) {
1989-12-18 15:04:14 +00:00
if (((base == 10) && isdigit(c))
|| ((base == 16) && isxdigit(c))
|| ((base == 8) && isdigit(c) && (c < '8'))
|| ((base == 2) && isdigit(c) && (c < '2'))) {
1989-05-30 13:34:25 +00:00
*bufp++ = c;
1989-12-18 15:04:14 +00:00
if (--width)
c = getc(stream);
1989-05-30 13:34:25 +00:00
}
else break;
}
1989-12-18 15:04:14 +00:00
if (width && c != EOF) ungetc(c, stream);
if (type == 'i') base = 0;
*basep = base;
*bufp = '\0';
return bufp - 1;
1989-05-30 13:34:25 +00:00
}
#ifndef NOFLOAT
/* The function f_collect() reads a string that has the format of a
* floating-point number. The function returns as soon as a format-error
* is encountered, leaving the offending character in the input. This means
* that 1.el leaves the 'l' in the input queue. Since all detection of
* format errors is done here, _doscan() doesn't call strtod() when it's
1989-12-18 15:04:14 +00:00
* not necessary, although the use of the width field can cause incomplete
* numbers to be passed to strtod(). (e.g. 1.3e+)
1989-05-30 13:34:25 +00:00
*/
static char *
1989-12-18 15:04:14 +00:00
f_collect(register int c, register FILE *stream, register int width)
1989-05-30 13:34:25 +00:00
{
register char *bufp = inp_buf;
int digit_seen = 0;
if (c == '-' || c == '+') {
*bufp++ = c;
1989-12-18 15:04:14 +00:00
if (--width)
c = getc(stream);
1989-05-30 13:34:25 +00:00
}
while (width && isdigit(c)) {
digit_seen++;
*bufp++ = c;
1989-12-18 15:04:14 +00:00
if (--width)
c = getc(stream);
1989-05-30 13:34:25 +00:00
}
if (width && c == '.') {
*bufp++ = c;
1989-12-18 15:04:14 +00:00
if(--width)
c = getc(stream);
1989-05-30 13:34:25 +00:00
while (width && isdigit(c)) {
digit_seen++;
*bufp++ = c;
1989-12-18 15:04:14 +00:00
if (--width)
c = getc(stream);
1989-05-30 13:34:25 +00:00
}
}
if (!digit_seen) {
1989-12-18 15:04:14 +00:00
if (width && c != EOF) ungetc(c, stream);
1989-05-30 13:34:25 +00:00
return inp_buf - 1;
}
else digit_seen = 0;
if (width && (c == 'e' || c == 'E')) {
*bufp++ = c;
1989-12-18 15:04:14 +00:00
if (--width)
c = getc(stream);
1989-05-30 13:34:25 +00:00
if (width && (c == '+' || c == '-')) {
*bufp++ = c;
1989-12-18 15:04:14 +00:00
if (--width)
c = getc(stream);
1989-05-30 13:34:25 +00:00
}
while (width && isdigit(c)) {
digit_seen++;
*bufp++ = c;
1989-12-18 15:04:14 +00:00
if (--width)
c = getc(stream);
1989-05-30 13:34:25 +00:00
}
if (!digit_seen) {
1989-12-18 15:04:14 +00:00
if (width && c != EOF) ungetc(c,stream);
1989-05-30 13:34:25 +00:00
return inp_buf - 1;
}
}
1989-12-18 15:04:14 +00:00
if (width && c != EOF) ungetc(c, stream);
*bufp = '\0';
return bufp - 1;
1989-05-30 13:34:25 +00:00
}
#endif /* NOFLOAT */
/*
* the routine that does the scanning
*/
int
_doscan(register FILE *stream, const char *format, va_list ap)
{
int done = 0; /* number of items done */
int nrchars = 0; /* number of characters read */
1989-06-26 10:37:05 +00:00
int conv = 0; /* # of conversions */
1989-05-30 13:34:25 +00:00
int base; /* conversion base */
unsigned long val; /* an integer value */
char *str, *tmp_string; /* temporary pointers */
unsigned width; /* width of field */
int flags; /* some flags */
int reverse; /* reverse the checking in [...] */
int kind;
1989-12-18 15:04:14 +00:00
register int ic; /* the input character */
1989-05-30 13:34:25 +00:00
#ifndef NOFLOAT
1989-06-26 10:37:05 +00:00
long double ld_val;
1989-05-30 13:34:25 +00:00
#endif
while (1) {
if (isspace(*format)) {
1989-12-18 15:04:14 +00:00
while (isspace(*format))
1989-05-30 13:34:25 +00:00
++format; /* skip whitespace */
ic = getc(stream);
nrchars++;
while (isspace (ic)) {
ic = getc(stream);
nrchars++;
}
1989-12-18 15:04:14 +00:00
if (ic != EOF) ungetc(ic,stream);
nrchars--;
1989-05-30 13:34:25 +00:00
}
1989-12-18 15:04:14 +00:00
if (!*format) break; /* end of format */
ic = getc(stream);
nrchars++;
1989-05-30 13:34:25 +00:00
if (ic == EOF)
1989-06-26 10:37:05 +00:00
return conv ? done : EOF;
1989-05-30 13:34:25 +00:00
if (*format != '%') {
if (ic != *format)
break; /* matching error */
++format;
continue;
}
++format;
if (*format == '%') {
if (ic == '%') {
format++;
continue;
}
else break;
}
flags = 0;
if (*format == '*') {
++format;
flags |= FL_NOASSIGN;
}
if (isdigit (*format)) {
flags |= FL_WIDTHSPEC;
for (width = 0; isdigit (*format);)
width = width * 10 + *format++ - '0';
}
switch (*format) {
case 'h': flags |= FL_SHORT; format++; break;
case 'l': flags |= FL_LONG; format++; break;
case 'L': flags |= FL_LONGDOUBLE; format++; break;
}
kind = *format;
if ((kind != 'c') && (kind != '[') && (kind != 'n')) {
while (ic != EOF && isspace(ic)) {
ic = getc(stream);
nrchars++;
}
1989-06-26 10:37:05 +00:00
if (ic == EOF) return conv ? done : EOF;
1989-05-30 13:34:25 +00:00
}
1989-06-26 10:37:05 +00:00
conv++;
1989-05-30 13:34:25 +00:00
switch (kind) {
default:
if (kind == ic) continue;
break;
case 'n':
1989-12-18 15:04:14 +00:00
if (ic != EOF) ungetc(ic, stream);
1989-05-30 13:34:25 +00:00
nrchars--;
if (flags & FL_SHORT)
*va_arg(ap, short *) = (short) nrchars;
else if (flags & FL_LONG)
*va_arg(ap, long *) = (long) nrchars;
else
*va_arg(ap, int *) = (int) nrchars;
break;
case 'b': /* binary */
case 'd': /* decimal */
case 'i': /* general integer */
case 'o': /* octal */
case 'p': /* pointer */
case 'u': /* unsigned */
case 'x': /* hexadecimal */
case 'X': /* ditto */
if (!(flags & FL_WIDTHSPEC))
width = NUMLEN;
1989-12-18 15:04:14 +00:00
if (!width) {
if (ic != EOF) ungetc(ic, stream);
return done;
}
1989-05-30 13:34:25 +00:00
str = o_collect(ic, stream, kind, width, &base);
if (str < inp_buf) return done;
nrchars += str - inp_buf + 1;
if (!(flags & FL_NOASSIGN)) {
if (kind == 'd' || kind == 'i')
val = strtol(inp_buf, &tmp_string, base);
else
val = strtoul(inp_buf, &tmp_string, base);
if (flags & FL_LONG)
*va_arg(ap, unsigned long *) = (unsigned long) val;
else if (flags & FL_SHORT)
*va_arg(ap, unsigned short *) = (unsigned short) val;
else
*va_arg(ap, unsigned *) = (unsigned) val;
done++;
}
break;
case 'c':
if (!(flags & FL_WIDTHSPEC))
width = 1;
if (!(flags & FL_NOASSIGN))
tmp_string = va_arg(ap, char *);
1989-12-18 15:04:14 +00:00
if (!width) {
if (ic != EOF) ungetc(ic, stream);
return done;
}
if (!(flags & FL_NOASSIGN) && (ic != EOF))
1989-05-30 13:34:25 +00:00
++done;
while (width && ic != EOF) {
if (!(flags & FL_NOASSIGN))
*tmp_string++ = (char) ic;
1989-12-18 15:04:14 +00:00
if (--width) {
ic = getc(stream);
nrchars++;
}
1989-05-30 13:34:25 +00:00
}
1989-12-18 15:04:14 +00:00
if (width) {
if (ic != EOF) ungetc(ic,stream);
nrchars--;
}
1989-05-30 13:34:25 +00:00
break;
case 's':
if (!(flags & FL_WIDTHSPEC))
width = 0xffff;
if (!(flags & FL_NOASSIGN))
tmp_string = va_arg(ap, char *);
1989-12-18 15:04:14 +00:00
if (!width) {
if (ic != EOF) ungetc(ic,stream);
return done;
}
1989-05-30 13:34:25 +00:00
if (!(flags & FL_NOASSIGN))
++done;
while (width && ic != EOF && !isspace(ic)) {
if (!(flags & FL_NOASSIGN))
*tmp_string++ = (char) ic;
1989-12-18 15:04:14 +00:00
if (--width) {
ic = getc(stream);
nrchars++;
}
}
/* terminate the string */
if (!(flags & FL_NOASSIGN))
*tmp_string = '\0';
if (width) {
if (ic != EOF) ungetc(ic,stream);
nrchars--;
1989-05-30 13:34:25 +00:00
}
break;
case '[':
1989-12-18 15:04:14 +00:00
{ int old_nrchars;
1989-05-30 13:34:25 +00:00
if (!(flags & FL_WIDTHSPEC))
width = 0xffff;
1989-12-18 15:04:14 +00:00
if (!width) {
if (ic != EOF) ungetc(ic, stream);
return done;
}
1989-05-30 13:34:25 +00:00
if ( *(++format) == '^' ) {
reverse = 1;
format++;
} else
reverse = 0;
1989-12-18 15:04:14 +00:00
for (tmp_string = Xtable; tmp_string < &Xtable[NR_CHARS]
1989-05-30 13:34:25 +00:00
; tmp_string++)
*tmp_string = 0;
if (*format == ']') Xtable[*format++] = 1;
while (*format && *format != ']') {
Xtable[*format++] = 1;
if (*format == '-') {
format++;
if (*format
&& *format != ']'
&& *(format) >= *(format -2)) {
int c;
for( c = *(format -2) + 1
; c <= *format ; c++)
Xtable[c] = 1;
format++;
}
else Xtable['-'] = 1;
}
}
1989-12-18 15:04:14 +00:00
if (!*format) {
if (ic != EOF) ungetc(ic,stream);
1989-05-30 13:34:25 +00:00
return done;
1989-12-18 15:04:14 +00:00
}
1989-05-30 13:34:25 +00:00
1989-12-18 15:04:14 +00:00
old_nrchars = nrchars;
1989-05-30 13:34:25 +00:00
if (!(flags & FL_NOASSIGN))
tmp_string = va_arg(ap, char *);
1989-12-18 15:04:14 +00:00
if (ic == EOF || !(Xtable[ic] ^ reverse)) {
if (ic != EOF) ungetc(ic, stream);
return done;
}
while (width && ic != EOF && (Xtable[ic] ^ reverse)) {
1989-05-30 13:34:25 +00:00
if (!(flags & FL_NOASSIGN))
*tmp_string++ = (char) ic;
1989-12-18 15:04:14 +00:00
if (--width) {
ic = getc(stream);
nrchars++;
}
}
if (width) {
if (ic != EOF) ungetc(ic, stream);
nrchars--;
1989-05-30 13:34:25 +00:00
}
if (!(flags & FL_NOASSIGN)) { /* terminate string */
*tmp_string = '\0';
++done;
}
break;
1989-12-18 15:04:14 +00:00
}
1989-05-30 13:34:25 +00:00
#ifndef NOFLOAT:
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
if (!(flags & FL_WIDTHSPEC)) width = NUMLEN;
if (width > NUMLEN) width = NUMLEN;
if (!width) return done;
str = f_collect(ic, stream, width);
if (str < inp_buf) return done;
nrchars += str - inp_buf + 1;
if (!(flags & FL_NOASSIGN)) {
1989-06-26 10:37:05 +00:00
ld_val = strtod(inp_buf, &tmp_string);
1989-05-30 13:34:25 +00:00
if (flags & FL_LONGDOUBLE)
1989-06-26 10:37:05 +00:00
*va_arg(ap, long double *) = (long double) ld_val;
1989-05-30 13:34:25 +00:00
else
if (flags & FL_LONG)
1989-06-26 10:37:05 +00:00
*va_arg(ap, double *) = (double) ld_val;
1989-05-30 13:34:25 +00:00
else
1989-06-26 10:37:05 +00:00
*va_arg(ap, float *) = (float) ld_val;
1989-05-30 13:34:25 +00:00
done++;
}
break;
#endif
} /* end switch */
++format;
}
1989-06-26 10:37:05 +00:00
return conv ? done : EOF;
1989-05-30 13:34:25 +00:00
}