/******************************************************************************
   Perfect Formatter.C -- Perfect Formatter Text Formatter, Main Section

	(C) 1982 Perfect Software, Inc.

	01/26/82	Version 1.00 by Barry A. Dobyns

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

#include "pf.gbl"


#ifndef CPM
char *SSToC(), *SNToC(), *MemGet();
STRING *SNToS(), *SCToS();
#endif

main(argc,argv)
	int argc;
	char *argv[];
{
	char out, dev, *outname, *devname,
#ifdef CPM
	*inname;
#endif

	TInit();
	TPuts("\nPerfect Formatter ");
	TPuts(VERSION);
#ifdef ALPHA
	TPuts(ALPHA);
#endif
	TPuts("\n(C) 1982 Perfect Software, Inc.\n");
#ifdef CPM
	if (serchk() || checksum())	/* check for valid program */
		Broken();	
#endif
	if (argc<=1) {
#ifdef CPM
		Fatal("Usage is:\n pf <file> {-dev <device>} {-text} {-c | -t | -l | -p | -o <name>}\n");
#else
		Fatal("Usage is:\n pf <file> {-dev <device>} {-verbatim} {-tab <number>} {-endspace <number>} {-c | -l | -p | -print | -o <name>}\n");
#endif
		}

 	DefSpace=PageSpace=AInit(STACKSIZE);
	SInit();

	dev=out=NUL;
	outname=devname=inname=NULL;
	termerrf=termrow=pausehack=FALSE;
#ifndef	BADOB
	txtontop=TRUE;		/* This gives TXT for top level */
#else
	txtontop=FALSE;
#endif

	while (--argc>0) {
		argv++;
		if (MulComp(*argv,"-c")) {
			TPuts("Sending output to console.\n");
			out=dev='c';
			}
		else if (MulComp(*argv,"-version")) {
			TPuts("Version ");
			TPuts(VERSION);
#ifdef ALPHA
			TPuts(ALPHA);
#endif
			TPuts(" of ");
			TPuts(DATE);
#ifdef CPM
			TPuts(" serial ");
			TPuts(sernum());
#endif
			TPuts("\n");
			}
#ifndef CPM
		else if (MulComp(*argv,"-endspace")) {
			if (!IsNum(*++argv) || (*((char *) *argv) == '-')) {
				TPuts("Invalid endspace specified.\n");
			}
		     	else {
				tendset = TRUE;
				tendspace = (unsigned) SStoN(*argv);
				TPuts("Endspace reset.\n");
			}
			--argc;
		      }
#endif
		else if (MulComp(*argv,"-pause")){
			TPuts("Pausing between pages.\n");
			pausehack=TRUE;
			}
#ifndef CPM
		else if (MulComp(*argv,"-tab")){
			if (!IsNum(*++argv) || (*((char *) *argv) == '-')) {
				TPuts("Invalid tab spacing specified.\n");
			}
		     	else if (((tabspace = SStoN(*argv)) < 1) ||
					(tabspace > 132)) {
				TPuts("Invalid tab spacing specified.\n");
			}
			else {
				resettabs = TRUE;
				TPuts("Tab spacing reset.\n");
			}
			--argc;
			}
#endif
		else if (MulComp(*argv,"-verbatim", "-v")) {
			txtontop=FALSE;
			TPuts("Verbatim is the top level environment.\n");
			}
		else if (SCComp(*argv, "-typewriter")) {
			txtontop=FALSE;
			typewriter=TRUE;
			}
		else if (MulComp(*argv,"-p","-l")
#ifndef CPM
			 || SCComp(*argv, "-print")
#endif
			){
			TPuts("Sending output to printer.\n");
			out=dev='p';
			}
		else if (SCComp(*argv,"-o")) {
			if (argc>1) {
				out='f';
				TPuts("Output file is named ");
 				outname= *++argv;
				TPuts(outname);
				TPuts(".FIN\n");
				argc--;
				}
			else TPuts("No file specified for -o.  Using default file.\n");
			}
		else if (SCComp(*argv,"-dev")) {
			if (argc>1) {
				dev='n';
				devname= *++argv;
				argc--;
				}
			else TPuts("No device specified for -dev.  Using default device.\n");
			}
		else {
			if (**argv=='-') {
				TPuts("Unknown option '");
				TPuts(*argv);
				TPuts("'\n");
				}
			else inname= *argv;
			}
		}

	if (!inname) {
		Fatal("No input file specified!\n");
		}
#ifdef OVERLAID
	OvlInit();
	OvlCall(IINIT,inname);
	OvlCall(DINIT,dev,devname);
