#include "all.h"

#include <math.h>

#include "mygraph.h"   /* Prototypes for all the graph routines */
int text_def(char *ss);
int g_psarrow(double x1, double y1, double x2, double y2, int flag);
int g_arrowline(double dx, double dy, int flag);
int g_arrowpoints(double cx,double cy,double dx,double dy, double *ax1,double *ay1
	,double *ax2,double *ay2, double *nx, double *ny);
int name_join(char *a, char *b, int j);
int box_start(void);
int box_end(void);
int g_arrow(double dx, double dy);
int run_bigfile(char *ss);
#define MAXBOX 10
extern int this_line;
extern int trace_on;
static char *box_name[MAXBOX];
static double box_y1[MAXBOX], box_x1[MAXBOX], box_y2[MAXBOX], box_x2[MAXBOX];
static int box_nobox[MAXBOX];
static long box_fill[MAXBOX];
static double box_add[MAXBOX];
static int path_clip[4],path_stroke[4];
static long path_fill[4];
static double path_x[4],path_y[4];
static int npath,nbox;
int done_open;
#define true (!false)
#define false 0
/*---------------------------------------------------------------------------*/
/* gle FILE io stuff.  (fopen fclose fread fwrite) */
#define F_MAXCHAN 5
static int f_end[F_MAXCHAN];
static char *f_buff[F_MAXCHAN];
static FILE *f_chan[F_MAXCHAN];
static int f_read[F_MAXCHAN];
static char *f_nexttok[F_MAXCHAN];
int f_getchan(void);
void f_readahead(int chn);
int f_testchan(int chn);
void siffree(char **s);
void f_getline(int chn);
char *f_gettok(int chn);
static int chn;
int f_eof(int chn);
char *f_getnext(int chn);
/*---------------------------------------------------------------------------*/
/* pos=   Offset to find the data			*/
/* idx=   For switches, which can only have one value. 	*/
/* The pos is the order the items will be placed in the pcode */
/*
/* Switches 	long 	placed in directly, 1 present, 0 not present
/* expressions 	LONG* 	pointed to, 0 if not present.
/* color/fill	LONG* 	Pointer to exp 0 if not present.
/* marker	LONG*	Pointer to exp 0 if not present.
/* lstyle 	LONG*	Pointer to exp 0 if not present.
/* font 	long* 	Pointer to string expression.
*/

extern char *mainkey[];
extern int gle_debug;
int can_fillpath;
int init_run_done;

