#define VERSION "2.10gf"
/* modified by Peter Damron 1987 University of Washington */
/*---------------------------------------------------------------------*/
/* $Header: dvi2ps.c,v 2.1 88/06/08 14:12:24 peterd Rel2 $ */
/*---------------------------------------------------------------------*/
#ifndef lint
static char rcsid[] = "$Header: dvi2ps.c,v 2.1 88/06/08 14:12:24 peterd Rel2 $";
#endif lint
/*---------------------------------------------------------------------*/
/*
 * AUTHOR(s)
 *     Mark Senn wrote the early versions of this program 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.  Neal Holtz ported
 *     it to the Apollo, and from there to producing PostScript
 *     output. Scott Jones added intelligent font substitution.
 *     Howard Trickey made it read GF files instead of PXL ones.
 *
 */

/*---------------------------------------------------------------------*/
/* Basic method:
 * Using the local font cacheing machinery that was in the previewer,
 * we can easily manage to send the bitmap for each chracter only once.
 * Two passes are made over each page in the DVI file.  The first pass
 * simply outputs the bitmaps for all characters on that page that haven't
 * been sent before.  The second pass outputs all the character setting
 * and positioning commands.  This allows us to bracket the setting portion
 * with PostScript save's and restore's, thus reclaiming considerable
 * virtual memory after each page.
 *
 * All coordinates are output in the PostScript system (TeX origin),
 * and in integer units of rasters (300/inch) -- except for character
 * widths, which are sent as floating point numbers.
 *
 * About 2 pages of PostScript support code must be sent to the LaserWriter
 * before this stuff goes.  It is automatically included unless the
 * -h option is given.
 */

/*---------------------------------------------------------------------*/
/* Change log:
 *
 * Early 1985, (nmh) -- ported sun version to Apollo. 
 * A little later (nmh) -- changed to continue on in the event of missing
 *		font files.
 * 30-Mar-85 (nmh) -- added -a option to specify a different PXL area
 * 30-Mar-85 (nmh) -- changed default PXL area to /pxl118
 * 31-Mar-85 (nmh) -- fixed bug in OpenFontFile() regarding more than MAXOPEN
 *		      PXL files -- changed to mark files as closed in font_entry.
 *  7-Apr-85 (nmh) -- made command line argument decoding case insensitive.
 *		      cleaned up handling of DVI file name argument.
 * 30-May-85 (nmh) -- new version; hacked to output PostScript commands
 *  6-Jun-85 (nmh) -- added relative positioning (20% smaller PostScript output)
 *		      add -m option to specify mag
 * 11-Jun-85 (nmh) -- fixed bug regarding char spacings in very long "words"
 * 12-Jun-85 (nmh) -- v1.02 - process DVI pages in reverse order
 * 13-Jun-85 (nmh) -- fixed bug re PXL files getting opened too often when no PreLoad
 * 14-Jun-85 (nmh) -- font dict created in PostScript only when 1st char of font downloaded
 *		      add -m0 -mh -m1 etc. to specify magsteps
 * 16-Aug-85 (nmh) -- added -c option t0 create output file in spool area (Apollo specific)
 *		      added -h option to copy header file to output
 *		      added -o option to pass options through to PostScript
 * 20-Aug-85 (nmh) -- v1.03
 * 24-Aug-85 (nmh) -- add -q option (for quiet operation).
 *		      changed -o option to check PostScript option
 *		      changed to output coordinates in TeX system (but
 *		      scaled to raster units) -- (0,0) at 1" in and down from
 *		top left (better for use with different size paper).
 *		   -- v2.00
 * 25-Aug-85 (nmh) -- added dictionary enclosure to Tex.ps, and output
 *		suitable prolog here.
 * 26-Aug-85 (nmh) -- changes to tex.ps to support Macintosh documents.
 * 14-Sep-85 (nmh) -- added keyword=value decoding to \special;
 * 15-Sep-85 (nmh) -- added -i file option.
 * 23-Sep-85 (saj) -- added font substitution for case when font is
 *		      unavailable at requested mag. (a frequent occurrence
 *		      with some macro packages like LaTeX)
 *  7-Jun-86 (hwt) -- use gf files ("pxl" survives in variable names)
 *  8-Aug-86 (rkf) -- merged together the pxl and the gf versions.  Define
 *		      USEPXL should you want PXL files---gf is the default
 *  14-Dec-87 (peterd) -- Changed sub-directory structure used by findfile.
 *			Style names are used for subdirectories, not style
 *			plus design size.
 *			Changed font substitutions to compare <design size>
 *			* <mag size>, not design and mag separately.
 *			Changed all progress messages to go through Progress().
 *			Changed all warning messages to go through Warning().
 *			Note that Stats and Debug still use stderr.
 *			Added support for font paths (multiple -a options).
 *			Added support for environment variable for font path.
 */

/* This version purports to drive a PostScript device (slowly) */

/**********************************************************************/
/***********************  external definitions  ***********************/
/**********************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <strings.h>
#include <signal.h>
#include <sys/file.h>
#include <sys/param.h>
#include "dvi2ps.h"
#include "font.h"
#include "commands.h"
#include "findfile.h"
#include "gf.h"

/* why is this not included from c libs? */
extern char *getenv();

#ifdef NOTDEF
extern int  access();
extern char *malloc();
extern int  free();
extern char *logname();
#endif NOTDEF

/**********************************************************************/
/************************  Global Definitions  ************************/
/**********************************************************************/

/* default font area (directory) */
#ifndef FONTAREA
#define  FONTAREA	"/usr/local/lib/tex/fonts"
#endif FONTAREA

/* name for environment variable for tex font path/directory */
#ifndef ENVTEXFONTS
#define ENVTEXFONTS	"TEXFONTS"
#endif ENVTEXFONTS

/* default header file location */
#ifndef HDRFILE
#define HDRFILE		"/usr/local/lib/tex.ps"
#endif HDRFILE

#ifdef apollo
#ifndef SPOOLFILE
#define SPOOLFILE	"/local/spool/laser/tex."
#endif not SPOOLFILE
#define MAXFLEN 28
#endif apollo

#ifdef apollo
#define  MAXOPEN	 45	/* limit on number of open font files */
#else !apollo
#define  MAXOPEN	 12	/* limit on number of open font files */
#endif !apollo

#ifdef apollo	   /* define for enabling of -c option (create output file) */
#define CREOPT
#endif apollo

#define USEGLOBALMAG 1			/* when defined, the dvi global */
					/*   magnification is applied   */
	/* We can leave USEGLOBALMAG undefined when we have a limited
	   number of font magnifications (at 300dpi) available.  Otherwise, we
	   will simply complain about missing font files
	 */
/* #undef USEGLOBALMAG */
	/* define for "optimal" relative postioning, rather
	   than absolute.  Relative can reduce size of postcript
	   output 20% (and reduce print time by almost as much */

#define USERELPOS	1 

#define DEBUG   1			/* for massive printing of input */
					/* trace information; select by -d */
					/* option after filename: */
					/* dviview filename -d */
#define STATS		/* to enable statistics reporting via -s option */

#define MACROS		/* use macros for some of the low level routines */

#define BINARYOPEN fopen		/* byte-oriented host version */

#define ARITHRSHIFT 1	   /* define if ">>" operator is a */
					/* sign-propagating arithmetic  */
					/*   right shift */

#define  RESOLUTION      300
#define  hconvRESOLUTION 300
#define  vconvRESOLUTION 300

#ifdef NOTDEF
#define  MAXFONTCHARS	128
#define  PXLID		1001
#endif NOTDEF

#define  DVIFORMAT	2

#define  STACKSIZE	100
#define  STRSIZE	257
#define  PATHSIZE	50

#define  NONEXISTANT	-1	/* offset for font files not found */
#define  NO_FILE	(FILE *)-1

#define  TRUE		1
#define  FALSE		0

/* define constants for fseek() */
#define FROM_START	0
#define FROM_HERE	1
#define FROM_END	2

/**********************************************************************/
/***********************  external definitions  ***********************/
/**********************************************************************/
/* macro definitions */

#define NEW(A) ((A *) malloc(sizeof(A)))

#define EQ(a,b) (strcmp(a,b)==0)

			/* output a formatted string */
#define EMIT      fprintf
			/* output a simple string */
#define EMITS(s)  fputs(s,G_outfp)
			/* output an escaped octal number */
#define EMITO(c)  PutOct(c)
			/* output a decimal integer */
#define EMITN(n)  PutInt(n)
			/* output a byte value in Hex */
#define EMITH(h)  putc(*(digit+((h>>4)&0xF)),G_outfp),\
		  putc(*(digit+(h&0xF)),G_outfp)
			/* output a single character */
#define EMITC(c)  putc(c,G_outfp)
			/* output a scaled X dimension */
#define EMITX(x)  PutInt(PixRound(x,G_hconv))
			/* output a scaled Y dimension */
#define EMITY(y)  PutInt(PixRound(y,G_vconv))

			/* formatted i/o was killing us, so build some tables */
char    *digit = "0123456789ABCDEF";

/* intended as replacements to NoSignExtend */
/* may not be transportable */
#define GET1(fp)  (int)(getc(fp))
#define GET2(fp)  NoSignExtend(fp,2)
#define GET3(fp)  NoSignExtend(fp,3)
#define GET4(fp)  NoSignExtend(fp,4)

#ifdef NOTDEF
/* may not be transportable or even work */
#define GET2(fp)  ((getc(fp)<<8) | getc(fp))
#define GET3(fp)  ((getc(fp)<<16) | (getc(fp)<<8) | getc(fp))
#define GET4(fp)  ((getc(fp)<<24) | (getc(fp)<<16) | (getc(fp)<<8) | getc(fp))
#define UNGET1(fp,ch)  fseek(fp, -1, FROM_HERE)
#endif NOTDEF

#define GETN(fp,n)  NoSignExtend(fp,n)
#define GETSN(fp,n)  SignExtend(fp,n)

#define UNGET1(fp,ch)  ungetc(ch,fp)
#define SKIP(fp,n)  fseek(fp,n,FROM_HERE)

/*---------------------------------------------------------------------*/
/* convertion to macro */

#ifdef MACROS
#define PixRound(x,conv)  ((int)((x + (conv >> 1)) / conv))
#define MoveDown(a)  G_v += a;
#define MoveOver(b)  G_h += b;
#endif MACROS

/**********************************************************************/
/*************************  Global Variables  *************************/
/**********************************************************************/

