/*
 * Hypertex modifications to DVI previewer for X.
 * This portion of xhdvi is completely in the public domain. The
 * author renounces any copyright claims. It may be freely used for
 * commercial or non-commercial purposes. The author makes no claims
 * or guarantees - use this at your own risk.
 * 
 * Arthur Smith, U. of Washington, 1994
 *
 * 5/1994       code written from scratch, probably inspired by (but
 *                incompatible with) the CERN WWW library.
 * 3/1995       CERN WWW library called to do document fetching.
 */

/* Note: several coordinate systems are in use in this code.  Note
   that the relation between a screen pixel and a dvi coordinate is
   the current shrink factor times 2^16.

   When multiple pages are allowed in one drawing area, the variables
   xscroll_pages or yscroll_pages are set.

   Conversion between coordinates is done using the screen_to_page,
   page_to_screen, screen_to_abs, abs_to_screen routines.

*/

#include "config.h"
#if defined(XHDVI) || defined(HTEX)
#include <kpathsea/c-ctype.h>
#include <kpathsea/c-fopen.h>
#include <kpathsea/c-stat.h>
#define GETCWD(str,size) xgetcwd()
#include <X11/Shell.h>	/* needed for def of XtNtitle */
#include <X11/StringDefs.h>

#include "WWWLib.h"
#include "WWWInit.h"
#include "WWWCache.h"

#if 0	/* seems unnecessary?! */
#ifdef HAVE_NETDB_H
#include <netdb.h> /* for struct hostent */
#endif
#endif	/* seems unnecessary?! */


/* Implementation of HyperTeX through \specials */
/* One option: allow HTML tags and recognize them */
/* applicationDoSpecial strips leading blanks, so first char should
   be '<' if it's a tag */

char *MyStrAllocCopy();
char *refscan();
extern void put_rule();
extern void make_absolute ();
extern int invokeviewer ();

static void extractbase ();
static void retitle ();

#define HTeX_A_NAME 1
#define HTeX_A_HREF 2

#define htex_shrinkfactor mane.shrinkfactor /* Only main win has refs */

typedef struct {
	int type; /* Type of anchor: URL, etc from WWW anchor list */
	char *name; /* Name string for anchor (Null by default) */
	char *href; /* A reference from this anchor */
	int llx, lly, urx, ury; /* Box on page where anchor located */
} HTeX_Anchor;

/* Structure to remember where we have been: */
typedef struct {
	char *refname; /* File name (or href?) */
	int pageno; /* Which page anchor was on */
	int x,y; /* Approximate location on page... */
	int which; /* Number of anchor on this page */
	int type; /* And the other properties of this anchor */
	char *name;
	char *href;
} Anchors;

int waiting_for_anchor = -1; /* If waiting for anchor to be properly parsed? */
int cur_anchor_on_page; /* Keep track of current page as it's read in */

#define HTeX_AnchorSTEP 20
HTeX_Anchor **HTeX_anchorlist = NULL;
int *nHTeX_anchors, *maxHTeX_anchors;

Anchors *HTeX_visited = NULL;
int nHTeX_visited = 0, maxHTeX_visited = 0;

#define HTeX_NSTACK 32
int HTeXAnest[HTeX_NSTACK]; /* Maximum number of nested anchors */
int HTeXAnestlevel; /* Current nesting level */

int HTeXreflevel; /* 0 if not currently inside an href anchor */

int *htex_parsedpages = NULL; /* List of all pages, = 1 if already parsed, zero if not */
int htex_total_pages;
/* size = total_pages, current page = current_page defined in xhdvi.h */

void htex_parseanchor ARGS((char *, HTeX_Anchor *));
char *MyStrAllocCopy ARGS((char **, char *));
char *refscan      ARGS(( char*, char **, char **));
void htex_loc_on_page ARGS((Anchors *));
void freeHTeXAnchors ARGS((HTeX_Anchor *, int));
void htex_img	ARGS((int, char *, int));
void htex_base	ARGS((int, char *, int));

#define BEGIN 0
#define END 1

/* Dave Oliver's hypertex format: */
/* Only understand my version of anchors so far */
/* ie.: his TYPE=text for hrefs, frag for names */
void
hy_handletag(cp, pageno)
    char *cp;
    int pageno;
{
	int beginend=BEGIN;

	while (isspace(*cp)) cp++;
	if (!strncasecmp(cp, "END", 3)) {
		beginend = END;
		cp += 3;
	}
/* Leave the parsing to htex_anchor! */
	htex_anchor(beginend, cp, pageno);
}

int
checkHyperTeX(cp, pageno)
char *cp;
int pageno;
{
	int htexfound = 0;

	if (strncasecmp(cp, "html:", 5) == 0) {
		cp += 5;
		while (isspace(*cp)) cp++;
		htexfound = 1;
	}
	if (*cp == '<') { /* Possibly Missing the header part */
		htexfound = 1;
		htex_handletag(cp, pageno);
	} else if (strncasecmp(cp, "hyp", 3) == 0) {
		/* Dave Oliver's HyperTeX */
		htexfound = 1;
		cp += 4;
		hy_handletag(cp, pageno);
	}

	return htexfound;
}

