/* ptplus.C		Top level of Perfect Formatter print driver

	this is basically pp.c without main(), used to access
	common routines between pp.c and pt.c

******************************************************************************/


#include "pp.h"

menu(fname)
char	*fname;
{
	if(lpt_fd != -1)
		close(lpt_fd);
	fflush(stdout);
	exec("pw", fname);
	exec("menu", "");
	exec("a:menu", "");
	exit(1);
}

Fatal(fmt, a1, a2, a3, a4, a5)	/* lose, lose */
char *fmt;
int a1, a2, a3, a4, a5;
{
	printf(fmt, a1, a2, a3, a4, a5);
	menu();
}

DoLine()					/* process an input line */
{
	if(ReadLine()) {
		PrintLine();
		return(TRUE);
	}
	return(FALSE);
}


ReadLine()				/* read the line and build data structure */
{
	FLAG done;
	FLAG sbrkprev;				/* was previous char a SENTENCEBREAK? */
	char inchar, brklevel, IChar();
	struct token *temptok, *attrtok;
	int tdist;

	inchars = intokens = 0;
	superp = subp = FALSE;
	justp = FALSE;
	ForceTok();
	done = FALSE;
	while(!done) {
		PSendChk();
		switch(IChar()) {


		case LF:
			if(curpage >= startpage) PLF();
			break;

		case FF:
			NextPage();
			break;

		case VERTSPACE:
			tdist = GetDist(VERTICAL);
			if(curpage >= startpage) PMoveV(tdist);
			break;
		default:
			done = TRUE;
			break;
		}
		if(!done) INext();
	}
	done = sbrkprev = FALSE;
	repeat {
		PSendChk();
		inchar = IChar();
		switch(inchar) {

		case CR:
			INext();

		case LF:
		case FF:
			done = TRUE;
			break;

		case CPM_EOF:
			return(FALSE);
			break;

		case VERTSPACE:
			done = TRUE;
			break;

		case HORIZSPACE:
			ForceTok();
			temptok = GetTok();
			temptok->toklead += GetDist(HORIZONTAL);
			break;

		case BOLDON:
			attrtok = AttrPush();
			attrtok->tokbold = TRUE;
			ForceTok();
			break;

		case ROMANON:
			attrtok = AttrPush();
			attrtok->tokbold = FALSE;
			attrtok->tokital = FALSE;
			ForceTok();
			break;

		case UNNBON:
		case UNALLON:
		case UNANON:
			attrtok = AttrPush();
			attrtok->tokuscore = inchar;
			ForceTok();
			break;

		case ITALON:
			attrtok = AttrPush();
			attrtok->tokital = TRUE;
			ForceTok();
			break;

		case F2ON:
			attrtok = AttrPush();
			attrtok->tokf2 = TRUE;
			ForceTok();
			break;

		case F3ON:
			attrtok = AttrPush();
			attrtok->tokf3 = TRUE;
			ForceTok();
			break;

		case F1ON:
			attrtok = AttrPush();
			attrtok->tokf1 = TRUE;
			ForceTok();
			break;

		case BITALON:
			attrtok = AttrPush();
			attrtok->tokbold = TRUE;
			attrtok->tokital = TRUE;
			ForceTok();
			break;

		case SUPERON:
			superp = TRUE;
			attrtok = AttrPush();
			attrtok->tokscript = inchar;
			ForceTok();
			break;

		case SUBON:
			subp = TRUE;
			attrtok = AttrPush();
			attrtok->tokscript = inchar;
			ForceTok();
			break;

		case EXITON:
			attrtok = AttrPush();
			attrtok->tokscript = FALSE;
			attrtok->tokuscore = FALSE;
			attrtok->tokbold = FALSE;
			attrtok->tokital = FALSE;
			attrtok->tokf1 = FALSE;
			attrtok->tokf2 = FALSE;
			attrtok->tokf3 = FALSE;
			attrtok->tokjust = FALSE;
			ForceTok();
			break;

		case SUBOFF:
		case SUPEROFF:
		case BITALOFF:
		case ITALOFF:
		case UNANOFF:
		case UNALLOFF:
		case UNNBOFF:
		case ROMANOFF:
		case BOLDOFF:
		case F1OFF:
		case F2OFF:
		case F3OFF:
		case EXITOFF:
			AttrPop();
			ForceTok();
			break;

		case JUSTIFY:
			justp = TRUE;
			just.jlefttok = intokens;
			just.jspace = GetDist(HORIZONTAL);
			just.jnbrk = 0;
			break;

		case TOKENBREAK:
			ForceTok();
			temptok = GetTok();
			temptok->tokjust = TRUE;
			temptok->toklead += PWidChar(' ');
			++just.jnbrk;
			break;

		case SENTENCEBREAK:
			ForceTok();
			temptok = GetTok();
			temptok->toklead += PWidSent();
			break;
		default:
			temptok = GetTok();
			iline[inchars++] = inchar;
			temptok->tokwidth += PWidChar(inchar);
			break;
		}
		if(done) break;
		sbrkprev = inchar == SENTENCEBREAK;
		INext();
	}
	if(sbrkprev  &&  justp) just.jspace += PWidSent();
	itokens[intokens].tokbegin = inchars;
	return(TRUE);
}


