DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T d

⟦f8f3379b0⟧ TextFile

    Length: 48924 (0xbf1c)
    Types: TextFile
    Names: »dvipage.c«

Derivation

└─⟦060c9c824⟧ Bits:30007080 DKUUG TeX 2/12/89
    └─⟦this⟧ »./DVIware/crt-viewers/others/dvipage.c« 

TextFile

/*
 * dvipage: DVI Previewer Program for Suns
 *
 * Neil Hunt (hunt@spar.slb.com)
 *
 * This program is based, in part, upon the program dvisun,
 * distributed by the UnixTeX group, extensively modified by
 * Neil Hunt at the Schlumberger Palo Alto Research Laboratories
 * of Schlumberger Technologies, Inc.
 *
 * From the dvisun manual page entry:
 *	Mark Senn wrote the early versions of [dvisun] for the
 *	BBN BitGraph. Stephan Bechtolsheim, Bob Brown, Richard
 *	Furuta, James Schaad and Robert Wells improved it. Norm
 *	Hutchinson ported the program to the Sun. Further bug fixes
 *	by Rafael Bracho at Schlumberger.
 *
 * Copyright (c) 1988 Schlumberger Technologies, Inc 1988.
 * Anyone can use this software in any manner they choose,
 * including modification and redistribution, provided they make
 * no charge for it, and these conditions remain unchanged.
 *
 * This program is distributed as is, with all faults (if any), and
 * without any warranty. No author or distributor accepts responsibility
 * to anyone for the consequences of using it, or for whether it serves any
 * particular purpose at all, or any other reason.
 *
 * $Log:	dvipage.c,v $
 * Revision 1.6  88/12/15  09:08:03  hunt
 * Added iteration to gobble inputs in panning and magnifier.
 * 
 * Revision 1.5  88/11/28  18:39:21  hunt
 * Major rewrite for 4.0 and sparc architecture.
 * Split up into multiple files for easier maintenance.
 * Reads GF files as well as PXL files now.
 * 
 * Revision 1.4  88/11/26  11:10:53  hunt
 * Used varargs with *_prompt() functions for correct behaviour on a sun4.
 * 
 * Revision 1.3  88/08/30  13:04:13  hunt
 * Changed default cmap for darker looking letters.
 * 
 * Revision 1.2  88/08/30  09:26:29  hunt
 * Fixed problem pointed out by pell@rainier.UUCP
 * (pell@rainier.se, enea!rainier!pell@uunet.UU.NET)
 * so that opened files are closed on exec, and do not clutter up
 * space in print spoolers which may be invoked
 * to print the document.
 * 
 * Revision 1.1  88/08/30  09:05:19  hunt
 * Initial revision
 * 
 * HISTORY
 *
 * 12 April 1988 - Neil Hunt
 *	Version 2.0 released for use.
 *
 * 11 April 1988 - Neil Hunt
 *	Applied fixes supplied by Rafael Bracho (Schlumberger Austin)
 *	for operation on Sun-4 workstations.
 *
 * Earlier history unavailable.
 */

#include <stdio.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <varargs.h>
#include <sys/param.h>		/* For MAXPATHLEN */
#include <sys/stat.h>
#include <suntool/sunview.h>
#include <suntool/canvas.h>
#include <suntool/panel.h>
#include <suntool/icon.h>
#include "dvipage.h"
#include "dvi.h"

#define GOBBLE_PAN
#define GOBBLE_MAGNIFY

/*
 * Forward functions.
 * =================
 */

forward int			main();

forward Notify_value		page_paint();
forward Notify_value		page_event();
forward int			page_menu();
forward void			page_magnify();
forward void			page_pan();
forward bool			goto_sheet();
forward bool			goto_page();

forward bool			init_dvi_file();
forward void			close_dvi_file();
forward bool			check_dvi_file();

forward bool			read_postamble();
forward bool			find_postamble_ptr();

forward bool			process_page();

forward void			set_font_num();
forward void			set_char();
forward void			set_rule();
forward void			move_down();
forward void			move_over();

forward char *			a_prog_name;
forward char			a_next();
forward char *			a_arg();
forward double			a_number();
forward int			a_integer();

/*
 * Internal data structures.
 * ========================
 */

struct stack_entry		/* stack entry */
{
	int h, v, w, x, y, z;  /* what's on stack */
};

/*
 * Globals.
 * =======
 */

int hconv, vconv;		/* converts DVI units to pixels */
int num;			/* numerator specified in preamble */
int den;			/* denominator specified in preamble */
int mag;			/* magnification specified in preamble */

struct font_entry *fontptr;     /* font_entry pointer */
struct font_entry *hfontptr=NULL;/* font_entry pointer */

double page_w = PAGE_WIDTH;	/* page width (inches) */
double page_h = PAGE_HEIGHT;	/* page width (inches) */

int h;				/* current horizontal position */
int hh;				/* current horizontal position in pixels */
int v;				/* current vertical position */
int vv;				/* current vertical position in pixels */

bool pre_load = TRUE;		/* preload the font descriptions? */
bool silent = FALSE;		/* suppress messages */
bool show_page_frame = FALSE;	/* show page window */

long postambleptr;		/* Pointer to the postamble */

char *font_path;		/* Font path name for search */
bool use_gf = USE_GF;		/* Enable the use of GF fonts. */
bool use_pxl = USE_PXL;		/* Enable the use of PXL fonts. */

FILE *dvifp = NULL;		/* File pointer */

struct stat stat_buf;		/* For checking file changes. */
time_t mtime = 0;

char label[STRSIZE];

char pathname[STRSIZE] = "";	/* Complete path */
char directory[STRSIZE] = "";	/* Directory */
char filename[STRSIZE] = "";	/* File name */
char print_spooler[STRSIZE] = PRINT_SPOOLER;	/* Print commands. */
char print_page_spooler[STRSIZE] = PRINT_PAGE_SPOOLER;

int last_sheet = 0;		/* Sheet number of last page in file */
int file_sheet = 1;		/* Position of file pointer */
int disp_sheet = 0;		/* Current displayed page */
int disp_page = 0;		/* Real Page number */
int sheet_page[MAX_SHEETS];	/* Page number of each sheet. */
long sheet_table[MAX_SHEETS];	/* pointers to start of each page in file */
int last_known_sheet = 0;	/* Points to table at next unread sheet */

int resolution = 0;		/* Assumed screen resolution for rendering */
int sampling = 0;		/* Sample down sampling factor */
bool mono;			/* Monochrome screen */

Frame disp_frame;		/* Frame for display window. */
Canvas disp_canvas;		/* Canvas for display window. */

struct mem_pixrect page_mpr;	/* Page bitmap. */
struct pixrect *page_pr;
struct mem_pixrect sample_mpr;	/* Sampled/filtered bitmap. */
struct pixrect *sample_pr;

int origin_x;			/* Nominal origin of the dvi on the page */
int origin_y;			/* Nominal origin of the dvi on the page */

int offset_x;			/* Offsets of page in window. */
int offset_y;			/* Offsets of page in window. */

int start_x;			/* Starting position of page in the window */
int start_y;			/* Starting position of page in the window */

int verbose;			/* Flags for debugging. */

int mag_size_x = DEFAULT_MAG_SIZE_X;	/* Magnifier parameters. */
int mag_size_y = DEFAULT_MAG_SIZE_Y;
int mag_border = DEFAULT_MAG_BORDER;

forward uchar cmap_red[];
forward uchar cmap_green[];
forward uchar cmap_blue[];