#define readval(x) eval(pcode,&cp,&x,ostr,&otyp)
#define readxy(x,y) {eval(pcode,&cp,&x,ostr,&otyp);eval(pcode,&cp,&y,ostr,&otyp);}
#define readstr(s) eval(pcode,&cp,&x,s,&otyp)
#define readlong(i) i = *(pcode+cp++)
#define readvalp(x,p) {zzcp=0; eval(p,&zzcp,&x,ostr,&otyp);}
#define dbg if ((gle_debug & 16)>0)
static int for_level,for_skip,for_loop,if_findelse,if_findendif,if_level;
for_init()
{
	for_loop = 0;
	for_level = 0;
	for_skip = 0;
	if_findelse = 0;
	if_findendif = 0;
	if_level = 0;
}
/*---------------------------------------------------------------------------*/
/* Input is pcode, output is text equiv.*/
do_pcode(int *srclin, long *pcode, int plen, int *pend)
/* srclin = The source line number */
/* pcode =  a pointer to the pcode output buffer */
/* plne =   a pointer to the length of the pcode output */
{
	double oval;
	union {double d; long l; long ll[2];} both;
	int otyp,cp=0,i,zzcp;
	static double loopstep[30];
	static int loopadr[30],nloop;
	double lll,rrr,uuu,ddd;
	char ostr[255];
	char *pss;
	char *zzz;
	int p;
	union ppboth {long l; short s[2];} bth;
	double x,y,sx,sy,ox,oy,x1,y1,x2,y2,x3,y3,a1,a2,r,z;
	int t,j,jj,ptr,ptr_fill,mask_just,mask_nostroke,marrow;
	static char ss[255],ss2[80];


	if (!init_run_done) {
		init_run_done = true;
		g_get_type(ss);
		if (strstr(ss,"FILLPATH")!=NULL) can_fillpath = true;
	}

	this_line = *srclin;
	/* dbg gprint("Gle debug %d \n ",gle_debug);
	dbg gprint("PP> ");
	dbg for (i=0; i<plen; i++) {
		bth.l = *(pcode+i);
		dbg gprint("%ld  ",bth.l);
	}
	dbg gprint("\n");
	dbg gprint("HEX> ");
	dbg for (i=0; i<plen; i++) {
		bth.l = *(pcode+i);
		dbg gprint("%lx  ",bth.l);
	}
	dbg gprint("\n");
	*/
	if (plen==0) return;
	if (*(pcode)==0) return;
	p = *(pcode+((cp)++));
	cmd_name(p,&pss);
	dbg gprint("Command %d {%s} \n",p,pss);
	sprintf(ss,"Command %d {%s} \n",p,pss);
	g_source(ss);
	if (if_findelse) {
		dbg gprint("SKIP in skip findelse \n");
		switch (p) {
			case 13 : /* else */
				if (if_level==0) {
					if_findelse = false;
				}
				break;
			case 14 : /* end xxx */
				readlong(jj);
				if (jj==6) { /* end if */
					if (if_level==0) {
						if_findelse = false;
					} else if_level--;
				}
				break;
			case 22 : /* if */
				if_level++;
				break;
		}
		return;
	}
	if (if_findendif) {
		dbg gprint("SKIP, findendif \n");
		switch (p) {
			case 14 : /* end xxx */
				readlong(jj);
				if (jj==6) { /* end if */
					if (if_level==0) {
						if_findendif = false;
					} else if_level--;
				} else gprint("Wrong end %d \n",jj);
				break;
			case 22 : /* if */
				if_level++;
				break;
		}
		return;
	}
	if (for_skip ) {
		switch (p) {
			case 47: /* until */
			case 18 : /* for */
				for_level++;
				break;
			case 30 : /* next */
				if (for_level==0) {
					for_skip = false;
				} else for_level--;
				break;
		}
		return;
	}
	if (!done_open) {
		if (p!=42 && p!=53 && p!=0) {
			gprint("No SIZE command at top of graph, assuming 10cm by 10cm\n");
			g_open(10.0,10.0);
			done_open = true;
			return;
		}
	}
	switch (p) {
	  case 53: /* comment */
	  case 0: /* blank line */
		break;
	  case 1:  /* ALINE x y ARROW both | start | end */
		readval(x);
		readval(y);
		dbg gprint("x=%f, y=%f \n",x,y);
		marrow = *(pcode + (cp++));
		dbg gprint("arrow mask %d \n",marrow);
		g_arrowline(x,y,marrow);
		break;
	  case 2:  /* AMOVE */
		readval(x);
		readval(y);
		g_move(x,y);
		break;
	  case 3: /* ARC */
		readval(r);
		readxy(a1,a2);
		g_get_xy(&ox,&oy);
		g_arc(r,a1,a2,ox,oy);
		break;
	  case 4: /* ARCTO */
		readxy(x1,y1);
		readxy(x2,y2);
		readval(r);
		g_get_xy(&ox,&oy);
		g_arcto(x1+ox,y1+oy,x2+ox+x1,y2+oy+y1,r);
		break;
	  case 51: /* Assignment  var=exp */
		readlong(jj);
		readval(x);
		if (otyp==1) var_set(jj,x);
		if (otyp==2) var_setstr(jj,ostr);
		break;
	  case 5:  /* BEGIN box | path | scale | rotate | EXTERNAL */
		g_flush();
		i = *(pcode + cp++);
		dbg gprint(" begin %d \n",i);
		switch (i) {
			case 1: /* PATH stroke fill clip */
				npath++;
				g_get_xy(&path_x[npath],&path_y[npath]);
				path_stroke[npath] = *(pcode + cp);
				ptr = *(pcode + ++cp);
				path_fill[npath] = 0;
				if (ptr) {
					readvalp(z,pcode+cp+ptr);
					memcpy(&path_fill[npath],&z,4);
				}
				path_clip[npath] = *(pcode + ++cp);
				g_set_path(true);
				g_newpath();
				break;
			case 2: /* BOX   add,fill,nobox,name */
				box_start();
				ptr = *(pcode + cp);
				if (ptr) readvalp(box_add[nbox],pcode+cp+ptr);
				ptr = *(pcode + ++cp);
				if (ptr) {
					readvalp(z,pcode+cp+ptr);
					memcpy(&box_fill[nbox],&z,4);
				}
				box_nobox[nbox] = *(pcode + ++cp);
				ptr = *(pcode + ++cp);
				if (ptr) {
					readvalp(z,pcode+cp+ptr);
					box_name[nbox] = sdup(ostr);
				}
				break;
			case 3: /* SCALE */
				readxy(x,y);
				g_gsave();
				g_scale(x,y);
				break;
			case 4: /* ROTATE */
				readval(x);
				g_gsave();
				g_rotate(x);
				break;
			case 5: /* TRANSLATE */
				readval(x); readval(y);
				g_gsave();
				g_translate(x,y);
				g_rmove(0.0,0.0);
				break;
			case 6: /* if */
			case 7: /* sub */
				gprint("odd begin %d\n",i);
				break;
			case 8: /* name */
				box_start();
				box_nobox[nbox] = true;
				readval(z);
				box_name[nbox] = sdup(ostr);
				break;
			case 9: /* text */
				ptr = *(pcode + cp);
				z = 0;
				if (ptr) readvalp(z,pcode+cp+ptr);
				begin_text(srclin,pcode,&cp,z);
				break;
			case 18: /* tab  (tabbing, table) */
				begin_tab(srclin,pcode,&cp);
				break;
			case 10: /* graph */
				begin_graph(srclin,pcode,&cp);
				break;
			case 11: /* xaxis */
			case 12: /* yaxis */
			case 13: /* x2axis */
			case 14: /* y2axis */
				break;
			case 16: /* KEY */
				begin_key(srclin,pcode,&cp);
				break;
			case 19: /* begin  clip */
				g_beginclip();
				break;
			case 17: /* ORIGIN */
				g_gsave();
				g_get_xy(&x,&y);
				g_translate(x,y);
				g_move(0.0,0.0);
				break;
			default: /* error  */
				gprint("Error, illegal begin option %d \n",i);
				break;
		}
		break;
	  case 6: /* BEZIER */
		readxy(x1,y1);
		readxy(x2,y2);
		readxy(x3,y3);
		g_bezier(x1,y1,x2,y2,x3,y3);
		break;
	  case 7:  /* BOX x y left|center|right FILL fexp NAME string */
		readval(x);
		readval(y);
		g_get_xy(&ox,&oy);
		x += ox;
		y += oy;
		dbg gprint("x=%f, y=%f \n",x,y);
		mask_just = *(pcode + cp++);
		g_dojust(&ox,&oy,&x,&y,mask_just);

		mask_nostroke = *(pcode + cp);
		ptr_fill = *(pcode + ++cp);
		if (ptr_fill) {
			readvalp(z,pcode + cp + ptr_fill);
			memcpy(&both.d,&z,sizeof(z));
			g_set_fill(both.l);
			g_box_fill(ox,oy,x,y);
		}
		if (!mask_nostroke)
			g_box_stroke(ox,oy,x,y);
		ptr = *(pcode + ++cp); /* name */
		if (ptr) {
			readvalp(z,pcode + cp + ptr);
			name_set(ostr,ox,oy,x,y);
		}
		dbg gprint("justify mask %d ",mask_just);
		dbg gprint("nostroke mask %d ",mask_nostroke);
		dbg gprint("fill pointer %d \n",ptr_fill);
		break;
	  case 52:  /* CALL or @   (nope, do this inside ASSIGN */
		readval(r);
		break;
	  case 8:  /* CIRCLE */
		readval(r);
		g_get_xy(&ox,&oy);
		sx = ox; sy = oy;
		mask_just = *(pcode + cp++);
		x = ox + r;
		y = oy + r;
		g_dojust(&ox,&oy,&x,&y,mask_just);
		g_move(ox,oy);
		mask_nostroke = *(pcode + cp++);
		ptr_fill = *(pcode + cp);
		if (ptr_fill) {
			readvalp(z,pcode + cp + ptr_fill);
			memcpy(&both.l,&z,4);
			g_set_fill(both.l);
			g_circle_fill(r);
		}
		if (!mask_nostroke)
			g_circle_stroke(r);
		g_move(sx,sy);
		break;
	  case 9: /* CLOSEPATH */
		g_closepath();
		break;
	  case 10: /* CURVE  x y x y ...  change to BEGIN CURVE ... END CURVE */
		g_curve(pcode+cp);
		break;
	  case 11: /* DEFINE  MARKER name  subname */
		break;
	  case 12: /* DFONT */
		readstr(ss);
		g_dfont(ss);
		break;
	  case 13: /* ELSE */
		if_findendif = true;
		break;
	  case 14: /* END */
		readlong(jj);
		switch (jj) {
		  case 1: /* end path  (stroke,fill,clip) */
			if (path_fill[npath]!=0) {
				g_set_fill(path_fill[npath]);
				g_fill();
			}
			if (path_stroke[npath]==true) g_stroke();
			if (path_clip[npath]==true) g_clip();
			if (npath==0) {
				gprint("Too many end path's \n");
				break;
			}
			g_move(path_x[npath],path_y[npath]);
			npath--;
			g_set_path(false);
			break;
		  case 2: /* end box */
			box_end();
			break;
		  case 3: /* end scale */
		  case 4: /* end rotate */
		  case 5: /* end translate */
			g_grestore();
			break;
		  case 6: /* end if */
			/* do nothing,  all done elsewhere I think?? */
			break;
		  case 8: /* end name */
			box_end();
			break;
		  case 19: /* clip */
			g_endclip();
			break;
		  case 18: /* tab */
		  case 9: /* text */
			break;
		  case 17: /* end origin */
			g_grestore();
			break;
	 	  default :
			gprint("Not a valid end %d \n",jj);
		}
		break;
	  case 15: /* FCLOSE */
		readval(x);
		chn = x;
		chn = f_testchan(chn);
		if (f_chan[chn]!=NULL) fclose(f_chan[chn]);
		f_chan[chn] = NULL;
		siffree(&f_buff[chn]);
		siffree(&f_nexttok[chn]);
		f_buff[chn] = NULL;
		break;
	  case 16: /* FILL */
		g_fill();
		break;
	  case 61 : /* fread CHAN a$ x   */
	  case 62 : /* freadln */
		readlong(t);
		if (t!=49) gprint("FREAD, PCODE ERROR, %d  cp %d plen %d\n",t,cp,plen);
		readlong(i);
		readlong(t);
		var_get(i,&x);
		chn = x;
		chn = f_testchan(chn);
		if (p==61 && cp>=plen) gprint("FREAD requires at least two parameters\n");
		while (cp<plen) {
			readlong(t);
			if (t!=49) gprint("FREAD2, PCODE ERROR, %d  cp %d plen %d\n",t,cp,plen);
			readlong(i); /* variable number */
			readlong(t); /* type of variable */
			if (t==1) {
				x = atof(f_gettok(chn));
				var_set(i,x);
			} else {
				var_setstr(i,f_gettok(chn));
			}
		}
		if (p==62) f_getline(chn);
		break;
	  case 63 : /* fwrite */
	  case 64 : /* fwriteln */
		strcpy(ss2,"");
		readlong(t);
		readlong(t);
		readval(x);
		chn = x;
		chn = f_testchan(chn);
		if (f_read[chn]==0) gprint("You cannot WRITE from a file open for READ {#%d %d} \n",chn,f_read[chn]);
		while (cp<plen) {
			readlong(t);
			if (t!=49) gprint("WRITE, PCODE ERROR, %d  cp %d plen %d\n",t,cp,plen);
			readlong(t);
			if (t==1) {
				readval(x);
				sprintf(ss,"%g ",x);
			} else readstr(ss);
			strcat(ss2,ss);
		}
		if (p==64) strcat(ss2,"\n");
		fprintf(f_chan[chn],"%s",ss2);
		break;
	  case 17: /* FOPEN "a.a" inchan read|write */
		readstr(ss);
		readlong(i); /* channel variable */
		readlong(jj); /* 0 = read, 1 = write */
		chn = f_getchan();
		f_read[chn] = jj;	
		var_set(i,chn);
		if (f_chan[chn]!=NULL) fclose(f_chan[chn]);
		if (jj==0) f_chan[chn] = fopen(ss,"r");
		else 	f_chan[chn] = fopen(ss,"w");
		if (f_chan[chn]==NULL) gprint("Can't open {%s}  \n",
			ss);
		dbg gprint("Opened {%s} chan %d, access %d  vari %d \n",ss,chn,jj,i);
		f_end[chn] = false;
		if (f_read[chn]==0) f_readahead(chn);
		break;
	  case 18: /* FOR   v,exp,exp,op,exp */
		if (for_loop) {
			readlong(jj);
			readval(x);
			readval(y);
			var_get(jj,&x);
			x = x + loopstep[nloop];
			var_set(jj,x);
			var_get(jj,&x);
			dbg gprint("got back %f \n",x);
			if ( (x > y  && loopstep[nloop]>=0) ||
			     (y > x  && loopstep[nloop]<=0) ) {
				for_skip = true;
				nloop--;
			}
			for_loop = false;
			break;
		}
		loopadr[++nloop] = *srclin;
		readlong(jj);	  /* variable */
		readval(x);
		readval(y);
		var_set(jj,x);
		loopstep[nloop] = 1;
		p = *(pcode + cp);
		if (p) {
			readvalp(z,pcode + cp + p);
			loopstep[nloop] = z;
		}
		break;
	  case 19: /* GOTO */
	  case 20: /* GSAVE */
		g_gsave();
		break;
	  case 54: /* GRESTORE */
		g_grestore();
		break;
	  case 21: /* ICON */
		break;
	  case 22: /* IF EXP */
		readval(x);
		dbg gprint("If expression = %f \n",x);
		if (x==0) if_findelse = true;	/* exp was false */
		break;
	  case 23: /* INCLUDE (done in pass,  already included) */
		break;
	  case 24: /* INPUT */
	  case 25: /* JOIN  str1,type,str2 */
		{
			char ss1[90];
			readval(z);
			strcpy(ss1,ostr);
			readlong(jj);
			readval(z);
			name_join(ss1,ostr,(int) jj);
		}
		break;
	  case 26: /* MARKER */
		readval(x);
		memcpy(&both.d,&x,sizeof(x));
		jj = both.l;
		g_get_hei(&z);
		y = 1;
		if (*(pcode+cp)!=0) readval(y);
		y = y * z;
		g_marker((int) both.l,y);
		break;
	  case 27: /* MOVE  name */
		readval(z);
		name_get(ostr,&ox,&oy,&x,&y);
		x = ox;
		y = oy;
		g_dojust(&ox,&oy,&x,&y,jj);
		g_move(x+(x-ox),y+(y-oy));
		break;
	  case 28: /* NARC */
		readval(r);
		readxy(a1,a2);
		g_get_xy(&ox,&oy);
		g_narc(r,a1,a2,ox,oy);
		break;
	  case 29: /* NEWPATH */
		g_newpath();
		break;
	  case 30:  /* NEXT */
		if (nloop==0) {
			gprint("Next without for\n");
			break;
		}
		*srclin = loopadr[nloop]-1;
		dbg gprint("%FOR   Setting line back to %d  nloop %d \n ",*srclin,nloop);
		for_loop = true;
		break;
	  case 31: /* PIE ,, not implemented yet */
		break;
	  case 57: /* plotter fonts */
		plotter_fonts();
		break;
	  case 58: /* bigfile "filename" */
		readstr(ss);
		strlwr(ss);		/* bit of a kludge but ... */
		run_bigfile(ss);
		break;
	  case 55: /* Postscrip fiename x y */
		readstr(ss);
	        strlwr(ss);
		readxy(x1,y1);
		g_postscript(ss,x1,y1);
		break;
	  case 32: /* PRINT */
		break;
	  case 33: /* RBEZIER */
		readxy(x1,y1);
		readxy(x2,y2);
		readxy(x3,y3);
		g_get_xy(&ox,&oy);
		x1 += ox;  x2 += ox;  x3 += ox;
		y1 += oy;  y2 += oy;  y3 += oy;
		g_bezier(x1,y1,x2,y2,x3,y3);
		break;
	  case 34: /* REGION */
		break;
	  case 50: /* RETURN exp */
		readval(x);
		sub_set_return(x);
		break;
	  case 35: /* REVERSE */
		g_reverse();
		break;
	  case 36:  /* RLINE */
		readval(x);
		readval(y);
		g_get_xy(&ox,&oy);
		marrow = *(pcode + (cp++));
		dbg gprint("RLINE getxy %f %f \n",ox,oy);
		g_arrowline(x+ox,y+oy,marrow);
		break;
	  case 37:  /* RMOVE */
		readval(x);
		readval(y);
		g_get_xy(&ox,&oy);
		g_move(x+ox,y+oy);
		break;
	  case 38: /* ROTATE */
		readval(x);
		g_rotate(x);
		break;
	  case 39: /* SAVE  name */
		g_get_xy(&x,&y);
		readval(z);
		name_set(ostr,x,y,x,y);
		break;
	  case 40: /* SCALE */
		readxy(x,y);
		g_scale(x,y);
		break;
	  case 41: /* SET */
		for (i=1;i<plen;i++) {
		cp = i+1;
		dbg gprint("set sub command %d \n",(*(pcode+i)-500));
		switch (*(pcode+i)-500) {
		/* FIDDLE CP SO THAT READVAL WORKS *** */
		  case 1: /* height */
			readval(x);
			g_set_hei(x);
			break;
		  case 2: /* font */
			readval(x);
			memcpy(&both.l,&x,4);
			g_set_font(both.l);
			break;
		  case 3: /* justify */
			readlong(jj);
			g_set_just(jj);
			break;
		  case 4: /* color */
			readval(x);
			memcpy(&both.l,&x,4);
			g_set_color(both.l);
			break;
		  case 5: /* dashlen */
			readval(x);
			g_set_line_styled(x);
			break;
		  case 6: /* dash */
			readval(x);
			i = x;
			sprintf(ss,"%d",i);
			g_set_line_style(ss);
			break;
		  case 7: /* lwidth */
			readval(x);
			g_set_line_width(x);
			break;
		  case 10: /* fontlwidth */
			readval(x);
			g_set_font_width(x);
			break;
		  case 8: /* join */
			readlong(jj);
			g_set_line_join(jj);
			break;
		  case 9: /* cap */
			readlong(jj);
			g_set_line_cap(jj);
			break;
		  default :
			gprint("Not a valid set sub command {%d} i=%d \n",*(pcode+i),i);
		  }
		i = cp-1;
		}
		break;
	  case 42: /* size x y [box]*/
		readxy(x,y);
		g_open(x,y);
		g_get_xy(&ox,&oy);
		done_open = true;
		mask_just = *(pcode + cp++);
		if (mask_just) { /* then draw box */
			g_box_stroke(ox,oy,x,y);
		}
		break;
	  case 43: /* STROKE */
		g_stroke();
		break;
	  case 44: /* SUB */
		readlong(jj);
		sub_get_startend(jj,&i,&j);
		*srclin = j;	/* skip past the subroutine */
		break;
	  case 45: /* TEXT */
		strcpy(ss,(char *) (pcode+cp));
	/*	readstr(ss); */
		g_text(ss);
		g_get_bounds(&x1,&y1,&x2,&y2);

		break;
	  case 60: /* DEFMARKER */
		break;
	  case 59: /* TEXTDEF */
		strcpy(ss,(char *) (pcode+cp));
		text_def(ss);
		break;
	  case 46: /* TRANSLATE */
		readxy(x,y);
		g_translate(x,y);
		break;
	  case 47: /* UNTIL */
		readval(x);
		if (!for_loop) loopadr[++nloop] = *srclin;
		for_loop = true;
		if (x) {for_skip = true; for_loop = false;}
		break;
	  case 48: /* WHILE */
		break;
	  case 49: /* WRITE */
		g_get_xy(&ox,&oy); 
		strcpy(ss2,"");
		while (cp<plen) {
			readlong(t);
			if (t!=49) gprint("WRITE, PCODE ERROR, %d  cp %d plen %d\n",t,cp,plen);
			readlong(t);
			if (t==1) {
				readval(x);
				sprintf(ss,"%g ",x);
			} else readstr(ss);
			strcat(ss2,ss);
		}
		g_text(ss2);
		g_move(ox,oy); 
		break;
	  default :
		gprint("Not a recognized command \n");
	}
	*pend = cp;
}
/* should be in core.c */
g_arrowline(double x2, double y2, int flag)
{
	double x1,y1;
	if ((flag&3)==0) {
		g_line(x2,y2);
		return;
	}
	g_get_xy(&x1,&y1);
	if (!can_fillpath) {
		if (flag & 1)  g_arrow(x2-x1,y2-y1);
		g_line(x2,y2);
		if (flag & 2)  g_arrow(x1-x2,y1-y2);
		return;
	}
	g_psarrow(x1,y1,x2,y2,flag);
}
g_psarrow(double x1, double y1, double x2, double y2, int flag)
{
	double ax1,ax2,ay1,ay2,dx,dy,nx,ny,nnx,nny,xx2,yy2;
	long cur_color;
	xx2 = x2; yy2 = y2;
	dx = x2-x1;  dy = y2-y1;
	g_arrowpoints(x1,y1,dx,dy,&ax1,&ay1,&ax2,&ay2,&nx,&ny);
	g_set_path(true);
	g_newpath();
	if ((flag & 1)>0) {
		g_move(ax2,ay2);
		g_line(x1,y1);
		g_line(ax1,ay1);
		g_closepath();
		x1 = nx; y1 = ny;
	}
 	g_arrowpoints(x2,y2,-dx,-dy,&ax1,&ay1,&ax2,&ay2,&nx,&ny);
	if ((flag & 2)>0) {
		g_move(ax2,ay2);
		g_line(x2,y2);
		g_line(ax1,ay1);
		g_closepath();
		xx2 = nx; yy2 = ny;
	}
	g_get_color(&cur_color);
	g_set_fill(cur_color);
	g_fill();
	g_set_path(false);
	g_newpath();
	g_move(x1,y1);
	g_line(xx2,yy2);
	g_move(x2,y2);
}
g_arrowpoints(double cx,double cy,double dx,double dy, double *ax1,
	double *ay1,double *ax2,double *ay2, double *nnx, double *nny)
{
	double radius,angle,alen,nx,ny,width,arrow_angle;
	g_get_line_width(&width);
	if (width==0) width = .02;
	arrow_angle = 10;
	if (width>.1) arrow_angle = 20;
	if (width>.3) arrow_angle = 30;
	g_get_hei(&alen);  alen = alen/2;
	if (sin(arrow_angle*3.14159/180)*alen < width/1.5) {
		alen = (width/1.5) / sin(arrow_angle*3.141592/180);
	}
	xy_polar(dx,dy,&radius,&angle);
	if (radius<0) alen = -alen;
	polar_xy(alen,angle+arrow_angle,&dx,&dy);
	*ax2 = cx+dx;  *ay2 = cy+dy;
	polar_xy(alen,angle-arrow_angle,&dx,&dy);
	*ax1 = cx+dx;  *ay1 = cy+dy;

	polar_xy(alen*cos(arrow_angle*3.141592/180),angle,&nx,&ny);
	alen = width/2;
	if (radius<0) alen = -alen;

	cx += nx; cy += ny;
	*nnx = cx; *nny = cy;
}
g_arrow(double dx, double dy)
{
	double cx,cy,radius,angle,alen;
	g_get_xy(&cx,&cy);
	xy_polar(dx,dy,&radius,&angle);
	g_get_hei(&alen);  alen = alen/2;
	if (radius<0) alen = -alen;
	polar_xy(alen,angle+10.0,&dx,&dy);
	g_line(cx+dx,cy+dy);
	g_move(cx,cy);
	polar_xy(alen,angle-10.0,&dx,&dy);
	g_line(cx+dx,cy+dy);
	g_move(cx,cy);
}