ForceTok()				/* break current token */
{
	forcetok = TRUE;
}


struct token *
GetTok()					/* make a new token if needed */
{
	if(forcetok) {
		movmem(&attrstack[attrsp], &itokens[intokens],
		sizeof(attrstack[0]));
		itokens[intokens].tokbegin = inchars;
		itokens[intokens].tokwidth = 0;
		itokens[intokens].toklead = 0;
		++intokens;
		forcetok = FALSE;
	}
	return(&itokens[intokens-1]);
}


GetDist(direc)			/* get a distance from the input stream */
int direc;
{
	unsigned tdist;

	tdist = (INext() << 8) + INext();
	if(tdist > MAXMICA)
		Fatal("Invalid mica value in intermediate file.\n");
	return(PUnMica(tdist, direc));
}


AttrInit()				/* initialize the attribute stack */
{
	attrsp = 0;
	attrstack[0].tokscript
	    = attrstack[0].tokuscore
	    = attrstack[0].tokbold
	    = attrstack[0].tokital
	    = attrstack[0].tokf1
	    = attrstack[0].tokf2
	    = attrstack[0].tokf3
	    = attrstack[0].tokjust
	    = FALSE;
}


struct token *
AttrPush()				/* push attribute stack */
{
	if(attrsp >= ATTRMAX)
		Fatal("Character attributes nested too deeply.\n");
	else {
		movmem(&attrstack[attrsp], &attrstack[attrsp+1],
		sizeof(attrstack[0]));
		++attrsp;
	}
	return(&attrstack[attrsp]);
}


AttrPop()
{
	if(attrsp <= 0) Fatal("AttrPop: stack underflow\n");
	--attrsp;
}


PagePause()				/* pause, get fresh page from user */
{
	PForce();
	puts("\nInsert fresh page; type any character when ready --> ");
	getch();
	putchar('\n');
}


PrintLine()				/* output the accumulated line */
{
	int tvert;

	if(curpage < startpage) return;
	if(justp) JustLine();
	tvert = max(1, PLineV() / 2);
	if(superp) {
		PMoveV(-tvert);
		PrintScript(SUPERON);
		PCR();
		PMoveV(tvert);
	}
	PrintScript(FALSE);
	PCR();
	if(subp) {
		PMoveV(tvert);
		PrintScript(SUBON);
		PCR();
		PMoveV(-tvert);
	}
}


PrintScript(script)		/* print all of super, normal, or subscripts */
char script;
{
	int thmove, itok;

	thmove = 0;
	for(itok=0; itok < intokens; ++itok) {
		if(itokens[itok].tokscript == script) {
			PMoveH(thmove);
			PrintTok(itok, script);
			PSendChk();
			thmove = 0;
		}
		else thmove += itokens[itok].tokwidth + itokens[itok].toklead;
	}
}


PrintTok(itok, script)		/* print a token */
int itok;
char script;
{
	int ichar;

	if(itok != 0  &&  !script
	    &&  itokens[itok-1].tokuscore == UNALLON
	    &&  itokens[itok].tokuscore == UNALLON)
		PSetAttr(UNDERSCORE, UNALLON);
	PMoveH(itokens[itok].toklead);
	PSetAttr(UNDERSCORE, itokens[itok].tokuscore);
	PSetAttr(BOLD, itokens[itok].tokbold);
	PSetAttr(ITALIC, itokens[itok].tokital);
	PSetAttr(F1, itokens[itok].tokf1);
	PSetAttr(F2, itokens[itok].tokf2);
	PSetAttr(F3, itokens[itok].tokf3);
	for(ichar = itokens[itok].tokbegin; ichar < itokens[itok+1].tokbegin;
								  ++ichar)
		    PPrtChar(iline[ichar]);
	PSetAttr(UNDERSCORE, FALSE);
}