short icon_image[] =
{
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x00C0,0x0001,0x8000,0x0000,0x0338,0x0001,
	0x8000,0x0000,0x0447,0x0001,0x8000,0x0000,0x1911,0xE001,
	0x8000,0x0000,0x2888,0x9E01,0x8000,0x0000,0xC222,0x23C1,
	0x8000,0x0001,0x4444,0x4479,0x8000,0x0007,0x1111,0x1117,
	0x8000,0x0008,0x8888,0x8889,0x8000,0x0032,0x2222,0x2223,
	0x8000,0x0044,0x4444,0x4445,0x8000,0x0191,0x1111,0x1111,
	0x8000,0x0288,0x8888,0x888B,0x8000,0x0C22,0x2222,0x2225,
	0x8000,0x1444,0x4444,0x4447,0x8000,0x6111,0x1111,0x1119,
	0x8000,0xC888,0x8888,0x889B,0x8000,0xB222,0x2222,0x2235,
	0x8000,0xCE44,0x4444,0x446B,0x8000,0x91D1,0x1111,0x11D5,
	0x8000,0x88B8,0x8888,0x88AB,0x8000,0xAC2E,0x2222,0x2355,
	0x8000,0xC345,0xC444,0x46AB,0x8000,0x9CD1,0x3111,0x1555,
	0x8000,0xB338,0x8E88,0x8AA5,0x8000,0xE0C6,0x23E2,0x3559,
	0x8000,0xC039,0x8474,0x56B1,0x8000,0xC006,0x611F,0x2D41,
	0x8000,0x9001,0x9C89,0xDEA1,0x8001,0x0C00,0x6322,0xFD41,
	0x8002,0x43E0,0x1CC4,0xBE81,0x8004,0x2C18,0x0331,0xFD01,
	0x8008,0x3606,0x00C8,0xBB01,0x8031,0xC781,0x0032,0xF601,
	0x8040,0x4CC1,0x0014,0xAC01,0x818C,0x9840,0xC021,0xD801,
	0x8602,0xB208,0xB048,0xA801,0x9861,0x6788,0x4CC2,0xD001,
	0xB011,0xCCD9,0xC0C4,0xA001,0xA30D,0x1817,0x60B1,0xC001,
	0x9883,0x3E1C,0x590E,0xC001,0x8463,0x6270,0x4101,0x8001,
	0x821F,0xC1D0,0xC100,0x0001,0x813F,0xB310,0xB200,0x0001,
	0x81FC,0x5831,0x0200,0x0001,0x87F8,0x4021,0x0400,0x0001,
	0x9F8C,0x3006,0xC400,0x0001,0x9E07,0x0C18,0x0800,0x0001,
	0x8C00,0x83E8,0x1000,0x0001,0x8000,0x6106,0x2000,0x0001,
	0x8000,0x18C0,0x4000,0x0001,0x8000,0x0430,0x8000,0x0001,
	0x8000,0x0309,0x0000,0x0001,0x8000,0x0081,0x0000,0x0001,
	0x8000,0x0062,0x0000,0x0001,0x8000,0x0014,0x0000,0x0001,
	0x8000,0x0008,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0xFFFF,0xFFFF,0xFFFF,0xFFFF
};

DEFINE_ICON_FROM_IMAGE(icon, icon_image);

short hand[] =
{
  0x0C00,0x1200,0x1200,0x1380,0x1240,0x7270,0x9248,0x924E,
  0x9249,0x9249,0x9009,0x8001,0x4002,0x4002,0x2004,0x2004
};
mpr_static(hand_pr, 16, 16, 1, hand);
Cursor hand_cursor;
\f


/*
 * Functions.
 * =========
 */

/*
 * main:
 *	Interpret args, open windows, loop forever.
 */

