/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:colpro.c 12.0$ */
/* $ACIS:colpro.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ibm/proff/RCS/colpro.c,v $ */

#ifndef lint
static char *rcsid = "$Header:colpro.c 12.0$";
#endif

# include <stdio.h>
#include "proff.h"

# define PL 256
# define ESC '\033'
# define RLF '\013'
# define ESCF 0200
# define LINELN 800
# define NESCQ 127
# define FULL   1
# define EMPTY  0

static int 	is_super = 0,
		is_sub = 0,
		is_bold = 0,
		held_state = 0;
struct escq {
	char	c1;
	char	c2;
	char	status;
};

char *page[PL];
char lbuff [LINELN], *line;
struct escq escq[NESCQ];
char	ecount=0,xcount=0;
int bflag, hflag, fflag;
int half;
int cp, lp;
int ll, llh, mustwr;
int pcp = 0;
char *pgmname;
char	*strcpy();

main (argc, argv)
	int argc; char **argv;
{
	int i;
	register int c;
	int c2;
	char fbuff[BUFSIZ];

	setbuf (stdout, fbuff);
	pgmname = argv[0];

	for (i = 1; i < argc; i++) {
		register char *p;
		if (*argv[i] != '-') {
			fprintf (stderr, "%s: bad option %s\n",
				pgmname, argv[i]);
			exit (2);
		}
		for (p = argv[i]+1; *p; p++) {
			switch (*p) {
			case 'b':
				bflag++;
				break;

			case 'h':
				hflag++;
				break;

			case 'f':
				fflag++;
				break;

			default:
				fprintf (stderr, "%s: bad option letter %c\n",
					pgmname, *p);
				exit (2);
			}
		}
	}

	for (ll=0; ll<PL; ll++)
		page[ll] = 0;

	cp = 0;
	ll = 0;
	mustwr = PL;
	line = lbuff;

	while ((c = getchar()) != EOF) {
		switch (c) {
		case '\n':
			/* 
			 * to prevent double-column problems,
			 * reset printer, then restore control states after.
			 */
			xcount=queuec('A',CHR_REST); /* reset printer */
			outc(ESCF | xcount);
			incr();
			incr();
			cp = 0;
			held_state = 1;
			continue;

		case '\0':
			continue;

		case ESC:
			c = getchar();
			switch (c) {
			case '7':	/* reverse full line feed */
				decr();
				decr();
				break;

			case '8':	/* reverse half line feed */
				if (fflag)
					decr();
				else {
					if (--half < -1) {
						decr();
						decr();
						half += 2;
					}
				}
				break;

			case '9':	/* forward half line feed */
				if (fflag)
					incr();
				else {
					if (++half > 0) {
						incr();
						incr();
						half -= 2;
					}
				}
				break;

			default:
				/* Find the next free slot */
				c2=getchar();
				xcount=queuec(c,c2);
				outc(ESCF | xcount);
				if (c != 'A')
					cp++;
				else
					switch(c2) {
					case CHR_INIT:
					case CHR_REST:
						is_super = 0;
						is_sub = 0;
						is_bold = 0;
						break;
					case CHR_SUPON: 
						is_super = 1;
						break;
					case CHR_SUBON:
						is_sub = 1;
						break;
					case CHR_SCROF:
						is_super = 0;
						is_sub = 0;
						break;
					case CHR_BDON:
						is_bold = 1;
						break;
					case CHR_BDOF:
						is_bold = 0;
						break;
					}
			}
			continue;

		case RLF:
			decr();
			decr();
			continue;

		case '\r':
			cp = 0;
			continue;

		case '\t':
			cp = (cp + 8) & -8;
			continue;

		case '\b':
			if (cp > 0)
				cp--;
			continue;

		case ' ':
			cp++;
			continue;

		default:
			/* now put back previous state */
			if (held_state) {
				if (is_super)
					{
					xcount=queuec('A',CHR_SUPON); 
					outc(ESCF | xcount);
					}
				else if (is_sub)
					{
					xcount=queuec('A',CHR_SUBON); 
					outc(ESCF | xcount);
					}
				if (is_bold)
					{
					xcount=queuec('A',CHR_BDON); 
					outc(ESCF | xcount);
					}	
			}
			held_state = 0;
			c &= 0177;
			if (c > 040 && c < 0177) {	/* if printable */
				outc(c);
				cp++;
			}
			continue;
		}
	}

	for (i=0; i<PL; i++)
		if (page[(mustwr+i)%PL] != 0)
			emit (page[(mustwr+i) % PL], mustwr+i-PL);
	emit (" ", (llh + 1) & -2);
	exit(0);
}

