/*
 *	tek2eepic - convert Tektronix 4015 escape sequences to EEPIC for
 *		    inclusion of graphs in LaTeX documents.
 *
 *	Based largely on Tek2ps written by:
 *		Edward Moy
 *		Academic Computing Services
 *		University of California
 *		Berkeley, CA  94720
 *		edmoy@opal.Berkeley.EDU
 *		ucbvax!opal!edmoy
 *	and modified by:
 *		Mic Kaczmarczik
 *		User Services Digital Support Group
 *		Computation Center
 *		University of Texas at Austin
 *		Austin, TX 78712
 *		mic@ngp.utexas.edu
 *		...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *	and by:
 *		Bryon Hance
 *		User Services VAX/VMS Support
 *		Computation Center
 *		University of Texas at Austin
 *		Austin, TX 78712
 *
 *
 *	Tek2eepic written by:
 *		Atul K. Chhabra
 *		Dept. of Electrical & Computer Engineering
 *		University of Cincinnati
 *		Cincinnati, OH 45221-0030
 *		achhabra@ucesp1.ece.uc.edu
 *		Feb 25, 1989
 *
 *	Requires Tekparse.h and Tekparsetable.c, which is part of the
 *	xterm 6.6* distribution.
 *
 *	Tektronix alphanumerics are currently not supported.
 *
 */
#include "Tekparse.h"
#include <stdio.h>
#include <ctype.h>
#include <math.h>

#define	COURIER		(10. / 6.)	/* ratio of height to width */
#define	DEF_HEIGHT	4.9
#define	DEF_WIDTH	6.5
#define	FALSE		0
#define	MAXPATH		35
#define	EPINCHES	300
#define	S_H		010
#define	S_W		04
#define	TRUE		1
#define UNITLENGTH	0.0033
#define UNITLINETHICKNESS	0.014

/* Tek defines */

#define	BEL		07
#define	CANCEL		030
#define	DASHEDLINE	2
#define	DOTTEDLINE	1
#define	EAST		01
#define	ETX		03
#define	LARGEFONT	0
#define	LINEMASK	07
#define	LONGDASHEDLINE	4
#define	MARGIN1		0
#define	MARGIN2		1
#define MAX_PTS		150
#define MAX_VTX		300
#define	NAK		025
#define	NORTH		04
#define	PENDOWN		1
#define	PENUP		0
#define	SHORTDASHEDLINE	3
#define	SMALLFONT	3
#define	SOLIDLINE	0
#define	SOUTH		010
#define	TEKBOTTOMPAD	24
#define	TEKFULLHEIGHT	(TEKHEIGHT + TEKTOPPAD + TEKBOTTOMPAD)
#define	TEKFULLWIDTH	(TEKWIDTH + TEKPAD)
#define	TEKHEIGHT	3072
#define	TEKHOME		((TekChar[fontsize].nlines - 1)\
			 * TekChar[fontsize].vsize)
#define	TEKNUMFONTS	4
#define	TEKPAD		58
#define	TEKTOPPAD	34
#define	TEKWIDTH	4096
#define	THREEFONT	2
#define	TWOFONT		1
#define	WEST		02

#ifdef	TOPS20
/* Make global identifiers unique to 6 (uppercase) characters */
#define	Tpushback Tpshbck
#define	TCursorUp TCUp
#define	TCursorDown TCDown
#define	TCursorBack TCBack
#define	TCursorForward TCForward
#define	cur_X curXXX
#define cur_Y curYYX
#define psprolog2 pspr2
#endif

#define	GOOD	0		/* good exit status */

#ifdef	VMS
#define	rindex	strrchr
#include <ssdef.h>
#undef	GOOD
#define	GOOD	SS$_NORMAL
#endif

#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))

int Tbcnt = 0;
char Tbuffer[BUFSIZ];
char *Tbptr = Tbuffer;
char Tpushb[BUFSIZ];
char *Tpushback = Tpushb;

/* use comma operator so these macros act like one line (hack!!!) */
#define TekFlush()	fputs("\n", stdout), nplot = 0
#define TekMove(x,y)	cur_X = (x), cur_Y = (y)
#define	input()		Tinput()
#define	unput(c)	*Tpushback++ = (c)

