static char rcsid[] = "$Header: /usr/jjc/dvitops/RCS/dvi.c,v 1.6 90/08/14 13:09:18 jjc Rel $";
 
#include "dvitops.h"
 
 
static struct stack {
	integer h, v, w, x, y, z;
} *stack;
 
 
 
struct addr_count0 { 
	integer addr; 
	integer count0; 
}; 
struct postamble_info {
	integer num;
	integer den;
	int mag;
	integer last_bop; /* address of last bop */
	int npages;
	int stack_size;
};
 
 
#ifdef PROTO
static void first_pass(int, long *, int, struct addr_count0 *);
static void read_page(struct page_info *page);
static int read_opcode(void);
static void read_postamble(struct postamble_info *postamble);
static void bad_dvi_file(void);
static int count_compare(char *p1, char *p2);
#else
static void first_pass();
static void read_page();
static int read_opcode();
static void read_postamble();
static void bad_dvi_file();
static int count_compare();
#endif
#if 0
typedef enum { SET_CHAR_0 = 0, SET1 = 128, SET_RULE = 132, PUT1 = 133,
PUT_RULE = 137, BOP = 139, EOP = 140, PUSH = 141, POP = 142, RIGHT1 = 143,
W0 = 147, W1 = 148, X0 = 152, X1 = 153, DOWN1 = 157, Y0 = 161, Y1 = 162,
Z0 = 166, Z1 = 167, FNT_NUM_0 = 171, FNT1 = 235, NOP = 138, XXX1 = 239,
FNT_DEF1 = 243, PRE = 247, POST = 248, POST_POST = 249 } dvi_opcode;
#else
#define SET_CHAR_0  0
#define SET1  128
#define SET_RULE  132
#define PUT1  133
#define PUT_RULE  137
#define BOP  139
#define EOP  140
#define PUSH  141
#define POP  142
#define RIGHT1  143
#define W0  147
#define W1  148
#define X0  152
#define X1  153
#define DOWN1  157
#define Y0  161
#define Y1  162
#define Z0  166
#define Z1  167
#define FNT_NUM_0  171
#define FNT1  235
#define NOP  138
#define XXX1  239
#define FNT_DEF1  243
#define PRE  247
#define POST  248
#define POST_POST  249 
#endif
 
static FILE *dvifp;
 