void
htex_handletag(cp, pageno)
char *cp;
int pageno;
{
	int beginend=BEGIN;

	if (*cp != '<') return;
	++cp;
	while (isspace(*cp)) cp++;
	if (*cp == '/') {
		beginend = END;
		cp++;
	}
	switch(*cp) {
	    case 'A':
	    case 'a': /* Anchors */
		htex_anchor(beginend, cp+1, pageno);
		break;
	    case 'b': /* Base name? */
		htex_base(beginend, cp, pageno);
		break;
	    case 'i': /* Included images? */
		htex_img(beginend, cp, pageno);
		break;
	    default: /* Tag not implemented yet */
		break;
	}
}

/* Basically just want to parse the line... */
/* Should use WWW library stuff ? */

void 
htex_anchor(beginend, cp, pageno)
int beginend, pageno;
char *cp;
{
	int *nap, *maxp;
	int oldllx, oldlly, oldurx, oldury;
	HTeX_Anchor *HTeXAp, **HTeXApp;

	HTeXApp = HTeX_anchorlist + pageno;
	nap = nHTeX_anchors + pageno;
	maxp = maxHTeX_anchors + pageno;
	if (*HTeXApp == NULL) {
		*maxp = HTeX_AnchorSTEP;
		*HTeXApp = (HTeX_Anchor *) xmalloc((*maxp)*sizeof(HTeX_Anchor), "Anchor");
	} else if (*nap == *maxp) {
		*maxp += HTeX_AnchorSTEP;
		*HTeXApp = (HTeX_Anchor *) realloc(*HTeXApp,
					(*maxp)*sizeof(HTeX_Anchor));
	}
	if (htex_parsedpages[pageno] != 1) {  /* Only do if page not done yet */
	  if (beginend == END) {
	    HTeXAnestlevel--;
	    if (HTeXAnestlevel < 0) {
		HTeXAnestlevel = 0; /* Extra </a>'s? */
	    } else {
		HTeXAp = *HTeXApp + HTeXAnest[HTeXAnestlevel];
		if (HTeXAp->llx > DVI_H) {
			HTeXAp->llx = DVI_H;
		}
		if (HTeXAp->urx < DVI_H) {
			HTeXAp->urx = DVI_H;
		}
		if (HTeXAp->lly > DVI_V) {
			HTeXAp->lly = DVI_V;
		}
		if (HTeXAp->ury < DVI_V) {
			HTeXAp->ury = DVI_V;
		}
		oldllx = HTeXAp->llx;
		oldlly = HTeXAp->lly;
		oldurx = HTeXAp->urx;
		oldury = HTeXAp->ury;
		if (debug & DBG_ANCHOR) {
		    Printf("Added anchor %d, level %d:\n",
			    HTeXAnest[HTeXAnestlevel], HTeXAnestlevel);
		    if (HTeXAp->type&HTeX_A_HREF) {
			Printf("href = %s\n", HTeXAp->href);
		    }
		    if (HTeXAp->type&HTeX_A_NAME) {
			Printf("name = %s\n", HTeXAp->name);
		    }
		    Printf("box %d %d %d %d\n",
			HTeXAp->llx, HTeXAp->lly, HTeXAp->urx, HTeXAp->ury);
		}
		if (waiting_for_anchor == HTeXAnest[HTeXAnestlevel]) {
			htex_to_anchor(current_page, waiting_for_anchor);
			waiting_for_anchor = -1; /* Reset it! */
		}
		/* End of debug section */
		if (HTeXAnestlevel > 0) {
			HTeXAp = *HTeXApp + HTeXAnest[HTeXAnestlevel-1];
			/* Check llx, lly, urx, ury info */
			if (oldllx < HTeXAp->llx) {
				HTeXAp->llx = oldllx;
			}
			if (oldlly < HTeXAp->lly) {
				HTeXAp->lly = oldlly;
			}
			if (oldurx > HTeXAp->urx) {
				HTeXAp->urx = oldurx;
			}
			if (oldury > HTeXAp->ury) {
				HTeXAp->ury = oldury;
			}
		}
	    }
	  } else {
		HTeXAp = *HTeXApp + *nap;
		/* Set type, and the name, href */
		htex_parseanchor(cp, HTeXAp);
		if (HTeXAp->type != 0) {
		    cur_anchor_on_page++; /* Increment the count of anchors here */
		    if (htex_parsedpages[pageno] == 2) {
    			/* Go to this anchor in list we already have: */
			HTeXAp = *HTeXApp + cur_anchor_on_page;
			HTeXAp->urx = HTeXAp->llx = DVI_H; /* Current horiz pos.*/
			HTeXAp->ury = HTeXAp->lly = DVI_V; /* Current vert. pos. */
			if (HTeXAnestlevel >= HTeX_NSTACK) {
				/* Error - too many nested anchors! */
			} else {
				HTeXAnest[HTeXAnestlevel++] = cur_anchor_on_page;
			}
		    } else if (htex_parsedpages[pageno] != 1) {  /* Only do if page not done yet */
			HTeXAp->urx = HTeXAp->llx = DVI_H; /* Current horiz pos.*/
			HTeXAp->ury = HTeXAp->lly = DVI_V; /* Current vert. pos. */
			if (HTeXAnestlevel >= HTeX_NSTACK) {
				/* Error - too many nested anchors! */
			} else {
				HTeXAnest[HTeXAnestlevel++] = *nap;
			}
			(*nap)++;
		    }
		}
	  }
	} else { /* if page has been properly parsed before */
	  if (beginend != END) {
		HTeXAp = *HTeXApp + *nap;
		/* Set type, and the name, href */
		htex_parseanchor(cp, HTeXAp);
	  }
	}
	if (beginend == END) {
		if (HTeXreflevel > 0) HTeXreflevel--;
	} else {
		if (HTeXAp->type&HTeX_A_HREF) HTeXreflevel++;
	}
}