JustLine()				/* justify the line */
{
	int totspace, njust;
	int itok, evenspace, oddstart, oddcnt;

	totspace = just.jspace;
	njust = just.jnbrk;
	if(!njust) return;
	evenspace = totspace / njust;
	oddstart = ((just.jspace * 17) & 0x7FFF) % njust; 	/* random num */
	oddcnt = totspace % njust;
	PSendChk();
	for(itok = just.jlefttok; itok < intokens; ++itok)
		if(itokens[itok].tokjust) itokens[itok].toklead += evenspace;
	PSendChk();
	for(itok = just.jlefttok; oddstart > 0; ++itok)
		if(itokens[itok].tokjust) --oddstart;
	PSendChk();
	for(; oddcnt > 0; ++itok) {
		if(itok >= intokens) itok = just.jlefttok;
		if(itokens[itok].tokjust) {
			--oddcnt;
			++itokens[itok].toklead;
		}
	}
	PSendChk();
}


/* Buffered file input routines */

struct fbuf *
IOpen(name, mode)			/* open file <name> in <mode> */
char *name;
int mode;
{
	int tfd;
	struct fbuf *buf;

	buf = &inputbuf;		/* someday this might allocate */
	tfd = open(name, mode);
	if(tfd < 0) return((struct fbuf *)ERROR);
	buf->fchan = tfd;
	strcpy(buf->fname, name);
	buf->fnleft = 0;
	INext(buf);
	return(buf);
}


char
IChar()					/* return current char */
{
	struct fbuf *buf;

	buf = &inputbuf;
	if(!buf->fchan) return(ERROR);
	return(*(buf->fnextp));
}


char
INext()					/* get next char & return */
{
	struct fbuf *buf;
	int nread;

	buf = &inputbuf;
	if(!buf->fchan) return(ERROR);
	++(buf->fnextp);
	if(--(buf->fnleft) <= 0) {
		nread = read(buf->fchan, buf->fbuff, BUFBLKS * 128); /* XXX */
		if(nread < 0) {
			IClose();
			return(ERROR);
		}
		buf->fnleft = nread;		/* nread*128 */	/* XXX */
		buf->fnextp = &(buf->fbuff[0][0]);
	}
	return(*(buf->fnextp));
}


char *
IName()					/* return file name */
{
	struct fbuf *buf;

	buf = &inputbuf;
	if(!buf  ||  !buf->fchan) return("");
	return(buf->fname);
}


IRew()					/* rewind file */
{
	struct fbuf *buf;

	buf = &inputbuf;
	if(!buf  ||  !buf->fchan) return;
	lseek(buf->fchan, (long)0, ABSOLUTE);
	buf->fnleft = 0;
	INext(buf);
}


IClose()					/* close the file */
{
	struct fbuf *buf;

	buf = &inputbuf;
	if(!buf  ||  !buf->fchan) return(ERROR);
	close(buf->fchan);
	buf->fchan = NULL;
}


match(s1, s2)				/* case-independent string equality */
char *s1, *s2;
{
	while(TRUE) {
		if(toupper(*s1) != toupper(*s2)) return(FALSE);
		if(!*s1++ || !*s2++) return(TRUE);
	}
}


Ask(q)					/* ask a yes/no question */
char *q;
{
	char	chr;

	puts(q);
	repeat {
		chr = TGetKb();
		switch(toupper(chr)) {

		case ' ':
		case 'Y':
			puts("Yes\n");
			return(TRUE);
		case DEL:
		case BS:
		case BEL:
		case 'N':
			puts("No\n");
			return(FALSE);
		default:
			puts("\nAnswer 'Y' or 'N' --> ");
			fflush(stdout);
			break;
		}
	}
}


dfltext(fn, ext)			/* default fn's extension to ext */
char *fn, *ext;
{
	while(*fn) if(*fn++ == '.') return;
	*fn++ = '.';
	strcpy(fn, ext);
}


/* "Terminal abstraction" */

static int gotch = 0;

TKbRdy()				/* Returns TRUE if input available */
{
	if(gotch)
		return(gotch);
	return(gotch = bdos(6, -1));
}


TGetKb()				/* Returns an input character */
{
	int tmp;

	while(!gotch)
		TKbRdy();
	tmp = gotch;
	gotch = 0;
	return(tmp);
	
}


Copyright()
{
	puts("(C) Perfect Software, Inc.");
}

/* End of pp.C  --  Perfect Formatter's print driver top level */