#define SPECIAL_BUF_SIZE 2048
static char special_buf[SPECIAL_BUF_SIZE];
 
 
static void first_pass(totalpages, start_page, maxpages, pagevec)
int totalpages, maxpages;
long start_page[10];
struct addr_count0 *pagevec;
{
	integer count[10];
	int pageno;
	int started = FALSE;
	int selected = FALSE;
	int pages_selected = 0;
	fseek(dvifp, 14L, 0);
	if (fseek(dvifp, (long)uread1(dvifp), 1) != 0)
		bad_dvi_file();
	for (pageno = 0; pageno < totalpages; pageno++) {
		unsigned char opcode;
		int i;
		int donepage = FALSE;
		int f = -1;
		pagevec[pageno].addr = ftell(dvifp);
		while (!donepage) {
			unsigned char c;
			integer n = 0;
			/* deal with the most common case in its own loop */
			while ((opcode = uread1(dvifp)) <= 127) {
				if (feof(dvifp) || ferror(dvifp))
					bad_dvi_file();
				if (selected)
				    used_char(f, opcode);
			}
			/* we use the fact that SET_CHAR_0 is 0 */
 
			if (FNT_NUM_0 <= opcode && opcode <= FNT_NUM_0 + 63) {
				n  = opcode - FNT_NUM_0;
				opcode = FNT1;
			}
			else
				switch (opcode) {
				case SET1 + 3 : case PUT1 + 3 : case XXX1 + 3:
				case FNT_DEF1 + 3: case FNT1 + 3:
					n = sread4(dvifp); break;
				case SET1 + 2 : case PUT1 + 2 : case XXX1 + 2:
				case FNT_DEF1 + 2: case FNT1 + 2:
					n = uread3(dvifp); break;
				case SET1 + 1 : case PUT1 + 1 : case XXX1 + 1:
				case FNT_DEF1 + 1: case FNT1 + 1:
					n = uread2(dvifp); break;
				case SET1 : case PUT1 : case XXX1:
				case FNT_DEF1: case FNT1:
					n = uread1(dvifp); break;
				case RIGHT1 + 3: case W1 + 3: case X1 + 3:
				case DOWN1 + 3: case Y1 + 3: case Z1 + 3:
					(void)uread1(dvifp);
				/* falls through */
				case RIGHT1 + 2: case W1 + 2: case X1 + 2:
				case DOWN1 + 2: case Y1 + 2: case Z1 + 2:
					(void)uread1(dvifp);
					/* falls through */
				case RIGHT1 + 1: case W1 + 1: case X1 + 1:
				case DOWN1 + 1: case Y1 + 1: case Z1 + 1:
					(void)uread1(dvifp);
					/* falls through */
				case RIGHT1: case W1: case X1:
				case DOWN1: case Y1: case Z1:
					(void)uread1(dvifp);
					break;
				case SET_RULE: case PUT_RULE :
					sread4(dvifp); sread4(dvifp);
					break;
				}
				switch (opcode) {
				case SET1 + 3 : case SET1 + 2 : case SET1 + 1 : case SET1 :
				case PUT1 + 3 : case PUT1 + 2 : case PUT1 + 1 : case PUT1 :
					if (!selected)
						break;
					c = (unsigned char) n;
					if (n >= MAXCHARS || n < 0)
						message(ERROR, 
							"character code %ld too large: using %d instead", 
																(long)n, c);
					used_char(f, c);
					break;
				case BOP :
					for (i = 0; i < 10; ++i)
						count[i] = sread4(dvifp);
					pagevec[pageno].count0 = count[0];
					(void) sread4(dvifp);
					if (!started) {
						int j;
						started = TRUE;
						for (j = 0; j < 10; j++)
							if (start_page[j] != WILD
									&& start_page[j] != count[j])
								started = FALSE;
					}
					if (started && ++pages_selected <= maxpages)
						selected = TRUE;
					else {
						selected = FALSE;
						pagevec[pageno].addr = -1;
					}
					break;
				case EOP :
					if (selected)
						store_page_usage();
					donepage = TRUE;
					break;
				case FNT1 : case FNT1 + 1 : case FNT1 + 2 : case FNT1 + 3 :
					if (!selected)
						break;
					if (n < 0 || n >= MAXFONTS)
						/* this should have been spotted in the postamble */
						cant_happen();
					f = (int) n;
					break;
				case XXX1 + 3 : case XXX1 + 2 : case XXX1 + 1 : case XXX1:
					if (n >= SPECIAL_BUF_SIZE) {
						message(ERROR, 
							"special string too long: ignoring it");
						fseek(dvifp, (long)n, 1);
						break;
					}
					fread(special_buf, 1, (size_t)n, dvifp);
					special_buf[n] = '\0';
					STRXCHAR(special_buf)
					if (selected)
						special(PASS1, special_buf,(integer)0,(integer)0);
					break;
				case FNT_DEF1 : case FNT_DEF1 + 1 :
				case FNT_DEF1 + 2 : case FNT_DEF1 + 3 :
					fseek(dvifp, 12L, 1);			
					n = uread1(dvifp);
					n += uread1(dvifp);
					fseek(dvifp, (long)n, 1);
					break;
				case POST :
					bad_dvi_file();
				default :
					break;
				}
		}
	}
}
 
