/*
 *	NRO Text Formatter -
 *	similar to Unix NROFF or RSX-11M RNO -
 *	adaptation of text processor given in
 *	"Software Tools", Kernighan and Plauger.
 *
 *	Originally by Stephen L. Browning, 5723 North Parker Avenue
 *	Indianapolis, Indiana 46220
 *
 *	Transformed beyond immediate recognition, and
 *	adapted for Amiga by Olaf Seibert, KosmoSoft
 *
 *	Vossendijk 149-1 (study)   Beek 5 (home)
 *	5634 TN  Nijmegen	   5815 CS  Merselo
 *	The Netherlands 	   The Netherlands
 *	Phone:
 *	       (...-31)80561045     (...-31)4786205
 *	    or 080-561045	    04786-205
 *
 *	This program is NOT in the public domain. It may, however
 *	be distributed only at no charge, and this notice must be
 *	included unaltered.
 */

#include <stdio.h>
#include "nro.h"
#include "nroxtrn.h"


main(argc, argv)
int argc;
uchar *argv[];
{
	int i;
	int ofp;

	pout = stdout;
	ofp = 0;
	init();
	for (i=1; i<argc; i++) {
		if (*argv[i] == '-' || *argv[i] == '+') {
			pswitch(argv[i]);
		} else if (*argv[i] == '>') {
			if (ofp == 0) {
#ifdef CPM
				if (!strcmp(argv[i]+1, "$P")) {
					ofp = 1;
					co.lpr = TRUE;
				} else
#endif
				if ((pout = fopen(argv[i]+1, "w")) == NULL) {
					error("*** nro: cannot create %s\n", argv[i]+1);
				} else {
					ofp = 1;
				}
			} else {
				error("*** nro: too many output files\n");
			}
		}
	}

	for (i=1; i<argc; i++) {
		if (*argv[i] != '-' && *argv[i] != '+' && *argv[i] != '>') {
			if ((sofile[0] = fopen(argv[i], "r")) == NULL) {
				error("nro: unable to open file %s\n", argv[i]);
				continue;
			} else {
				if (verbose > 2)
					error("nro: processing file '%s'\n", argv[i]);
				profile();
				fclose(sofile[0]);
			}
		}
	}

	if (argc == 1) {
		error("Usage: nro [-#] [+#] [-p#] [-v] [-b#] [-rn#] [-rd#] [-rp#]\n"
		      "           [-mmacfile] infile(s) ... [>outfile]\n");
		fatal();
	} else {
		endfiles();
	}

#ifdef CMPEOF
	putc(CPMEOF, pout);
#endif CMPEOF
	fflush(pout);
	if (pout != stdout) {
		fclose(pout);
	}
}



/*
 *	Retrieve one line of input text
 */

getlin(p, infile)
uchar *p;
FILE *infile;
{
	int i;
	int c;
	uchar *q;

	q = p;
	for (i=0; i<MAXLINE-1; i++) {
		c = ngetc(infile);
#ifdef CPMEOF
		if (c == CPMEOF || c == EOF) {
#if 0
}	/* To keep ctags, cb and such happy */
#endif
#else
		if (c == EOF) {
#endif CPMEOF
			*q = EOS;
			return i == 0 ? EOF : 0;
		}
		*q++ = c;
		if (iseol(c)) break;
	}
	*q = EOS;
	return q - p;	/* strlen(p) */
}



/*
 *	Initialize parameters for nro word processor
 */

init()
{
	int i;
	static char nl[] = "\n";

	dc.bsflg   = FALSE;
	dc.curmode = FXPLAIN;
	dc.envsp   = 0;
	dc.envstack[0] = 0;
	for (i=0; i<26; i++) dc.nr[i] = 0;
#ifdef CPM
	dc.lpr	   = FALSE;
#endif

	for (i=0; i<NUMENV; i++) environ[i] = NULL;

	initenv();

	pg.curpag   = 0;
	pg.newpag   = 1;
	pg.lineno   = 0;
	pg.plval    = PAGELEN;
	pg.m1val    = 2;
	pg.m2val    = 2;
	pg.m3val    = 2;
	pg.m4val    = 2;
	pg.bottom   = pg.plval - pg.m4val - pg.m3val;
	pg.offset   = 0;
	pg.frstpg   = 0;
	pg.lastpg   = 32767;
	pg.prflg   = TRUE;

	strncpy(pg.ehead, nl, MAXLINE);
	strncpy(pg.ohead, nl, MAXLINE);
	strncpy(pg.efoot, nl, MAXLINE);
	strncpy(pg.ofoot, nl, MAXLINE);

	pg.ehlim[LEFT] = pg.ohlim[LEFT] = env.inval;
	pg.eflim[LEFT] = pg.oflim[LEFT] = env.inval;
	pg.ehlim[RIGHT] = pg.ohlim[RIGHT] = env.rmval;
	pg.eflim[RIGHT] = pg.oflim[RIGHT] = env.rmval;

	mac.mxmdef = MXMDEF;
	mac.macbuf = MACBUF;
	mac.mxmlen = MXMLEN;
	mac.mnames = NULL;
	mac.mb	   = NULL;
	mac.pbb    = NULL;
	mac.lastp  = 0;
	mac.ppb    = NULL;
}


