#ifndef lint
static char sccsid[] = "@(#)beefscan.c	1.1 (Dale Mosby/BeEF) 1/5/95";
#endif
#include <stdio.h>
#ifdef __STDC__
#include <string.h>
extern void exit(int);
#endif
#define Printf	(void) printf

#include "ucbtest.h"
static int anyfail=0;		/* Count kinds of failures */

#define BEGIN		0
#define DONE		1
#define NEW_FUNC	2
#define NEW_REGION	3
#define NME		4
#define PME		5
#define IGNORE		6

#define CTRL_L		12

static FILE *fp = (FILE *) NULL;

static char linebuf[BUFSIZ];
static char func[BUFSIZ];

static int last_state = BEGIN;
static int f_data;
static int s_data;
static int s_count;
static int m_count;
static long rt_count, st_count;
static int s_symm;
static long rt_symm, st_symm;
static int have_me;
static double rt_nme, rt_pme;
static double st_nme, st_pme, m_err;
static double rt_bgn = 1.e38, rt_end = -1.e38;
static double st_bgn = 1.e38, st_end = -1.e38;


static void
#ifdef __STDC__
line_err(void)
#else
line_err()
#endif
{
	(void) fprintf(stderr, "Cannot scan line - '%s'\n", linebuf);
	ucbfail( __FILE__ , __LINE__ );
	exit(1);
}

static void
#ifdef __STDC__
scan_line(void)
#else
scan_line()
#endif
{
	char *p;

	m_count = 0;
	s_symm = 0;
	have_me = 0;

	p = linebuf;
	while (*p == ' ')		/* move to first string */
		p++;
	if (*p == '\0')
		line_err();

	while (*p != '\0' && *p != ' ')		/* skip over first string */
		p++;
	if (*p != ' ')
		line_err();

	while (*p == ' ')		/* move to second string */
		p++;
	if (*p == '\0')
		line_err();

	if (p[1] != '-') {
		if (sscanf(p, "%lf", &m_err) != 1)
			line_err();
		have_me++;
	}

	while (*p != '\0' && *p != ' ')		/* skip over second string */
		p++;
	if (*p == '\0')
		line_err();

	while (*p == ' ')		/* move to third string */
		p++;
	if (*p == '\0')
		line_err();

	while (*p != ' ')		/* skip over third string */
		p++;
	if (*p == '\0')
		line_err();

	while (*p == ' ')		/* move to fourth string */
		p++;

	/* May not have string if on PME line. */

	if (*p == '\0' || *p == '0' && *(p + 1) == 'x')
		return;
	if (*p != '{') /* } */ {
		if (sscanf(p, "%d", &m_count) != 1)
			line_err();
		while (*p != '\0' && *p != ' ')	/* skip over fourth string */
			p++;
		if (*p == '\0')
			return;

		while (*p == ' ')		/* move to fifth string */
			p++;
		if (*p != '{')	/* } */
			line_err();
	}
	p++;
	if (sscanf(p, "%d", &s_symm) != 1)
		line_err();
}

static void
#ifdef __STDC__
add_pme(void)
#else
add_pme()
#endif
{
	scan_line();

	if (have_me) {
		if (rt_pme < m_err)
			rt_pme = m_err;
		if (st_pme < m_err)
			st_pme = m_err;
	}

	if (s_symm) {
		rt_symm += (long) s_symm;
		st_symm += (long) s_symm;
	}
}

static void
#ifdef __STDC__
add_nme(void)
#else
add_nme()
#endif
{
	scan_line();

	if (have_me) {
		if (rt_nme > m_err)
			rt_nme = m_err;
		if (st_nme > m_err)
			st_nme = m_err;
	}

	if (m_count) {
		rt_count += (long) m_count;
		st_count += (long) m_count;
	}

	if (s_symm) {
		rt_symm += (long) s_symm;
		st_symm += (long) s_symm;
	}

	f_data = 1;
	s_data = 1;
}

static void
#ifdef __STDC__
pr_range(char *funcp, int region, double bgn, double end)
#else
pr_range(funcp, region, bgn, end)
char *funcp;
int region;
double bgn, end;
#endif
{
	switch (region) {
	case -1:
		Printf("%-10.10s      [      From     ,        to     ) ", "");
		break;
	case 0:
		Printf("%-10.10s  ALL [ %14.7f, %14.7f) ", funcp, bgn, end);
		break;
	default:
		Printf("%-10.10s %4d [ %14.7f, %14.7f) ", funcp, region, bgn, end);
		break;
	}
}

static void
#ifdef __STDC__
pr_error(double e, int ok)
#else
pr_error(e, ok)
double e;
int ok;
#endif
{
	double E;

	switch (ok) {
	case -1:
		Printf(" %c.M.E.", e < (double) 0 ? 'N' : 'P');
		break;
	case 0:
		Printf(" ------");
		break;
	case 1:
		E = e >= (double) 0 ? e : -e;
		if (E >= 1) anyfail += E;	/* add ulps */
		if (E < (double) 100)
			Printf("%7.3f", e);
		else if (E < (double) 10000)
			Printf("%7.1f", e);
		else
			Printf("%7.0e", e);
		break;
	default:
		(void) fprintf(stderr, "beefscan: pr_error() botch\n");
		ucbfail( __FILE__ , __LINE__ );
		exit(1);
	}
	Printf(" ");
}