#else
	IInit(inname);			/* set up */
	DInit(dev,devname);
#endif
#ifndef CPM
	if (tendset) sty.endsize = tendspace;
#endif
	EndSpace=ASubSpace(PageSpace,sty.endsize);
	pagesize=ASpace(PageSpace);

	if (!out) outname=inname;
#ifdef OVERLAID
	OvlCall(OINIT,out,outname);
	OvlCall(PINIT);
	OvlCall(LINIT);
	OvlCall(EINIT);
	OvlCall(ENVINIT);
	OvlCall(NUMINIT);
	OvlCall(HEADERINIT);
#else
	OInit(out,outname);
	PInit();
	LInit();
	EInit();
	EnvInit();
	NumInit();
	HeaderInit();
#endif

	pf();			/* do the formatting */

	BreakLine();

#ifdef OVERLAID
	OvlCall(PRINTNOTES);
	OvlCall(PRINTINDEX);
	OvlCall(TABLEOFCONTENTS);
#else
	PrintNotes();
	PrintIndex();
	TableOfContents();
#endif
	BreakLine();
#ifdef OVERLAID
	OvlCall(EFINI);
	OvlCall(IFINI);
	OvlCall(DFINI);
	OvlCall(LFINI);
	OvlCall(PFINI);
	OvlCall(OFINI);
#else
	EFini();				/* clean up */
	IFini();
	DFini();
	LFini();
	PFini();
	OFini();
#endif
#ifndef CPM
	putchar('\n');
#endif
	TFini();
#ifdef CPM
	menu();
#else
	menu(inname);
#endif
	}

pf()
{
	char *IGetTok();
	FLAG sentp;
	MICA temp;

	newl=sentp=FALSE;
	repeat {
		switch(IGetType()) {

		case EOFT:
			return;

		case WHITESPACE:
			if (sentp && env.isfill) DoSent();
			sentp=FALSE;
			BreakTok();
			if (env.iswhiteintact)
				/* If column > MAXMICA pass zero (0) */
				/* Fixes bad tabbing on first line of file */
				LPutWhite(LGetLength(IGetTok(),((temp = LGetCol()) > MAXMICA) ? 0 : temp - env.eleft));
			break;

		case NEWLINE:
			if (sentp && env.isfill) DoSent();
			sentp=FALSE;
			BreakTok();
			if (newl && env.isfill && env.paraproc) {
				BreakLine();
				PPutVert(sty.spread);
				(*env.paraproc)();
				FlushWhite();
				}
			if (!env.isfill) {
				LNewLine();
				if (env.paraproc) (*env.paraproc)();
				}
			newl= !newl;
			break;

		case DELIM:
			newl=FALSE;
			if (env.howclose == *IGetTok()) (*env.closeproc)();
			else IAppTok();
			break;

		case COMMAND:
			newl=sentp=FALSE;
			SAtSign();
			break;

		case HEADERCENTER:
			LHdrCenter();
			break;

		case HEADERRIGHT:
			LHdrRight();
			break;

		case HEADERLINE:
			LHdrLine();
			break;

		case TOKENBREAK:
			sentp=(*IGetTok()=='.' || *IGetTok()=='?' || *IGetTok()=='!');
			newl=FALSE;
			IAppTok();
			break;

		default:
			newl=sentp=FALSE;
			IAppTok();
			break;
			}
		INextTok();
		}
	}

SAtSign()			/* process an @ */
{
	char *IGetTok();

	INextTok();
	switch(IGetType()) {

	case EOFT:
		IUnGet();
		break;

	case COMMAND:					/* quote an '@' */
		IAppTok();
		break;

	default:
		openneeded=TRUE;
		SCommand(IGetTok());
		break;
		}
	}

/* -------------------------------------------------- */

/* put text to output file */

PutInit()					/* initialize the special put stuff */
{
	if (put.plevel++ <=0) {
		put.pptr=put.pbuffer;
		*put.pptr=NUL;
		}
	}

PutOneCh(chr)					/* do just one character */
	char chr;
{
	PutInit();
	PutChr(chr);
	PutFini();
	}

PutChr(chr)				/* append a character */
	char chr;
{
	if (put.pptr> &put.pbuffer[PUTMAXLEN-1]) {
		TIntError("Put buffer overflow");
		put.pptr-=50;
		}
	*put.pptr++=chr;
	*put.pptr=NUL;
	}

PutNum(str1,num,str2)		/* put out a number */
	char *str1, *str2;
	int num;
{
	char nums[10];

	PutInit();
	PutStr(str1);
	SNToC(num,nums);
	PutStr(nums);
	PutStr(str2);
	PutFini();
	}