static struct Tek_Char {
	int hsize;	/* in Tek units */
	int vsize;	/* in Tek units */
	int charsperline;
	int nlines;
} TekChar[TEKNUMFONTS] = {
	{56, 88, 74, 35},	/* large */
	{51, 82, 81, 38},	/* #2 */
	{34, 53, 121, 58},	/* #3 */
	{31, 48, 133, 64},	/* small */
};

static int cur_X;
static int cur_Y;
static int cur_x;
static int cur_y;
static double sx, sy;
static int ep_x, ep_y;
static int fontsize;
static lastfont = -1;
static int linetype;
static int margin;
static int noerase = FALSE;
static double penscale = 1.0;
static int ignorefirst = FALSE;
static int pageno = 1;
static int nplot;
static int pen;
static int infd = 0;
static double w, h;
static int min_x = 5000, max_x = -5000;
static int min_y = 5000, max_y = -5000;

extern int Talptable[];
extern int Tbestable[];
extern int Tbyptable[];
extern int Tesctable[];
extern int Tipltable[];
extern int Tplttable[];
extern int Tpttable[];
extern int Tspttable[];

static int *curstate = Talptable;
static int *Tparsestate = Talptable;

char *myname;

main(argc, argv)
int argc;
char **argv;
{
	register int set = 0;
	register double scale, temp;
	char *rindex();

	if(myname = rindex(*argv, '/'))
		myname++;
	else
		myname = *argv;
	for(argc--, argv++ ; argc > 0 && **argv == '-' ; argc--, argv++)
		switch((*argv)[1]) {
		 case 'o':
			if (argc < 2)
				Usage();
			if (freopen(*++argv,"w",stdout) == NULL) {
				fprintf(stderr,"%s: can't open %s\n",
					myname,*argv);
				exit(1);
			}
			argc--;
			break;
		 case 'w':
			if(argc < 2)
				Usage();
			w = atof(*++argv);
			argc--;
			set |= S_W;
			break;
		 case 'h':
			if(argc < 2)
				Usage();
			h = atof(*++argv);
			argc--;
			set |= S_H;
			break;
		 case 'i':	/* ignore *first* PAGE */
			ignorefirst = TRUE;
			break;
		 case 'n':      /* scale the pen nib */
			if (argc < 2)
				Usage();
			penscale = atof(*++argv);
			argc--;
			break;
		 case 'p':	/* Don't erase on PAGE */
			noerase = TRUE;
			break;
		 default:
			Usage();
		}
	init(set);
	if(argc > 1)
		Usage();
	if((argc == 1) && ((infd = open(*argv, 0)) < 0)) {
		fprintf(stderr, "%s: can't open %s\n", myname, *argv);
		exit(1);
	}
	cur_X = 0;
	cur_Y = TEKHOME;
	Tekparse();
}

Usage()
{
	fprintf(stderr,"Usage: %s [-inp] [-o file] [-w #] [-h #] [file ...]\n",
	 myname);
	exit(1);
}

char ephead[] = "\
%% tek2eepic version 1.0 (Feb 24, 1989)\n\
%% Written by: Atul Chhabra, University of Cincinnati\n\
%%             (achhabra@ucesp1.ece.uc.edu)\n\
%%\n\
";

char epprolog[] = "\
\\setlength{\\unitlength}{%fin}\n\
\\begin{picture}(%d,%d)(0,0)\n\
\\allinethickness{%fin}\n\
";

/*
char psfont[] =
	"/f%d {/Courier findfont [%.2f 0 0 %d 0 0] makefont setfont} def\n";
*/

init(set)
int set;
{
	printf (ephead);

	switch(set & (S_W | S_H)) {
	 case 0:
		w = DEF_WIDTH;
		h = DEF_HEIGHT;
		break;
	 case S_W:
		h = w * TEKFULLHEIGHT / TEKFULLWIDTH;
		break;
	 case S_H:
		w = h * TEKFULLWIDTH / TEKFULLHEIGHT;
		break;
	}
	sx = EPINCHES * w / TEKFULLWIDTH;
	sy = EPINCHES * h / TEKFULLHEIGHT;

	printf (epprolog, UNITLENGTH,
		((int) (w * EPINCHES)), ((int) (h * EPINCHES)), 
		UNITLINETHICKNESS * penscale);
}