box_start(void)
{
	g_get_bounds(&box_x1[nbox],&box_y1[nbox],&box_x2[nbox],&box_y2[nbox]);
	g_init_bounds();
	nbox++;
	box_add[nbox]=0;
	box_fill[nbox] = 0;
	box_name[nbox] = 0;
}
box_end(void)
{
	double x1,y1,x2,y2,ox,oy;
	if (nbox==0) {
		gprint("Too many end boxes \n");
		return;
	}
	g_get_bounds(&x1,&y1,&x2,&y2);
	if (x1>(x2+100)) return;
	x1 -= box_add[nbox];	y1 -= box_add[nbox];
	x2 += box_add[nbox];	y2 += box_add[nbox];
	g_get_xy(&ox,&oy);
	if (box_fill[nbox]!=0) {
		g_set_fill(box_fill[nbox]);
		g_box_fill(x1,y1,x2,y2);
	}
	if (!box_nobox[nbox]) {
		g_box_stroke(x1,y1,x2,y2);
	}
	if (box_name[nbox]!=0) {
		name_set(box_name[nbox],x1,y1,x2,y2);
		myfree(box_name[nbox]);
	}
	nbox--;
	if (box_x1[nbox] <= box_x2[nbox]) {
		g_set_bounds(box_x1[nbox],box_y1[nbox]);
		g_set_bounds(box_x2[nbox],box_y2[nbox]);
	}
	g_move(ox,oy);
}
int nm_adjust(int jj,double *sx, double *sy, double ex, double ey,
	double x1, double y1, double x2, double y2);