queuec(c1,c2)
register char c1,c2;
{
	int	empty=NESCQ; /* initialize to an invalid value */
	int	ecount;

	for (ecount=0; ecount < NESCQ ; ecount ++)
		if (escq[ecount].status != EMPTY) {
			if ((escq[ecount].c1==c1) && (escq[ecount].c2==c2)) {
				escq[ecount].status++;
				return(ecount);
			}
		} else {
			empty=ecount;
		}
	if (empty != NESCQ) {
		escq[empty].c1=c1;
		escq[empty].c2=c2;
		escq[empty].status++;
		return(empty);
	} else {
		fprintf(stderr,"ESC queue is full\n");
		exit(1);
	}
}
outc (c)
	register char c;
{
	if (lp > cp) {
		line = lbuff;
		lp = 0;
	}

	while (lp < cp) {
		switch (*line) {
		case '\0':
			*line = ' ';
			lp++;
			break;

		case '\b':
			lp--;
			break;

		default:
			if (!special(*line))
				lp++;
		}
		line++;
	}
	while ((*line == '\b') || (special(*line))) {
		if (*line == '\b') 
			line += 2;
		else
			line +=1 ;
	}
	if (bflag || *line == '\0' || *line == ' ')
		*line = c;
	else {
		register char c1, c2, c3;
		c1 = *++line;
		*line++ = '\b';
		c2 = *line;
		*line++ = c;
		while (c1) {
			c3 = *line;
			*line++ = c1;
			c1 = c2;
			c2 = c3;
		}
		lp = 0;
		line = lbuff;
	}
}

special(c)
char c;
{
	if ((c & ESCF) && (escq[c & ~ESCF].c1 == 'A'))
			return(1);
	return(0);
}

store (lno)
{
	char *malloc();

	lno %= PL;
	if (page[lno] != 0)
		free (page[lno]);
	page[lno] = malloc((unsigned)strlen(lbuff) + 2);
	if (page[lno] == 0) {
		fprintf (stderr, "%s: no storage\n", pgmname);
		exit (2);
	}
	strcpy (page[lno],lbuff);
}

fetch(lno)
{
	register char *p;

	lno %= PL;
	p = lbuff;
	while (*p)
		*p++ = '\0';
	line = lbuff;
	lp = 0;
	if (page[lno])
		strcpy (line, page[lno]);
}
emit (s, lineno)
	char *s;
	int lineno;
{
	static int cline = 0;
	register int ncp;
	register char *p;

	if (*s) {
		while (cline < lineno - 1) {
			putchar ('\n');
			pcp = 0;
			cline += 2;
		}
		if (cline != lineno) {
			putchar (ESC);
			putchar ('9');
			cline++;
		}
		if (pcp)
			putchar ('\r');
		pcp = 0;
		p = s;
		while (*p) {
			ncp = pcp;
			while (*p++ == ' ') {
				if ((++ncp & 7) == 0 && hflag) {
					pcp = ncp;
					putchar ('\t');
				}
			}
			if (!*--p)
				break;
			while (pcp < ncp) {
				putchar (' ');
				pcp++;
			}
			if (*p & ESCF) {
				putchar(ESC);
				putchar(escq[*p & ~ ESCF].c1);
				putchar(escq[*p & ~ESCF].c2);
				escq[*p & ~ESCF].status--;
			} else 
				putchar (*p);
			if (*p++ == '\b')
				pcp--;
			else if (!special(p[-1]))
				pcp++;
		}
	}
}

incr()
{
	store (ll++);
	if (ll > llh)
		llh = ll;
	if (ll >= mustwr && page[ll%PL]) {
		emit (page[ll%PL], ll - PL);
		mustwr++;
		free (page[ll%PL]);
		page[ll%PL] = 0;
	}
	fetch (ll);
}

decr()
{
	if (ll > mustwr - PL) {
		store (ll--);
		fetch (ll);
	}
}