void
htex_initpage() /* Starting a new page */
{
	if (htex_parsedpages == NULL) htex_reinit();
	HTeXAnestlevel = 0; /* Start with zero nesting level for a page */
	HTeXreflevel = 0;
	HTeXnext_extern = 0; /* Go to links in current window */
	cur_anchor_on_page = -1;
	paint_anchor(NULL);
}

/* A character or something was written: record position for current anchor */
void 
htex_recordbits(x, y, w, h) /* x,y are pixel positions on current page */
int x, y, w, h;
{
	HTeX_Anchor *HTeXAp;
	int dvix, dviy, dvix2, dviy2;
	int ch = 0;

	dvix = x *(htex_shrinkfactor << 16);
	dviy = y*(htex_shrinkfactor << 16);
	dvix2 = (x+w)*(htex_shrinkfactor << 16);
	dviy2 = (y+h)*(htex_shrinkfactor << 16);
	HTeXAp = HTeX_anchorlist[current_page] + HTeXAnest[HTeXAnestlevel-1];
	if (HTeXAp->llx > dvix) {
		HTeXAp->llx = dvix;
		ch++;
	}
	if (HTeXAp->lly > dviy) {
		HTeXAp->lly = dviy;
		ch++;
	}
	if (HTeXAp->urx < dvix2) {
		HTeXAp->urx = dvix2;
		ch++;
	}
	if (HTeXAp->ury > dviy2) {
		HTeXAp->ury = dviy2;
		ch++;
	}
	if (debug & DBG_ANCHOR) {
	    if (ch > 0) {
		Printf("New box for anchor %d, level %d: %d %d %d %d\n",
			HTeXAnest[HTeXAnestlevel-1], HTeXAnestlevel,
			HTeXAp->llx, HTeXAp->lly, HTeXAp->urx, HTeXAp->ury);
	    }
	}
}

void 
htex_donepage(i, pflag) /* This page has been completed */
int i, pflag;
{
	HTeX_Anchor *HTeXAp;

	/* Finish off boxes for nested anchors not done on this page */
	while (HTeXAnestlevel > 0) {
		HTeXAnestlevel--;
		HTeXAp = HTeX_anchorlist[i] + HTeXAnest[HTeXAnestlevel];
		if (HTeXAp->llx > DVI_H) {
			HTeXAp->llx = DVI_H;
		}
		if (HTeXAp->urx < DVI_H) {
			HTeXAp->urx = DVI_H;
		}
		if (HTeXAp->lly > DVI_V) {
			HTeXAp->lly = DVI_V;
		}
		if (HTeXAp->ury < DVI_V) {
			HTeXAp->ury = DVI_V;
		}
	}
	if (pflag == 1) { /* Really parsed this page */
		htex_drawboxes(); /* Draw boxes around the anchor positions */
		htex_parsedpages[i] = 1;
	} else {
		htex_parsedpages[i] = 2; /* Means htex_parsed, not done properly */
	}
}

/* If the dvi file has changed, assume all links have changed also,
   and reset everything! */
void 
htex_reinit()
{
	int i;

	if (htex_parsedpages == NULL) { /* First call to this routine */
		htex_parsedpages = (int *) xmalloc(total_pages*sizeof(int), "HTeXpages");
		HTeX_anchorlist = (HTeX_Anchor **) xmalloc(total_pages*
				sizeof(HTeX_Anchor *), "Anchors");
		nHTeX_anchors = (int *) xmalloc(total_pages* sizeof(int), "Anchors");
		maxHTeX_anchors = (int *) xmalloc(total_pages* sizeof(int), "Anchors");
		for (i=0; i < total_pages; i++) maxHTeX_anchors[i] = 0;
	} else if (htex_total_pages != total_pages) {
		htex_parsedpages = (int *) realloc(htex_parsedpages,
					total_pages*sizeof(int));
	/* Following operates if new has fewer pages than old: */
		for (i=total_pages; i < htex_total_pages; i++) {
		    	if (maxHTeX_anchors[i] > 0)
			    freeHTeXAnchors(HTeX_anchorlist[i], nHTeX_anchors[i]);
		}
		HTeX_anchorlist = (HTeX_Anchor **) realloc(HTeX_anchorlist,
				total_pages*sizeof(HTeX_Anchor *));
		nHTeX_anchors = (int *) realloc(nHTeX_anchors,
					total_pages* sizeof(int));
		maxHTeX_anchors = (int *) realloc(maxHTeX_anchors,
					total_pages* sizeof(int));
	/* Following operates if new has more pages than old: */
		for (i= htex_total_pages; i < total_pages; i++)
				maxHTeX_anchors[i] = 0;
	}
	htex_total_pages = total_pages;
	for (i=0; i < total_pages; i++) {
	    htex_parsedpages[i] = 0;
	    if (maxHTeX_anchors[i] > 0) { /* Get rid of the old anchor lists: */
		   freeHTeXAnchors(HTeX_anchorlist[i], nHTeX_anchors[i]);
		   free(HTeX_anchorlist[i]);
	    }
	    HTeX_anchorlist[i] = NULL;
	    nHTeX_anchors[i] = 0;
	    maxHTeX_anchors[i] = 0;
	}
}