int nm_point(int jj,double *rx, double *ry, double x1,double y1,double x2,double y2);
int nm_split(char *s, char *n, char *p);
name_join(char *o1,char *o2,int marrow)
{
	char n1[40],n2[40],p1[9],p2[9],*ss;
	double ox,oy,sx,sy,ex,ey,x,y,x1,y1,x2,y2,x3,y3,x4,y4;
	int i,jj1,jj2;

	strupr(o1);	strupr(o2);
	if (strstr(o1,".H")!=0 || strstr(o1,".V")!=0) {
		ss = o1; o1 = o2; o2 = ss;
		if (marrow==2) marrow = 1;
		else if (marrow==1) marrow = 2;
	}
	nm_split(o1,n1,p1);
	nm_split(o2,n2,p2);

	x1 = 1e30; x3 = 1e30;
	name_get(n1,&x1,&y1,&x2,&y2);
	name_get(n2,&x3,&y3,&x4,&y4);
	if (x1==1e30 || x3 == 1e30) return;
	jj1 = pass_justify(p1);
	jj2 = pass_justify(p2);

	nm_point(jj1,&sx,&sy,x1,y1,x2,y2);
	ex = sx; ey = sy;
	nm_point(jj2,&ex,&ey,x3,y3,x4,y4);

	nm_adjust(jj1,&sx,&sy,ex,ey,x1,y1,x2,y2);
	nm_adjust(jj2,&ex,&ey,sx,sy,x3,y3,x4,y4);

	g_get_xy(&ox,&oy);
	g_move(sx,sy);
	if (marrow==2) marrow = 1;
	else if (marrow==1) marrow = 2;
	g_arrowline(ex,ey,marrow);

/* 	g_line(ex,ey);
	x = ex-sx;
	y = ey-sy;
	if (x!=0 || y!=0) {
		if (marrow & 1)  g_arrow(-x,-y);
		g_move(sx,sy);
		if (marrow & 2)  g_arrow(x,y);
		g_move(ox,oy);


	}

*/
}
nm_point(int jj,double *rx, double *ry, double x1,double y1,double x2,double y2)
{
	int jx,jy;
	double w,y,d;

	if ((jj == 0x2000)) { /* virtical */
		if (y2<*ry) *ry = y2;
		if (y1>*ry) *ry = y1;
		return;
	}
	if ((jj == 0x3000)) { /* horizontal centre   */
		if (x2<*rx) *rx = x2;
		if (x1>*rx) *rx = x1;
		return;
	}
	jx = (jj & 0xf0) / 16;
	jy = jj & 0x0f;
	d = jx * (x2-x1)/2;
	*rx = x1 + d;
	d = jy * (y2-y1)/2;
	*ry = y1 + d;
}
nm_adjust(int jj,double *sx, double *sy, double ex, double ey,
	double x1, double y1, double x2, double y2)
{
	double r1,r2,xa,da,ca,dr,dx,dy,pi,rz,r;
	pi = 3.1415925;
	if ((jj & 0xf000)==0x5000) {
		r1 = (x2-x1)/2;
		r2 = (y2-y1)/2;
			xy_polar(*sx - ex,*sy - ey,&dr,&da);
		xa = da - 180;
xxxa:		if (xa > 180) xa = xa - 180;
		if (xa < 0) xa = xa + 180;
		if ((xa<0) || (xa> 180)) goto xxxa;
		if (r1==0) return;
		ca = atan(r2/r1)*180/pi;
		if (xa < 90) {
			rz = r1/cos(pi*xa/180);
			if (xa>ca) rz = r2/sin(pi*xa/180);
		} else {
			xa = xa - 90;
			rz = r2/cos(pi*xa/180);
			if (xa>(90-ca)) rz = r1/sin(pi*xa/180);
		}
		dr = dr - rz ;
		polar_xy(dr,da,&dx,&dy);
		*sx = ex + dx;
		*sy = ey + dy;
	}
	if ((jj & 0xff00)==0x1000) {
		r = (x2-x1)/2;
		xy_polar(*sx-ex,*sy-ey,&dr,&da);
		dr = dr - r;
		polar_xy(dr,da,&dx,&dy);
		*sx = ex + dx;
		*sy = ey + dy;
	}
}
nm_split(char *s, char *n, char *p)
{
	char *d;
	d = strchr(s,'.');
	if (d!=0) {
		ncpy(n,s,d-s);
		strcpy(p,d+1);
	} else {
		strcpy(n,s);
		strcpy(p,"BO");
	}
}