/* file name stuff */
FILE *G_dvifp  = NULL;		/* DVI file pointer */
FILE *G_outfp = NULL;		/* output file */
char G_filename[STRSIZE];	/* DVI file name */
char G_rootname[STRSIZE];	/* DVI filename without extension */
char G_Logname[STRSIZE];	/* name of log file, if created */
int  G_logging = FALSE;		/* Are we logging into a file? */
FILE *G_logfp = NULL;		/* log file pointer (for errors) */
char G_progname[STRSIZE];	/* this program name (dvi2ps) */
int  G_quiet = FALSE;		/* for quiet operation */
int  G_nowarn = FALSE;		/* don't print out warnings */
long G_ppagep;			/* previous page pointer */
#ifdef CREOPT
int  G_create = FALSE;		/* create an output file in spool area ? */
char G_outfname[STRSIZE];	/* name of output file */
#endif CREOPT

/* status stuff */
int  G_errenc = FALSE;		/* has an error been encountered? */
int  G_ndone = 0;		/* number of pages converted */

/* measurement stuff */
int  G_hconv;			/* converts DVI units to pixels - horiz. */
int  G_vconv;			/* converts DVI units to pixels - vertical */
int  G_h;			/* current horizontal position */
int  G_hh = 0;			/* current h on device */
int  G_v;			/* current vertical position */
int  G_vv = 0;			/* current v on device */
int  D_mag;			/* magnification(*1000) specified in preamble */
int  D_num;			/* numerator specified in preamble */
int  D_den;			/* denominator specified in preamble */

/* font stuff */
int   G_nopen = 0;		/* number of open font files (pxl/gf) */
struct open_font_list G_font_files[MAXOPEN];
struct font_entry *G_prevfont=NULL;	/* font_entry pointer, previous font */
struct font_entry *G_fontptr;		/* font_entry pointer, current font */
struct font_entry *G_hfontptr=NULL;	/* head of font_entry pointer list */
struct font_entry *G_pfontptr = NULL;	/* previous font_entry pointer */
				/* list of open font file identifiers */
long  G_postambleptr;		/* Pointer to the postamble */
FILE *G_fontfp;			/* font file pointer */
char *G_fontpath[PATHSIZE];	/* a list of directories for fonts */
int   G_fontpathsize = 0;		/* number of directories on font path */

/* command line stuff */
int  CL_FirstPage = -1000000;     /* first page to print (uses count0)   */
int  CL_LastPage = 1000000;       /* last page to print	    */
int  CL_header = TRUE;		/* copy header file to output? */
int  CL_ncopies = 1;		/* number of copies to print */
int  CL_nif = 0;		/* number of files to include */
char *CL_Ifile[PATHSIZE];	/* files to include */
int  CL_nps = 0;		/* number of PostScript commands to send */
char *CL_PScmd[PATHSIZE];	/* PostScript commands to send */
int  CL_Reverse = TRUE;	/* process DVI pages in reverse order ? */
int  CL_usermag = 0;	      /* user specified magnification */
int  CL_PreLoad = TRUE;		/* preload the font descriptions? */

/* statistics stuff */
#ifdef STATS
int  Stats = FALSE;		/* are we reporting stats ? */
int  S_nbpxl = 0;		/* # of bytes of pixel data */
int  S_onbpx = 0;		/* "optimal" number of bytes of pixel data  */
int  S_ndc = 0;			/* # of different characters typeset*/
int  S_tnc = 0;			/* total # of chars typeset */
int  S_nbpx0, S_ndc0, S_tnc0;	/* used for printing incremental changes per dvi page */
#endif STATS

#ifdef DEBUG
int Debug = 0;			/* print debugging info if true */
#endif DEBUG

/*---------------------------------------------------------------------*/

/**********************************************************************/
/*******************************  main  *******************************/
/**********************************************************************/

main(argc, argv)
int argc;
char *argv[];

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


    register int command;	/* current command */
    int count[10];		/* the 10 counters at begining of each page */
    long cpagep;		/* current page pointer */
    int  Emitting = FALSE;	/* are we outputting typsetting instructions? */
    register int i;		/* command parameter; loop index */
    register int k;		/* temporary parameter */
				/* what happens on pass 0? */
    int PassNo = 0;		/* which pass over DVI page? (0,1) */
    int SkipMode = FALSE;	/* in skip mode flag (page not converted) */
    register int sp;		/* stack pointer */
    struct stack_entry stack[STACKSIZE];	/* stack */
    char SpecialStr[STRSIZE];	/* "\special" strings	  */
    char *tchp;			/* temporary character pointer */
    register int val;		/* temporary to hold command information */
    register int val2;		/* temporary to hold command information */
    int w;			/* current horizontal spacing (w-reg) */
    int x;			/* current horizontal spacing (x-reg) */
    int y;			/* current vertical spacing (y-reg) */
    int z;			/* current vertical spacing (z-reg) */

    /* set program name - for error messages */
    if ((tchp = rindex(argv[0],'/')) == 0) {
    	strcpy(G_progname, argv[0]);
    } else {
    	strcpy(G_progname, tchp+1);
    }

    /* set up default font directory on path */
    G_fontpath[0] = FONTAREA;
    G_fontpathsize = 1;

    /* interpret the command line */
    DecodeArgs(argc,argv);

#ifdef apollo
    set_sbrk_size( 2048*1024 );
#endif apollo

    /* check for presence of pre-amble */
    CheckPreAmble();

    /* create output file */
#ifdef CREOPT
    if( G_create )
	G_outfp = OpenOutput();
    else
#endif CREOPT
	G_outfp = stdout;

/* it is important that these be the very first things output !!! */

    /* copy the postscript header file to output */
    if( CL_header ) {
	CopyFile( HDRFILE );
    }

    /* copy all included user files - from command line */
    for( i=0; i<CL_nif; i++ ) {
	CopyFile( CL_Ifile[i] );
    }

    /* start output */
    EMITS("TeXDict begin @start\n");
    EMIT(G_outfp, "%%%%Title: %s\n", G_filename);
    EMIT(G_outfp, "%%%%Creator: %s\n", G_progname);
    EMITS("%%EndProlog\n");

    /* prefix valid PostScript options with a "@" - from command line */
    for (i=0; i<CL_nps; i++) {
	if( ChkOpt(CL_PScmd[i]) ) {
	    EMIT(G_outfp, "@%s\n", CL_PScmd[i]);
	} else {
	    Fatal("%s is an invalid PostScript option\n", CL_PScmd[i] );
	}
    }

    /* multiple copies ? */
    if (CL_ncopies > 1) EMIT(G_outfp, "%d @copies\n", CL_ncopies);

    /* read the pre-amble (and postamble) info */
    ReadPreAmble();

    PassNo = 0;
    
    /* interpret the dvi file byte code commands */
    while (TRUE)

	switch (command=GET1(G_dvifp))  {

	case SET1:case SET2:case SET3:case SET4:
	    val = GETN(G_dvifp, command-SET1+1);
	    if (!SkipMode) SetChar(val, command, PassNo);
	    break;

	case SET_RULE:
	    val = GET4(G_dvifp);
	    val2 = GET4(G_dvifp);
	    if (Emitting) SetRule(val, val2, TRUE);
	    break;

	case PUT1:case PUT2:case PUT3:case PUT4:
	    val = GETN(G_dvifp,command-PUT1+1);
	    if (!SkipMode) SetChar(val, command, PassNo);
	    break;

	case PUT_RULE:
	    val = GET4(G_dvifp);
	    val2 = GET4(G_dvifp);
	    if (Emitting) SetRule(val, val2, FALSE);
	    break;

	case NOP:
	    break;

	/* beginning of a page */
	case BOP:
	    cpagep = ftell(G_dvifp) - 1;
	    for (i=0; i<=9; i++) {
		count[i] = GET4(G_dvifp);
	    }
	    G_ppagep = GET4(G_dvifp);

	    G_h = G_v = w = x = y = z = 0;
	    G_hh = G_vv = 0;
	    sp = 0;
	    G_fontptr = NULL;
	    G_prevfont = NULL;

	    if( count[0] < CL_FirstPage || count[0] > CL_LastPage ) {
		/* skip this page */
		SkipMode = TRUE;
	    } else {
		SkipMode = FALSE;
	    }

	    Emitting = (PassNo != 0) && !SkipMode;

	    if( !SkipMode ) {
		if( PassNo == 0) {
			EMIT(G_outfp,"%d @bop0\n", count[0]);
#ifdef STATS
			if( Stats ) {
				S_ndc0 = S_ndc;
				S_tnc0 = S_tnc;
				S_nbpx0 = S_nbpxl;
			}
#endif STATS
			Progress("[%d",count[0]);
		} else {
			EMIT(G_outfp,"%d @bop1\n", count[0]);
		}
	    }
	    break;

	/* end of this page */
	case EOP:
	    if( !SkipMode ) {
		if( PassNo == 0 ) {
		    /* start second pass on current page */
		    fseek(G_dvifp,cpagep,FROM_START);
		    PassNo = 1;
		} else {
		    /* end of second pass, and of page processing */
		    EMITS("@eop\n");
#ifdef STATS
		    if( Stats )
			fprintf(stderr," - %d total ch,  %d diff ch,  %d pxl bytes\n",
				S_tnc-S_tnc0, S_ndc-S_ndc0, S_nbpxl-S_nbpx0);   
#endif STATS
		    Progress("] ");
		    G_ndone += 1;
		    if ((G_ndone % 10) == 0) Progress("\n");
		    PassNo = 0;
		}
	    } else {
		/* just finished pass 1 (second pass) on this page */
		/* next page starts in pass 0 */
		PassNo = 0;
	    }
	    if( PassNo == 0 && CL_Reverse ) { 
		/* go back to previous page, or done */
		if( G_ppagep > 0 ) {
		    fseek(G_dvifp, G_ppagep, FROM_START);
		} else {
		    AllDone();
		}
	    }
	    break;

	case PUSH:
	    if (sp >= STACKSIZE)
		Fatal("stack overflow");
	    stack[sp].h = G_h;
	    stack[sp].v = G_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)
		Fatal("stack underflow");
	    G_h = stack[sp].h;
	    G_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 = GETSN(G_dvifp,command-RIGHT1+1);
	    if (Emitting) MoveOver(val);
	    break;

	/* move right with the w-reg value */
	case W0:
	    if (Emitting) MoveOver(w);
	    break;

	case W1:case W2:case W3:case W4:
	    w = GETSN(G_dvifp,command-W1+1);
	    if (Emitting) MoveOver(w);
	    break;

	/* move right with the x-reg value */
	case X0:
	    if (Emitting) MoveOver(x);
	    break;

	case X1:case X2:case X3:case X4:
	    x = GETSN(G_dvifp,command-X1+1);
	    if (Emitting) MoveOver(x);
	    break;

	case DOWN1:case DOWN2:case DOWN3:case DOWN4:
	    val = GETSN(G_dvifp,command-DOWN1+1);
	    if (Emitting) MoveDown(val);
	    break;

	/* move down with the y-reg value */
	case Y0:
	    if (Emitting) MoveDown(y);
	    break;

	case Y1:case Y2:case Y3:case Y4:
	    y = GETSN(G_dvifp,command-Y1+1);
	    if (Emitting) MoveDown(y);
	    break;

	/* move down with the z-reg value */
	case Z0:
	    if (Emitting) MoveDown(z);
	    break;

	case Z1:case Z2:case Z3:case Z4:
	    z = GETSN(G_dvifp,command-Z1+1);
	    if (Emitting) MoveDown(z);
	    break;

	case FNT1:case FNT2:case FNT3:case FNT4:
	    if (!SkipMode) {
		SetFontNum(GETN(G_dvifp,command-FNT1+1), Emitting);
	    }
	    break;

	case XXX1:case XXX2:case XXX3:case XXX4:
	    /* get the special string from the dvi file */
	    k = GETN(G_dvifp,command-XXX1+1);
	    GetBytes(G_dvifp, SpecialStr, k);
	    if (Emitting) DoSpecial(SpecialStr, k);
	    break;

	case FNT_DEF1:case FNT_DEF2:case FNT_DEF3:case FNT_DEF4:
	    k = GETN(G_dvifp, command-FNT_DEF1+1);
	    if (CL_PreLoad || HasBeenRead(k)) {
		SkipFontDef();
	    } else {
		ReadFontDef(k);
	    }
	    break;

	case PRE:
	    Fatal("PRE occurs within file\n");
	    break;

	case POST:
	    if (CL_Reverse) {
		Warning("internal - unexpected POST in dvi file\n");
	    }
	    AllDone();
	    break;

	case POST_POST:
	    Fatal("POST_POST with no preceding POST\n");
	    break;

	default:
	    if (command >= FONT_00 && command <= FONT_63) {
		if (!SkipMode) SetFontNum(command - FONT_00, Emitting);
	    } else if (command >= SETC_000 && command <= SETC_127) {
		if (!SkipMode) SetString(command, PassNo);
	    } else {
		Fatal("%d is an undefined DVI command\n", command);
	    }
	    break;
	}
}