/*
 *	Initenv - initialise the environment to default
 */

initenv()
{
	int i;

	env.fill    = YES;
	env.lsval   = 1;
	env.inval   = 0;
	env.tival   = 0;
	env.rmval   = PAGEWIDTH - 1;
	env.tmval   = PAGEWIDTH - 1;
	env.ceval   = 0;
	env.ulval   = 0;
	env.cuval   = 0;
	env.boval   = 0;
	env.itval   = 0;
	env.juval   = YES;
	env.pgchr   = PGCHAR;
	env.cmdchr  = CMDCHAR;
	env.c2chr   = C2CHAR;
	env.sprdir  = 0;
	env.dontbrk = FALSE;
	env.reqmode = FXPLAIN;
	env.expmode = FXPLAIN;
	env.curmode = FXPLAIN;
	env.outp    = 0;
	env.outw    = 0;
	env.outwds  = 0;

	strncpy(env.outbuf, "", MAXLINE);

	for (i=0; i<MAXTAB; i++) env.tabstop[i] = NOTAB;
}


/*
 *	Initialize buffers for macros
 */

initbuffers()
{
	static short inited = FALSE;

	int i;

	if (inited)
		return;

	if ((mac.mnames=(uchar **)malloc(sizeof(*mac.mnames)*mac.mxmdef)) &&
	    (mac.mb    =(uchar *)malloc(sizeof(*mac.mb)    *mac.macbuf)) &&
	    (mac.pbb   =(uchar *)malloc(sizeof(*mac.pbb)   *mac.mxmlen)) ) {

		for (i=0; i<mac.mxmdef; i++) mac.mnames[i] = NULL;
		mac.emb = mac.mb;
		mac.ppb = mac.pbb - 1;
		mac.pbbend = mac.pbb + mac.mxmlen;

		if (verbose > 1) {
			error("nro: max number of macros: %d\n", mac.mxmdef);
			error("     max total length of macro definitions: %d\n",
					mac.macbuf);
			error("     max pushback characters: %d\n", mac.mxmlen);
		}
		inited = TRUE;
	} else {
		error("*** nro: cannot allocate memory for macro buffers\n");
	}
}

#define rawgetc(infp)\
	((mac.ppb >= mac.pbb) ? pbbgetc() : getc(infp))

#define pbbgetc()       (*mac.ppb--)

/*
 *	Get character from input file or push back buffer
 */

ngetc(infp)
FILE *infp;
{
	register int chr;
	int i, j;

again:
	chr = rawgetc(infp);

	if (chr != ESCCHAR) {
		if (dc.iflvl == 0) return chr;
		else goto again;
	}

	chr = rawgetc(infp);

	switch (chr) {
	case '\n':              /* Concealed newline */
		goto again;
	case ' ':               /* Non breakable space */
		chr =  NBSP;
		break;
	case '"':               /* A comment */
		while (isnteol(chr)) chr = rawgetc(infp);
		break;
	case ESCCHAR:		/* Escaped escape character */
	case 'e':               /* Current value of escape character */
		chr = ESCCHAR;
		break;
	case 'n':               /* Substitute numeric register */
		if (dc.iflvl) goto again;
		chr = rawgetc(infp);
		i = dc.nr[tolower(chr) - 'a'];
		j = abs(i);
		do {
			putbak('0' + j % 10);
			j /= 10;
		} while (j);
		if (i < 0)      chr = '-';
		else chr = pbbgetc();
		break;
	case 't':               /* Tab */
		chr = '\t';
		break;
	case 'X':               /* eXtended Character */
		chr = getval(&i, infp);
		break;
	case '{':               /* Begin of if block */
		if (dc.iflvl) dc.iflvl++;
		chr = BEGIF;
		break;
	case '}':               /* End of if block */
		if (dc.iflvl) dc.iflvl--;
		goto again;
			/* @. to conceal line beginning with command chr */
	default:	/* Dunno. Somebody is mistakin'. Maybe even EOF. */
		if (chr == env.cmdchr) chr |= 0x8000;
	}

	if (dc.iflvl) goto again;

	return chr;
}

