#ifdef MSDOS	/* { */
#include	"grep_msd.h"
#endif		/* } */
char sccsID[] = "@(#) nroff.c V1.0 Copyright http://www.berklix.com (pjc) 1987\n";
			
/* nroff - a unix nroff lookalike */

#include "nroff.h"	/* define structures etc. */

			/* page parameters */
int	curpage;
int	newpage;
int	lineno;
int	plval;
int	m1val;
int	m2val;
int	m3val;
int	m4val;
int	bottom;

struct	hft	headers[2];
struct	hft	footers[2];

		/* global parameters */
int	fill;
int	lsval;
int	spval;
int	inval;
int	rmval;
int	tival;
int	ceval;
int	ulval;
int	embval;

int	dlval;	/* length of last diversion */

		/* output */
int	out_p;
int	outw;
int	outwds;
int	dir;
char	inbuf[LINESIZE];
char	extrabuf[LINESIZE];
char	outbuf[LINESIZE];

		/* sourceing */
int	nsources;

/* sources */
struct	source	stin;	/* standard input file */
struct	source	stout;	/* standard output file */

struct	source	*macros;		/* list of all macros */
struct	source	*mname;
struct	source	*curmacro;		/* current macro being expanded */
struct	source	*regstrs;		/* registers that are known */
struct	source	*curdiv;		/* current diversion */

struct	when	*whens;		/* traps */
struct	nregs	*numregs;	/* numeric registers */

char	echar = '\\';		/* escape char */
char	pageo = '\f';

char	cmdchar1 = '.';		/* command chars, 1 causes break */
char	cmdchar2 = '\'';	/* command doesn't */

int	save_space;

char	emacro[2];		/* name of end macro */
int	inemacro;

int	adj_type;		/* adjustment type */

char	**ARGV ;
main(argc, argv)
char	**argv;
{
#ifdef	VSL	/* { */
	ARGV = argv ;
#include	"../../include/vsl.h"
#endif		/* } */

	initfmt();
	for(argc--, argv++ ; argc && **argv == '-';argc--, argv++){
		/* ignore for now */
		if(argv[0][1] == '\0')
			break;
	}
	do {
		if(argc > 0){
			if(**argv == '-'){	/* wants stdin */
				stin.m_fp = stdin;
				clearerr(stdin);
			}
			else
				stin.m_fp = fopen(*argv, "r");
		}
		if(stin.m_fp == NULL){
			fprintf(stderr, "Cannot open '%s'\n", *argv);
			exit(1);
		}
		argv++;
	doe:;
						/* main loop */
		while(ngets(inbuf, FALSE))
			if(CMD(inbuf[0]))
				command(inbuf);
			else
				text(inbuf);
		if(stin.m_fp != stdin)
			fclose(stin.m_fp);
	} while(--argc > 0);
	if(!inemacro && emacro[0] != '\0'){
		inemacro = TRUE;
		doemacro(emacro);
		goto doe;
	}
	pageo = 0;
	page(TRUE);			/* flush output */
	dflsh(0);			/* force out output */
	exit(0);
}

initfmt()
{
	stin.m_fp = stdin;	/* set up current input */
	stin.m_val = mymalloc(LINESIZE+1, TRUE);
	stin.m_curp = stin.m_val;
	curmacro = &stin;
	stout.m_fp = stdout;
	stout.m_val = mymalloc(LINESIZE+1, FALSE);
	stout.m_curp = stout.m_val;
	stout.m_eptr = stout.m_val + LINESIZE;
	curdiv = &stout;
	fill = TRUE;
	rmval = PAGEWIDTH;
	lsval = 1;
	newpage = 1;
	plval = PAGELEN;
	m1val = 3;
	m2val = 2;
	m3val = 2;
	m4val = 3;
	bottom = plval - m3val - m4val;
	setfonts();
}

text(ibuf)
char	*ibuf;
{
	char	*cp;
	char	*getword();

	if(ibuf[0] == ' ' || ibuf[0] == '\0')
		leadbl(ibuf);
	if(ulval > 0){
		ulval--;
		underln(ibuf);
	}
	if(embval > 0){
		embval--;
		embold(ibuf);
	}
	if(ceval > 0){
		center(ibuf);
		put(ibuf);
		ceval--;
	}
	else if(ibuf[0] == '\0')
		put(ibuf);
	else if(!fill)
		put(ibuf);
	else {
		int	len;
		for(cp = ibuf; (cp = getword(cp, extrabuf, &len));)
			putword(extrabuf, len);
	}
}