/*-->ActualFactor*/
/**********************************************************************/
/**************************  ActualFactor  ****************************/
/**********************************************************************/

float		/* compute the actual size factor given the approximation */
ActualFactor(unmodsize)
int unmodsize;		/* actually factor * 1000 */
{
    float realsize;	/* the actual magnification factor */

    realsize = (float)unmodsize / 1000.0;
    /* a real hack to correct for rounding in some cases--rkf */
    if(unmodsize==1095) realsize = 1.095445;	/*stephalf*/
    else if(unmodsize==1315) realsize=1.314534;	/*stepihalf*/
    else if(unmodsize==1577) realsize=1.577441;	/*stepiihalf*/
    else if(unmodsize==1893) realsize=1.892929;	/*stepiiihalf*/
    else if(unmodsize==2074) realsize=2.0736;	/*stepiv*/
    else if(unmodsize==2488) realsize=2.48832;  /*stepv*/
    else if(unmodsize==2986) realsize=2.985984;	/*stepiv*/
    /* the remaining magnification steps are represented with sufficient
	   accuracy already */
    return(realsize);
}


/*-->AllDone*/
/**********************************************************************/
/****************************** AllDone  ******************************/
/**********************************************************************/

void
AllDone()
{
    register struct font_entry *p;

    EMITS("@end\n");
    Progress("\n");

#ifdef CREOPT
    if( G_create ) {
	fclose(G_outfp);
	Progress("Output written on \"%s\"\n", G_outfname);
    }
#endif CREOPT

#ifdef STATS
    if (Stats) {
	fprintf(stderr,"Total-chars   diff-chars bitmap-bytes #open #close\n");
	fprintf(stderr,"       #   %%       #   %%       #   %%\n");
	fprintf(stderr,"-------- ---  ------ ---  ------ ---  ----- ------\n");
	for (p = G_hfontptr; p != NULL; p = p->next) {
	    fprintf(stderr,"%8d%4d",p->ncts,(100*p->ncts + S_tnc/2)/S_tnc);
	    fprintf(stderr,"%8d%4d",p->ncdl,(100*p->ncdl + S_ndc/2)/S_ndc);
	    fprintf(stderr,"%8d%4d",p->nbpxl,(100*p->nbpxl + S_nbpxl/2)/S_nbpxl);
	    fprintf(stderr,"  %5d %5d",p->nopen,p->nclose);
	    fprintf(stderr,"  %s\n",p->psname);
	}
	fprintf(stderr,"\nTotal number of characters typeset: %d\n",S_tnc);
	fprintf(stderr,"Number of different characters downloaded: %d\n",S_ndc);
	fprintf(stderr,"Number of bytes of bitmap data downloaded: %d\n",S_nbpxl);
	fprintf(stderr,"Optimal # of bytes of bitmap data: %d\n",S_onbpx);
    }
#endif STATS

    if (G_logging) {
	if (G_logfp == NULL) {
	    Fatal("missing log file\n");
	}
	fclose(G_logfp);
	Progress("Log file created\n");
    }

    exit(G_errenc);
}


/*-->ChkOpt*/   /* check a user supplied option for validity */
/*********************************************************************/
/****************************** ChkOpt *******************************/
/*********************************************************************/

#define ISOPT(s) if( EQ(str,s) ) return( TRUE )

int
ChkOpt( str )
char    *str;
{
/*	lcase(str);  doesn't work */

	ISOPT("note");	  /* its a shame to build this into the program */
	ISOPT("letter");
	ISOPT("legal");
	ISOPT("landscape");
	ISOPT("manualfeed");
	return( FALSE );
}


/*-->CopyFile*/
/*********************************************************************/
/***************************** CopyFile ******************************/
/*********************************************************************/
/* copy a file straight through to output */

void
CopyFile(filename)
	char *filename;
{
	register FILE *spfp;
	register int ch;

	if( (spfp=fopen(filename,"r")) == NULL ) {
		Warning("Unable to open file %s\n", filename );
		return;
	}
	Progress("[%s", filename);
	while ((ch = getc(spfp)) != EOF) {
		EMITC(ch);
	}	      
	fclose(spfp);
	Progress("] ");
}


/*-->DecodeArgs*/
/*********************************************************************/
/***************************** DecodeArgs ****************************/
/*********************************************************************/
/* this is a pretty brain-damaged command line processor */
/* but good enough for now */