int
main(argc, argv)
int argc;
char *argv[];
{
	int f;
	char opt;
	char *slash;
	char *extension;
	Pixwin *pw;
	bool fake_mono;
	char *printer;
	double set_origin_x;
	double set_origin_y;
	double set_start_x;
	double set_start_y;

	/*
	 * local initialisations.
	 */
	fake_mono = FALSE;
	set_origin_x = 0.0;
	set_origin_y = 0.0;
	set_start_x = 0.0;
	set_start_y = 0.0;

	/*
	 * Customise this part for your local printer environment.
	 * ======================================================
	 */
#ifdef SPAR_HACKS

	/*
	 * Set local printer hacks.
	 */
	printer = getenv("PRINTER");
	if(printer && strncmp(printer, "lw", 2) == 0)
	{
		sprintf(print_spooler,
		  "dvips -P%s %%s >/dev/null 2>/dev/null",
		  printer);
		sprintf(print_page_spooler,
		  "dvips -P%s -f %%d -t %%d %%s >/dev/null 2>/dev/null",
		  printer);
	}
	else if(printer && strncmp(printer, "im", 2) == 0)
	{
		sprintf(print_spooler,
		  "dviimp -P%s %%s >/dev/null 2>/dev/null",
		  printer);
		sprintf(print_page_spooler,
		  "dviimp -P%s -S %%d -E %%d %%s >/dev/null 2>/dev/null",
		  printer);
	}
	else
	{
		fprintf(stderr, "PRINTER environment not recognised:\n");
		fprintf(stderr, " using `%s' to print files\n",
		  print_spooler);
		fprintf(stderr, " using `%s' to print pages\n",
		  print_page_spooler);
	}

	if(verbose & DEBUG_PRINTER)
	{
		fprintf(stderr, "Using `%s' to print files\n",
		  print_spooler);
		fprintf(stderr, "Using `%s' to print pages\n",
		  print_page_spooler);
	}

#endif SPAR_HACKS

	/*
	 * Find font path environment.
	 */
	if((font_path = getenv(FONT_PATH)) == NULL)
		font_path = FONT_AREA;

	/*
	 * Get cursor.
	 */
	hand_cursor = cursor_create(
	  CURSOR_IMAGE, &hand_pr,
	  CURSOR_XHOT, 5,
	  CURSOR_YHOT, 0,
	  CURSOR_OP, PIX_SRC ^ PIX_DST,
	  0);

	/*
	 * Create a disp_frame.
	 */
	disp_frame = window_create(0, FRAME,
	  WIN_X, 300,
	  WIN_Y, 50,	/* I hate the bug where the window is off the screen */
	  WIN_WIDTH,
	    (int)(page_w * DEFAULT_COLOUR_RES /
	    DEFAULT_COLOUR_SAMPLING) + 10,
	  WIN_HEIGHT,
	    (int)(page_h * DEFAULT_COLOUR_RES /
	    DEFAULT_COLOUR_SAMPLING) + 20,
	  FRAME_ARGC_PTR_ARGV, &argc, argv,
	  FRAME_LABEL, DVIPAGE_LABEL,
	  FRAME_ICON, &icon,
	  0);

	/*
	 * Create the disp_canvas.
	 */
	disp_canvas = window_create(disp_frame, CANVAS,
	  CANVAS_RETAINED, FALSE,
	  CANVAS_AUTO_CLEAR, FALSE,
	  CANVAS_FIXED_IMAGE, TRUE,
	  WIN_CURSOR, hand_cursor,
	  WIN_CONSUME_PICK_EVENTS,
	    WIN_NO_EVENTS,
	    LOC_DRAG,
	    WIN_MOUSE_BUTTONS,
	    LOC_WINENTER,	/* Otherwise misses first event */
	    LOC_WINEXIT,
	    0,
	  WIN_CONSUME_KBD_EVENTS,
	    WIN_NO_EVENTS,
	    WIN_ASCII_EVENTS,
	    WIN_LEFT_KEYS,	/* For Expose, Hide, Close etc. */
	    KBD_USE,		/* Otherwise click to type doesn't work */
	    KBD_DONE,
	    0,
	  CANVAS_REPAINT_PROC, page_paint,
	  WIN_EVENT_PROC, page_event,
	  WIN_WIDTH, WIN_EXTEND_TO_EDGE,
	  WIN_HEIGHT, WIN_EXTEND_TO_EDGE,
	  0);

	/*
	 * Interpret args.
	 */
	f = 0;
	while((opt = a_next(argc, argv)) != A_END)
	{
		switch(opt)
		{
		default:
			fprintf(stderr, "%s: illegal flag -%c\n",
			  a_prog_name, opt);
			/* FALLTHROUGH */
		case 'H':
			fprintf(stderr,
  "Usage: %s \\\n", a_prog_name);
			fprintf(stderr,
  "	[-v mode]		# Verbose mode (for debugging) \\\n");
			fprintf(stderr,
  "	[-m]			# Force monochrome mode \\\n");
			fprintf(stderr,
  "	[-p font-file-path]	# List of font directories \\\n");
			fprintf(stderr,
  "	[-P flag]		# Enable or disable the use of PXL files \\\n");
			fprintf(stderr,
  "	[-G flag]		# Enable or disable the use of GF files \\\n");
			fprintf(stderr,
  "	[-l]			# Don't preload font data \\\n");
			fprintf(stderr,
  "	[-q]			# Quiet: no warning messages \\\n");
			fprintf(stderr,
  "	[-f]			# Show rendering frame on page \\\n");
			fprintf(stderr,
  "	[-r res]		# Use `res' dpi fonts \\\n");
			fprintf(stderr,
  "	[-s sample]		# Reduce by factor of `sample' \\\n");
			fprintf(stderr,
  "	[-x x] [-y y]		# Initial pos of sheet in inches \\\n");
			fprintf(stderr,
  "	[-X ox] [-Y oy]		# Pos of (0, 0) on page in inches \\\n");
			fprintf(stderr,
  "	[-w width] [-h height]	# Total size of page in inches \\\n");
			fprintf(stderr,
  "	[dvifile[.dvi]]\n");
			exit(1);

		case 'v':
			verbose = a_integer(argc, argv);
			break;

		case 'm':
			fake_mono = TRUE;
			break;

		case 'p':
			font_path = a_arg(argc, argv);
			break;

		case 'P':
			use_pxl = a_integer(argc, argv);
			break;

		case 'G':
			use_gf = a_integer(argc, argv);
			break;

		case 'l':
			pre_load = ! pre_load;
			break;

		case 'q':
			silent = ! silent;
			break;

		case 'f':
			show_page_frame = ! show_page_frame;
			break;

		case 'r':
			resolution = a_integer(argc, argv);
			break;

		case 's':
			sampling = a_integer(argc, argv);
			break;

		case 'x':
			set_start_x = a_number(argc, argv);
			break;

		case 'y':
			set_start_y = a_number(argc, argv);
			break;

		case 'X':
			set_origin_x = a_number(argc, argv);
			break;

		case 'Y':
			set_origin_y = a_number(argc, argv);
			break;

		case 'w':
			page_w = a_number(argc, argv);
			break;

		case 'h':
			page_h = a_number(argc, argv);
			break;

		case A_ARG:
			switch(f++)
			{
			case 0:
				/*
				 * Get the whole pathname.
				 */
				strcpy(pathname, a_arg(argc, argv));

				/*
				 * Get the filename and directory
				 */
				strcpy(directory, pathname);
				if((slash = rindex(directory, '/')) != NULL)
				{
					strcpy(filename, slash+1);
					*++slash = '\0';
				}
				else
				{
					directory[0] = '\0';
					strcpy(filename, pathname);
				}

				/*
				 * If the filename has no extension, or if it
				 * has an extension and it is not '.dvi' then
				 * cat .dvi onto the filename.
				 */
				if((extension = rindex(pathname, '.')) == NULL ||
				  strcmp(extension, ".dvi") != 0)
					strcat(pathname, ".dvi");

				break;

			default:
				fprintf(stderr,
  "%s: too many dvi files\n", a_prog_name);
				exit(1);
			}
			break;
		}
	}

	pw = canvas_pixwin(disp_canvas);

	/*
	 * Now that we know whether we are on a colour machine or a monochrome,
	 * we can set the defaults for the resolution and sampling, unless
	 * they have already been set from the args.
	 */
	if(fake_mono || (pw->pw_pixrect->pr_depth == 1))
	{
		/*
		 * Monochrome
		 */
		mono = TRUE;

		if(resolution == 0)
			resolution = DEFAULT_MONO_RES;
		if(sampling == 0)
			sampling = DEFAULT_MONO_SAMPLING;
	}
	else
	{
		/*
		 * Colour
		 */
		mono = FALSE;

		if(resolution == 0)
			resolution = DEFAULT_COLOUR_RES;
		if(sampling == 0)
			sampling = DEFAULT_COLOUR_SAMPLING;

		/*
		 * Compute and set a colour map
		 */
		make_cmap();
		pw_setcmsname(pw, "dvipage-greys");
		pw_putcolormap(pw, 0, 64, cmap_red, cmap_green, cmap_blue);
	}

	/*
	 * Now that we know the resolution and sampling, we can set
	 * the margin and origin properly.
	 */
	if(set_origin_x != 0.0)
		origin_x = (int)(set_origin_x * resolution);
	else
		origin_x = (int)(DEFAULT_ORIGIN_X * resolution);

	if(set_origin_y != 0.0)
		origin_y = (int)(set_origin_y * resolution);
	else
		origin_y = (int)(DEFAULT_ORIGIN_Y * resolution);

	if(set_start_x != 0.0)
		start_x = (int)(set_start_x * resolution);
	else
		start_x = 0;

	if(set_start_y != 0.0)
		start_y = (int)(set_start_y * resolution);
	else
		start_y = 0;

	/*
	 * Insert the window into the heap now, so that if a message is
	 * generated by the init_dvi_file below, it is displayed after the
	 * window, and is therefore on top of it. If we display the window
	 * after doing the initialisation of the dvi file, it would obscure
	 * any error messages which might have been generated.
	 */
	window_set(disp_frame,
	  WIN_SHOW, TRUE,
	  0);

	/*
	 * If we don't run the notifier at this time, the window will
	 * not be painted, and the effect will be a gross area of the
	 * screen which is not painted, through which the previous windows
	 * are still visible.
	 */
	notify_dispatch();

	/*
	 * If there was a filename specified, then open it
	 * and prepare the first page.
	 */
	if(f >= 1)
	{
		/*
		 * Init the file.
		 */
		if(init_dvi_file())
		{
			process_page(RASTERISE);
			offset_x = start_x / sampling;
			offset_y = start_y / sampling;
			page_paint(disp_canvas, pw, 0);
		}
	}
	else
		getwd(directory);

	/*
	 * Loop forever in the notifier.
	 */
	notify_start();
}
\f


/*
 * Window Functions.
 * ================
 */

/*
 * page_paint:
 *	Called whenever the window is to be painted.
 *	Just maps the sampled pixrect into the screen at the appropriate
 *	offset position.
 */

Notify_value
page_paint(canvas, pw, area)
Canvas canvas;
Pixwin *pw;
Rectlist *area;
{
	if(sample_pr == NULL)
	{
		pw_rop(pw, 0, 0,
		  (int)window_get(canvas, WIN_WIDTH),
		  (int)window_get(canvas, WIN_HEIGHT),
		  PIX_CLR, NULL, 0, 0);

		sprintf(label, "%s:   No File", DVIPAGE_LABEL);
		window_set(disp_frame,
		  FRAME_LABEL, label,
		  0);
	}
	else
	{
		pw_cover(pw, 0, 0,
		  (int)window_get(canvas, WIN_WIDTH),
		  (int)window_get(canvas, WIN_HEIGHT),
		  PIX_SRC, sample_pr, -offset_x, -offset_y);

		sprintf(label, "%s:   File \"%s\"   %s %d",
		  DVIPAGE_LABEL, filename,
		  (last_sheet && disp_sheet >= last_sheet-1)?
		  "Last page" : "Page",
		  disp_page);
		window_set(disp_frame,
		  FRAME_LABEL, label,
		  0);
	}
}

/*
 * page_event:
 *	Called whenever an input event arrives at the window.
 *	Controls panning of the page, turning to the next page,
 *	and reloading a new file.
 */