int f_getchan(void)
{
	int i;
	for (i=0; i<F_MAXCHAN; i++) {
		if (f_chan[i]==NULL) {
			return i;
		}
	}
	gprint("Fatal error, ran out of file handles, don't open so many\n");
	return 0;
}
void f_getline(int chn)
{
	char buff[1002];
	if (f_chan[chn]==NULL) {return;}
	if (f_read[chn]!=0) gprint("You cannot read from a file open for WRITe #%d %d \n",chn,f_read[chn]);
	if (fgets(buff,1000,f_chan[chn])==NULL) {
		f_end[chn] = true;
	}
	if (feof(f_chan[chn])) f_end[chn] = true;
	if (f_buff[chn]!=NULL) myfree(f_buff[chn]);
	f_buff[chn] = sdup(buff);
}
int f_eof(int chn)
{
	return f_end[chn];
}
char *f_gettok(int chn)
{
	static char thistok[80];
	if (f_end[chn]) gprint("Reading past end of file %d\n",chn);
	strcpy(thistok,f_nexttok[chn]);
	f_readahead(chn);
	return thistok;
}
void f_init()
{
	int i;
	for (i=0;i<F_MAXCHAN; i++) {
		if (f_chan[i] != NULL) {fclose(f_chan[i]); f_chan[i] = NULL;}
		siffree(&f_buff[i]);
		siffree(&f_nexttok[chn]);
	}
}
void f_readahead(chn)
{
	siffree(&f_nexttok[chn]);
	f_nexttok[chn] = sdup(f_getnext(chn));
}	
char *f_getnext(int chn)
{
	char *s;
	int blen;
	static char tk[81];
	if (f_buff[chn]==NULL) f_getline(chn);
	tk[0] = 0;
try_again:
	if (f_buff[chn]==NULL) return tk;
	blen = strlen(f_buff[chn]);
	s = strtok(f_buff[chn]," ,=\t\n\x0a\x0c\x0d");	
	if (s==NULL) goto next_line;
	strcpy(tk,s);
	if (strlen(s)==blen) f_buff[0] = 0;
	else strcpy(f_buff[chn],s+1+strlen(s));
	if (*s == '"' || *s == '!' || *s == ';') goto next_line;
	return tk;
next_line:
	if (f_eof(chn)) return tk;
	f_getline(chn);
	goto try_again;
}
void siffree(char **s) 
{
	if (*s != NULL) myfree(*s);
	*s = NULL;
}
int f_testchan(int chn)
{
	if (chn<0 || chn>F_MAXCHAN) {
		gprint("Error in channel number %d\n",chn);
		return 0;
	}
	return chn;
}