char	*
getword(sp, out, len)
char	*sp, *out;
int	*len;
{
	char	*sap;

	if(*sp == ' '){
		if(*++sp == ' '){
			sap = sp;
			while(*sp == ' ')
				*out++ = *sp++;
		}
		else
			sap = sp;
	}
	else
		sap = sp;
	if(*sp == '\0')
		return( (char *)0);
	while(*sp != '\0' && *sp != ' '){
		if(*sp == echar)	/* non interupted space */
			if(*(sp+1) == ' ')
				*out++ = *sp++;
		*out++ = *sp++;
	}
	*out = '\0';
	*len = sp - sap + 1;
	return(sp);
}
	
command(buf)
char	*buf;
{
	int	cmd;
	int	argtype, spval, val;
	struct	cm	*getcmd();
	struct	cm	*cp;
	int	tobreak = TRUE;

	cp = getcmd(buf);
	if(cp == 0)		/* not a built-in */
		if(mname == 0)	/* macro name ?? */
			return; /* nope, I don't know what it is */
	if(*buf != cmdchar1)
		tobreak = FALSE;
	while(*buf && *buf != ' ')
		buf++;
	while(*buf == ' ')
		buf++;
	if(cp == 0){			/* it's a macro */
		if(mname->m_last != 0){
			fprintf(stderr, "Recursive macro\n");
			exit(3);
		}
		mname->m_last = curmacro;
		curmacro = mname;
		curmacro->m_curp = curmacro->m_val;
		setmargs(curmacro, buf);
		return;
	}
	/* a normal command */
	if(cp->sea != 0)
		val = getval(&buf, &argtype, cp->sea);
	switch(cmd = cp->sev){
	case FI:
		if(tobreak)
			fbreak();
		fill = TRUE;
		break;
	case NF:
		if(tobreak)
			fbreak();
		fill = FALSE;
		break;
	case BR:
		if(tobreak)
			fbreak();
		break;
	case LS:
		setparam(&lsval, val, argtype, 1, 1, HUGE);
		break;
	case CE:
		if(tobreak)
			fbreak();
		setparam(&ceval, val, argtype, 1, 0, HUGE);
		break;
	case UL:
		setparam(&ulval, val, argtype, 1, 0, HUGE);
		break;
	case BO:
		setparam(&embval, val, argtype, 1, 0, HUGE);
		break;
	case HE:
		getftl(buf, &headers[EVEN]);
		headers[ODD] = headers[EVEN];
		break;
	case FO:
		getftl(buf, &footers[EVEN]);
		footers[ODD] = footers[EVEN];
		break;
	case EH:
		getftl(buf, &headers[EVEN]);
		break;
	case OH:
		getftl(buf, &headers[ODD]);
		break;
	case EF:
		getftl(buf, &footers[EVEN]);
		break;
	case OF:
		getftl(buf, &footers[ODD]);
		break;
	case SO:
		setsource(buf);
		break;
	case BP:
		page(tobreak);
		setparam(&curpage, val, argtype, curpage+1, -HUGE, HUGE);
		newpage = curpage;
		break;
	case SP:
		setparam(&spval, val, argtype, 1, 0, HUGE);
		if(tobreak)
			fbreak();
		val = towhen(lineno);
		if(val == 0)
			val = 1;
		space(MIN(val, spval));
		break;
	case IND:
		if(tobreak)
			fbreak();
		setparam(&inval, val, argtype, 0, 0, rmval-1);
		break;
	case RM:
		setparam(&rmval, val, argtype, PAGEWIDTH, inval+tival+1, HUGE);
		break;
	case TI:
		if(tobreak)
			fbreak();
		setparam(&tival, val, argtype, 0, -HUGE, rmval);
		break;
	case PL:
		setparam(&plval, val, argtype, PAGELEN,
				m1val+m2val+m3val+m4val+1, HUGE);
		if(curdiv->m_last == 0)	/* don't set in a diversion */
			bottom = plval - m3val -m4val;
		break;
		/*
		 * macros
		 */
	case DE:
	case AM:
		setmdef(buf, cmd);
		break;
	case EN:
		break;
	case HL:
		spval = m1val + m2val;
		setparam(&spval, val, argtype, spval, 0,
					plval-m3val-m4val-1);
		m1val = spval/2 + (spval & 1);
		m2val = spval/2;
		break;
	case FL:
		spval = m3val + m4val;
		setparam(&spval, val, argtype, spval, 0,
					plval-m1val-m2val-1);
		m3val = spval/2;
		m4val = spval/2 + (spval & 1);
		if(curdiv->m_last == 0)	/* don't set in a diversion */
			bottom = plval - m3val -m4val;
		break;
		/* define a string */
	case DS:
	case AS:	/* append to a string */
		defstr(buf, cmd);
		break;
		/* set a diversion */
	case DI:
	case DA:	/* append to a diversion */
		setdiv(buf, cmd);
		break;
			/* when at line N, call macro XX */
	case WH:
		setwhen(buf);
		break;
	case TL:
		if(tobreak)
			fbreak();
		titil(buf);
		break;
	case NE:
		spval = 0;
		setparam(&spval, val, argtype, 1, 0, HUGE);
		val = towhen(lineno);
		if(val < spval)
			save_space = spval;
		else
			space(MIN(val, spval));
		break;
	case OS:
		if(save_space){
			val = towhen(lineno);
			space(MIN(save_space, val));
		}
		save_space = 0;
		break;
	case NR:
		defreg(buf);
		break;
	case AF:
		setnform(buf);
		break;
	case IF:
	case EI:
		cond(buf, cmd);
		break;
	case EL:
		econd(buf);
		break;
	case EC:		/* set escape char */
		if(*buf == '\0')
			echar = '\\';
		else
			echar = *buf;
		break;
	case EO:		/* turn off the escape char */
		echar = '\0';
		break;
	case CH:
		chwhen(buf);
		break;
	case PN:
		setparam(&newpage, val, argtype, newpage, -HUGE, HUGE);
		break;
	case AD:
	case NA:
		setadtype(buf, cmd);
		break;
	case CC:
		if(*buf)
			cmdchar1 = *buf;
		else
			cmdchar1 = '.';
		break;
	case C2:
		if(*buf)
			cmdchar2 = *buf;
		else
			cmdchar2 = '\'';
		break;
	case EM:
		emacro[0] = *buf;
		if(*buf == '\0')
			break;
		if(buf[1])
			emacro[1] = buf[1];
		break;
	}
}