/* Following parses the stuff after the '<' in the html tag */
/* Only understands name and href in anchor */
/*     html: <A HREF="..." NAME="..."> */
/*     html: <A NAME="..." HREF="...> */
void 
htex_parseanchor(cp, anchor)
char *cp;
HTeX_Anchor *anchor;
{
	char *ref, *str;

	anchor->type = 0;
	anchor->href = NULL;
	anchor->name = NULL;
	while (isspace(*cp)) cp++;
	while ((*cp) && (*cp != '>')) {
		cp = refscan(cp, &ref, &str);
		if (cp == NULL) break;
		if (strcasecmp(ref, "href") == 0) {
			anchor->type |= HTeX_A_HREF;
			MyStrAllocCopy(&(anchor->href), str);
		} else if (strcasecmp(ref, "name") == 0) {
			anchor->type |= HTeX_A_NAME;
			MyStrAllocCopy(&(anchor->name), str);
		}
	}
}

char *MyStrAllocCopy(dest, src)
char **dest, *src;
{
	if (*dest) free(*dest);
	if (! src)
		*dest = NULL;
	else {
		*dest = (char *) xmalloc(strlen(src) + 1, "MyStrAllocCopy");
		strcpy(*dest, src);
	}
	return *dest;
}

/* Parses cp containing 'ref="string"more', returning pointer to "more" */
char *refscan(name, ref, str)
char *name, **ref, **str;
{
	char *cp;

	*str = name;
	for (cp=name; *cp; cp++) {
		if (*cp == '=') {
			*cp = 0;
			*ref = name;
			*str = cp+1;
			break;
		}
	}
	cp = *str;
	if (cp != name) {
	    while (isspace(*cp)) cp++;
	    if (*cp == '"') { /* Yes, this really is a string being set */
		*str = cp+1;
		while ((cp = strchr(cp+1, '"')) != NULL) {
			if (cp[-1] != '\\') break; /* Check if quote escaped */
		}
		if (cp != NULL) {
			*cp = 0;
			cp++;
		}
	    } else {
		cp = NULL;
	    }
	} else {
		cp = NULL;
	}
	return cp;
}

/* What happens when mouse moves on screen: */
void 
htex_displayanchor(page, x, y)
int page, x, y; /* x,y coordinates of mouse position */
{
	char astr[256];
	char namestr[256];
	char hrefstr[256];
	HTeX_Anchor *HTeXAp;
	long dvix, dviy;
	int i;

/* Don't display until we've finished with the page: */
	if (htex_parsedpages == NULL) return;
/* Locate current page if we're scrolling them: */
	current_page = page;
	if (htex_parsedpages[current_page] != 1) return;
	dvix = x *(htex_shrinkfactor << 16);
	dviy = y *(htex_shrinkfactor << 16);
	/* Find anchor that fits current position: */
	HTeXAp = HTeX_anchorlist[current_page] + nHTeX_anchors[current_page] - 1;
	for (i=nHTeX_anchors[current_page] - 1; i >= 0; i--, HTeXAp--) {
		if (HTeXAp->llx > dvix) continue;
		if (HTeXAp->lly > dviy) continue;
		if (HTeXAp->urx < dvix) continue;
		if (HTeXAp->ury < dviy) continue;
		if (debug & DBG_ANCHOR) {
		    Printf("In anchor #%d\n", i);
		}
		if (HTeXAp->type & HTeX_A_NAME) {
			sprintf(namestr, "name = %s ", HTeXAp->name);
		} else {
			*namestr= 0;
		}
		if (HTeXAp->type & HTeX_A_HREF) {
			sprintf(hrefstr, "href = %s ", HTeXAp->href);
		} else {
			*hrefstr = 0;
		}
		sprintf(astr, "anchor #%d: %s%s", i, namestr, hrefstr);
		paint_anchor(astr);
		break;
	}
	if (i == -1) paint_anchor(NULL);
}

/* What happens when mouse is clicked: */
int
htex_handleref(page, x, y)
    int page, x, y; /* current mouse location when ref clicked */
{
	HTeX_Anchor *HTeXAp;
	long dvix, dviy;
	int i, afound;

/* Check that we've finished at least one page first! */
	if (htex_parsedpages == NULL) return 0;
/* Locate current page if we're scrolling them: */
	current_page = page;
	if (htex_parsedpages[current_page] != 1) return 0;
	dvix = x *(htex_shrinkfactor << 16);
	dviy = y *(htex_shrinkfactor << 16);
	/* Find anchor that fits current position: */
	HTeXAp = HTeX_anchorlist[current_page] + nHTeX_anchors[current_page] - 1;
	afound = -1;
	for (i=nHTeX_anchors[current_page]-1; i >= 0; i--, HTeXAp--) {
		if ((HTeXAp->type&HTeX_A_HREF) == 0) continue; /* Only ref on hrefs */
		if (HTeXAp->llx > dvix) continue;
		if (HTeXAp->lly > dviy) continue;
		if (HTeXAp->urx < dvix) continue;
		if (HTeXAp->ury < dviy) continue;
		afound = i; /* Get the last of them in case of nesting */
		break;
	}
	if (afound == -1) return 0; /* There was no href at this location */
/* Then just do it: */
	htex_dohref(HTeXAp->href);
	return 1;
}