Tekparse()
{
	register int c, x, y;
	register char *cp;
	char text[BUFSIZ];

	for( ; ; )
		switch(Tparsestate[c = input()]) {
		 case CASE_REPORT:	/* report address */
		 case CASE_VT_MODE:	/* special return to vt102 mode */
		 case CASE_BEL:		/* BEL */
		 case CASE_COPY:	/* make copy */
		 case CASE_CURSTATE:
			Tparsestate = curstate;	/* clear bypass condition */
			break;

		 case CASE_SPT_STATE:	/* Enter Special Point Plot mode */
			Tparsestate = curstate = Tspttable;
			break;

		 case CASE_GIN:		/* Do Tek GIN mode */
			Tparsestate = Tbyptable;	/* Bypass mode */
			break;

		 case CASE_BS:		/* BS */
			Tparsestate = curstate;	/* clear bypass condition */
			TCursorBack();
			break;

		 case CASE_PT_STATE:	/* Enter Tek Point Plot mode */
			Tparsestate = curstate = Tpttable;
			break;

		 case CASE_PLT_STATE:	/* Enter Tek Plot mode */
			Tparsestate = curstate = Tplttable;
			if((c = input()) == BEL)
				pen = PENDOWN;
			else {
				unput(c);
				pen = PENUP;
			}
			break;

		 case CASE_TAB:		/* HT */
			Tparsestate = curstate;	/* clear bypass condition */
			TCursorForward();
			break;

		 case CASE_IPL_STATE:	/* Enter Tek Incremental Plot mode */
			Tparsestate = curstate = Tipltable;
			break;

		 case CASE_ALP_STATE:	/* Enter Tek Alpha mode */
			/* if in one of graphics states, move alpha cursor */
			if(nplot > 0) /* flush line Tbuffer */
				TekFlush();
			Tparsestate = curstate = Talptable;
			break;

		 case CASE_UP:		/* cursor up */
			Tparsestate = curstate;	/* clear bypass condition */
			TCursorUp();
			break;

		 case CASE_PAGE:	/* Page Function */
			TekPage();	/* clear bypass condition */
			break;

		 case CASE_BES_STATE:	/* Byp: an escape char */
			Tparsestate = Tbestable;
			break;

		 case CASE_BYP_STATE:	/* set bypass condition */
			Tparsestate = Tbyptable;
			break;

		 case CASE_IGNORE:	/* Esc: totally ignore CR, ESC, LF, ~ */
			break;

		 case CASE_ASCII:	/* Select ASCII char set */
			/* ignore for now */
			Tparsestate = curstate;
			break;

		 case CASE_APL:		/* Select APL char set */
			/* ignore for now */
			Tparsestate = curstate;
			break;

		 case CASE_CHAR_SIZE:	/* character size selector */
			fontsize = c & 03;
			Tparsestate = curstate;
			break;

		 case CASE_BEAM_VEC:	/* beam and vector selector */
			/* only line types */
			if((c &= LINEMASK) != linetype) {
				if(nplot > 0)
					TekFlush();
				linetype = c;
			}
			Tparsestate = curstate;
			break;

		 case CASE_PENUP:	/* Ipl: penup */
			pen = PENUP;
			break;

		 case CASE_PENDOWN:	/* Ipl: pendown */
			pen = PENDOWN;
			break;

		 case CASE_IPL_POINT:	/* Ipl: point */
			x = cur_X;
			y = cur_Y;
			if(c & NORTH)
				y++;
			else if(c & SOUTH)
				y--;
			if(c & EAST)
				x++;
			else if(c & WEST)
				x--;
			if(pen == PENDOWN)
				TekDraw(x, y);
			else
				TekMove(x, y);
			break;

		 case CASE_PLT_VEC:	/* Plt: vector */
			unput(c);
			if(getpoint()) {
				if(pen == PENDOWN)
					TekDraw(cur_x, cur_y);
				else
					TekMove(cur_x, cur_y);
				pen = PENDOWN;
			}
			break;

		 case CASE_PT_POINT:	/* Pt: point */
			unput(c);
			if(getpoint()) {
				TekMove(cur_x, cur_y);
				TekDraw(cur_x, cur_y);
			}
			break;

		 case CASE_SPT_POINT:	/* Spt: point */
			/* ignore intensity character in c */
			if(getpoint()) {
				TekMove(cur_x, cur_y);
				TekDraw(cur_x, cur_y);
			}
			break;

		 case CASE_CR:		/* CR */
			if(nplot > 0)	/* flush line Tbuffer */
				TekFlush();
			cur_X = margin == MARGIN1 ? 0 :
			 TEKWIDTH / 2;
			Tparsestate = curstate = Talptable;
			break;

		 case CASE_ESC_STATE:	/* ESC */
			Tparsestate = Tesctable;
			break;

		 case CASE_LF:		/* LF */
			TCursorDown();
			break;

		 case CASE_SP:		/* SP */
			TCursorForward();
			break;

		 case CASE_PRINT:	/* printable character */
			if(lastfont != fontsize)
				lastfont = fontsize;
/*
				printf("f%d\n", lastfont = fontsize);
			printf("%d %d (", cur_X, y = cur_Y);
*/
			cp = text;
			x = sizeof(text) - 1;
			unput(c);
			while(x-- > 0 && y == cur_Y) {
				if(!isprint(c = input())) {
					unput(c);
					break;
				}
				if(c == '(' || c == ')' || c == '\\') {
					if(x <= 0) {
						unput(c);
						break;
					}
					*cp++ = '\\';
					x--;
				}
				*cp++ = c;
				TCursorForward();
			}
			*cp = 0;
/*
			printf("%s) p\n", text);
*/
			break;
		 case CASE_OSC:		/* do osc escape */
			while(!iscntrl(input()));	/* ignore */
			Tparsestate = curstate;
			break;
		}
}