Notify_value
page_event(canvas, event, arg)
Window canvas;
Event *event;
caddr_t arg;
{
	Pixwin *pw;
	int e;
	int pp;
	bool page_or_sheet;
	static int num = 0;
	static bool valid_num = FALSE;
	bool keep_num;
	char *extension;
	char command[STRSIZE];
	double x, y;

	if(event_is_up(event))
		return;

	pw = canvas_pixwin(canvas);

	keep_num = FALSE;

	if(verbose & DEBUG_SHEET)
		fprintf(stderr, "page_event: num = %d @ %d\n", num, valid_num);

	/*
	 * If there is a call for a menu, then translate that into
	 * a command character.
	 */
	if((e = event_id(event)) == MS_RIGHT)
		if((e = page_menu(canvas, pw, event)) == 0)
			return;

	switch(e)
	{
	case MS_LEFT:
		page_magnify(canvas, pw, event);
		break;

	case MS_MIDDLE:
		page_pan(canvas, pw, event);
		break;

	default:
		if(e >= '0' && e <= '9')
			num = num * 10 + e - '0';
		else if(e == DEL || e == Control('H'))
			num = num / 10;
		else
			break;

		keep_num = TRUE;
		valid_num = TRUE;

		break;

	case '\r':		/* Next page */
	case 'n':
	case ' ':
	case '+':
		if(! valid_num)
			num = 1;
		if(! goto_sheet(disp_sheet + num))
			break;

		process_page(RASTERISE);
		offset_x = start_x / sampling;
		offset_y = start_y / sampling;
		page_paint(canvas, pw, 0);

		break;

	case '\n':		/* Previous page */
	case 'p':
	case '-':
		if(! valid_num)
			num = 1;
		if(! goto_sheet(disp_sheet - num))
			break;

		process_page(RASTERISE);
		offset_x = start_x / sampling;
		offset_y = start_y / sampling;
		page_paint(canvas, pw, 0);

		break;

	case '_':
		num = 1;
		valid_num = TRUE;
		/* FALLTHROUGH */

	case 'G':
		if(! valid_num)
			num = LAST_PAGE;

		if(! goto_sheet(num))
			break;

		process_page(RASTERISE);
		offset_x = start_x / sampling;
		offset_y = start_y / sampling;
		page_paint(canvas, pw, 0);

		break;

	case 'g':
		if(! valid_num)
			num = LAST_PAGE;

		if(! goto_page(num))
			break;

		process_page(RASTERISE);
		offset_x = start_x / sampling;
		offset_y = start_y / sampling;
		page_paint(canvas, pw, 0);

		break;

	case 'h':		/* Home page */
		offset_x = start_x / sampling;
		offset_y = start_y / sampling;
		page_paint(canvas, pw, 0);
		break;

	case 'l':		/* Left page */
		offset_x -= 200;
		page_paint(canvas, pw, 0);
		break;

	case 'r':		/* Right page */
		offset_x += 200;
		page_paint(canvas, pw, 0);
		break;

	case 'u':		/* Up page */
		offset_y -= 300;
		page_paint(canvas, pw, 0);
		break;

	case 'd':		/* Down page */
		offset_y += 300;
		page_paint(canvas, pw, 0);
		break;

		
	case 'm':		/* Mark margins */
		start_x = offset_x * sampling;
		start_y = offset_y * sampling;
		break;

	case 'M':		/* Set margins */
		x = ((double)start_x / resolution);
		y = ((double)start_y / resolution);
		if(! doubles_prompt(1152/2, 900/2,
		  "left margin: (inches) ", &x,
		  "top margin: (inches)  ", &y,
		  0))
			break;
		start_x = (int)(x * resolution);
		start_y = (int)(y * resolution);
		offset_x = start_x / sampling;
		offset_y = start_y / sampling;
		page_paint(canvas, pw, 0);
		break;

	case '*':
	case '!':
	case '@':
	case '#':
	case '$':
	case '%':
	case '^':
		valid_num = TRUE;
		if(e == '*')
			num = (mono ?
			  DEFAULT_MONO_SAMPLING : DEFAULT_COLOUR_SAMPLING);
		else if(e == '!')
			num = 1;
		else if(e == '@')
			num = 2;
		else if(e == '#')
			num = 3;
		else if(e == '$')
			num = 4;
		else if(e == '%')
			num = 5;
		else
			valid_num = FALSE;
		/* FALLTHROUGH */

	case 's':
		if(mono)
			break;
		if(! valid_num || (num < 1 || num > 5))
			sampling = DEFAULT_COLOUR_SAMPLING;
		else
			sampling = num;

		sample_page();
		offset_x = start_x / sampling;
		offset_y = start_y / sampling;
		page_paint(canvas, pw, 0);
		break;

	case 'S':
		if(mono)
			break;
		if(! integers_prompt(1152/2, 900/2,
		  "sampling: (1, 2, 3, 4) ", &sampling,
		  0))
			break;
		if(sampling < 1 || sampling > 5)
			sampling = DEFAULT_COLOUR_SAMPLING;

		sample_page();
		offset_x = start_x / sampling;
		offset_y = start_y / sampling;
		page_paint(canvas, pw, 0);
		break;

	case 'x':
		if(valid_num)
			mag_size_x = num;
		else
			mag_size_x = DEFAULT_MAG_SIZE_X;
		break;

	case 'y':
		if(valid_num)
			mag_size_y = num;
		else
			mag_size_y = DEFAULT_MAG_SIZE_Y;
		break;

	case 'X':
	case 'Y':
		if(mono)
			break;
		if(! integers_prompt(1152/2, 900/2,
		  "magnifier size (x) : ", &mag_size_x,
		  "magnifier size (y) : ", &mag_size_y,
		  0))
			break;
		break;

	case '[':
		mag_size_x = 128;
		mag_size_y = 64;
		break;

	case ']':
		mag_size_x = 128;
		mag_size_y = 128;
		break;

	case '{':
		mag_size_x = 256;
		mag_size_y = 128;
		break;

	case '}':
		mag_size_x = 256;
		mag_size_y = 256;
		break;

	case '(':
		mag_size_x = 512;
		mag_size_y = 256;
		break;

	case ')':
		mag_size_x = 512;
		mag_size_y = 512;
		break;

	case 'b':
		if(valid_num)
			mag_border = num;
		else
			mag_border = DEFAULT_MAG_BORDER;
		break;

	case 'F':
		if(! strings_prompt(1152/2, 900/2,
		  "Directory: ", directory,
		  "Filename:  ", filename,
		  0))
			break;

		/*
		 * Build the whole pathname.
		 */
		if(directory[0] != '\0')
		{
			strcpy(pathname, directory);
			if(pathname[strlen(pathname)-1] != '/')
				strcat(pathname, "/");
			strcat(pathname, filename);
		}
		else
			strcpy(pathname, filename);

		/*
		 * If the filename has no extension, or if it
		 * has an extension and it is not '.dvi' then
		 * cat .dvi onto the filename.
		 */
		if((extension = rindex(pathname, '.')) == NULL ||
		  strcmp(extension, ".dvi") != 0)
			strcat(pathname, ".dvi");

		sprintf(label, "%s:   Opening file \"%s\"",
		  DVIPAGE_LABEL, filename);
		window_set(disp_frame,
		  FRAME_LABEL, label,
		  0);
		close_dvi_file();
		if(init_dvi_file())
		{
			process_page(RASTERISE);
			offset_x = start_x / sampling;
			offset_y = start_y / sampling;
			page_paint(canvas, pw, 0);
		}
		break;

	case 'R':		/* Reopen file */
		sprintf(label, "%s:   Reopening file \"%s\"",
		  DVIPAGE_LABEL, filename);
		window_set(disp_frame,
		  FRAME_LABEL, label,
		  0);
		if(valid_num)
		{
			pp = num;
			page_or_sheet = TRUE;
		}
		else
		{
			pp = disp_sheet;
			page_or_sheet = FALSE;
		}
		close_dvi_file();
		if(init_dvi_file())
		{
			if(page_or_sheet)
			{
				if(! goto_page(pp))
					(void)goto_sheet(1);
			}
			else
			{
				if(! goto_sheet(pp))
					(void)goto_sheet(1);
			}

			process_page(RASTERISE);
			offset_x = start_x / sampling;
			offset_y = start_y / sampling;
			page_paint(canvas, pw, 0);
		}
		break;

	case 'P':
		sprintf(command, print_page_spooler,
		  disp_page, disp_page, pathname);
		if(verbose & DEBUG_PRINTER)
			fprintf(stderr, "Printer command '%s'\n", command);
		system(command);
		break;

	case Control('P'):
		sprintf(command, print_spooler, pathname);
		if(verbose & DEBUG_PRINTER)
			fprintf(stderr, "Printer command '%s'\n", command);
		system(command);
		break;

	case 'w':
		show_page_frame = ! show_page_frame;

		if(goto_sheet(disp_sheet))
		{
			process_page(RASTERISE);
			offset_x = start_x / sampling;
			offset_y = start_y / sampling;
			page_paint(canvas, pw, 0);
		}

		break;

	case 'v':
		if(valid_num)
			verbose = num;
		else
			verbose = 0;
		break;

	case 'Q':
		exit();
	}

	if(! keep_num)
	{
		num = 0;
		valid_num = FALSE;
	}

	if(verbose & DEBUG_SHEET)
		fprintf(stderr, "end__event: num = %d @ %d\n", num, valid_num);
}