setparam(param, val, argtype, defval, minval, maxval)
int	*param;
{
	switch(argtype){
	case '\0':
		*param = defval;
		break;
	case '+':
		*param += val;
		break;
	case '-':
		*param -= val;
		break;
	default:
		*param = val;
		break;
	}
	if(*param > maxval)
		*param = maxval;
	else if(*param < minval)
		*param = minval;
}

put(buf)
char	*buf;
{
	int	i;

	if(lineno <= 0 || lineno > bottom)
		puthead();
	tival += inval;
	if(*buf != '\0' && tival > 0){
		if(curfont->f_putc)
			for(i = 0 ; i < tival ; i++)
				(*curfont->f_putc)(' ');
		else
			for(i = 0 ; i < tival ; i++)
				PUT(' ');
	}
	tival = 0;
	putl(buf);
	i = MIN(lsval - 1, bottom - lineno);
	lineno++;
	SPRING(lineno);
	skip(i);
	if(lineno > bottom)
		putfoot(pageo);
}

/* put out the line, with respect for escape sequences */

putl(buf)
char	*buf;
{
	register struct	font	*fp = curfont;
	char	*p, *q;
	char	tbuf[11];

	for(; *buf ; buf++){	/* now put out the text */
		if(*buf == echar){
			switch(*++buf){
			default:
				break;
			case '&':	/* null width char */
				continue;
			case 'e':	/* current scape char */
				if(echar){
					if(fp->f_putc)
						(*fp->f_putc)(echar);
					else
						PUT(echar);
				}
				continue;
			case 'f':
				buf++;
				chfont(&buf);
				fp = curfont;
				continue;
			case 'o':
				p = ++buf;
				if(*p != '\'')
					break;
				p++;
				q = tbuf;
				while(*p && *p != '\'' && p < buf + 9)
					*q++ = *p++;
				if(*p != '\''){
					if(*p == '\0')
						buf = p -1;
					continue;
				}
				buf = p;
				*q = '\0';
				if(fp->f_putc){
					for(q = tbuf ; *q ; q++){
						(*fp->f_putc)(*q);
						if(*(q+1))
							(*fp->f_putc)('\b');
					}
					continue;
				}
				for(q = tbuf ; *q ; q++){
					PUT(*q);
					if(*(q+1))
						PUT('\b');
				}
				continue;
			}
		}
		if(fp->f_putc)
			(*fp->f_putc)(*buf);
		else
			PUT(*buf);
	}
	if(fp->f_putc)
		(*fp->f_putc)('\n');
	else
		PUTNL();
}

skip(n)
{
	register struct	font	*fp = curfont;

	if(n <= 0)
		return;
	for(; n ; n--){
		lineno++;
		if(fp->f_putc)
			(*fp->f_putc)('\n');
		else
			PUTNL();
		if(SPRING(lineno))
			break;
	}
}