Tinput()
{
	register int c;

	if(Tpushback > Tpushb)
		return(*--Tpushback);
again:
	if(Tbcnt-- <= 0) {
		if((Tbcnt = read(infd, Tbptr = Tbuffer, BUFSIZ)) <= 0) {
			fputs("\n\\end{picture}\n\n\n",stdout);
			printf("%%\\begin{picture}(%d,%d)(%d,%d)\n",
				max_x-min_x, max_y-min_y, min_x, min_y);
			exit(1);
		}
		Tbcnt--;
	}
	if(!(c = (*Tbptr++ & 0177)))
		goto again;
	return(c);
}

TekPage()
{
	/* if ignorefirst is TRUE, totally ignore first PAGE command */
	if (ignorefirst) {
		ignorefirst = FALSE;
		return;
	}
	pageno++;
	if(noerase)
		TekFlush();
	else {
		fputs("\n\\end{picture}\n\n\n",stdout);
		printf("%% xsize=%d ysize=%d xpos=%d ypos=%d\n",
			max_x-min_x, max_y-min_y, min_x, min_y);
		printf(epprolog, UNITLENGTH,
			((int) (w * EPINCHES)), ((int) (h * EPINCHES)),
			UNITLINETHICKNESS * penscale);
	}
	cur_X = 0;
	cur_Y = TEKHOME;
}

#define	EXTRABITS	017
#define	FIVEBITS	037
#define	HIBITS		(FIVEBITS << SHIFTHI)
#define	LOBITS		(FIVEBITS << SHIFTLO)
#define	SHIFTHI		7
#define	SHIFTLO		2
#define	TWOBITS		03

getpoint()
{
	register int c, x, y, e, lo_y = 0;

	x = cur_x;
	y = cur_y;
	for( ; ; ) {
		if((c = input()) < ' ') {	/* control character */
			if (c == '\n' || c == '\r' || c == '\0')
				continue;	/* ignore CR, LF */
			unput(c);
			return(0);
		}
		if(c < '@') {	/* Hi X or Hi Y */
			if(lo_y) {	/* seen a Lo Y, so this must be Hi X */
				x &= ~HIBITS;
				x |= (c & FIVEBITS) << SHIFTHI;
				continue;
			}
			/* else Hi Y */
			y &= ~HIBITS;
			y |= (c & FIVEBITS) << SHIFTHI;
			continue;
		}
		if(c < '`') {	/* Lo X */
			x &= ~LOBITS;
			x |= (c & FIVEBITS) << SHIFTLO;
			cur_x = x;
			cur_y = y;
			return(1);	/* OK */
		}
		/* else Lo Y */
		if(lo_y) {	/* seen a Lo Y, so other must be extra bits */
			e = (y >> SHIFTLO) & EXTRABITS;
			x &= ~TWOBITS;
			x |= e & TWOBITS;
			y &= ~TWOBITS;
			y |= (e >> SHIFTLO) & TWOBITS;
		}
		y &= ~LOBITS;
		y |= (c & FIVEBITS) << SHIFTLO;
		lo_y++;
	}
}