static int read_opcode()
{
	for (;;) {
		int c = uread1(dvifp);
		if (c == EOF)
			cant_happen();
		if (XXX1 <= c && c <= XXX1 + 3) {
			integer k;
			switch (c) {
			case XXX1 :
				k = uread1(dvifp); break;
			case XXX1 + 1:
				k = uread2(dvifp); break;
			case XXX1 + 2:
				k = uread3(dvifp); break;
			case XXX1 + 3:
				k = sread4(dvifp); break;
			}
			fseek(dvifp, (long)k, 1);
		}
		else if (c != NOP)
			return c;
	}
}
 
 
static void read_postamble(postamble)
struct postamble_info *postamble;
{
	int c;
	integer n;
	long offset;
	/* Avoid using a negative offset.
	   Some implementations can't handle this. */
	if (fseek(dvifp, 0L, 2) != 0)
		message(FATAL_ERROR, "can't perform seek on dvi file");
	offset = ftell(dvifp);
	do {
		offset--;
		fseek(dvifp, offset, 0);
		c = uread1(dvifp);
	} while (c == 223);
	if (c != ID_BYTE)
		  message(FATAL_ERROR, "this is the wrong type of dvi file");
	offset -= 5;
	fseek(dvifp, offset, 0);
	if (uread1(dvifp) != POST_POST) 
		cant_happen();
	offset = sread4(dvifp);
	fseek(dvifp, offset, 0);
	if (uread1(dvifp) != POST)
		cant_happen();
	postamble->last_bop = sread4(dvifp);
	postamble->num = sread4(dvifp);
	postamble->den = sread4(dvifp);
	n = sread4(dvifp);
	if (postamble->mag == 0) {
		postamble->mag = (int)n;
		if (n < 0 || n > INT_MAX) {
			message(ERROR, "bad magnification: using 1000 instead");
			n = 1000;
		}
	}
	(void) sread4(dvifp);
	(void) sread4(dvifp);
	postamble->stack_size = sread2(dvifp);
	postamble->npages = sread2(dvifp);
	while ((c = read_opcode()) >= FNT_DEF1 && c <= FNT_DEF1 + 3) {
		integer k;
		integer cs, s, d;
		int a, l;
		char area[257];
		char name[257];
		switch (c + 1 - FNT_DEF1) {
		case 1:
			k = uread1(dvifp); break;
		case 2:
			k = uread2(dvifp); break;
		case 3:
			k = uread3(dvifp); break;
		case 4:
			k = sread4(dvifp); break;
		}
		cs = sread4(dvifp);
		s = sread4(dvifp);
		d = sread4(dvifp);
		a = uread1(dvifp);
		l = uread1(dvifp);
		if (a)
			fread(area, 1, a, dvifp);
		area[a] = '\0';
		fread(name, 1, l, dvifp);
		name[l] = '\0';
		STRXCHAR(name)
		STRXCHAR(area)
		f_def(k, name, area, cs, s, d, postamble->mag);
	}
	if (c != POST_POST)
		cant_happen();
}
 
 
static int count_compare(p1, p2)
char *p1, *p2;
{
	integer n1 = ((struct addr_count0 *)p1)->count0;
	integer n2 = ((struct addr_count0 *)p2)->count0;
	if (n1 == n2)
		return (int)(((struct addr_count0 *)p1)->addr -
				((struct addr_count0 *)p2)->addr);
	else if (n1 <= 0 || n2 <= 0) {
		if (n2 > 0)
			return -1;
		else if (n1 > 0)
			return 1;
		else
			return (int)(n2 - n1);
	}
	else
		return (int)(n1 - n2);
}
			
 
 
void read_dvi_file(fp, ofp, option)
FILE *fp, *ofp;
struct option_info *option;
{
	struct postamble_info postamble;
	struct page_info page;
	FILE *psfp;
	int i;
	int actual_npages;
	struct addr_count0 *pagevec;
	dvifp = fp;
	postamble.mag = option->mag;
	read_postamble(&postamble);
	if ((stack = 
		(struct stack *)malloc(sizeof(struct stack)*postamble.stack_size))
							== NULL)
		out_of_memory();
	if ((pagevec = (struct addr_count0 *)malloc((1 + postamble.npages)
					*sizeof(struct addr_count0))) == NULL)
		out_of_memory();
	first_pass(postamble.npages, option->start_page, option->maxpages, 
		pagevec);
	psfp = ofp;
#ifdef HAVE_SETVBUF
	setvbuf(psfp, (char *)NULL, _IOFBF, 16384); /* ignore any error */
#endif
	for (i = 0, actual_npages = 0; i < postamble.npages; i++)
		if (pagevec[i].addr != -1L)
			actual_npages++;
	prologue(psfp, actual_npages, option->reverse, option->paper);
	page.num = postamble.num;
	page.den = postamble.den;
	page.hoffset = option->hoffset;
	page.voffset = option->voffset;
	page.copies = option->copies;
	page.paper = option->paper;
	page.max_drift = (int)((254000.0/page.num) * (page.den/(double)dpi) * 2.0);
	if (option->sort)
		qsort((char *)pagevec, postamble.npages, sizeof(struct addr_count0),
								count_compare);
	if (option->reverse) {
		for (i = postamble.npages - 1; i >= 0; i--)
			if (pagevec[i].addr != -1L) {
				page.address = pagevec[i].addr;
				page.mag = postamble.mag;
				read_page(&page);
			}
	}
	else {
		for (i = 0; i < postamble.npages; i++)
			if (pagevec[i].addr != -1L) {
				page.address = pagevec[i].addr;
				page.mag = postamble.mag;
				read_page(&page);
			}
	}
	trailer();
}
 