void
DecodeArgs( argc, argv )
    int argc;
    char *argv[];
{
    int argind;			/* argument index for flags */
    char curarea[STRSIZE];	/* current file area */
    char curname[STRSIZE];	/* current file name */
    register char *tcp;		/* temporary character pointer */
    register char *tcp1;	/* temporary character pointer */


    /* now process command line arguments */
    argind = 1;
    while (argind < argc) {
	tcp = argv[argind];
	if (*tcp == '-') {
	    /* this is a command line option, which one? */
	    switch(*++tcp) {

		case 'a':       /* a selects different font area */
		    if( ++argind >= argc ) Fatal("No argument following -a\n");
		    if (!access(argv[argind],F_OK)) {
			if (G_fontpathsize < PATHSIZE-1) {
			    if (G_fontpathsize < 1) Fatal("internal - bad font path\n");
			    G_fontpath[G_fontpathsize-1] = argv[argind];
			    G_fontpath[G_fontpathsize] = FONTAREA;
			    G_fontpathsize += 1;
			}
		    } else {
			Warning("cannot access font directory %s\n",argv[argind]);
		    }
		    break;
		case 'F':       /* F selects different font area */
		    if( ++argind >= argc ) Fatal("No argument following -F\n");
		    if (!access(argv[argind],F_OK)) {
			if (G_fontpathsize < PATHSIZE-1) {
			    if (G_fontpathsize < 1) Fatal("internal - bad font path\n");
			    G_fontpath[G_fontpathsize-1] = argv[argind];
			    G_fontpath[G_fontpathsize] = FONTAREA;
			    G_fontpathsize += 1;
			}
		    } else {
			Warning("cannot access font directory %s\n",argv[argind]);
		    }
		    break;
#ifdef CREOPT
		case 'c':       /* create an output file in spool area */
		    G_create = TRUE;
		    break;
#endif CREOPT
#ifdef DEBUG
		case 'd':	/* d selects Debug output */
		    Debug = TRUE;
		    break;
#endif DEBUG
		case 'f':       /* next arg is starting pagenumber */
		    if( ++argind >= argc || sscanf(argv[argind], "%d", &CL_FirstPage) != 1 )
			Fatal("Argument to -f is not a valid integer\n", 0);
		    break;

		case 'h':       /* don't copy PostScript header file through to output */
		    CL_header = FALSE;
		    break;

		case 'i':       /* next arg is a PostScript file to copy */
		    if( ++argind >= argc ) Fatal("No argument following -i\n");
		    CL_Ifile[CL_nif++] = argv[argind];
		    break;

		case 'l':	/* enable logging into a log file */
		    G_logging = TRUE;
		    break;
#ifdef USEGLOBALMAG
		case 'm':       /* specify magnification to use */
		    switch (*++tcp) {

		    case '\0':       /* next arg is a magnification to use */
			if (++argind >= argc || sscanf(argv[argind], "%d", &CL_usermag) != 1)
			    Fatal("Argument to -m is not a valid integer\n", 0);
			break; 
		    case '0': CL_usermag = 1000; break;
		    case 'h': CL_usermag = 1095; break;
		    case '1': CL_usermag = 1200; break;
		    case '2': CL_usermag = 1440; break;
		    case '3': CL_usermag = 1728; break;
		    case '4': CL_usermag = 2074; break;
		    case '5': CL_usermag = 2488; break;
		    default: Fatal("%c is a bad mag step\n", *tcp);
		    }
		    break;
#endif USEGLOBALMAG
		case 'n':       /* next arg is number of copies to print */
		    if( ++argind >= argc || sscanf(argv[argind], "%d", &CL_ncopies) != 1 )
			Fatal("Argument to -n is not a valid integer\n", 0);
		    break;    

		case 'o':       /* next arg is a PostScript command to send */
		    if( ++argind >= argc )
			Fatal("No argument following -o\n", 0);
		    CL_PScmd[CL_nps++] = argv[argind];
		    break;

		case 'p':	/* p prohibits pre-font loading */
		    CL_PreLoad = 0;
		    CL_Reverse = FALSE;    /* must then process in forward order */
		    break;

		case 'q':       /* quiet operation */
		    G_quiet = TRUE;
		    break;

		case 'r':       /* don't process pages in reverse order */
		    CL_Reverse = FALSE;
		    break;
#ifdef STATS
		case 's':       /* print some statistics */
		    Stats = TRUE;
		    break;
#endif STATS
		case 't':       /* next arg is ending pagenumber */
		    if( ++argind >= argc || sscanf(argv[argind], "%d", &CL_LastPage) != 1 )
			Fatal("Argument to -t is not a valid integer\n", 0);
		    break;

		case 'w':       /* don't print out warnings */
		    G_nowarn = TRUE;
		    break;

		default:
		    Warning("%c is not a legal flag\n", *tcp);
	    }
	} else {
	    /* this is not a command line option, must be the dvi file name */
	    if (G_dvifp != NULL) {
		Warning("more than one dvi input file - ignoring \"%s\"\n",
				G_filename);
		fclose(G_dvifp);
	    }
	    /* split into directory + file name */
	    tcp = rindex(argv[argind], '/');
	    /* store the directory name */
	    if (tcp == NULL)  {
		curarea[0] = '\0';
		tcp = argv[argind];
	    } else  {
		strcpy(curarea, argv[argind]);
		curarea[tcp-argv[argind]+1] = '\0';
		tcp += 1;
	    }
	
	    /* store the file name */
	    strcpy(curname, tcp);

	    /* split into file name + extension */
	    tcp1 = rindex(tcp, '.');
	    if (tcp1 == NULL) {
		strcpy(G_rootname, curname);
		strcat(curname, ".dvi");
	    } else {
		*tcp1 = '\0';
		strcpy(G_rootname, curname);
		*tcp1 = '.';
	    }
	
	    /* store the full path name */
	    strcpy(G_filename, curarea);
	    strcat(G_filename, curname);
	
	    /* open the dvi input file */
	    if ((G_dvifp=BINARYOPEN(G_filename,"r")) == NULL)  {
		Fatal("can't open DVI file \"%s\"\n", G_filename);
	    }
	
	    if (G_logging) {
		strcpy(G_Logname, curname);
		strcat(G_Logname, ".log");
		if ((G_logfp=BINARYOPEN(G_Logname,"w+")) == NULL)  {
		    Fatal("can't open log file \"%s\"\n", G_Logname);
		}
	    }
	}
	argind++;
    }

    /* check that we got an input file to process */
    if (G_dvifp == NULL)  {
	Fatal("usage: %s [-a area] [-c] [-h] [-o option] [-p] [-s] [-r] [-f n] [-t n] [-m{0|h|1|2|3|4| mag}] [-a fontarea] dvifile\n\n", 
		G_progname);
    }

    /* now check the environment for default values */
    /* check for font area (directory/path) */
    if ((tcp = getenv(ENVTEXFONTS)) != (char *)NULL) {
	/* split out the path name components */
	/* note this code modifies the string returned by getenv() */
	while ((tcp1 = index(tcp,':')) != 0) {
	    *tcp1 = '\0';
	    if ((tcp == tcp1) || (*tcp == NULL)) {
		Warning("null entry in font path environment variable\n");
		continue;
	    }
	    if (access(tcp,F_OK)) {
		Warning("cannot access font directory %s\n",tcp);
		continue;
	    }
	    if (G_fontpathsize < PATHSIZE-1) {
		if (G_fontpathsize < 1) Fatal("internal - bad font path\n");
		G_fontpath[G_fontpathsize-1] = tcp;
		G_fontpathsize += 1;
	    } else {
		Warning("too many entries in font path environment variable, ignoring %s\n",tcp);
	    }
	    tcp = tcp1 + 1;
	}
	if ((tcp == tcp1) || (*tcp == NULL)) {
	    Warning("null entry in font path environment variable\n");
	} else if (access(tcp,F_OK)) {
	    Warning("cannot access font directory %s\n",tcp);
	} else {
	    if (G_fontpathsize < PATHSIZE-1) {
		if (G_fontpathsize < 1) Fatal("internal - bad font path\n");
		G_fontpath[G_fontpathsize-1] = tcp;
		G_fontpathsize += 1;
	    } else {
		Warning("too many entries in font path environment variable, ignoring %s\n",tcp);
	    }
	}
	G_fontpath[G_fontpathsize-1] = FONTAREA;
    }
}


/*-->DoConv*/
/*********************************************************************/
/********************************  DoConv  ***************************/
/*********************************************************************/

int DoConv(num, den, convResolution)
{
    register float conv;
    conv = ((float)num/(float)den) * 
#ifdef USEGLOBALMAG
/*	ActualFactor(D_mag) * why was this in as Actual Factor?  jls */
	((float) D_mag/1000.0) *
#endif USEGLOBALMAG
	((float)convResolution/254000.0);
    return((int) (1.0 / conv + 0.5));
}


/*-->DoSpecial*/
/*********************************************************************/
/*****************************  DoSpecial  ***************************/
/*********************************************************************/

typedef enum {None, String, Integer, Number, Dimension} ValTyp;

typedef struct {
	char    *Key;	   /* the keyword string */
	char    *Val;	   /* the value string */
	ValTyp  vt;	     /* the value type */
	union {	 /* the decoded value */
	    int  i;
	    float n;
	    } v;
	} KeyWord;

typedef struct {
	char    *Entry;
	ValTyp  Type;
	} KeyDesc;

#define PSFILE 0
KeyDesc KeyTab[] = {{"psfile", String},
		    {"hsize", Dimension},
		    {"vsize", Dimension},
		    {"hoffset", Dimension},
		    {"voffset", Dimension},
		    {"hscale", Number},
		    {"vscale", Number}};

#define NKEYS (sizeof(KeyTab)/sizeof(KeyTab[0]))

/*-->DoSpecial*/
/*********************************************************************/
/*****************************  DoSpecial  ***************************/
/*********************************************************************/
/* interpret a \special command, made up of keyword=value pairs */

void
DoSpecial( str, n )
    char    *str;
    int n;
{ 
    char spbuf[STRSIZE]; 
    char *sf = NULL;
    KeyWord k;
    int i;

    str[n] = '\0';
    spbuf[0] = '\0';

    SetPosn(G_h, G_v);
    EMITS("@beginspecial\n");

    /* get all keyword-value pairs */
    while( (str=GetKeyStr(str,&k)) != NULL ) {
	/* for compatibility, single words are taken as file names */
	if( k.vt == None && access(k.Key,F_OK) == 0) {
	    if (sf) {
		Warning("More than one \\special file name given. %s ignored", sf );
	    }
	    strcpy(spbuf, k.Key);
	    sf = spbuf;
	} else if( GetKeyVal( &k, KeyTab, NKEYS, &i ) && i != -1 ) {
	    if( i == PSFILE ) {
		if (sf) {
		    Warning("More than one \\special file name given. %s ignored", sf );
		}
		strcpy(spbuf, k.Val);
		sf = spbuf;
	    } else {
		/* the keywords are simply output as PS procedure calls */
		EMIT(G_outfp, "%f @%s\n", k.v.n, KeyTab[i].Entry);
	    }
	} else {
	    Warning("Invalid keyword or value in \\special - \"%s\" ignored", k.Key );
	}
    }

    EMITS("@setspecial\n");

    if (sf) {
	CopyFile( sf );
    } else {
	Warning("No special file name provided.");
    }

    EMITS("@endspecial\n");
}


/*-->EmitCharBitMap*/
/**********************************************************************/
/************************  EmitCharBitMap  ****************************/
/**********************************************************************/
/* output a character bitmap */

void
EmitCharBitMap(ch, ce)
	int ch;
	register struct char_entry *ce;
{
	register unsigned char *sl;
	register struct font_entry *fontp;
#ifdef USEPXL
	int i;
	register int j;
	register int cc;
	int nbpl, nwpl;
#else not USEPXL
	register int cc;
	register int i;
	int nbpl;		/* number of bytes per line of bitmap */
#endif USEPXL
	float cw;	/* char width, in "dots" */
		    /* we rely on PostScript maintaining sufficient accuracy */

	/* sanity check */
	fontp = G_fontptr;
	if (fontp == NULL) {
		Fatal("internal - null pointer in EmitCharBitMap()\n");
	}

	/* Output in PostScript coord system (y +ive up, x +ive right)
	   (0,0) of char bitmap at lower left.  Output scan lines
	   from bottom to top */

	/* open font dict before first char */
	if (fontp->ncdl == 0) {
	    EMIT(G_outfp,"[ %d ] /%s @newfont\n",
		fontp->font_mag,fontp->psname);
	}
 
	/* because this isn't done on pass 0 */
	if (fontp != G_prevfont) {
		EMIT(G_outfp,"%s @sf\n", fontp->psname);
		G_prevfont = fontp;
	}

	/* start the bitmap */
	EMITS("[<");
	cc = 2;

#ifdef USEPXL
	nbpl = (ce->width + 7) >> 3;
	nwpl = (ce->width + 31) >> 5;
	for(i = ce->height-1;  i >= 0;  i--) {
		sl = (unsigned char *)(ce->where.address.pixptr + i*nwpl);
		for(j = 0;  j < nbpl;  j++, sl++) {
			if( cc > 100 ) {
				EMITS("\n  ");   cc = 2;
			}
			EMITH(*sl);
			cc += 2;
		}
	}
#else not USEPXL
	/* assume we just called gf_readbits() */
	nbpl = (gf_num_cols + 7) >> 3;
	for(i=0, sl=gf_bits; i<gf_num_bytes; i++, sl++) {
		if (cc > 100) {
		    EMITS("\n  ");
		    cc = 2;
		}
		EMITH(*sl);
		cc += 2;
	}
#endif USEPXL

	/* compute character width */
	cw = (float)ce->tfmw / (float)G_hconv;

#ifdef USEPXL
	EMIT(G_outfp,"> %d %d %d %d %.3f] %d @dc\n", 
	     nbpl<<3, ce->height, ce->xOffset,
	     (((int)ce->height)-ce->yOffset)-1, cw, ch);
#else not USEPXL
	EMIT(G_outfp,"> %d %d %d %d %.3f] %d @dc\n", 
	     nbpl<<3, gf_num_rows, gf_x_offset, gf_y_offset, cw, ch);
#endif USEPXL

	/* one more character down loaded */
	fontp->ncdl += 1;

#ifdef STATS
#ifdef USEPXL
	S_nbpxl += nbpl*ce->height;
	fontp->nbpxl += nbpl*ce->height;
	S_onbpx += (ce->width*ce->height + 7) >> 3;
#else not USEPXL
	S_nbpxl += nbpl*gf_num_rows;
	fontp->nbpxl += nbpl*gf_num_rows;
	S_onbpx += (gf_num_cols*gf_num_rows + 7) >> 3;
#endif USEPXL
	S_ndc += 1;
#endif STATS
}