/*
 * page_menu:
 *	Displays a menu in response to MS_RIGHT, returns a character
 *	code to calling function to effect action.
 */

int
page_menu(canvas, pw, event)
Canvas canvas;
Pixwin *pw;
Event *event;
{
	static Menu menu = NULL;
	int action;

	if(! menu)
	{
		/*
		 * Return values from this menu are passed to the
		 * event procedure, and the codes here must match
		 * codes in that function.
		 */
		menu = menu_create(
		  MENU_ITEM,
		    MENU_STRING, "Next Page",
		    MENU_VALUE, 'n',
		    0,
		  MENU_ITEM,
		    MENU_STRING, "Previous Page",
		    MENU_VALUE, 'p',
		    0,
		  MENU_ITEM,
		    MENU_STRING, "First Page",
		    MENU_VALUE, '_',
		    0,
		  MENU_ITEM,
		    MENU_STRING, "Last Page",
		    MENU_VALUE, 'G',
		    0,
		  MENU_ITEM,
		    MENU_STRING, "Sampling",
		    MENU_PULLRIGHT, menu_create(
		      MENU_ITEM,
			MENU_STRING, "Default Sampling",
			MENU_VALUE, '%',
			0,
		      MENU_ITEM,
			MENU_STRING, "No Sampling",
			MENU_VALUE, '!',
			0,
		      MENU_ITEM,
			MENU_STRING, "2:1 Sampling",
			MENU_VALUE, '@',
			0,
		      MENU_ITEM,
			MENU_STRING, "3:1 Sampling",
			MENU_VALUE, '#',
			0,
		      MENU_ITEM,
			MENU_STRING, "4:1 Sampling",
			MENU_VALUE, '$',
			0,
		      0),
		    0,
		  MENU_ITEM,
		    MENU_STRING, "Magnifier",
		    MENU_PULLRIGHT, menu_create(
		      MENU_ITEM,
			MENU_STRING, "128 x 64",
			MENU_VALUE, '[',
			0,
		      MENU_ITEM,
			MENU_STRING, "128 x 128",
			MENU_VALUE, ']',
			0,
		      MENU_ITEM,
			MENU_STRING, "256 x 128",
			MENU_VALUE, '{',
			0,
		      MENU_ITEM,
			MENU_STRING, "256 x 256",
			MENU_VALUE, '}',
			0,
		      MENU_ITEM,
			MENU_STRING, "512 x 256",
			MENU_VALUE, '(',
			0,
		      MENU_ITEM,
			MENU_STRING, "512 x 512",
			MENU_VALUE, ')',
			0,
		      0),
		    0,
		  MENU_ITEM,
		    MENU_STRING, "Reopen DVI file",
		    MENU_VALUE, 'R',
		    0,
		  MENU_ITEM,
		    MENU_STRING, "New DVI file",
		    MENU_VALUE, 'F',
		    0,
		  MENU_ITEM,
		    MENU_STRING, "Print Page",
		    MENU_VALUE, 'P',
		    0,
		  MENU_ITEM,
		    MENU_STRING, "Print Document",
		    MENU_VALUE, Control('P'),
		    0,
		  MENU_ITEM,
		    MENU_STRING, "Quit",
		    MENU_VALUE, 'Q',
		    0,
		  0);
	}

	return (int)menu_show(menu, canvas, event, 0);
}

/*
 * page_magnify:
 *	Pops up magnified region (only if sampling != 1).
 *	Currently unimplemented.
 */

void
page_magnify(canvas, pw, event)
Canvas canvas;
Pixwin *pw;
Event *event;
{
	Rect r;
	int w, h;
	double scale_x, scale_y;
	int sample_w, sample_h;
	int new_sample_x, new_sample_y;
	int delta_x, delta_y;
	int sample_x, sample_y;
	int dst_x, dst_y;
	int page_x, page_y;
	int old_cursor_op;
	bool first_time = TRUE;
	int win_x, win_y;
#ifdef GOBBLE_MAGNIFY
	int ninputs, canvasfd;
#endif GOBBLE_MAGNIFY

	if(sampling == 1 || sample_pr == NULL)
		return;

	if(mag_size_x < 4)
		mag_size_x = 4;
	if(mag_size_y < 4)
		mag_size_y = 4;
	if(mag_size_x > sample_pr->pr_width)
		mag_size_x = sample_pr->pr_width;
	if(mag_size_y > sample_pr->pr_height)
		mag_size_y = sample_pr->pr_height;
	if(mag_border < 0)
		mag_border = 0;
	if(mag_border > 8)
		mag_border = 8;

	/*
	 * Get Lock rect.
	 */
	r.r_left = 0;
	r.r_top = 0;
	r.r_width = (int)window_get(canvas, WIN_WIDTH);
	r.r_height = (int)window_get(canvas, WIN_HEIGHT);

	/*
	 * Precompute some window sizes.
	 */
	w = sample_pr->pr_width;
	h = sample_pr->pr_height;
	switch(sampling)
	{
	case 2:
		scale_x = 2.0;
		scale_y = 2.0;
		break;

	case 3:
		scale_x = 8.0 / 3.0;
		scale_y = 3.0;
		break;

	case 4:
		scale_x = 4.0;
		scale_y = 4.0;
		break;

	case 5:
		scale_x = 8.0/3.0;
		scale_y = 4.0;
		break;

	default:
		return;
	}
	sample_w = mag_size_x / scale_x;
	sample_h = mag_size_y / scale_y;

	if(verbose & DEBUG_MAGNIFY)
		fprintf(stderr, "page_magnify: scale %lf %lf; %d %d -> %d %d\n",
		  scale_x, scale_y, sample_w, sample_h, mag_size_x, mag_size_y);

	/*
	 * Remove the cursor
	 */
	old_cursor_op = (int)cursor_get(hand_cursor, CURSOR_OP);
	cursor_set(hand_cursor, CURSOR_OP, PIX_DST, 0);
	window_set(canvas, WIN_CURSOR, hand_cursor, 0);

	/*
	 * Grab all input
	 */
	window_set(canvas, WIN_GRAB_ALL_INPUT, TRUE, 0);
#ifdef GOBBLE_MAGNIFY
	canvasfd = (int)window_get(canvas, WIN_FD);
#endif GOBBLE_MAGNIFY

	/*
	 * Loop until up mouse.
	 */
	sample_x = MAXINT;
	sample_y = MAXINT;
	while(! event_is_up(event))
	{
		/*
		 * Compute the region which will be magnified.
		 */
		new_sample_x =
		  Range(0, event_x(event)-offset_x-sample_w/2, w-sample_w);
		new_sample_y =
		  Range(0, event_y(event)-offset_y-sample_h/2, h-sample_h);

		/*
		 * See how this differs from last magnified region.
		 */
		delta_x = new_sample_x - sample_x;
		delta_y = new_sample_y - sample_y;

		/*
		 * Lock
		 */
		pw_lock(pw, &r);

		if(! first_time)
		{
			if(verbose & DEBUG_MAGNIFY)
				fprintf(stderr, " covering with %d %d\n",
				  delta_x, delta_y);

			/*
			 * Paint those portions of the image which were
			 * covered by the last magnifier, and exposed now.
			 * We could just paint the entire patch, but this
			 * gives unpleasant flashing when moving the window.
			 */
			if(delta_x > 0)
				pw_cover(pw, win_x, win_y,
				  delta_x, mag_size_y,
				  PIX_SRC, sample_pr, dst_x, dst_y);
			else if(delta_x < 0)
				pw_cover(pw, win_x+mag_size_x+delta_x, win_y,
				  -delta_x, mag_size_y,
				  PIX_SRC, sample_pr,
				  dst_x+mag_size_x+delta_x, dst_y);
			if(delta_y > 0)
				pw_cover(pw, win_x, win_y,
				  mag_size_x, delta_y,
				  PIX_SRC, sample_pr, dst_x, dst_y);
			else if(delta_y < 0)
				pw_cover(pw, win_x, win_y+mag_size_y+delta_y,
				  mag_size_x, -delta_y,
				  PIX_SRC, sample_pr,
				  dst_x, dst_y+mag_size_y+delta_y);
		}
		else
			first_time = FALSE;

		/*
		 * Compute the new destination and window positions
		 * for the new magnified region.
		 */
		sample_x = new_sample_x;
		sample_y = new_sample_y;
		dst_x = sample_x - (mag_size_x - sample_w)/2;
		dst_y = sample_y - (mag_size_y - sample_h)/2;
		win_x = dst_x + offset_x;
		win_y = dst_y + offset_y;
		page_x = sample_x * scale_x;
		page_y = sample_y * scale_y;

		if(verbose & DEBUG_MAGNIFY)
			fprintf(stderr, " painting at %d %d from %d %d\n",
			  dst_x, dst_y, page_x, page_y);

		/*
		 * Display the magnified region.
		 */
		pw_write(pw, win_x, win_y, mag_size_x, mag_size_y,
		  PIX_SRC, page_pr, page_x, page_y);
		if(mag_border)
			pw_rect(pw, win_x, win_y, mag_size_x, mag_size_y,
			  mag_border, PIX_SRC, -1);

		/*
		 * Unlock
		 */
		pw_unlock(pw);

		/*
		 * Read another event.
		 */
		window_read_event(canvas, event);
#ifdef GOBBLE_MAGNIFY
		if(ioctl(canvasfd, FIONREAD, &ninputs) == 0)
			while(ninputs >= sizeof(Event) &&
			  window_read_event(canvas, event) == 0 &&
			  ! event_is_up(event))
				ninputs -= sizeof(Event);
#endif GOBBLE_MAGNIFY
	}

	/*
	 * Ungrab all input.
	 */
	window_set(canvas, WIN_GRAB_ALL_INPUT, FALSE, 0);

	/*
	 * Repaint
	 */
	pw_cover(pw, win_x, win_y, mag_size_x, mag_size_y,
	  PIX_SRC, sample_pr, dst_x, dst_y);

	/*
	 * Restore the cursor.
	 */
	cursor_set(hand_cursor, CURSOR_OP, old_cursor_op, 0);
	window_set(canvas, WIN_CURSOR, hand_cursor, 0);
}