void
htex_dohref(href)
   char *href;
{
	int i;

/* Update the list of where we used to be: */
	if (HTeX_visited == NULL) {
		maxHTeX_visited = HTeX_AnchorSTEP;
		HTeX_visited = (Anchors *) xmalloc(maxHTeX_visited*sizeof(Anchors), "Visited");
		for (i=0; i < maxHTeX_visited; i++) {
			HTeX_visited[i].refname = NULL;
			HTeX_visited[i].name = NULL;
			HTeX_visited[i].href = NULL;
		}
	} else if (nHTeX_visited >= maxHTeX_visited - 1) {
		maxHTeX_visited += HTeX_AnchorSTEP;
		HTeX_visited = (Anchors *) realloc(HTeX_visited,
			maxHTeX_visited*sizeof(Anchors));
		for (i=nHTeX_visited; i < maxHTeX_visited; i++) {
			HTeX_visited[i].refname = NULL;
			HTeX_visited[i].name = NULL;
			HTeX_visited[i].href = NULL;
		}
	}
	MyStrAllocCopy(&(HTeX_visited[nHTeX_visited].refname), dvi_name);
	HTeX_visited[nHTeX_visited].pageno = current_page;
#ifdef HTEX
	HTeX_visited[nHTeX_visited].x = mane.base_x;
	HTeX_visited[nHTeX_visited].y = mane.base_y;
#else
	HTeX_visited[nHTeX_visited].x = mane.base_ax;
	HTeX_visited[nHTeX_visited].y = mane.base_ay;
#endif
	HTeX_visited[nHTeX_visited].type = HTeX_A_HREF;
	HTeX_visited[nHTeX_visited].which = 0;
	MyStrAllocCopy(&(HTeX_visited[nHTeX_visited].href), href);
	nHTeX_visited++;
	if (htex_is_url(href)) htex_do_url(href);
	else htex_do_loc(href);
	/* Need to handle properly when ref doesn't exist! */
}

/* Draw boxes around the anchor positions */
void htex_drawboxes()
{
	HTeX_Anchor *HTeXAp;
	int i;
	int x, y, w, h;

	if (!underline_link) return;
	HTeXAp = HTeX_anchorlist[current_page];
	for (i=0; i < nHTeX_anchors[current_page]; i++, HTeXAp++) {
		if ((HTeXAp->type&HTeX_A_HREF) == 0) continue; /* Only box hrefs */
		x = pixel_conv(HTeXAp->llx)-1;
		y = pixel_conv(HTeXAp->lly)-1;
		w = pixel_conv(HTeXAp->urx) - x+2;
		h = pixel_conv(HTeXAp->ury) - y+2;
/* The last arg of put_rule is whether or not to
   use the "highlight" graphics context. */
#ifdef HTEX
		highlight = True;
		put_rule(x+1, y+h, w, 1);
		highlight = False;
#else
/*		put_rule(x, y, w, 1, True);
		put_rule(x+w, y, 1, h, True); */
		put_rule(x+1, y+h, w, 1, True);
/*		put_rule(x, y+1, 1, h, True); */
#endif
	}
}

static	Arg	arg_wh[] = {
	{XtNwidth,	(XtArgVal) &window_w},
	{XtNheight,	(XtArgVal) &window_h},
};

static void
invokedviviewer(filename, aname, height)
    char *filename;
    char *aname;
    int height;
{
	char buf[1024];

	if (filename == NULL) return;
#if 0
	/* Ignore the given height -- some calls use unreasonable values. */
	if (height < 50) height = 50;
	height += 50;
#endif

	XtGetValues(draw_widget, arg_wh, XtNumber(arg_wh));

	if (aname == NULL) {
	    sprintf(buf, "%s %s -geometry %dx%d &", program_invocation_name, 
                        filename,
			window_w, window_h); /* Same width and height */
	} else {
	    sprintf(buf, "%s %s#%s -geometry %dx%d &", program_invocation_name,
	                filename, aname,
			window_w, window_h); /* Same width and height */
	}
	if (debug & DBG_HYPER) {
	  fprintf(stderr, "Executing: %s\n", buf);
	}
	system(buf);
}