TCursorBack()
{
	register struct Tek_Char *t;
	register int x, l;

	x = (cur_X -= (t = &TekChar[fontsize])->hsize);
	if(margin == MARGIN1 && x < 0 || margin == MARGIN2
	 && x < TEKWIDTH / 2) {
		if((l = (cur_Y + (t->vsize - 1)) / t->vsize + 1) >=
		 t->nlines) {
			margin = !margin;
			l = 0;
		}
		cur_Y = l * t->vsize;
		cur_X = (t->charsperline - 1) * t->hsize;
	}
}

TCursorForward()
{
	register struct Tek_Char *t;
	register int l;

	if((cur_X += (t = &TekChar[fontsize])->hsize) > TEKWIDTH) {
		if((l = cur_Y / t->vsize - 1) < 0) {
			margin = !margin;
			l = t->nlines - 1;
		}
		cur_Y = l * t->vsize;
		cur_X = margin == MARGIN1 ? 0 : TEKWIDTH / 2;
	}
}

TCursorUp()
{
	register struct Tek_Char *t;
	register int l;

	t = &TekChar[fontsize];
	if((l = (cur_Y + (t->vsize - 1)) / t->vsize + 1) >= t->nlines) {
		l = 0;
		if((margin = !margin) != MARGIN1) {
			if(cur_X < TEKWIDTH / 2)
				cur_X += TEKWIDTH / 2;
		} else if(cur_X >= TEKWIDTH / 2)
			cur_X -= TEKWIDTH / 2;
	}
	cur_Y = l * t->vsize;
}

TCursorDown()
{
	register struct Tek_Char *t;
	register int l;

	t = &TekChar[fontsize];
	if((l = cur_Y / t->vsize - 1) < 0) {
		l = t->nlines - 1;
		if((margin = !margin) != MARGIN1) {
			if(cur_X < TEKWIDTH / 2)
				cur_X += TEKWIDTH / 2;
		} else if(cur_X >= TEKWIDTH / 2)
			cur_X -= TEKWIDTH / 2;
	}
	cur_Y = l * t->vsize;
}

TekDraw (x, y)
int x, y;
{
	static int lastx = -1, lasty = -1;

	if((nplot == 0) || (nplot >= MAXPATH) || (lastx != cur_X) ||
	(lasty != cur_Y)) {
		if(nplot > 0)
			TekFlush();
		switch(linetype){
		case 0:
			printf("\\path");
			break;
		case 1:
			printf("\\dottedline{6}");
			break;
		case 2:
			printf("\\drawline[-50]");
			break;
		case 3:
			printf("\\dashline{12}[3]");
			break;
		case 4:
			printf("\\dashline[-30]{12}[3]");
			break;
		default:
			printf("\\path");
			break;
		}
		min_x = MIN(min_x, ((int) (sx * cur_X))); 
		max_x = MAX(max_x, ((int) (sx * cur_X))); 
		min_y = MIN(min_y, ((int) (sy * cur_Y))); 
		max_y = MAX(max_y, ((int) (sy * cur_Y))); 
		printf("(%d,%d)", ((int) (sx * cur_X)), ((int) (sy * cur_Y)));
		nplot = 1;
	}
	lastx = cur_X = x;
	lasty = cur_Y = y;
	min_x = MIN(min_x, ((int) (sx * x))); 
	max_x = MAX(max_x, ((int) (sx * x))); 
	min_y = MIN(min_y, ((int) (sy * y))); 
	max_y = MAX(max_y, ((int) (sy * y))); 
	printf("(%d,%d)", ((int) (sx * x)), ((int) (sy * y)));
	nplot++;
}