puthead()
{
	if(curdiv->m_last != 0){	/* don't do it in a diversion */
		lineno = 1;
		return;
	}
	curpage = newpage;
	newpage++;
	lineno = 1;
	SPRING(0);
	if(m1val > 0){
		skip(m1val -1);
		puttl(&headers[curpage & 1], curpage);
	}
	skip(m2val);
}

putfoot(c)
{
	skip(m3val);
	if(m4val > 0){
		puttl(&footers[curpage & 1], curpage);
		skip(m4val-1);
	}
	if(c)
		PUT(c);
}

space(n)
{
	int	sv;

	fbreak();
	if(lineno > bottom)
		return;
	if(lineno <= 0)
		puthead();
	n = MIN(n, bottom+1-lineno);
	skip(n);
	if(lineno > bottom)
		putfoot(pageo);
}

page(tobreak)
{
	if(tobreak)
		fbreak();
	if(lineno > 0 && lineno <= bottom){
		skip(bottom+1-lineno);
		putfoot(pageo);
	}
	lineno = 0;
}

center(buf)
char	*buf;
{
	int	w = rmval - tival - inval;
	int	ww = width(buf);

	if(ww > w)	/* won't fit, left adjust */
		tival -= ww-w;
	else
		tival += (w - ww)/2;
	if(tival < 0)
		tival = 0;
}

underln(buf)
char	*buf;
{
	chft(buf, 'I');
}

chft(buf, fnt)
char	*buf;
{
	char	name[2];
	char	*bp = buf;
	char	*tp = extrabuf;
	static	char	*ep = extrabuf + LINESIZE -5;
	int	on = FALSE;

	name[0] = curfont->f_name[0];
	name[1] = curfont->f_name[1];
	for(; *bp != '\0' && tp < ep; ){
		if(*bp == ' '){
			if(on){
				*tp++ = echar;
				*tp++ = 'f';
				if(name[1]){
					*tp++ = '(';
					*tp++ = name[0];
					*tp++ = name[1];
				}
				else
					*tp++ = name[0];
				on = FALSE;
			}
			*tp++ = *bp++;
			continue;
		}
		if(!on){
			*tp++ = echar;
			*tp++ = 'f';
			*tp++ = fnt;
			on = TRUE;
		}
		if(*bp == echar)
			*tp++ = *bp++;
		*tp++ = *bp++;
	}
	if(on && tp < ep){
		*tp++ = echar;
		*tp++ = 'f';
		if(name[1]){
			*tp++ = '(';
			*tp++ = name[0];
			*tp++ = name[1];
		}
		else
			*tp++ = name[0];
	}
	*tp = '\0';
	strcpy(buf, extrabuf);
}

embold(buf)
char	*buf;
{
	chft(buf, 'B');
}

putword(wordbuf, len)
char	*wordbuf;
{
	int	last, llval, nextra, w;

	w = width(wordbuf);
	last = out_p + len;
	llval = rmval - tival - inval;
	if(out_p > 0 && (outw + w > llval || last >= LINESIZE)){
		last = len;
		nextra = llval - outw + 1;
		if(nextra > 0 && outwds > 1 && adj_type >= 0){
			spread(outbuf, out_p, nextra, outwds);
			out_p += nextra;
		}
		fbreak();
	}
	strcpy(outbuf + out_p, wordbuf);
	out_p = last;
	outbuf[out_p-1] = ' ';
	outw += w + 1;
	outwds++;
}

spread(buf, outp, nextra, outwds)
char	*buf;
{
	int	i,nb, nholes;
	register char	*ip, *jp;

	if(nextra <= 0 || outwds <= 1)
		return;
	dir = 1-dir;
	nholes = outwds -1;
	i = outp -1;
	ip = buf + i - 1;
	i += nextra;
	if(i > LINESIZE - 1)
		i = LINESIZE - 1;
	jp = buf + i - 1;
	for(;ip < jp && ip >= buf; ip--, jp--){
		*jp = *ip;
		if(*ip == ' '){
			/*
			 * if have multiple space in input don't
			 * play with them, or a no splitable space
			 */
			/*
			 * should check, to see
			 * if its not '\\'
			 */
			if(*(ip-1) == ' ' && *(ip-2) != echar)
				continue;
			if(*(ip-1) == echar)
				continue;

			if(dir == 0)
				nb = (nextra -1) / nholes + 1;
			else
				nb = nextra / nholes;
			nextra -= nb;
			nholes--;
			while(nb > 0){
				*--jp = ' ';
				nb--;
			}
		}
	}
}