/* It's a local reference - find the anchor and go to it */
void 
htex_do_loc(href)
char *href;
{
	int ipage, ia, reffound;
	HTeX_Anchor *HTeXAp, **HTeXApp;
	char astr[256];
	char *cp;
	
	if (href == NULL) return; /* shouldn't happen? */
	if (debug & DBG_HYPER) {
	    fprintf(stderr,"htex_do_url(%s)\n", href);
	}
	cp = href;
	while (*cp == '#') cp++;
	HTeXApp = HTeX_anchorlist;
	reffound = 0;
	/* Should hash based on "name" value? - to speed this up! */
	for (ipage = 0; ipage < total_pages; ipage++, HTeXApp++) {
	    	if (htex_parsedpages[ipage] == 0) continue;
		HTeXAp = *HTeXApp;
		for (ia=0; ia < nHTeX_anchors[ipage]; ia++, HTeXAp++) {
			if ((HTeXAp->type&HTeX_A_NAME) == 0) continue;
			if (!strcmp(HTeXAp->name, cp)) {
				reffound = 1;
				break;
			}
		}
		if (reffound) break;
	}
	if (reffound == 0) { /* Need to parse remaining pages */
		if (debug & DBG_ANCHOR) {
		    Printf("Searching for remaining anchors\n");
		}
		htex_parsepages();
		/* And try again: */
		HTeXApp = HTeX_anchorlist;
		for (ipage = 0; ipage < total_pages; ipage++, HTeXApp++) {
			if (htex_parsedpages[ipage] < 2) continue;
			HTeXAp = *HTeXApp;
			for (ia=0; ia < nHTeX_anchors[ipage]; ia++, HTeXAp++) {
				if ((HTeXAp->type&HTeX_A_NAME) == 0) continue;
				if (!strcmp(HTeXAp->name, cp)) {
					reffound = 1;
					break;
				}
			}
			if (reffound) break;
		}
	}
	if (reffound) {
	    if (HTeXnext_extern == 1) {
		invokedviviewer(dvi_name, cp, 
				pixel_conv(HTeXAp->ury) - pixel_conv(HTeXAp->lly));
	    } else {
		htex_to_anchor(ipage, ia); /* move to anchor */
	    }
	} else {
		if ((nHTeX_visited == 0) ||
		   (!strcmp(HTeX_visited[nHTeX_visited-1].refname, dvi_name))) {
		/* Really was from same file - just print error message */
			sprintf(astr, "Error: reference \"%s\" not found\n", cp);
			paint_anchor(astr);
		} else {
			/* Go to page 1 and print error message */
		sprintf(astr, "Error: reference \"%s\" in file %s not found\n",
				cp, dvi_name);
			htex_to_page(0); /* Go to first page! */
			paint_anchor(astr);
		}
	}
}

void
htex_can_it()
{
	canit = True;
	XFlush(DISP);
}

void
htex_to_page(pageno)
    int pageno;
{
	/* taken from keystroke subroutine: */
	current_page = pageno;
	hush_spec_now = hush_spec;
	htex_can_it();
}

void
htex_to_anchor(pageno, n)
    int pageno, n;
{
	int xp, yp;
	HTeX_Anchor *HTeXAp = HTeX_anchorlist[pageno] + n;

	if ((n < 0) || (n >= nHTeX_anchors[pageno])) return; /* Error message for this? */
	if (pageno != current_page) {
		if (htex_parsedpages[pageno] != 1) waiting_for_anchor = n;
	/* taken from keystroke subroutine: */
		current_page = pageno;
		hush_spec_now = hush_spec;
		htex_can_it();
	}
	xp = (HTeXAp->llx + HTeXAp->urx)/(2*htex_shrinkfactor << 16);
	yp = (HTeXAp->lly + HTeXAp->ury)/(2*htex_shrinkfactor << 16);
	if (debug & DBG_ANCHOR) {
		Printf("Moving to pixel %d %d\n", xp, yp);
	}
#ifndef HTEX
	if (htex_parsedpages[pageno] > 0) centerpage(pageno, xp, yp);
#endif
}

/* Following goes back to previous anchor point */
void 
htex_goback()
{
	int i;

	if (nHTeX_visited <= 0) return; /* There's nowhere to go! */
	if (debug & DBG_ANCHOR) {
	    Printf("Currently %d anchors in sequence:\n", nHTeX_visited);
	    for (i=0; i < nHTeX_visited; i++) {
		Printf("%d file %s, href=%s\n", i,
			HTeX_visited[i].refname, HTeX_visited[i].href);
	    }
	}
	nHTeX_visited--;
	if (strcmp(HTeX_visited[nHTeX_visited].refname, dvi_name) != 0) {
		/* Need to read in old file again! */
		MyStrAllocCopy(&dvi_name, HTeX_visited[nHTeX_visited].refname);
		i = URL_aware;
		URL_aware = FALSE;
		open_dvi_file();
		URL_aware = i;
		if (dvi_file != NULL) {
		    retitle(dvi_name);
		    extractbase(dvi_name);
		} else {
		    perror(dvi_name);
		    cleanup_and_exit(1);
		}
		htex_can_it();
		htex_reinit();
	}
	htex_loc_on_page(HTeX_visited + nHTeX_visited);
	/* taken from keystroke subroutine: */
	hush_spec_now = hush_spec;
	htex_can_it();
}

/* Is this a url we recognize? */
int
htex_is_url(href)
const char *href;
{
    /* Why reinvent the wheel?  Use libwww routines! */
    return (HTURL_isAbsolute(href)==YES ? 1 : 0);
}


static	Arg	temp_args4[] = {
	{XtNtitle,	(XtArgVal) 0},
	{XtNinput,	(XtArgVal) True},
};

