/*
*      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
*              [email protected]
*              ucbvax!opal!edmoy
*      and modified by:
*              Mic Kaczmarczik
*              User Services Digital Support Group
*              Computation Center
*              University of Texas at Austin
*              Austin, TX 78712
*              [email protected]
*              ...!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
*              [email protected]
*              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\
%%             ([email protected])\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++;
}