/*
 * page_pan:
 *	Pans page within screen.
 */

void
page_pan(canvas, pw, event)
Canvas canvas;
Pixwin *pw;
Event *event;
{
	int x, y;
	int dx, dy;
#ifdef GOBBLE_PAN
	int ninputs, canvasfd;
#endif GOBBLE_PAN


	if(sample_pr == NULL)
		return;

	window_set(canvas, WIN_GRAB_ALL_INPUT, TRUE, 0);
#ifdef GOBBLE_PAN
	canvasfd = (int)window_get(canvas, WIN_FD);
#endif GOBBLE_PAN

	do
	{
		x = event_x(event);
		y = event_y(event);

		window_read_event(canvas, event);
#ifdef GOBBLE_PAN
		if(ioctl(canvasfd, FIONREAD, &ninputs) == 0)
			while(ninputs >= sizeof(Event) &&
			  window_read_event(canvas, event) == 0 &&
			  ! event_is_up(event))
				ninputs -= sizeof(Event);
#endif GOBBLE_PAN

		dx = event_x(event) - x;
		dy = event_y(event) - y;

		if(dx != 0 || dy != 0)
		{
			offset_x += dx;
			offset_y += dy;

			pw_cover(pw, 0, 0,
			  (int)window_get(canvas, WIN_WIDTH),
			  (int)window_get(canvas, WIN_HEIGHT),
			  PIX_SRC, sample_pr, -offset_x, -offset_y);
		}
	}
	while(! event_is_up(event));

	window_set(canvas, WIN_GRAB_ALL_INPUT, FALSE, 0);
}

/*
 * goto_sheet:
 *	Opens requested sheet on screen.
 */

bool
goto_sheet(new_sheet)
int new_sheet;
{
	if(! check_dvi_file())
		return FALSE;

	if(verbose & DEBUG_SHEET)
		fprintf(stderr, "goto_sheet(%d)\n", new_sheet);

	/*
	 * Check against page limits.
	 */
	if(new_sheet <= 0)
	{
		message("Attempt to go to sheet %d.", new_sheet);
		return FALSE;
	}

	/*
	 * Are we already at the desired page ?
	 */
	if(file_sheet == new_sheet)
		return TRUE;

	/*
	 * Do we already know where the page is ?
	 */
	if(new_sheet < MAX_SHEETS && new_sheet <= last_known_sheet)
	{
		fseek(dvifp, sheet_table[new_sheet], 0);
		file_sheet = new_sheet;
		return TRUE;
	}

	/*
	 * Can't find it directly in the table:
	 * Go to the last known sheet...
	 */
	file_sheet = last_known_sheet;
	fseek(dvifp, sheet_table[file_sheet], 0);

	/*
	 * Skip through the rest of the pages to the new page.
	 */
	while(file_sheet < new_sheet)
	{
		/*
		 * Check for last page:
		 * Last page is always returned.
		 */
		if(last_sheet && file_sheet >= last_sheet)
		{
			file_sheet = last_sheet - 1;
			fseek(dvifp, sheet_table[file_sheet], 0);
			return TRUE;
		}

		/*
		 * Otherwise, skip this page and look at the next.
		 */
		process_page(SKIP);
	}
	return TRUE;
}

/*
 * goto_page:
 *	Opens requested page on screen.
 */

bool
goto_page(new_page)
int new_page;
{
	int sheet;

	if(! check_dvi_file())
		return FALSE;

	if(verbose & DEBUG_SHEET)
		fprintf(stderr, "goto_page(%d)\n", new_page);

	/*
	 * Search for page in the table.
	 */
	for(sheet = 1; sheet < last_known_sheet; sheet++)
	{
		if(sheet_page[sheet] == new_page)
		{
			file_sheet = sheet;
			fseek(dvifp, sheet_table[file_sheet], 0);
			return TRUE;
		}
	}

	/*
	 * Can't find it directly in the table:
	 * Go to the last known sheet...
	 */
	file_sheet = last_known_sheet;
	fseek(dvifp, sheet_table[file_sheet], 0);

	/*
	 * Skip through the rest of the pages to the new page.
	 */
	for( ; ; )
	{
		/*
		 * Check for last page:
		 */
		if(last_sheet && file_sheet >= last_sheet)
		{
			if(new_page == LAST_PAGE)
			{
				file_sheet = last_sheet - 1;
				fseek(dvifp, sheet_table[file_sheet], 0);
				return TRUE;
			}
			else
				return FALSE;
		}

		/*
		 * Otherwise, examine this page.
		 */
		sheet = file_sheet;
		process_page(SKIP);

		/*
		 * If this was the page, go back,
		 * and return it.
		 */
		if(sheet_page[sheet] == new_page)
		{
			file_sheet = sheet;
			fseek(dvifp, sheet_table[file_sheet], 0);
			return TRUE;
		}
	}
}
\f


/*
 * DVI file functions.
 * ==================
 */

/*
 * init_dvi_file:
 *	Opens the dvi file, and checks for valid codes etc.
 *	Reads the postamble (if enabled)
 *	Leaves the file pointer at the start of the first page.
 */