/*-->Fatal*/
/**********************************************************************/
/******************************  Fatal  *******************************/
/**********************************************************************/
/* emit a message and die */
/* VARARGS1 for lint */

void
Fatal(fmt, a, b, c, d, e, f)		/* issue a fatal error message */
    char *fmt;				/* format */
    char *a, *b, *c, *d, *e, *f;	/* arguments */
{
    /* always print fatal error message */
    fprintf(stderr, "\n%s: FATAL--", G_progname);
    fprintf(stderr, fmt, a, b, c, d, e, f);
    fprintf(stderr, "\n");

    /* if logging to a file, print again there */
    if (G_logging) {
	if (G_logfp == NULL) {
	    fprintf(stderr,"\n%s: FATAL--missing log file\n",G_progname);
	}
	fprintf(G_logfp, "\n%s: FATAL--", G_progname);
	fprintf(G_logfp, fmt, a, b, c, d, e, f);
	fprintf(G_logfp, "\n");
    }

#ifdef CREOPT
    /* close and delete the output file */
    if (G_create && G_outfp != NULL) {
	fclose(G_outfp);
	unlink(G_outfname);
    }
#endif CREOPT
    exit(1);
}

/*-->GetBytes*/
/**********************************************************************/
/*****************************  GetBytes  *****************************/
/**********************************************************************/
/* get n bytes from file fp, store string in cp */

void
GetBytes(fp, cp, n)
    register FILE *fp;	/* file pointer	 */
    register char *cp;	/* character pointer */
    register int n;	/* number of bytes  */
{
    while (n--) {
	*cp++ = getc(fp);
    }
}

/*-->GetKeyStr*/
/**********************************************************************/
/*****************************  GetKeyStr  ****************************/
/**********************************************************************/

	/* extract first keyword-value pair from string (value part may be null)
	 * return pointer to remainder of string
	 * return NULL if none found
	 */

char    KeyStr[STRSIZE];
char    ValStr[STRSIZE];

char *GetKeyStr( str, kw )
char    *str;
KeyWord *kw;
{
	char *s, *k, *v, t;

	if( !str ) return( NULL );

	for( s=str; *s == ' '; s++ ) ;	  /* skip over blanks */
	if( *s == '\0' ) return( NULL );

	for( k=KeyStr;	  /* extract keyword portion */
	     *s != ' ' && *s != '\0' && *s != '='; 
	     *k++ = *s++ ) ;
	*k = '\0';
	kw->Key = KeyStr;
	kw->Val = v = NULL;
	kw->vt = None;

	for( ; *s == ' '; s++ ) ;	       /* skip over blanks */
	if( *s != '=' )	 /* look for "=" */
		return( s );

	for( s++ ; *s == ' '; s++ ) ;	   /* skip over blanks */
	if( *s == '\'' || *s == '\"' )	  /* get string delimiter */
		t = *s++;
	else
		t = ' ';
	for( v=ValStr;	  /* copy value portion up to delim */
	     *s != t && *s != '\0';
	     *v++ = *s++ ) ;
	if( t != ' ' && *s == t ) s++;
	*v = '\0';
	kw->Val = ValStr;
	kw->vt = String;

	return( s );
}

/*-->GetKeyVal*/
/**********************************************************************/
/*****************************  GetKeyVal  ****************************/
/**********************************************************************/

	/* get next keyword-value pair
	 * decode value according to table entry
	 */

int GetKeyVal( kw, tab, nt, tno)
KeyWord *kw; 
KeyDesc tab[];
int     nt;
int     *tno;
{
	int i;
	char c = '\0';

	*tno = -1;

	for(i=0; i<nt; i++)
		if( IsSame(kw->Key, tab[i].Entry) ) {
			*tno = i;
			switch( tab[i].Type ) {
				case None: 
					if( kw->vt != None ) return( FALSE );
					break;
				case String:
					if( kw->vt != String ) return( FALSE );
					break;
				case Integer:
					if( kw->vt != String ) return( FALSE );
					if( sscanf(kw->Val,"%d%c", &(kw->v.i), &c) != 1
					    || c != '\0' ) return( FALSE );
					break;
				case Number:
				case Dimension:
					if( kw->vt != String ) return( FALSE );
					if( sscanf(kw->Val,"%f%c", &(kw->v.n), &c) != 1
					    || c != '\0' ) return( FALSE );
					break;
				}
			kw->vt = tab[i].Type;
			return( TRUE );
			}

	return( TRUE );
}

/*-->HasBeenRead*/
/**********************************************************************/
/***************************  HasBeenRead  ****************************/
/**********************************************************************/

int
HasBeenRead(fontnum)
    int fontnum;
{
    register struct font_entry *ptr;

    /* search font entry list for this font number (from dvi file) */
    ptr = G_hfontptr;
    while ((ptr != NULL) && (ptr->dvi_fnum != fontnum)) {
	ptr = ptr->next;
    }
    return( ptr != NULL );
}

/*-->IsSame*/
/**********************************************************************/
/*******************************  IsSame  *****************************/
/**********************************************************************/
/* compare strings, ignore case */

int IsSame(a, b)
char *a, *b;
{
	for( ; *a != '\0'; ) {
		if( tolower(*a++) != tolower(*b++) ) return( FALSE );
	}
	return( *a == *b ? TRUE : FALSE );
}

/*-->MoveDown*/
/**********************************************************************/
/****************************  MoveDown  ******************************/
/**********************************************************************/

#ifndef MACROS
void
MoveDown(a)
int a;
{
    G_v += a;
}
#endif MACROS

/*-->MoveOver*/
/**********************************************************************/
/****************************  MoveOver  ******************************/
/**********************************************************************/

#ifndef MACROS
void
MoveOver(b)
int b;
{
    G_h += b;
}
#endif MACROS

/*-->NoSignExtend*/
/**********************************************************************/
/***************************  NoSignExtend  ***************************/
/**********************************************************************/

int
NoSignExtend(fp, n)	/* return n byte quantity from file fd */
register FILE *fp;	/* file pointer    */
register int n;		/* number of bytes */

{
    register int x;	/* number being constructed */

    x = 0;
    while (n--)  {
	x <<= 8;
	x |= getc(fp);
    }
    return(x);
}

/*-->OpenFontFile*/
/**********************************************************************/
/************************** OpenFontFile  *****************************/
/**********************************************************************/
/***********************************************************************
    The original version of this dvi driver reopened the font file  each
    time the font changed, resulting in an enormous number of relatively
    expensive file  openings.   This version  keeps  a cache  of  up  to
    MAXOPEN open files,  so that when  a font change  is made, the  file
    pointer, G_fontfp, can  usually be  updated from the  cache.  When  the
    file is not found in  the cache, it must  be opened.  In this  case,
    the next empty slot  in the cache  is assigned, or  if the cache  is
    full, the least used font file is closed and its slot reassigned for
    the new file.  Identification of the least used file is based on the
    counts of the number  of times each file  has been "opened" by  this
    routine.  On return, the file pointer is always repositioned to  the
    beginning of the file.

***********************************************************************/

void
OpenFontFile()
{
    register struct font_entry *fontp;
    register struct open_font_list *openp;
    register struct font_entry *least_fontp;
    register struct open_font_list *least_openp;
    register FILE *fid;


#ifdef DEBUG
    if (Debug) printf("Open Font file\n");
#endif DEBUG
    /* check for consistency here */
    if (G_fontptr == NULL) {
	Fatal("internal - null font structure pointer in OpenFontFile()\n");
    }

#ifdef STATS
    /* count number of times open attempted */
    G_fontptr->nopen += 1;
#endif STATS

    /* G_pfontptr is last font "OpenFontFile"'ed */
    if (G_pfontptr == G_fontptr) {
	/* we need not have been called */
	return;
    }

    /* check if this font file is already open */
    fontp = G_fontptr;
    if ((openp = fontp->open_font_ptr) == NULL) {
	/* this file is not open, so open it */
	if (G_nopen < MAXOPEN-1) {
		/* still open slots, so use one */
		openp = &G_font_files[G_nopen];
		G_nopen += 1;
#ifdef DEBUG
		if (Debug) fprintf(stderr,"opening (new slot) font file %s\n",fontp->name);
#endif DEBUG
	} else {
#ifdef DEBUG
		if (Debug) fprintf(stderr,"opening (find slot) font file %s\n",fontp->name);
#endif DEBUG
		/* find least used, close it, and reuse slot */
		/* list full -- find least used file, */
		/* close it, and reuse slot for new file */
	    least_openp = &G_font_files[0];
	    for (openp = &G_font_files[G_nopen-1]; openp > &G_font_files[0]; openp -= 1) {
		if (least_openp->use_count > openp->use_count) least_openp = openp;
	    }
	    openp = least_openp;
	    if (openp->pixel_file_id != NO_FILE) {
		fid = openp->pixel_file_id;
		if ((least_fontp = openp->font_entry_ptr) == NULL) {
		    Fatal("internal - bad open font in OpenFontFile()\n");
		}
		/* mark as closed */
		least_fontp->font_file_id = NULL;
		least_fontp->open_font_ptr = NULL;
#ifdef STATS
		/* count number of times open attempted */
		G_fontptr->nclose += 1;
		if (Stats) fprintf(stderr,"closed font file %s\n",least_fontp->name);
#endif STATS
		fclose( fid );

	    }
	}
	if ((G_fontfp=BINARYOPEN(fontp->name,"r")) == NULL) {
	    Warning("cannot open font file %s\n",fontp->name);
	    G_fontfp = NO_FILE;
	}
#ifdef STATS
	else {
	    if (Stats) fprintf(stderr,"opened font file %s\n",fontp->name);
	}
#endif STATS
	/* initialize this entry in open font file list */
	openp->pixel_file_id = G_fontfp;
	openp->font_entry_ptr = fontp;
	openp->use_count = 0;
	fontp->font_file_id = G_fontfp;	/* set file identifier */
	fontp->open_font_ptr = openp;	/* open file id */
    } else {
	/* this file is already open */
	if ((G_fontfp = openp->pixel_file_id) != NO_FILE) {
	    /* is this necessary? */
	    /* reposition to start of file */
	    fseek(G_fontfp,(long)0,FROM_START);
	} else {
	    Warning("internal - bad font file pointer - OpenFontFile()\n");
	}
    }

    /* have a new font file */
    G_fontptr = fontp;
    G_pfontptr = fontp;			/* make previous = current font */
    openp->use_count += 1;		/* update reference count */
#ifndef USEPXL
    gf_infile = G_fontfp;			/* used in gf reader */
    gf_filename = G_fontptr->name;		/* used in gf reader */
#endif USEPXL
}