width(buf)
char	*buf;
{
	int	w;
	char	*p;

	for(w = 0; *buf; buf++){
		if(*buf == echar){
			switch(*++buf){
			default:
				break;
			case '&':	/* a zero width char */
				continue;
			case 'e':
				if(echar)
					break;
				continue;
			case 'f':	/* sizeof font change */
				if(*++buf == '(')
					buf += 2;
				continue;
			case 'o':
				p = ++buf;
				if(*p != '\'')
					break;
				w++;
				p++;
				while(*p && *p != '\'' && p < buf + 9)
					p++;
				if(*p == '\''){
					buf = p;
					continue;
				}
				else if(*p == '\0'){
					buf = p - 1;
					continue;
				}
				else continue;
			}
		}
		else if(*buf == '\b'){
			w--;
			continue;
		}
		w++;
	}
	if(w < 0)
		w = 0;
	return(w);
}

fbreak()
{
	if(out_p > 0){
		outbuf[out_p - 1] = '\0';
		put(outbuf);
	}
	out_p = 0;
	outw = 0;
	outwds = 0;
}

leadbl(buf)
register char	*buf;
{
	register char	*bp = buf;
	
	fbreak();
	if(*bp == '\0')
		return;
	while(*bp == ' ')
		bp++;
	if(*bp != '\0'){
		tival += bp - buf;
		while(*buf = *bp)
			buf++,bp++;
	}
	else
		*buf = '\0';
}

/*
 * set adjustment type
 */
setadtype(buf, cmd)
char	*buf;
{
	if(cmd == NA)
		adj_type = -1;
	else
		adj_type = *buf;
}

struct	cm *
getcmd(buf)
char	*buf;
{
	register struct	source	*mp;
	register struct	cm *cp;
	char	c = buf[2];

	if(c == ' ')
		c = '\0';
	mname = 0;
	for(mp = macros ; mp ; mp = mp->m_next)
		if(buf[1] == mp->m_name[0] && c == mp->m_name[1]){
			mname = mp;
			return( (struct cm *)0);
		}
	for(cp = cmd ; cp->se ; cp++)
		if(buf[1] == cp->se[0] && c == cp->se[1])
			return(cp);
	return((struct cm *)0);
}

getval(ptr, argtype, calctype)
char	**ptr;
int	*argtype;
{
	char	*buf = *ptr;

	if(*buf == '\0'){
		*argtype = '\0';
		return(0);
	}
	if(calctype == ARG_REL){
		*argtype = *buf;
		if(*argtype == '+' || *argtype == '-')
			buf++;
	}
	else
		*argtype = '0';
	*ptr = buf;
	return(eval(ptr));
}

/* evaluate an expression */
#define	LEEQ	1	/* further operators */
#define GEEQ	2

eval(ptr)
char	**ptr;
{
	char	*buf = *ptr;
	int	res = 0;
	int	val;
	int	cmd = 0;
	int	minus = 0;

	for(;;){
		if(*buf == '('){	/* recurse */
			buf++;
			val = eval(&buf);
			if(*buf == ')')
				buf++;
			else
				break;
		}
		else if(*buf == '-'){
			buf++;
			minus = 1;
			continue;
		}
		else if(*buf == '+'){
			buf++;
			continue;
		}
		else if(!isdigit(*buf))		/* end of expression */
			break;
		else {	/* it's a digit */
			val = *buf++ - '0';
			while(isdigit(*buf))
				val = val * 10 + (*buf++ - '0');
		}
		if(minus){
			val = -val;
			minus = 0;
		}
		if(cmd){	/* had a command last time around */
			switch(cmd){
			case '+':
				res += val;
				break;
			case '-':
				res -= val;
				break;
			case '*':
				res *= val;
				break;
			case '/':
				res /= val;
				break;
			case '%':
				res %= val;
				break;
			case '&':
				res &= val;
				break;
			case ':':
				res |= val;
				break;
			case '=':
				res = (res == val);
				break;
			case '<':
				res = (res < val);
				break;
			case '>':
				res = (res > val);
				break;
			case LEEQ:
				res = (res <= val);
				break;
			case GEEQ:
				res = (res >= val);
				break;
			}
			cmd = 0;
		}
		else
			res = val;
		switch(*buf){
		case '+':
		case '-':
		case '*':
		case '/':
		case '%':
		case '&':
		case ':':
		case '=':
			cmd = *buf++;
			break;
		case '<':
		case '>':
			if(buf[1] == '='){
				if(*buf == '<')
					cmd = LEEQ;
				else
					cmd = GEEQ;
				break;
			}
			cmd = *buf++;
			break;
		}
		if(!cmd)
			break;
	}
	*ptr = buf;
	return(res);
}