PutStr(str)				/* put out a string */
	char *str;
{
	if (!str) return;
	PutInit();
	while (*str) PutChr(*str++);
	PutFini();
	}

PutFini()				/* write the damn thing out */
{
	if (--put.plevel <=0) {
		put.plevel=0;
		IUse(&put.pbuffer,"put");
		pf();
		}
	}

/* -------------------------------------------------- */

SInit()				/* set up the abstraction */
{
	}

FLAG
SCComp(a,b)			/* case indep. compare two C strings */
	char *a, *b;
{
	do {
		if (tolower(*a) != tolower(*b)) return(FALSE);
		} while (++b,*a++);
	return(TRUE);
	}

FLAG
SComp(a,b)			/* case indep. compare two strings */
	STRING *a, *b;
{
	return(SCComp(a,b));
	}

FLAG
SCompGT(a,b)			/* case independent lexical compare */
	char *a, *b;
{
	if (!a) return(FALSE);
	if (!b) return(TRUE);
	while (*a && *b) {
		if (toupper(*a) != toupper(*b)) return(toupper(*a) > toupper(*b));
		a++;
		b++;
		}
	return(*a);
	}

FLAG
SCompToC(a,b)			/* case indep. compare string a to C string b */
	STRING *a;
	char *b;
{
	return(SCComp(a,b));
	}

STRING *
SCToS(a)				/* convert C string to string */
	char *a;
{
	STRING *result;

	result=MemGet(strlen(a)+1);
	strcpy(result,a);
	return(result);
	}

SFree(a)				/* free a string */
	STRING *a;
{
	MemFree(a);
	}

int
SLength(a)			/* return the number of characters in string */
	STRING *a;
{
	return(strlen(a));
	}

char *				/* don't use the return value for anything */
SNToC(i,a)			/* convert a number to a C string */
	int i;
	char *a;
{
	if (i<0) {
		*a++ = '-';
		i = -i;
		}
	if (i>9) a=SNToC(i/10,a);
	*a++ = i%10 + '0';
	*a=NUL;
	return(a);
	}

STRING *
SNToS(i)				/* convert a number to a string */
	int i;
{
	STRING *result;

	result=MemGet(8);
	SNToC(i,result);
	return(result);
	}

char *
SSToC(a)				/* convert string to C string */
	STRING *a;
{
	return(a);
	}

int
SSToN(a)				/* string to number */
	STRING *a;
{
	if (*a=='-') return(-SParToN(a+1,0));
	return(SParToN(a,0));
	}

int
SParToN(a,i)			/* Partial string to number */
	STRING *a;
	int i;
{
	if (*a==NUL) return(i);
	if (!isdigit(*a)) {
		TError("Invalid number '",a,"'.");
		return(i);
		}
	return(SParToN(a+1,i*10 + *a - '0'));
	}

/* -------------------------------------------------- */

EPop()					/* pop an environment off of the stack */
{
	ENVIRON *tmp;

	if (!env.envprev) {
		TIntError("Popping base environment");
		return;
		}
	tmp=env.envprev;
	movmem(env.envprev,&env,sizeof(env));
	MemFree(tmp);
	}

EPush()					/* save the current environment */
{
	ENVIRON *tmp;

	tmp=MemGet(sizeof(env));
	movmem(&env,tmp,sizeof(env));
	env.envprev=tmp;
	}

/* -------------------------------------------------- */

char *
MemGet(size)				/* do an allocation */
	int size;
{
	char *ptr;

	ptr=AAlloc(DefSpace,size);
	if (!ptr) {
		TIntError("Out of memory");
		TPuts(DefSpace==PageSpace ? "in page space" : "in end space");
		xxit(1);
		}
	return(ptr);
	}

MemFree(ptr)				/* free up memory */
	char *ptr;
{
	AFree(DefSpace,ptr);
	}

MemSpace(spc)				/* set the default space */
	SPACE *spc;
{
	DefSpace=spc;
	}

menu()
{
#ifdef CPM
	exec("menu","");
	exec("a:menu","");
#else
	if ( !inname ) inname = "";
	exec("menu",inname);
	exec("a:menu",inname);
#endif
	exit(1);
	}

xxit(val)
int val;
{
	if (val) {
#ifdef OVERLAID
		OvlCall(PFINI);
		OvlCall(IFINI);
		OvlCall(OFINI);
#else
		PFini();
		IFini();
		OFini();
#endif
		}
	if (termerrf) TypeACR();
	menu();
	}

/* END OF pf.C */