static void
#ifdef __STDC__
sub_summary(void)
#else
sub_summary()
#endif
{
	if (s_data) {
		s_count++;
#ifndef QUIET
		pr_range(func, s_count, st_bgn, st_end);
		pr_error(st_nme, st_nme <= (double) 0);
		pr_error(st_pme, st_pme >= (double) 0);
		Printf("%ld", st_count);
		if (st_symm)
			Printf(" {%ld}", st_symm);
		Printf("\n");
#endif
	}

	if (*linebuf != '\0' && *linebuf == CTRL_L) {
		char *p = linebuf;

		while (*p != '\0' && *p != '[')
			p++;
		if (*p != '\0' && sscanf(p, "[%lf,%lf", &st_bgn, &st_end) != 2)
				line_err();
		if (!f_data)
			rt_bgn = st_bgn;
	}
	st_nme =  1.e38;
	st_pme = -1.e38;
	st_count = 0L;
	st_symm = 0L;
	s_data = 0;
}

static void
#ifdef __STDC__
func_summary(void)
#else
func_summary()
#endif
{
	char *p, *q;

	if (f_data) {
		rt_end = st_end;
#ifndef QUIET
		Printf("\n");
#endif
		pr_range(func, 0, rt_bgn, rt_end);
		pr_error(rt_nme, rt_nme <= (double) 0);
		pr_error(rt_pme, rt_pme >= (double) 0);
		anyfail += rt_count;	/* add mono fails */
		Printf("%ld", rt_count);
		anyfail += rt_symm; /* add symm fails */
		if (rt_symm)
			Printf(" {%ld}", rt_symm);
#ifdef QUIET
		Printf("\n");
#else
		Printf("\n\n");
#endif
	}

	rt_bgn = rt_nme =  1.e38;
	rt_end = rt_pme = -1.e38;
	f_data = 0;
	rt_count = 0L;
	rt_symm = 0L;
	s_count = 0;

	if (linebuf[1] != '>')
		return;
	p = linebuf;
	q = func;
	while (*p != '\0' && *p != '<')
		p++;
	if (*p != '<') {
		(void) fprintf(stderr, "Cannot find function name!\n");
		ucbfail( __FILE__ , __LINE__ );
		exit(1);
	}

	while (*p != ' ')	/* scan to front of function name */
		p--;
	while (*p == ' ')
		p--;
	p--;
	while (*p != ' ')
		p--;
	p++;
	while (*p != '\0' && *p != ' ')
		*q++ = *p++;
	*q = '\0';
}

static int
#ifdef __STDC__
line_type(void)
#else
line_type()
#endif
{
	char *p;

	if (linebuf[0] == CTRL_L && linebuf[1] == '\0') {
		(void) fgets(linebuf + 1, BUFSIZ - 1, fp);
		linebuf[strlen(linebuf) - 1] = '\0';
	}
	if (linebuf[1] == '>')
		return(NEW_FUNC);

	if (linebuf[0] == CTRL_L) {
		return(NEW_REGION);
	}

	if (linebuf[0] == '-' &&
		linebuf[1] == '-')	/* Have to discard this line now */
		return(IGNORE);		/* before the next test. */

	p = linebuf;
	while (*p == ' ')
		p++;
	if (*p == '-' || (*p >= '0' && *p <= '9')) {
		if (last_state == NME)
			return(PME);
		else
			return(NME);
	}

	return IGNORE;
}

static int
#ifdef __STDC__
get_rec(void)
#else
get_rec()
#endif
{
	linebuf[0] = linebuf[1] = '\0';

	if (fgets(linebuf, BUFSIZ, fp) == (char *) NULL)
		return(DONE);
	linebuf[strlen(linebuf) - 1] = '\0';

	return line_type();
}

static void
#ifdef __STDC__
header(void)
#else
header()
#endif
{
  Printf("\n NME  =  Negative Maximum Error observed in ULPs\n");
    Printf(" PME  =  Positive Maximum Error observed in ULPs\n");
    Printf(" NMC  =  Non-monotonicity count\n");
    Printf(" {SYM}=  Non-symmetry count if nonzero\n\n");
    pr_range((char *) NULL, -1, (double) 0, (double) 0);
    pr_error((double) -1, -1);
    pr_error((double) 1, -1);
    Printf("NMC {SYM}\n\n");
}

void
#ifdef __STDC__
main(int argc, char *argv[])
#else
main(argc, argv)
char *argv[];
#endif
{
	int result = last_state;

	argc--, argv++;
{char c; /* print ndiv/narg stuff */
c=0;
while (c != '\n') { /* first line */
c=getchar();
putchar(c);
}
c=0;
while (c != '\n') { /* second line */
c=getchar();
putchar(c);
}
}
	for (;;) {
		if (last_state != BEGIN)
			result = get_rec();
		switch (result) {
		case BEGIN:
			result = DONE;
			if (!argc) {
				if (fp != (FILE *) NULL) {
					if (anyfail == 0) 
						ucbpass( func , __LINE__ );
					else
						ucbfail( func , __LINE__ );
					exit(0);
				}
				fp = stdin;
				header();
			}
			else {
				if ((fp = fopen(*argv, "r")) == (FILE *) NULL) {
					(void) fprintf(stderr,
						"%s: file not found.\n", *argv);
					result = BEGIN;
				}
				else {
					Printf(":::::: %s ::::::\n", *argv);
					header();
				}
				argc--, argv++;
			}
			break;
		case DONE:
			sub_summary();
			func_summary();
			result = BEGIN;
			break;
		case NEW_FUNC:
			sub_summary();
			func_summary();
			break;
		case NEW_REGION:
			sub_summary();
			break;
		case NME:
			add_nme();
			break;
		case PME:
			add_pme();
			break;
		case IGNORE:
			break;
		default:
			(void) fprintf(stderr, "Logic error - botched switch\n");
			ucbfail( __FILE__ , __LINE__ );
			exit(1);
		}
		last_state = result;
	}
	/* NOTREACHED */
}