bool
init_dvi_file()
{
	int i;

	/*
	 * Open the file; close-on-exec.
	 */
	if((dvifp = fopen(pathname, "r")) == NULL)
	{
		message("Cant open file %s", pathname);
		return FALSE;
	}
	fcntl(fileno(dvifp),  F_SETFD,  1);

	/*
	 * Read the magic number and version number
	 */
	if((i = get_unsigned(dvifp, 1)) != PRE)
	{
		message("%s: not a dvi file.", filename);
		fclose(dvifp);
		return FALSE;
	}
	if((i = get_signed(dvifp, 1)) != DVIFORMAT)
	{
		message("%s: dvi format %d not supported.", filename, i);
		fclose(dvifp);
		return FALSE;
	}

	/*
	 * Make a note of the access time.
	 */
	if(fstat(fileno(dvifp), &stat_buf) == 0)
	{
		mtime = stat_buf.st_mtime;
	}
	else
	{
		message("%s: dvifile stat failed.", filename);
		mtime = 0;
	}

	if(pre_load)
	{
		/*
		 * Load font information from postable.
		 */
		if(! read_postamble())
		{
			fclose(dvifp);
			return FALSE;
		}

		/*
		 * Return to start of first page.
		 */
		fseek(dvifp, (long)14, 0);
	}
	else
	{
		/*
		 * Read basic data from preamble.
		 */
		num = get_unsigned(dvifp, 4);
		den = get_unsigned(dvifp, 4);
		mag = get_unsigned(dvifp, 4);
		hconv = vconv = do_convert(num, den, resolution);
	}

	/*
	 * Skip i more bytes of preamble.
	 */
	i = get_unsigned(dvifp, 1);
	fseek(dvifp, (long)i, 1);

	/*
	 * Allocate buffer for the page.
	 */
	if(! (page_pr = pr_alloc(&page_mpr,
	  (int)(page_w * resolution), (int)(page_h * resolution), 1)))
	{
		message("Out of memory for image allocation.");
		fclose(dvifp);
		return FALSE;
	}

	if(verbose & DEBUG_IMSIZE)
		fprintf(stderr, "Allocated buffer (%d x %d)\n",
		  page_pr->pr_width, page_pr->pr_height);

	/*
	 * Set up the page fseek pointer table.
	 * We are now at page 0.
	 */
	for(i = 0; i < MAX_SHEETS; i++)
	{
		sheet_table[i] = 0;
		sheet_page[i] = BAD_PAGE;
	}
	file_sheet = 1;
	last_sheet = 0;	/* last page == unknown */
	last_known_sheet = 1;
	sheet_table[last_known_sheet] = ftell(dvifp);

	if(verbose & DEBUG_SHEET)
		fprintf(stderr, "sheet_table[%d] = %d\n",
		  last_known_sheet, ftell(dvifp));

	return TRUE;
}

/*
 * close_dvi_file:
 *	Cleans up after reading a file.
 */

void
close_dvi_file()
{
	if(dvifp == NULL)
		return;

	/*
	 * Get rid of image memory.
	 */
	sample_pr = pr_free(&sample_mpr);
	page_pr = pr_free(&page_mpr);

	/*
	 * close the dvifile.
	 */
	fclose(dvifp);
	dvifp = NULL;
	mtime = 0;

	/*
	 * Hack the sheet numbers to prevent access to the file.
	 */
	last_sheet = -1;
	last_known_sheet = -1;

	/*
	 * Close the fonts and free up memory.
	 */
	close_fonts();
}

/*
 * check_dvi_file:
 *	Checks that this is the same file -- has not been modified since
 *	it was opened.
 */

bool
check_dvi_file()
{
	if(dvifp == NULL)
	{
		message("No dvifile open");
		return FALSE;
	}

	if(fstat(fileno(dvifp), &stat_buf) != 0)
	{
		message("%s: dvifile fstat failed.", filename);
		return FALSE;
	}

	if(stat_buf.st_mtime != mtime)
	{
		message("%s: dvifile modified", filename);
		return FALSE;
	}

	return TRUE;
}

/*
 * read_postamble:
 *	This  routine  is  used  to  read  in  the  postamble  values.	It
 *	initializes the magnification and checks  the stack height prior  to
 *	starting printing the document.
 *	Returns TRUE unless document cannot be processed.
 */

bool
read_postamble()
{
	if(! check_dvi_file())
		return FALSE;

	if(! find_postamble_ptr (&postambleptr))
		return FALSE;

	if(get_unsigned(dvifp, 1) != POST)
	{
		message("%s: bad dvi file: no POST at head of postamble.",
		  filename);
		return FALSE;
	}

	(void)get_unsigned(dvifp, 4); /* discard last page pointer */
	num = get_unsigned(dvifp, 4);
	den = get_unsigned(dvifp, 4);
	mag = get_unsigned(dvifp, 4);
	hconv = vconv = do_convert(num, den, resolution);

	(void)get_unsigned(dvifp, 4);	/* height-plus-depth of tallest page */
	(void)get_unsigned(dvifp, 4);	/* width of widest page */

	if(get_unsigned(dvifp, 2) >= STACKSIZE)
	{
		message("%s: bad dvi file: stack is too large.",
		  filename);
		return FALSE;
	}

	/* last_sheet = */ get_unsigned(dvifp, 2);

	if(! get_font_def())
		return FALSE;

	return TRUE;
}

/*
 * find_postamble_ptr
 *	Move to the end of the dvifile and find the start of the postamble.
 */

bool
find_postamble_ptr(postambleptr)
long *postambleptr;
{
	int i;

	fseek(dvifp, (long) 0, 2);
	*postambleptr = ftell(dvifp) - 4;
	fseek(dvifp, *postambleptr, 0);

	for( ; ; )
	{
		fseek(dvifp, --(*postambleptr), 0);
		if(((i = get_unsigned(dvifp, 1)) != 223) && (i != DVIFORMAT))
		{
			message("%s: Bad dvi file: bad end of file", filename);
			return FALSE;
		}
		if(i == DVIFORMAT)
			break;
	}

	fseek(dvifp, (*postambleptr) - 4, 0);
	*postambleptr = get_unsigned(dvifp, 4);
	fseek(dvifp, *postambleptr, 0);

	return TRUE;
}

/*
 * process_page:
 *	Rasterises the next page in the dvifile into page_mpr.
 *	Leaves the file pointer at the start of the next page.
 *
 *	If skip mode is true, then nothing is actually drawn, the commands
 *	are interpreted only for the side effect of moving the filepointer
 *	to the next page.
 */