static void read_page(page)
struct page_info *page;
{
	unsigned char opcode;
	int had_large_hmotion = FALSE;
	int i;
	char buf[512];
	int nbuf = 0; /* number of characters in buf */
	integer endh = 0, rounded_endh = 0;
	integer h, v, w, x, y, z;
	struct stack *sp = stack;
	int f = -1;
	integer fs = 0; /* = f_space(f) */
	h = v = w = x = y = z = 0;
 
	fseek(dvifp, (long)page->address, 0);
	for (;;) {
		integer starth;
		integer startv;
		int startf;
		unsigned char c;
		integer a, b;
		integer wd;
		integer n = (integer)0;
		opcode = uread1(dvifp);
		/* we use the fact that SET_CHAR_0 is 0 */
		if (opcode <= 127) {
			/* we checked for eof and error on pass 1 */
			n = opcode;
			opcode = SET1;
		}
		else if (FNT_NUM_0 <= opcode && opcode <= FNT_NUM_0 + 63) {
			n  = opcode - FNT_NUM_0;
			opcode = FNT1;
		}
		else
			switch (opcode) {
			case SET1 + 3 : case PUT1 + 3 : case XXX1 + 3:
			case RIGHT1 + 3: case W1 + 3: case X1 + 3:
			case DOWN1 + 3: case Y1 + 3: case Z1 + 3:
			case FNT_DEF1 + 3: case FNT1 + 3:
				n = sread4(dvifp); break;
			case SET1 + 2 : case PUT1 + 2 : case XXX1 + 2:
			case FNT_DEF1 + 2: case FNT1 + 2:
				n = uread3(dvifp); break;
			case SET1 + 1 : case PUT1 + 1 : case XXX1 + 1:
			case FNT_DEF1 + 1: case FNT1 + 1:
				n = uread2(dvifp); break;
			case SET1 : case PUT1 : case XXX1:
			case FNT_DEF1: case FNT1:
				n = uread1(dvifp); break;
			case RIGHT1 + 2: case W1 + 2: case X1 + 2:
			case DOWN1 + 2: case Y1 + 2: case Z1 + 2:
				n = sread3(dvifp); break;
			case RIGHT1 + 1: case W1 + 1: case X1 + 1:
			case DOWN1 + 1: case Y1 + 1: case Z1 + 1:
				n = sread2(dvifp); break;
			case RIGHT1: case W1: case X1:
			case DOWN1: case Y1: case Z1:
				n = sread1(dvifp); break;
			}
		switch (opcode) {
		case SET1 + 3 : case SET1 + 2 : case SET1 + 1 : case SET1 :
		case PUT1 + 3 : case PUT1 + 2 : case PUT1 + 1 : case PUT1 :
			/* we checked it wasn't out of range on pass 1 */
			c = (unsigned char)n;
			if (nbuf > 0 &&
				(h != endh || v != startv || f != startf 
				    || had_large_hmotion
					|| nbuf >= sizeof(buf)
					|| endh - rounded_endh > page->max_drift
					|| endh - rounded_endh < -page->max_drift)) {
				set_string(buf, nbuf, startf, starth, startv);
				nbuf = 0;
			}
			if (nbuf == 0) {
				if (endh != rounded_endh && !had_large_hmotion) {
					if (rounded_endh - endh > page->max_drift)
						starth = h + page->max_drift;
					else if (rounded_endh - endh  < -page->max_drift)
						starth = h - page->max_drift;
					else
						starth = h + (rounded_endh - endh);
				}
				else
					starth = h;
				endh = h;
				rounded_endh = starth;
				startv = v;
				startf = f;
				had_large_hmotion = FALSE;
			}
			buf[nbuf++] = c;
			used_char(f, c);
			wd = f_width(f, c);
			endh += wd;
			rounded_endh += f_rounded_width(f, c);
			/* we rely on the fact that SET1 < PUT1 */
			if (opcode < PUT1)
				h += wd;
			break;
		case SET_RULE : case PUT_RULE :
			a = sread4(dvifp);
			b = sread4(dvifp);
			set_rule(a, b, h, v);
			if (opcode == SET_RULE)
				h += b;
			break;
		case NOP :
			break;
		case BOP :
			for (i = 0; i < 10; ++i)
				page->count[i] = sread4(dvifp);
			(void) sread4(dvifp);
			break;
		case EOP :
			if (nbuf)
				set_string(buf, nbuf, startf, starth, startv);
			eop(page);
			return;
		case PUSH :
			sp->h = h; sp->v = v; sp->w = w;
			sp->x = x; sp->y = y; sp->z = z;
			++sp;
			break;
		case POP :
			--sp;
			h = sp->h; v = sp->v; w = sp->w;
			x = sp->x; y = sp->y; z = sp->z;
			break;
		case RIGHT1 : case RIGHT1 + 1 :
		case RIGHT1 + 2 : case RIGHT1 + 3 :
			h += n; 
			if (n >= fs || n <= fs*-4)
				had_large_hmotion = TRUE;
			break;
		case W0 :
			h += w;
			break;
		case W1 : case W1 + 1 : case W1 + 2 : case W1 + 3 :
			h += w = n;
			break;
		case X0 :
			h += x;
			break;
		case X1 : case X1 + 1 : case X1 + 2 : case X1 + 3 :
			h += x = n;
			break;
		case DOWN1 : case DOWN1 + 1 : case DOWN1 + 2 : case DOWN1 + 3:
			v += n;
			break;
		case Y0 :
			v += y;
			break;
		case Y1 : case Y1 + 1 : case Y1 + 2 : case Y1 + 3 :
			v += y = n;
			break;
		case Z0 :
			v += z;
			break;
		case Z1 : case Z1 + 1 : case Z1 + 2 : case Z1 + 3 :
			v += z = n;
			break;
		case FNT1 : case FNT1 + 1 : case FNT1 + 2 : case FNT1 + 3 :
			if (n < 0 || n >= MAXFONTS)
				/* this should have caused a fatal error on pass 1 */
				cant_happen(); 
			f = (int)n;
			fs = f_space(f);
			break;
		case XXX1 + 3 : case XXX1 + 2 : case XXX1 + 1 : case XXX1 :
			if (n >= SPECIAL_BUF_SIZE) {
				/* we printed an error on the first pass */
				fseek(dvifp, (long)n, 1);
				break;
			}
			if (nbuf > 0) { /* flush buf */
				set_string(buf, nbuf, startf, starth, startv);
				nbuf = 0;
			}
			fread(special_buf, 1, (int)n, dvifp);
			special_buf[n] = '\0';
			STRXCHAR(special_buf)
			special(PASS2, special_buf, h, v);
			break;
		case FNT_DEF1 : case FNT_DEF1 + 1 :
		case FNT_DEF1 + 2 : case FNT_DEF1 + 3 :
			fseek(dvifp, 12L, 1);			
			n = uread1(dvifp);
			n += uread1(dvifp);
			fseek(dvifp, (long)n, 1);
			break;
		case POST :
			if (nbuf)
				set_string(buf, nbuf, startf, starth, startv);
			return;
		default :
			bad_dvi_file();
		}
	}
}			
 
static void bad_dvi_file()
{
	message(FATAL_ERROR, "this dvi file is bad -- use dvitype");
}

/*
Local Variables:
tab-width: 4
c-indent-level: 4
c-continued-statement-offset: 4
c-brace-offset: -4
c-argdecl-indent: 0
c-label-offset: -4
End:
*/