#ifdef CREOPT
/*-->OpenOutput*/
/**********************************************************************/
/*************************** OpenOutput *******************************/
/**********************************************************************/
/* generate a unique file name and open it */
/* this is only for creating spool files, not general output files */

FILE*
OpenOutput()
{
	FILE*   fp;
	long t;
	int  n = 0;
	char *p, *pp, b[256];
	int nd;

	time( &t );
	t = t % 100000;
	strcpy( G_outfname, SPOOLFILE );
	sprintf( b, "%s.%s.%x", logname(), G_rootname, t );
	if( (nd=strlen(b)-MAXFLEN) > 0 ) {
	       for(pp=(p=rindex(b,'.')); p && *p != '\0'; *(pp-nd) = *p++, pp++) ;
	       *(pp-nd) = '\0';
	}
	strcat( G_outfname, b );

	while( access(G_outfname,F_OK) == 0 ) {
		n += 1;
		if( n > 10 ) 
			Fatal("Unable to create a unique output file name: %s\n", G_outfname );
		strcpy( G_outfname, SPOOLFILE );
		sprintf( b, "%s.%s.%x.%d", logname(), G_rootname, t, n );
		if( (nd=strlen(b)-MAXFLEN) > 0 ) {
			for(pp=(p=rindex(b,'.')); p && *p != '\0'; *(pp-nd) = *p++, pp++) ;
			*(pp-nd) = '\0';
			}
		strcat( G_outfname, b );
		}

	if( (fp=fopen(G_outfname,"w")) == NULL )
		Fatal("Unable to create output file: %s\n", G_outfname);

	return( fp );
}
#endif CREOPT

/*-->PixRound*/
/**********************************************************************/
/*****************************  PixRound  *****************************/
/**********************************************************************/

#ifndef MACROS
int
PixRound(x, conv)	/* return rounded number of pixels */
register int x;		/* in DVI units     */
int conv;		/* conversion factor */
{
    return((int)((x + (conv >> 1)) / conv));
}
#endif MACROS

/*-->PutInt*/
/**********************************************************************/
/*****************************  PutInt  *******************************/
/**********************************************************************/
/* output an integer followed by a space */
/* could we put this number out in hex to make this faster? */

void
PutInt(num)
    register int num;
{
    char buf[10];
    register char *b;

    if (num == 0) {
	EMITC('0'); 
    } else {
	if (num < 0) {
	    EMITC('-');
	    num = -num;
	}
    
	for (b=buf;  num>0; ) {
	    *b++ = digit[num%10];
	    num /= 10;
	}
    
	while (b>buf) {
	    EMITC(*--b);
	}
    }

    EMITC(' ');
}

/*-->PutOct*/
/**********************************************************************/
/*****************************  PutOct  *******************************/
/**********************************************************************/

void
PutOct(num)	       /* output an 3 digit octal number preceded by a "\" */
register int num;
{	  
    EMITC( '\\' ); 
    EMITC( digit[(num&0300)>>6] );
    EMITC( digit[(num&0070)>>3] ); 
    EMITC( digit[num&0007] );
}

/*-->GetFontDef*/
/**********************************************************************/
/**************************** GetFontDef  *****************************/
/**********************************************************************/
/***********************************************************************
   Read the font  definitions as they  are in the  postamble of the  DVI
   file.
***********************************************************************/

void
GetFontDef()
{
    register int byte;

    while (((byte = GET1(G_dvifp)) >= FNT_DEF1) &&
	(byte <= FNT_DEF4)) {
	switch (byte) {
	case FNT_DEF1:
	    ReadFontDef (GET1(G_dvifp));
	    break;
	case FNT_DEF2:
	    ReadFontDef (GET2(G_dvifp));
	    break;
	case FNT_DEF3:
	    ReadFontDef (GET3(G_dvifp));
	    break;
	case FNT_DEF4:
	    ReadFontDef (GET4(G_dvifp));
	    break;
	default:
	    Fatal ("Bad byte value in font defs - DVI post-amble");
	    break;
	}
    }
    if (byte != POST_POST) {
	Fatal ("POST_POST missing after font defs - DVI post-amble");
    }
}

/*-->ReadFontDef*/
/**********************************************************************/
/****************************  ReadFontDef  ***************************/
/**********************************************************************/
/* read a font definition out of the dvi file */
/* then go find the font file (pxl or gf) */
/* read a font definition out of the font file (pxl or gf) */

int
ReadFontDef(fontnum)
    int fontnum;		/* font number */
{
    int i;
    register struct font_entry *tfontptr;	/* temp font_entry pointer */
    register struct char_entry *tcharptr;	/* temp char_entry pointer */
    int nmag;
    char nname[STRSIZE];

    /* check to see if this font has already been read in */
    /* this was a bug in the last version */
    /* linear search of font entry list */
    tfontptr = G_hfontptr;
    while ((tfontptr != NULL) && (tfontptr->dvi_fnum != fontnum)) {
	tfontptr = tfontptr->next;
    }
    if (tfontptr != NULL) {
	/* we found this font, already read */
#ifdef STATS
	if (Stats) fprintf(stderr,"re-read font file %s\n",tfontptr->name);
#endif STATS
	G_fontptr = tfontptr;
	return;
    }

    /* allocate a new font entry */
    if ((tfontptr = NEW(struct font_entry)) == NULL) {
	Fatal("can't malloc space for font_entry");
    }

    /* link this entry into list (at head) */
    tfontptr->next = G_hfontptr;
    G_fontptr = G_hfontptr = tfontptr;

    /* initialize the new entry */
    tfontptr->font_file_id = NULL;	/* this font file not open yet */
    tfontptr->open_font_ptr = NULL;	/* this font file not open yet */
    tfontptr->ncdl = 0;			/* # chars downloaded */
#ifdef STATS
    tfontptr->nbpxl = 0;
    tfontptr->ncts = 0;
    tfontptr->nopen = 0;
    tfontptr->nclose = 0;
#endif STATS

    /* get font info from dvi file */
    tfontptr->dvi_fnum = fontnum;	/* font number */
    tfontptr->dvi_check = GET4(G_dvifp);	/* checksum */
    tfontptr->dvi_s = GET4(G_dvifp);	/* space size */
    tfontptr->dvi_d = GET4(G_dvifp);	/* design size */
    tfontptr->dvi_alen = GET1(G_dvifp);	/* area length for font name */
    tfontptr->dvi_nlen = GET1(G_dvifp);	/* device length */

    /* get font name string - according to dvi file (no mag) */
    /* note that this also gets the dvi area/directory */
    /* which is usually not present, but could be a bug */
    GetBytes(G_dvifp, tfontptr->dvi_name, tfontptr->dvi_alen+tfontptr->dvi_nlen);
    tfontptr->dvi_name[tfontptr->dvi_alen+tfontptr->dvi_nlen] = '\0';

    tfontptr->font_space = tfontptr->dvi_s/6;	/* never used */

#ifdef USEPXL
#ifdef USEGLOBALMAG
    tfontptr->font_mag = (int)((
	ActualFactor((int)
		(((float)tfontptr->dvi_s/(float)tfontptr->dvi_d)*1000.0 + 0.5)
	    )
	* ActualFactor(D_mag) * (float)RESOLUTION * 5.0) + 0.5);
#else not USEGLOBALMAG
    tfontptr->font_mag = (int)((
	ActualFactor((int)
		(((float)tfontptr->dvi_s/(float)tfontptr->dvi_d)*1000.0 + 0.5)
	    )
	* (float)RESOLUTION * 5.0) + 0.5);
#endif USEGLOBALMAG
#else not USEPXL
#ifdef USEGLOBALMAG
    tfontptr->font_mag = (int)((
	ActualFactor((int)
		(((float)tfontptr->dvi_s/(float)tfontptr->dvi_d)*1000.0 + 0.5)
	    )
	* ActualFactor(D_mag) * (float)RESOLUTION) + 0.5);
#else not USEGLOBALMAG
    tfontptr->font_mag = (int)((
	ActualFactor((int)
		(((float)tfontptr->dvi_s/(float)tfontptr->dvi_d)*1000.0 + 0.5)
	    )
	* (float)RESOLUTION) + 0.5);
#endif USEGLOBALMAG
#endif USEPXL

    /* for debugging */
#ifdef DEBUG
    if (Debug) {
	fprintf(stderr,"ReadFontDef k=%d c=%d s=%d d=%d a=%d l=%d n=%s mag=%d gmag=%d\n",
		tfontptr->dvi_fnum,
		tfontptr->dvi_check,
		tfontptr->dvi_s,
		tfontptr->dvi_d,
		tfontptr->dvi_alen,
		tfontptr->dvi_nlen,
		tfontptr->dvi_name,
		tfontptr->font_mag,
		D_mag
	);
    }
#endif DEBUG

    /* go find the font file */
    if (!FindFontFile(G_fontpath,G_fontpathsize,
		tfontptr->dvi_name,tfontptr->font_mag,tfontptr->name,
		nname, &nmag)) {
	Fatal("no font %s.%d\n",tfontptr->dvi_name,tfontptr->font_mag);
    }
 
    /* set postscript font name - dvi name plus mag (i.e. file name) */
    sprintf(tfontptr->psname, "%s.%d", tfontptr->dvi_name, tfontptr->font_mag);
    if (tfontptr != G_pfontptr) OpenFontFile();

#ifdef USEPXL
    /* allow missing pxl files */
    if ( G_fontfp == NO_FILE ) {
	tfontptr->magnification = 0;
	tfontptr->designsize = 0;
	/* initialize the character data */
	for (i = 0; i <= MAXFONTCHARS-1; i++) {
	    tcharptr = &(tfontptr->ch[i]);
	    tcharptr->width = 0;
	    tcharptr->height = 0;
	    tcharptr->xOffset= 0;
	    tcharptr->yOffset = 0;
	    tcharptr->where.isloaded = FALSE;
	    tcharptr->where.address.fileOffset = NONEXISTANT;
	    tcharptr->tfmw = 0;
	}
	return;
    }

    /* check the version number */
    if ((t = GET4(G_fontfp)) != PXLID) {
	Fatal("PXL ID = %d, can only process PXL ID = %d files", t, PXLID);
    }

    /* check that the "checksum" matches */
    fseek(G_fontfp, -20, FROM_END);	/* magic number */
    t = GET4(G_fontfp);
    if ((tfontptr->dvi_check != 0) && (t != 0) && (tfontptr->dvi_check != t)) {
	Warning("font = \"%s\",\n-->font checksum = %d,\n-->dvi checksum = %d",
		tfontptr->name, tfontptr->dvi_check, t);
    }

    tfontptr->magnification = GET4(G_fontfp);
    tfontptr->designsize = GET4(G_fontfp);

    /* get the character info */
    fseek(G_fontfp, GET4(G_fontfp) * 4, FROM_START);

    for (i = 0; i <= MAXFONTCHARS-1; i++) {
	tcharptr = &(tfontptr->ch[i]);
	tcharptr->width = GET2(G_fontfp);
	tcharptr->height = GET2(G_fontfp);
	tcharptr->xOffset= GETSN(G_fontfp, 2);
	tcharptr->yOffset = GETSN(G_fontfp, 2);
	tcharptr->where.isloaded = FALSE;
	tcharptr->where.address.fileOffset = GET4(G_fontfp) * 4;
	tcharptr->tfmw = ((float)GET4(G_fontfp)*(float)tfontptr->dvi_s) /
	    (float)(1<<20);
    }

#else not USEPXL
    /* allow missing gf files */
    if ( G_fontfp == NO_FILE ) {
	for (i = 0; i <= MAXFONTCHARS-1; i++) {
	    tcharptr = &(tfontptr->ch[i]);
	    tcharptr->where.isloaded = FALSE;
	    tcharptr->where.address.fileOffset = NONEXISTANT;
	    tcharptr->tfmw = 0;
	}
	return;
    }

    /* get the character info from gf font file */
    gf_infile = G_fontfp;
    gf_filename = tfontptr->name;		/* used in gf reader */
    gf_seekpost();
    gf_readpost();

    /* check the checksum */
    if ((tfontptr->dvi_check != 0)
	    && (gf_checksum != 0)
	    && (tfontptr->dvi_check != gf_checksum)) {
	Warning("font = \"%s\",\n-->font checksum = %d,\n-->dvi checksum = %d",
		tfontptr->name, tfontptr->dvi_check, gf_checksum);
    }

    /* initialize the character info */
    for(i=0; i<=MAXFONTCHARS-1; i++) {
	tcharptr = &(tfontptr->ch[i]);
	tcharptr->where.isloaded = FALSE;
	if (gf_char_exists[i]) {
	    tcharptr->tfmw = ((float)gf_tfm_wd[i]*(float)tfontptr->dvi_s) /
		(float)(1<<20);
	    tcharptr->where.address.fileOffset = gf_char_pointer[i];
	} else {
	    tcharptr->where.address.fileOffset = NONEXISTANT;
	    tcharptr->tfmw = 0;
	}
    }
#endif not USEPXL
}