static void
retitle(str) /* Change the title to "str" */
char *str;
{
	temp_args4[0].value = (XtArgVal) str;
	XtSetValues(top_level, temp_args4, XtNumber(temp_args4));
}

/* Can handle href's of form file:?.dvi#name */
/* Actually, supposedly we can handle anything now... */
void
htex_do_url(href)
char *href;
{
	if (href == NULL) return; /* shouldn't happen? */
	if (debug & DBG_HYPER) {
	    fprintf(stderr,"htex_do_url(%s)\n", href);
	}

	/* Have dvi_name parsed using libwww routines to make sure 
	   it doesn't contain invalid characters.  Actually, we may
	   assume that HTURL_isAbsolute(href) when this is called, 
	   so parsing it relative to URLbase shouldn't matter. */

	if (URLbase != NULL) { 
	    MyStrAllocCopy(&dvi_name, HTParse(href, URLbase, PARSE_ALL));
	} else {
	    MyStrAllocCopy(&dvi_name, HTParse(href, "", PARSE_ALL));
	}

	URL_aware = TRUE;
	detach_anchor();
	if (open_www_file() == 0) {
	    /* HTTP request was handled externally by invoking some viewer */
	    URL_aware = FALSE; 
	    htex_can_it();
	    htex_goback(); /* Go back to where we were! */
	    return;
	} else {
	    /* HTTP request was handled internally by calling open_dvi_file() */
	    URL_aware = FALSE;
	    /* retitle(dvi_name); */
	    /* extractbase(dvi_name); */
	    htex_reinit();
	    if (anchor_name != NULL) {
		/* This may be unnecessary, since it's already done in
		   check_for_anchor() which is called from draw_page(). */
	        htex_do_loc(anchor_name);
		free(anchor_name);
		anchor_name = NULL;
	    } else {
		/* This seems to be necessary if there's no anchor_name. */
		htex_to_page(0);
	    }
	    return;
	}
}

/* Find the anchor pointed to by ap on the given page */

void
htex_loc_on_page(ap)
Anchors *ap;
{
	if (htex_parsedpages[ap->pageno] == 0) {
		htex_parse_page(ap->pageno); /* Parse the needed page! */
	}
	htex_to_anchor(ap->pageno, ap->which); /* move to anchor i */
}

void
freeHTeXAnchors(HTeXAp, nHTeX)
HTeX_Anchor *HTeXAp;
int nHTeX;
{
	int i;

	for (i=0; i < nHTeX; i++) {
		if (HTeXAp[i].type&HTeX_A_NAME)
			free(HTeXAp[i].name);
		if (HTeXAp[i].type&HTeX_A_HREF)
			free(HTeXAp[i].href);
	}
}

/* Following shouldn't be necessary... */
/* Add the string cp to the current search string */
#define LINE 1024
char anchor_search_string[LINE];

void
add_search(cp, n)
char *cp;
int n;
{
	int anchor_search_len = 0;
	
	while (n>0) {
	    switch(*cp) {
		case '\v':
		case '\f':
		case '\r':
		case '\n': /* Finish string and search on it */
			anchor_search_string[anchor_search_len] = '\0';
			if (anchor_search_len > 0) {
				htex_dohref(anchor_search_string);
			/* Need to handle properly when ref doesn't exist! */
			}
			return;
			break;
		case '\b':
		case '\177':	/* Del */
			if (anchor_search_len > 0) anchor_search_len--; 
			break;
		case '':
			anchor_search_len = 0;
			break;
		default:
			if (*cp > 10) {
				anchor_search_string[anchor_search_len++] = *cp;
			}
			break;
		}
		cp++;
		n--;
	}
	anchor_search_string[anchor_search_len] = '\0';
	if (debug & DBG_ANCHOR) {
		Printf("Search string: %s\n", anchor_search_string);
	}
}

void
htex_base(beginend, cp, pageno)
int beginend, pageno;
char *cp;
{
	char *ref, *str;
	if (beginend == END) return;

	if (!strncasecmp(cp, "base", 4)) {
		cp += 4;
		cp = refscan(cp, &ref, &str);
		if (cp == NULL) return;
		while (isspace(*ref)) ref++;
		while (isspace(*str)) str++;
		if (strcasecmp(ref, "href") == 0) {
			cp = str + strlen(str) - 1; /* Fix end of anchor */
			while (isspace(*cp)) cp--;
			if (*cp == '>') cp --;
			while (isspace(*cp)) cp--;
			cp++;
			*cp = '\0'; 
			MyStrAllocCopy(&URLbase, str); /* Change base */
			if (debug & DBG_HYPER) {
				Printf("Changing base name to: %s\n", URLbase);
			}
		}
	}
}

void
htex_img(beginend, cp, pageno)
int beginend, pageno;
char *cp;
{
	char *ref, *str;
	char fullpathname[1024];

	if (beginend == END) return;
	if (pageno != current_page) return; /* Only do when on page */
	if (htex_parsedpages[pageno] == 1) return; /* And first time through */
	if (!strncasecmp(cp, "img", 3)) {
		cp += 3;
		cp = refscan(cp, &ref, &str);
		if (cp == NULL) return;
		while (isspace(*ref)) ref++;
		while (isspace(*str)) str++;
		if (strcasecmp(ref, "src") == 0) {
			cp = str + strlen(str) - 1; /* Fix end of anchor */
			while (isspace(*cp)) cp--;
			if (*cp == '>') cp --;
			while (isspace(*cp)) cp--;
			cp++;
			*cp = '\0'; 
			strcpy(fullpathname, str);
			make_absolute(fullpathname, URLbase, 1024);
			if (invokeviewer(fullpathname) != 1) {
	fprintf(stderr, "Don't know how to deal with <img src=%s>\n", fullpathname);
			}
		}
	}
}