/*
 *	Restore push back buffer after closing a file
 */

restorepbb()
{
	mac.ppb = mac.pbb - 1;	/* Should not even be necessary */
	mac.pbb = sopbb[dc.flevel - 1];
}


/*
 *	Process input files from command line
 */

profile()
{
	int chr;
	uchar ibuf[MAXWORD];

	initbuffers();

	for (dc.flevel=0; dc.flevel>=0; --dc.flevel) {
		infile = sofile[dc.flevel];
		while (ibuf[0] = chr = ngetc(infile), chr != EOF) {
			if (chr == env.cmdchr) {
				comand(ibuf);   /* Command line */
			} else
				text(ibuf, infile);             /* Text line */
		}
		if (dc.flevel > 0) {
			fclose(infile);
			restorepbb();
		}
	}
}


/*
 *	End processing of files: eject page if necessary.
 */

endfiles()
{
	if (pg.lineno > 0 || env.outp != 0)  space(pg.plval);
}


#define K *1024
#define M *1024 K

/*
 *	Process switch values from command line
 */

pswitch(p)
uchar *p;
{
	int swgood;

	swgood = TRUE;
	if (*p == '-') {
		switch (tolower(*++p)) {
		case 'b':
			set(&dc.bsflg, ctod(++p), '1', 1, 0, 2);
			break;
		case 'm':
			if ((sofile[0] = fopen(++p, "r")) == NULL) {
				error("*** nro: unable to open file %s\n", p);
			}
			if (verbose > 2)
				error("nro: processing file '%s'\n", p);
			profile();
			fclose(sofile[0]);
			break;
		case 'p':
			set(&pg.offset, ctod(++p), '1', 0, 0, HUGE);
			break;
		case 'r':       /* Reserve memory */
			switch(tolower(p[1])) {
			case 'd':       /* Total buffer for definitions */
				if (mac.mb == NULL)
					set(&mac.macbuf, ctod(&p[2]), '1', MACBUF, 0, 16 M);
				else swgood = FALSE;
				break;
			case 'n':       /* Number of definitions */
				if (mac.mnames == NULL)
					set(&mac.mxmdef, ctod(&p[2]), '1', MXMDEF, 0, 10 K);
				else swgood = FALSE;
				break;
			case 'p':       /* Pushback buffer */
				if (mac.pbb == NULL)
					set(&mac.mxmlen, ctod(&p[2]), '1', MXMLEN, 0, 100 K);
				else swgood = FALSE;
				break;
			default:
				swgood = FALSE;
			}
			break;
		case 'v':
			error("NRO - KosmoSoft version 1.5 - V25.06.88\n");
			verbose = max(1,ctod(++p));
			break;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			pg.lastpg = ctod(p);
			break;
		default:
			swgood = FALSE;
			break;
		}
	} else if (*p == '+') {
		pg.frstpg = ctod(++p);
	} else {
		swgood = FALSE;
	}
	if (swgood == FALSE) {
		error("*** nro: illegal switch %s\n", p);
	}
	return;
}

#undef K
#undef M


/*
 *	Print a message at the standard error channel
 */

/*VARARGS1*/
error(format, arg1, arg2, arg3)
uchar *format;
long arg1, arg2, arg3;
{
	fflush(pout);
	fprintf(stderr, format, arg1, arg2, arg3);

	if (*format == '*')
		fatal();
}

/*
 *	fatal - cannot recover from a serious error
 */

fatal()
{
	exit(10);
}

int tolower(c)
register char c;
{
	if (c >= 'A' && c <= 'Z')
		return c - 'A' + 'a';
	return c;
}

int toupper(c)
register char c;
{
	if (c >= 'a' && c <= 'z')
		return c - 'a' + 'A';
	return c;
}