/*-->FindPostAmble*/
/**********************************************************************/
/************************  FindPostAmble  **************************/
/**********************************************************************/
/* this routine will move to the end of the file and find the start
    of the postamble */

void
FindPostAmble()
{
    register long postambleptr;
    register int i;

    /* this seems like a dumb way to do this */
    /* why not just seek 4 from end */
    fseek (G_dvifp, (long) 0, FROM_END);   /* goto end of file */
    postambleptr = ftell (G_dvifp) - 4;
    fseek (G_dvifp, postambleptr, FROM_START);

    while (TRUE) {
	fseek (G_dvifp, --(postambleptr), FROM_START);
	if (((i = GET1(G_dvifp)) != 223) && (i != DVIFORMAT)) {
	    Fatal ("Bad end of DVI file");
	}
	if (i == DVIFORMAT) break;
    }
    fseek (G_dvifp, (postambleptr) - 4, FROM_START);
    postambleptr = GET4(G_dvifp);
    G_postambleptr = postambleptr;
    fseek (G_dvifp, postambleptr, FROM_START);
}

/*-->CheckPreAmble*/
/**********************************************************************/
/**************************  CheckPreAmble  ***************************/
/**********************************************************************/

void
CheckPreAmble()
{
    register int i;

    /* check for presence of pre-amble */
    if ((i = GET1(G_dvifp)) != PRE)  {
	Fatal("PRE doesn't occur first--are you sure this is a DVI file?\n\n");
    }

    /* check for correct DVI file format */
    i = GET1(G_dvifp);
    if (i != DVIFORMAT)  {
	Fatal("DVI format = %d, can only process DVI format %d files\n\n",
		i, DVIFORMAT);
    }
}

/*-->ReadPreAmble*/
/**********************************************************************/
/**************************  ReadPreAmble  ***************************/
/**********************************************************************/

void
ReadPreAmble()
{
    char sname[STRSIZE];
    register int k;

    /* read the preamble */
    D_num = GET4(G_dvifp);
    D_den = GET4(G_dvifp);
    D_mag = GET4(G_dvifp);

#ifdef USEGLOBALMAG
    if (CL_usermag > 0 && CL_usermag != D_mag) {
	Warning("DVI magnification of %d over-ridden by user mag of %d\n",
			D_mag, CL_usermag );
    }
#endif USEGLOBALMAG

    if (CL_usermag > 0) D_mag = CL_usermag;

#ifndef USEGLOBALMAG
    if( D_mag != 1000 ) Warning("Magnification of %d ignored.\n", D_mag);
#endif USEGLOBALMAG

    G_hconv = DoConv(D_num, D_den, hconvRESOLUTION);
    G_vconv = DoConv(D_num, D_den, vconvRESOLUTION);
    k = GET1(G_dvifp);
    GetBytes(G_dvifp, sname, k);
    sname[k] = '\0';
#ifdef DEBUG
    if (Debug) Warning("internal - pre-string - %s\n",sname);
#endif DEBUG

    /* read the post-amble */
    ReadPostAmble(CL_PreLoad);
    if (CL_Reverse) {
	/* start on last page */
	fseek(G_dvifp, G_ppagep, FROM_START);
    } else {
	/* start on first page */
	fseek(G_dvifp, (long) 14, FROM_START);
	k = GET1(G_dvifp);
	GetBytes(G_dvifp, sname, k);
    }
}

/*-->ReadPostAmble*/
/**********************************************************************/
/**************************  ReadPostAmble  ***************************/
/**********************************************************************/
/***********************************************************************
    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.
***********************************************************************/

void
ReadPostAmble(load)
int     load;
{
    /* go find the post-amble */
    FindPostAmble();
    if (GET1(G_dvifp) != POST) Fatal ("POST missing at head of postamble");
#ifdef DEBUG
    if (Debug) fprintf (stderr, "got POST command\n");
#endif DEBUG
    G_ppagep = GET4(G_dvifp);
    D_num = GET4(G_dvifp);
    D_den = GET4(G_dvifp);
    D_mag = GET4(G_dvifp);
#ifdef USEGLOBALMAG
    if( CL_usermag > 0 && CL_usermag != D_mag )
	Warning("DVI magnification of %d over-ridden by user mag of %d\n", D_mag, CL_usermag );
#endif USEGLOBALMAG
    if( CL_usermag > 0 ) D_mag = CL_usermag;
#ifndef USEGLOBALMAG
    if( D_mag != 1000 ) Warning("Magnification of %d ignored.\n", D_mag);
#endif USEGLOBALMAG
    G_hconv = DoConv(D_num, D_den, hconvRESOLUTION);
    G_vconv = DoConv(D_num, D_den, vconvRESOLUTION);

    GET4(G_dvifp);	/* height-plus-depth of tallest page */
    GET4(G_dvifp);	/* width of widest page */
    if (GET2(G_dvifp) >= STACKSIZE)
	Fatal ("Stack size is too small - static allocation");
    GET2(G_dvifp);	/* this reads the number of pages in */
    /* the DVI file */
#ifdef DEBUG
    if (Debug) fprintf (stderr, "now reading font defs");
#endif DEBUG
    if (load) GetFontDef ();
}

/*-->LoadAChar*/
/**********************************************************************/
/*****************************  LoadAChar  ******************************/
/**********************************************************************/
/* control the output of a character bit map */
/* 1. read the bitmap from the font file */
/* 2. emit the bitmap to the output */

LoadAChar(ch, ceptr)
    int ch;
    register struct char_entry *ceptr;
{
    /* sanity checks */
    if (ceptr == NULL) {
	Fatal("internal - null pointer in LoadAChar\n");
    }
    if (ceptr->where.isloaded) {
	/* should not have been called */
	return;
    }

    /* always set these values - here are the defaults */
    ceptr->where.isloaded = TRUE;

    /* if there is no font file, no need to proceed */
    if (ceptr->where.address.fileOffset == NONEXISTANT) {
	ceptr->where.address.pixptr = NULL;
	return;
    }

    /* first open the font file */
    /* note current font is global variable G_fontptr */
    OpenFontFile();

    /* next read the bit map for this character from the font file */
#ifdef USEPXL
    fseek(G_fontfp, ceptr->where.address.fileOffset, FROM_START);
    nints = ((ceptr->width + 31) >> 5) * ceptr->height;
    if( (pr = (long *)malloc( nints*sizeof(long) )) == NULL ) {
	Fatal("Unable to allocate memory for char bit map\n");
    }
    fread(pr, 4, nints, G_fontfp);
    ceptr->where.address.pixptr = pr;

#else not USEPXL

#ifdef NOTDEF
    /* unless re-read postamble of font file, may have lost file offsets */
    gf_seekchar(ch);
#endif NOTDEF
    fseek(gf_infile, ceptr->where.address.fileOffset, FROM_START);
    gf_gettochar();
    gf_readbits();
    ceptr->where.address.pixptr = NULL;

#endif USEPXL

    /* now emit the bit map to the output */
    EmitCharBitMap(ch, ceptr);
#ifdef USEPXL
	/* we should really free the space used by the PXL data after this
	   point, but it is not large, and besides, we may want to be
	   more clever in the future, about sending bitmaps.  So keep
	   the data around */
#endif USEPXL
}