#ifdef HTEX
#undef exit /* just in case */
void
cleanup_and_exit(arg)
int arg;
{
	/* Delete all the temp files we created */
	for (;nURLs>0; nURLs--) {
		unlink(filelist[nURLs-1].file);
	}
	if (temporary_dir) rmdir(temporary_dir);
	HTCache_flushAll();
	HTProfile_delete();
	exit(arg);
}

/* Extract the URL base name from initial file name */
static void
extractbase(file)
char *file;
{
	char *cp;
	static char *cwd = NULL;
	int n;

	if (URLbase != NULL) {
		free(URLbase);
		URLbase = NULL;
	}
	if (strrchr(file, '/') != NULL) { /* If no /'s then leave dir NULL */
		n = strlen(file);
		if (htex_is_url(file)) { /* It already is a URL */
			URLbase = xmalloc((unsigned) (n+1), "URL base");
		        Sprintf(URLbase, "%s", file);
		} else { /* Turn it into one: */
			cwd = GETCWD(cwd, 1024);
			URLbase = xmalloc((unsigned) (n+6 + strlen(cwd)), "URL base");
		        Sprintf(URLbase, "file:%s/%s", cwd, file);
		}
		cp = strrchr(URLbase, '/');
		if (cp == NULL) {
			fprintf(stderr, "This should not happen!\n");  
			free(URLbase);
			URLbase = NULL;
			return;
		}
/*		cp[1] = '\0'; */ /* Leave it alone */
	}
}

void	
detach_anchor()
{
	char *cp;

	cp = strchr(dvi_name, '#'); /* Handle optional # in name */
	if (cp != NULL) {
		*cp = '\0'; /* Terminate filename string */
		cp++;
		while (*cp == '#') cp++;
		MyStrAllocCopy(&anchor_name, cp);
	}
}

int
open_www_file()
{
	char *url = NULL;
	int n;

	if (debug & DBG_HYPER) {
	    fprintf(stderr, "open_www_file(%s)\n", dvi_name);
	}

	/* open_www_file() may be called from main() at program startup,
	   in which case we have to turn dvi_name into an absolute URL. */

	if (URLbase && !HTURL_isAbsolute(dvi_name)) {
	    MyStrAllocCopy(&url, dvi_name);
	    MyStrAllocCopy(&dvi_name, HTParse(url, "", PARSE_ALL));
	    free(url);
	}
	/* Otherwise, we're called with an absoulte URL in dvi_name,
	   either from main() or form htex_do_url(). In both cases
	   detach_anchor() has been called before open_www_file(). */

	n = strlen(dvi_name);
	if (n < sizeof(".dvi")
		|| strcmp(dvi_name + n - sizeof(".dvi") + 1, ".dvi") != 0) {
	    /* Try other standard extensions: */
	    if (!invokeviewer(dvi_name)) {
		/* Set the WWW browser on it: */
		if (browser) {
		    char *command = (char *) xmalloc(strlen(dvi_name)
			+ strlen(browser) + 10, "Command");
		    sprintf(command, "%s '%s' &", browser, dvi_name);
		    if (debug & DBG_HYPER) {
			fprintf(stderr, "Executing: %s\n", command);
		    }
		    system(command);
		    free(command);
		    return 0;
		} else {
#if 0 
                    /* Don't call paint_anchor before window was opened! */
		    paint_anchor("Error: WWWBROWSER not set for URL access\n");
#endif
		    fprintf(stderr, "You need to set the environment variable WWWBROWSER\n");
		    fprintf(stderr, "or specify -browser on the command line to access\n");
		    fprintf(stderr, "networked URL's which can't be handled otherwise.\n");
		}
	    }
	} else {
	    if (HTeXnext_extern == 1) {
		if (anchor_name != NULL) {
		    invokedviviewer(dvi_name, anchor_name, 0);
		    free(anchor_name); 
		    anchor_name = NULL;
		} else {
		    invokedviviewer(dvi_name, NULL, 0);
		}
		return 0;
	    } else {
		open_dvi_file();
		if (dvi_file != NULL) {
		    retitle(dvi_name);
		    extractbase(dvi_name);
		    return 1;
		} else {
		    perror(dvi_name);
		    cleanup_and_exit(1);
		}
	    }
	}
	return 0;
}

/* from xhdvi/events.c */

/* APS Pointer locator: */

int
pointerlocate(xpos, ypos) /* Return screen positions */
int *xpos, *ypos;
{
	Window root, child;
	int root_x, root_y;
	unsigned int keys_buttons;

	return XQueryPointer(DISP, mane.win, &root, &child,
			&root_x, &root_y, xpos, ypos, &keys_buttons);
}

#endif /* HTEX */
#endif /* XHDVI || HTEX */