bool
process_page(skip_mode)
register bool skip_mode;
{
	int command;	    /* current command */
	register int i;	    /* command parameter; loop index */
	int k;		    /* temporary parameter */
	int val, val2;      /* temporarys to hold command information*/
	int w;		    /* current horizontal spacing */
	int x;		    /* current horizontal spacing */
	int y;		    /* current vertical spacing */
	int z;		    /* current vertical spacing */
	int counter[10];
	int sp;		    /* stack pointer */
	static struct stack_entry stack[STACKSIZE];   /* stack */

	if(! check_dvi_file())
		return FALSE;

	if(verbose & DEBUG_SHEET)
		fprintf(stderr, "sheet %d starts at %d\n",
		  file_sheet, ftell(dvifp));

	while((command = get_unsigned(dvifp, 1)) != EOP)
	{
		switch(command)
		{

		case SET1:
		case SET2:
		case SET3:
		case SET4:
			val = get_unsigned(dvifp, command-SET1+1);
			if(! skip_mode)
				set_char(val, command);
			break;

		case SET_RULE:
			val = get_unsigned(dvifp, 4);
			val2 = get_unsigned(dvifp, 4);
			if(! skip_mode)
				set_rule(val, val2, 1);
			break;

		case PUT1:
		case PUT2:
		case PUT3:
		case PUT4:
			val = get_unsigned(dvifp,command-PUT1+1);
			if(! skip_mode)
				set_char(val, command);
			break;

		case PUT_RULE:
			val = get_unsigned(dvifp, 4);
			val2 = get_unsigned(dvifp, 4);
			if(! skip_mode)
				set_rule(val, val2, 0);
			break;

		case NOP:
			break;

		case BOP:
			/*
			 * These are the 10 counters.
			 * Discard previous page pointer.
			 */
			for(i=0; i<10; i++)
				counter[i] = get_unsigned(dvifp, 4);
			(void)get_unsigned(dvifp, 4);

			/*
			 * The first counter is the page number.
			 */
			disp_page = counter[0];
			if(file_sheet < MAX_SHEETS)
				sheet_page[file_sheet] = disp_page;

			/*
			 * Show what is happening.
			 */
			sprintf(label, "%s:   File \"%s\"   Page %d   %s",
			  DVIPAGE_LABEL, filename,
			  disp_page, (skip_mode) ? "Skipping" : "Processing");
			window_set(disp_frame,
			  FRAME_LABEL, label,
			  0);

			if(! skip_mode)
			{
				/*
				 * Clear the page
				 */
				pr_rop(page_pr, 0, 0,
				  page_pr->pr_width, page_pr->pr_height,
				  PIX_CLR, NULL, 0, 0);

				/*
				 * Mark the edges of the page
				 */
				pr_rect(page_pr, 0, 0,
				  (int)(page_w * resolution),
				  (int)(page_h * resolution),
				  3, PIX_SET, 1);

				/*
				 * Mark the nominal page window.
				 */
				if(show_page_frame)
				{
					pr_rect(page_pr, 0+origin_x, 0+origin_y,
					  (int)(page_w*resolution) - 2*origin_x,
					  (int)(page_h*resolution) - 2*origin_y,
					  1, PIX_SET, 1);
				}
			}

			h = v = w = x = y = z = 0;
			sp = 0;
			fontptr = NULL;
			break;

		case PUSH:
			if (sp >= STACKSIZE)
			{
				message("%s: Bad dvi file: stack overflow",
				  filename);
				return FALSE;
			}
			stack[sp].h = h;
			stack[sp].v = v;
			stack[sp].w = w;
			stack[sp].x = x;
			stack[sp].y = y;
			stack[sp].z = z;
			sp++;
			break;

		case POP:
			--sp;
			if (sp < 0)
			{
				message("%s: Bad dvi file: stack underflow",
				  filename);
				return FALSE;
			}
			h = stack[sp].h;
			v = stack[sp].v;
			w = stack[sp].w;
			x = stack[sp].x;
			y = stack[sp].y;
			z = stack[sp].z;
			break;

		case RIGHT1:
		case RIGHT2:
		case RIGHT3:
		case RIGHT4:
			val = get_signed(dvifp,command-RIGHT1+1);
			if(! skip_mode)
				move_over(val);
			break;

		case W0:
			if(! skip_mode)
				move_over(w);
			break;

		case W1:
		case W2:
		case W3:
		case W4:
			w = get_signed(dvifp,command-W1+1);
			if(! skip_mode)
				move_over(w);
			break;

		case X0:
			if(! skip_mode)
				move_over(x);
			break;

		case X1:
		case X2:
		case X3:
		case X4:
			x = get_signed(dvifp,command-X1+1);
			if(! skip_mode)
				move_over(x);
			break;

		case DOWN1:
		case DOWN2:
		case DOWN3:
		case DOWN4:
			val = get_signed(dvifp,command-DOWN1+1);
			if(! skip_mode)
				move_down(val);
			break;

		case Y0:
			if(! skip_mode)
				move_down(y);
			break;

		case Y1:
		case Y2:
		case Y3:
		case Y4:
			y = get_signed(dvifp,command-Y1+1);
			if(! skip_mode)
				move_down(y);
			break;

		case Z0:
			if(! skip_mode)
				move_down(z);
			break;

		case Z1:
		case Z2:
		case Z3:
		case Z4:
			z = get_signed(dvifp,command-Z1+1);
			if(! skip_mode)
				move_down(z);
			break;

		case FNT1:
		case FNT2:
		case FNT3:
		case FNT4:
			if(! skip_mode)
				set_font_num(
				  get_unsigned(dvifp,command-FNT1+1));
			break;

		case XXX1:
		case XXX2:
		case XXX3:
		case XXX4:
			k = get_unsigned(dvifp,command-XXX1+1);
			while(k--)
				get_unsigned(dvifp, 1);
			break;

		case FNT_DEF1:
		case FNT_DEF2:
		case FNT_DEF3:
		case FNT_DEF4:
			if(pre_load)
				skip_font_def(
				  get_unsigned(dvifp, command-FNT_DEF1+1));
			else
				if(! read_font_def(
				  get_unsigned(dvifp, command-FNT_DEF1+1)))
					return FALSE;
			break;

		case PRE:
			message(
  "%s: Bad dvi file: preamble found within main section.",
			  filename);
			return FALSE;

		case POST:
			fseek(dvifp, (long) -1, 1);
			last_sheet = file_sheet;

			/*
			 * We have done nothing, so there is no need to
			 * resample the page or increment the page counter.
			 */
			return FALSE;

		case POST_POST:
			message(
  "%s: Bad dvi file: postpostamble found within main section.",
			  filename);
			return FALSE;

		default:
			if(command >= FONT_00 && command <= FONT_63)
			{
				if(! skip_mode)
					set_font_num(command - FONT_00);
			}
			else if(command >= SETC_000 && command <= SETC_127)
			{
				if(! skip_mode)
					set_char(command - SETC_000, command);
			}
			else
			{
				message(
  "%s: Bad dvi file: undefined command (%d) found.",
				  filename, command);
				return FALSE;
			}
		}
	}

	/*
	 * End of page.
	 */
	if(! skip_mode)
	{
		/*
		 * Sample the page.
		 */
		sample_page();
		disp_sheet = file_sheet;
	}

	/*
	 * The file is now at the start of the next page.
	 */
	file_sheet++;
	if(file_sheet > last_known_sheet)
	{
		if(file_sheet < MAX_SHEETS)
		{
			last_known_sheet = file_sheet;
			sheet_table[file_sheet] = ftell(dvifp);
		}

		if(verbose & DEBUG_SHEET)
			fprintf(stderr, "sheet %d starts at %d\n",
			  file_sheet, ftell(dvifp));
	}

	return TRUE;
}

/*
 * Draw and Move Functions.
 * ========================
 */

/*
 * set_font_num:
 *	This routine is used to specify the font to be used in printing future
 *	chars.
 */

void
set_font_num(k)
int k;
{
	for(fontptr = hfontptr; fontptr != NULL; fontptr = fontptr->next)
	{
		if(fontptr->k == k)
		{
			fontptr->use_count++;
			return;
		}
	}

	fprintf(stderr, "I have lost a font; this cant happen\n");
	exit(1);
}

/*
 * set_char:
 */

void
set_char(c, command)
int c, command;
{
	register struct char_entry *ptr;

	ptr = &(fontptr->ch[c]);
	hh = Pix_round(h, hconv);
	vv = Pix_round(v, vconv);

	if(! ptr->where.isloaded)
		if(! load_char(fontptr, ptr))
			return;

	if(ptr->where.address.pixrectptr)
		pr_rop(page_pr, hh - ptr->xOffset + origin_x,
		  vv - ptr->yOffset + origin_y,
		  ptr->width, ptr->height, PIX_SRC | PIX_DST,
		  ptr->where.address.pixrectptr, 0, 0);

	if(command <= SET4)
		h += ptr->tfmw;

	return;
}

/*
 * set_rule:
 *	This routine will draw a rule on the screen
 */

void
set_rule(a, b, Set)
int a, b;
bool Set;
{
	int ehh, evv;

	hh = Pix_round(h, hconv);
	vv = Pix_round(v - a, vconv);
	ehh = Pix_round(h + b, hconv);
	evv = Pix_round(v, vconv);

	if(hh == ehh)
		ehh++;
	if(vv == evv)
		vv--;
	if((a > 0) && (b > 0))
		pr_rop(page_pr, hh+origin_x, vv+origin_y,
		  ehh-hh, evv-vv, PIX_SET, NULL, 0, 0);
	if(Set)
	{
		h += b;
/*		v += a; */
	}
}

/*
 * move_down:
 */

void
move_down(a)
int a;
{
	v += a;
}


/*
 * move_over:
 */

void
move_over(b)
int b;
{
	h += b;
}