/*-->SetChar*/
/**********************************************************************/
/*****************************  SetChar  ******************************/
/**********************************************************************/

void
SetChar(ch, command, PassNo)
    int ch, command, PassNo;
{
    register struct char_entry *ceptr;  /* temporary char_entry pointer */

    /* who sets G_fontptr? (OpenFontFile,SetFontNum) */
    if (G_fontptr == NULL) {
	Fatal("internal - unexpected null font in SetChar()\n");
    }
    ceptr = &(G_fontptr->ch[ch]);
    if (!ceptr->where.isloaded) LoadAChar(ch, ceptr);
    if (PassNo==0) return;

    SetPosn(G_h,G_v);
    if (G_fontptr->font_file_id != NO_FILE) {      /* ignore missing fonts */
	EMITN(ch); EMITS("c\n");
	G_hh += ceptr->tfmw;
    }

    /* if this is a set, then move, if this is a put, then do not move */
    if (command <= SET4) MoveOver(ceptr->tfmw);

#ifdef STATS
    S_tnc += 1;
    G_fontptr->ncts += 1;
#endif STATS
}

/*-->SetFontNum*/
/**********************************************************************/
/****************************  SetFontNum  *****************************/
/**********************************************************************/
/*  this routine is used to specify the font to be used in printing future
    characters */

void
SetFontNum(fontnum, Emitting)
    int fontnum, Emitting;
{
    register struct font_entry *fontp;

    /* linear search of font entry list */
    fontp = G_hfontptr;
    while ((fontp != NULL) && (fontp->dvi_fnum != fontnum)) {
	fontp = fontp->next;
    }
    if (fontp == NULL) {
	Fatal("font %d undefined", fontnum);
    }
    if (Emitting && (fontp->font_file_id != NO_FILE) )  {
	EMIT(G_outfp,"%s @sf\n", fontp->psname);
    }
    G_fontptr = fontp;
}

/*-->SetPosn*/
/**********************************************************************/
/*****************************  SetPosn  ******************************/
/**********************************************************************/
/* output a positioning command */

void
SetPosn(x, y)
	int x, y;
{
	register int rx;
#ifdef NOTDEF
	register int ry;
#endif NOTDEF

#ifdef USERELPOS
	/* use relative movement if just moving horizontally */
	if (y == G_vv) {
	    if ( x != G_hh ) {
		EMITN(rx=PixRound(x-G_hh,G_hconv));
		EMITS("r ");
		G_hh += rx*G_hconv;
	    }
	} else {
#endif USERELPOS
	    EMITN(rx=PixRound(x,G_hconv));
	    EMITN(PixRound(y,G_vconv));
#ifdef NOTDEF
	    EMITN(ry=PixRound(y,G_vconv));
#endif NOTDEF
	    EMITS("p ");

	    /* must know where device "really" is horizontally, */
	    /* for rel. posning. */
	    /* but we always use direct positioning for vertical movement */
	    G_hh = rx*G_hconv;
	    G_vv = y;
#ifdef USERELPOS
	}
#endif USERELPOS
}

/*-->SetRule*/
/**********************************************************************/
/*****************************  SetRule  ******************************/
/**********************************************************************/
/* this routine will draw a rule (a line) */

void
SetRule(a, b, Set)
    int a, b;
    BOOLEAN Set;
{
    if( a > 0 && b > 0 ) {
	SetPosn(G_h,G_v);		/* lower left corner */
	EMITN(PixRound(b,G_hconv));	/* width */
	EMITN(PixRound(a,G_vconv));	/* height */
	EMITS("ru\n");
    } else {
	Warning("internal - bad value to SetRule (%d,%d)\n",a,b);
    }
    /* perform a move also? true for set, false for put */
    if (Set) MoveOver(b);
}

/*-->SetString*/
/**********************************************************************/
/*****************************  SetString  ****************************/
/**********************************************************************/
/* read and set a consecutive string of chars */

void
SetString(firstch, PassNo)
    register int firstch;
    int PassNo;
{
    register int ch;
    register struct char_entry *ceptr;
    register struct font_entry *fontp;
    register int len;

    /* sanity check */
    fontp = G_fontptr;
    if (fontp == NULL) {
	Fatal("internal - null font pointer - SetString()\n");
    }

    /* on the first pass, just read string and load fonts */
    if (PassNo == 0) {
	/* read entire string of chars */
	for (ch = firstch; ch >= SETC_000 && ch <= SETC_127; ch = GET1(G_dvifp)) {
	    ceptr = &(fontp->ch[ch]);
	    if (!(ceptr->where.isloaded)) LoadAChar(ch, ceptr);
	}
	UNGET1(G_dvifp, ch);	/* backup one character */
	return;
    }

    /* this is the second pass - emit */
    len = 1;
    SetPosn(G_h,G_v);

    /* alway emit the first character, so compute movement */
    ceptr = &(fontp->ch[firstch]);
    G_h += ceptr->tfmw;
    /* ignore missing fonts */
    if (fontp->font_file_id != NO_FILE) {
	/* this will force a movement by SetPosn() later */
	G_hh += ceptr->tfmw;
    }

    /* read entire string of chars */
    /* first check for more compact case of one char */
    ch = GET1(G_dvifp);
    if (ch < SETC_000 || ch > SETC_127) {
	/* only one character to emit */
	EMITN(firstch); 
	EMITS("c\n"); 
    } else {
	/* start a sting */
	EMITC('(');
	/* emit the first char */
	if ((firstch < ' ') || (firstch >= 0177)) {
	    /* non-printable character */
	    EMITO(firstch);
	} else if (firstch == '(' || firstch == ')' || firstch == '\\') {
	    /* escaped character */
	    EMITC('\\'); 
	    EMITC(firstch); 
	} else {
	    EMITC(firstch);
	}
	while (ch >= SETC_000 && ch <= SETC_127) {
	    len += 1;
	    /* move the current location accordingly */
	    ceptr = &(fontp->ch[ch]);
	    G_h += ceptr->tfmw;
	    /* ignore missing fonts */
	    if (fontp->font_file_id != NO_FILE) {
		if (ch < ' ' || ch >= 0177) {
		    EMITO(ch);
		} else if (ch == '(' || ch == ')' || ch == '\\') {
		    EMITC('\\'); 
		    EMITC(ch); 
		} else {
		    EMITC(ch);
		}
		/* this will force a movement by SetPosn() later */
		G_hh += ceptr->tfmw;
	    }
	    ch = GET1(G_dvifp);
	}
	EMITS(") s\n");
    }
    UNGET1(G_dvifp, ch);	/* backup one character */

#ifdef STATS
    S_tnc += len;
    fontp->ncts += len;
#endif STATS
}

/*-->SignExtend*/
/**********************************************************************/
/****************************  SignExtend  ****************************/
/**********************************************************************/

int
SignExtend(fp, n)   /* return n byte quantity from file fd */
register FILE *fp;  /* file pointer    */
register int n;     /* number of bytes */
{
    int n1;	 /* number of bytes	    */
    register int x; /* number being constructed */

    x = getc(fp);   /* get first (high-order) byte */
    n1 = n--;
    while (n--)  {
	x <<= 8;
	x |= getc(fp);
    }

    /* NOTE: This code assumes that the right-shift is an arithmetic, rather
    than logical, shift which will propagate the sign bit right.   According
    to Kernighan and Ritchie, this is compiler dependent! */

    x<<=32-8*n1;
    x>>=32-8*n1;  /* sign extend */

#ifdef DEBUG
    if (Debug) {
	fprintf(stderr,"\tSignExtend(fp,%d)=%X\n",n1,x);
    }
#endif DEBUG
    return(x);
}

/*-->SkipFontDef*/
/**********************************************************************/
/****************************  SkipFontDef  ***************************/
/**********************************************************************/

void
SkipFontDef()
{
    register int a, l;

    SKIP(G_dvifp,12);
    a = GET1(G_dvifp);
    l = GET1(G_dvifp);
    SKIP(G_dvifp,a+l);
}

/*-->Progress*/
/**********************************************************************/
/*****************************  Progress  ******************************/
/**********************************************************************/
/* all communication to the user about progress goes through here */
/* VARARGS1 for lint */

void
Progress(fmt, a, b, c, d, e, f)	/* issue a warning */
char *fmt;			/* format   */
char *a, *b, *c, *d, *e, *f;	/* arguments */
{
    /* only print if not quiet */
    if (G_quiet) return;
    fprintf(stderr, fmt, a, b, c, d, e, f);

    /* print in log file, if logging */
    if (G_logging) {
	if (G_logfp == NULL) {
	    Fatal("missing log file\n");
	}
	fprintf(G_logfp, fmt, a, b, c, d, e, f);
    }
}

/*-->Warning*/
/**********************************************************************/
/*****************************  Warning  ******************************/
/**********************************************************************/
/* why is this routine so complicated? */
/* why is this routine used in some places and not in others? */
/* is G_logfp different than stderr? */
/* VARARGS1 for lint */

void
Warning(fmt, a, b, c, d, e, f)	/* issue a warning */
char *fmt;			/* format   */
char *a, *b, *c, *d, *e, *f;	/* arguments */
{
    G_errenc = TRUE;
    if (G_nowarn) return;
    fprintf(stderr, "%s: ", G_progname);
    fprintf(stderr, fmt, a, b, c, d, e, f);

    /* print in log file, if logging */
    if (G_logging) {
	if (G_logfp == NULL) {
	    Fatal("missing log file\n");
	}
	fprintf(G_logfp, "%s: ", G_progname);
	fprintf(G_logfp, fmt, a, b, c, d, e, f);
    }
}

/**********************************************************************/

