/* Ditroff driver */
#ifndef lint
static char *rcs = "$Header: nf.c.backup,v 1.2 88/02/03 08:52:15 simpson Exp $";
#endif
/*
$Log:	nf.c.backup,v $
Revision 1.2  88/02/03  08:52:15  simpson
added tpic support

Revision 1.1  88/01/15  13:04:37  simpson
initial release

Revision 0.3  88/01/06  08:46:06  simpson
corrected determination of charexists

Revision 0.2  87/12/18  11:37:32  simpson
added spline and ellipse support

Revision 0.1  87/12/11  18:31:04  simpson
beta test

*/
#include <stdio.h>
#include <signal.h>
#include <math.h>
#include <assert.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <local/standard.h>
#include <local/qms.h>
#include <local/profile.h>
#include "fontnode.h"
#include "fontinfo.h"
#include "constants.h"
#include "dev.h"

#define FONTDIR		"--FONTDIR--"
#define DEVNAME         "qms"		/* Name given to -T option*/
/* Cast for accessing LocalInfo */
#define LI(p)		((struct LocalInfo *)p->localinfo)
#define UNITWIDTH	10		/* Same as in DESC file */

typedef struct {
    double	x, y;
} COORDINATE;

struct FontState {
    int		pointsize;
    int		font;
};

struct Stack {
    struct FontState	fontstate;
    int			h, v;
};

struct LocalInfo {
    char	name[21];	/* Name of font */
    int		pointsize;
    int		qmsheight;	/* Height to give QMS printer */
    int		qmsbaseline;	/* Baseline height to give QMS printer */
    char	pathname[81];	/* Full pathname in file system */
    Boolean	charexists[256];/* True if there is such a character */
    short 	bitmapsize[2][256]; /* Width & height of each char bitmap */
    short	widths[256];	/* True width of each char in pixels */
    long 	maptable[160];	/* Encoding of troff special sequences */
    short 	maptablesize;	/* # of entries in map table */
};

char		EOFName[101];	/* Used by pkeofsocleanup & desceofsocleanup */
char		*Whoami;	/* argv[0] */
char		*Model;		/* Model of printer being used */
char		Orientation = 'P';
char		MountTable[51][21]; /* Max of 50 different fonts in table */
char		*Xxxstring;	/* String found in extension commands */
char		*Pxxxstring;	/* Pointer used by the lexical analyzer */
char		PKDir[101];	/* Directory PK fonts are in */
int		MaxBlocks;	/* # of free blocks w/out ram or rom fonts */
int		CurRamBlocks;	/* # of blocks used by ram fonts */
int		CurRomBlocks;	/* # of blocks used by rom fonts */
int		DownLoadFNum;	/* Number of font currently being downloaded */
int		Origin[2];	/* Origin of a postprocessor graph in pixels */
FILE		*ReadLine;	/* Debugger port tty */
struct sigvec	SigStruct;
struct FontNode	*FontList;	/* Contains all fonts & their attributes */
extern char	*Host, *User;
extern FILE	*Accting;
extern Boolean  Accounting;
extern int	NumPages;

main(argc, argv)
int	argc;
char	*argv[];
{
    Boolean	found, loadnametable(), setnewid();
    extern int	optind;
    extern char	*optarg;
    int		*intptr, *tempintptr;
    int		c;
    int		romlist[41];	/* List of rom font #s */
    int		simplyexit(), callcleanup();
    void	qmsfntfree(), cleanup(), seteoffunction(), process(),
                troffeofsocleanup(), adjust();
    PROFILE_VALUE   *v, *getbindingvalue();
    struct qmsram   *raminfo;
    struct qmsfnt   *fntinfo;
    struct fontnode *p;
    struct FontNode *createfontlist();
#ifdef DEBUG
    char	*malloc(), orien;
    int		i, fontno, size;
    FILE	*f;
    struct fontnode	*fn;
#endif

    Whoami = argv[0];
    while ((c = getopt(argc, argv, "x:y:n:h:w:l:i:")) != EOF)
    	switch (c) {
	case 'x':
	    if (atoi(optarg) > 2550)
	    	Orientation = 'L';
	    break;
	case 'h':
	    Host = optarg;
	    break;
	case 'n':
	    User = optarg;
	    break;
	case 'y':
	case 'w':
	case 'l':
	case 'i':
	    break;
	default:
	    exit(2);
	}
    SigStruct.sv_handler = simplyexit;
    SigStruct.sv_mask = 0;
    SigStruct.sv_onstack = 0;
    (void)sigvec(SIGINT, &SigStruct, (struct sigvec *)NULL);
    if (optind < argc) {
	Accounting = TRUE;
	if (!(Accting = fopen(argv[optind], "a"))) {
	    fprintf(stderr, "%s: cannot open accounting file %s\n", Whoami,
	    argv[optind]);
	    Accounting = FALSE;
	}
    }
    if (!(v = getbindingvalue("model")) || v->class != PROFILE_STRING &&
    v->class != PROFILE_OTHER) {
	fprintf(stderr,
	"%s: model binding missing or invalid in configuration file\n",
	Whoami);
	exit(2);
    }
    Model = v->value.s;
    fputs(QUICON, stdout);
    printf("%s^Z%s%s", CLEAROVERLAY, CLEARAOVERLAY, ENDCMD);
    fputs(QUICOFF, stdout);
    (void)fflush(stdout);
    if (!(v = getbindingvalue("readline")) || v->class != PROFILE_STRING &&
    v->class != PROFILE_OTHER) {
	fprintf(stderr,
	"%s: readline binding missing or invalid in configuration file\n",
	Whoami);
	exit(2);
    }
    if (!(ReadLine = fopen(v->value.s, "r"))) {
	fprintf(stderr, "%s: could not open %s for reading\n", Whoami,
	v->value.s);
	exit(2);
    }
#ifdef DEBUG
    raminfo = (struct qmsram *)malloc((unsigned)sizeof(struct qmsram));
    raminfo->TR = 401;
    raminfo->AR = 387;
    raminfo->FR = 13;
    raminfo->OR = 0;
#else
    qmsopen(fileno(stdout), fileno(ReadLine));
    if (!(raminfo = qmsram())) {
	fprintf(stderr, "%s: could not get printer ram info\n", Whoami);
	exit(2);
    }
#endif
    MaxBlocks = raminfo->AR + raminfo->FR;
    /* Save 5 block for filling with tpic */
    MaxBlocks -= 5;
#ifdef DEBUG
    fntinfo = (struct qmsfnt *)malloc((unsigned)sizeof(struct qmsfnt));
    fntinfo->ram = fntinfo->rom = NULL;
    for (i = 0; i < 13; i++) {
    	switch (i) {
	case 0:
	    fontno = 521;
	    orien = 'L';
	    break;
	case 1:
	    fontno = 522;
	    orien = 'L';
	    break;
	case 2:
	    fontno = 523;
	    orien = 'L';
	    break;
	case 3:
	    fontno = 524;
	    orien = 'L';
	    break;
	case 4:
	    fontno = 1100;
	    orien = 'P';
	    break;
	case 5:
	    fontno = 1103;
	    orien = 'P';
	    break;
	case 6:
	    fontno = 1200;
	    orien = 'P';
	    break;
	case 7:
	    fontno = 1204;
	    orien = 'P';
	    break;
	case 8:
	    fontno = 1217;
	    orien = 'L';
	case 9:
	    fontno = 7009;
	    orien = 'P';
	    break;
	case 10:
	    fontno = 7010;
	    orien = 'P';
	    break;
	case 11:
	    fontno = 7036;
	    orien = 'P';
	    break;
	case 12:
	    fontno = 7037;
	    orien = 'P';
	    break;
	}
        fn = (struct fontnode *)malloc((unsigned)sizeof(struct fontnode));
        fn->next = fntinfo->rom, fntinfo->rom = fn;
        fn->orientation = orien;
        fn->number = fontno;
	fn->bytes = 1024;	/* Rom fonts occupy one block */
	fn->version = '0';
	fn->class = '1';
    }
    /* For testing, ramfonts contains the already loaded fonts.  It should 
     * consist of lines containing two numbers and a letter, the first being 
     * the font number, the second being the font size, and the third letter
     * is an orientation.  The orientation should be the character immediately 
     * after the size of the font.
     */
    if (f = fopen("ramfonts", "r")) {	/* File is optional for testing */
	while (fscanf(f, "%d%d%c", &fontno, &size, &orien) == 3)
	{
	    fn = (struct fontnode *)malloc((unsigned)sizeof(struct fontnode));
	    fn->next = fntinfo->ram, fntinfo->ram = fn;
	    fn->orientation = orien;
	    fn->number = fontno;
	    fn->bytes = size;
	    fn->version = '0';
	    fn->class = '1';
	    raminfo->AR -= CEILING(size / 1024.0);
	    raminfo->FR += CEILING(size / 1024.0);
	}
	(void)fclose(f);
    }
#else
    if (!(fntinfo = qmsfnt())) {
	fprintf(stderr, "%s: could not get printer font info\n", Whoami);
	exit(2);
    }
#endif
    for (CurRomBlocks = 0, p = fntinfo->rom; p; p = p->next)
	CurRomBlocks += CEILING(p->bytes / (double)1024);
    CurRamBlocks = raminfo->FR - CurRomBlocks;
    /* Make an array of rom font #s */
    for (p = fntinfo->rom, intptr = romlist; p; p = p->next) {
	for (tempintptr = romlist, found = FALSE; tempintptr < intptr;
	tempintptr++)
	    if (*tempintptr == p->number)
	    	found = TRUE;
	if (!found)
	    *intptr++ = p->number;
    }
    *intptr = 0;
    if (EQ(Model, "QMS800"))
	(void)sprintf(PKDir, "%s/wbfonts", FONTDIR);
    else
	(void)sprintf(PKDir, "%s/wwfonts", FONTDIR);
    if (!loadnametable(PKDir, romlist, 10001)) {
	fprintf(stderr, "%s: could not open font directory %s\n", Whoami,
	PKDir);
	exit(2);
    }
    FontList = createfontlist(fntinfo);
    qmsfntfree(fntinfo);
    if (!setnewid(Host, User)) {
	fprintf(stderr, "%s: could not change user id to %s\n", Whoami,
	User);
	exit(2);		/* This is fatal bud */
    }
    SigStruct.sv_handler = callcleanup;
    (void)sigvec(SIGINT, &SigStruct, (struct sigvec *)NULL);
    fputs(QUICON, stdout);
    if (Orientation == 'P')
    	fputs(PORTRAIT, stdout);
    else
    	fputs(LANDSCAPE, stdout);
    printf("%s%c%c", TEXTPROC, '0', '0');
    printf("%s00000%05d", INITMARGVERT, Orientation == 'P' ? 11 * 1000 :
    (int)(8.5 * 1000));
    printf("%s00000%05d", INITMARGHORZ, Orientation == 'P' ? (int)(8.5 * 1000)
    : 11 * 1000);
    printf("%s0000", CHARSPACING);
    fputs(FREEOFF, stdout);
    seteoffunction(troffeofsocleanup);
    process();
    cleanup(FontList, SUCCEED);
}

/* Main routine that interprets the troff file */
void process()
{
    int		specialfont = 4;	/* Loc of S font in mount table */
    int		c, d, n;
    int		realhpxl = 0, realvpxl = 0,
    		virthpxl = 0, virtvpxl = 0; /*Virt & real cursor locs - pxls*/
    Boolean	switchedtospecial = FALSE;/* T if we switch to Special font */
    Boolean	firstpage = TRUE, fontselected = FALSE, download(), makeroom();
    char	*malloc(), *getstring(), *push(), *pop(), *strcpy(), *s, 
		*findpkfont(), extension[1000], *cgets(), *extractinteger();
    void	endoflist(), spline();
    double      radius, anglestart, angleend, decimalanglestart, a, b,
		decimalangleend, controlpoints[37][3], curvepoints[500][3];
    COORDINATE	start, end, center, cur, new;
    struct Stack    *tempstack;
    struct FontNode *fonttolist();
    struct FontNode *curnode = NULL; /* Current font ditroff has selected */
    struct FontNode *oldnode;	     /* 4 saving when trying special font */
    /* Current font ditroff has selected */
    struct FontState	current;
    /* For saving font when trying the special font */
    struct FontState	oldcurrent;

    current.font = current.pointsize = 0;
    bzero((char *)MountTable, sizeof(MountTable));
    while (TRUE) {	/* A non-returning function is called to exit */
	switch (c = getnonblank(stdin)) {
	case 's':
	    current.pointsize = getinteger(stdin);
	    curnode = fonttolist(MountTable[current.font], current.pointsize);
	    endoflist(&FontList, curnode);
	    fontselected = FALSE;
	    putchar('\r');	/* Ends the pass */
	    printf("%s00000%s", TAB, ENDCMD);
	    printf("%s00000%s", JUSTIFYMARGIN, ENDCMD);
	    realhpxl = realvpxl = 0;
	    break;
	case 'f':
	    current.font = getinteger(stdin);
	    curnode = fonttolist(MountTable[current.font], current.pointsize);
	    endoflist(&FontList, curnode);
	    fontselected = FALSE;
	    putchar('\r');	/* Ends the pass */
	    printf("%s00000%s", TAB, ENDCMD);
	    printf("%s00000%s", JUSTIFYMARGIN, ENDCMD);
	    realhpxl = realvpxl = 0;
	    break;
	case 'c':		/* Character at current location */
	    c = cgetchar();
	    /* If we couldn't find the font, don't print anything since we 
 	     * don't know how to increment the real & virtual pixel locations. 
	     */
	    if (!curnode || !curnode->localinfo || !LI(curnode)->charexists[c])
	    {
		oldcurrent = current;
		current.font = specialfont;
		oldnode = curnode;
		curnode = fonttolist(MountTable[current.font], 
		current.pointsize);
		if (curnode && curnode->localinfo && 
		LI(curnode)->charexists[c]) {
		    endoflist(&FontList, curnode);
		    fontselected = FALSE;
		    putchar('\r');
		    printf("%s00000%s", TAB, ENDCMD);
		    printf("%s00000%s", JUSTIFYMARGIN, ENDCMD);
		    realhpxl = realvpxl = 0;
		    switchedtospecial = TRUE;
		} else {
		    current = oldcurrent;
		    curnode = oldnode;
		    continue;
		}
	    }
	    adjust(&realhpxl, &realvpxl, virthpxl, virtvpxl);
	    if (!(curnode->flags & LOADED)) {
		if (!makeroom(FontList, MaxBlocks, &CurRamBlocks, CurRomBlocks,
		curnode->blocksize)) {
		    fprintf(stderr, 
		    "%s: could not free %d blocks for font %s\n", Whoami,
		    curnode->blocksize, LI(curnode)->pathname);
		    goto trytocontinue1;
		}
		if (!download(curnode))
		    goto trytocontinue1;
		realhpxl = realvpxl = 0;
		adjust(&realhpxl, &realvpxl, virthpxl, virtvpxl);
	    }
	    if (!fontselected) {
		printf("%s%d%s", DEFFONT, curnode->qmsnumber, ENDCMD);
		fontselected = TRUE;
	    }
	    /* For fonts with width but no bitmap, check the size of the
	     * raster.  The QMS will not print (or download) a character with
	     * width and no bitmap so we must not print characters that are not
	     * in the font.  If you do, you will get the lowest character in
	     * the font.
	     */
trytocontinue1:
	    if (LI(curnode)->bitmapsize[0][c] != 0 && 
	    LI(curnode)->bitmapsize[1][c] != 0) {
		if (c == '^' || c <= 32 || c >= 127)
		    printf("%s%02X", SPECIAL, c);
		else
		    (void)putchar(c);
		realhpxl += LI(curnode)->widths[c];
	    }
	    if (switchedtospecial) {		/* Switch back */
		switchedtospecial = FALSE;
		current = oldcurrent;
		curnode = oldnode;
		fontselected = FALSE;
	    }
	    break;
	case 'C':
	    s = getstring(stdin);
	    if (!curnode || !curnode->localinfo || (c = 
	    lookup(LI(curnode)->maptable, LI(curnode)->maptablesize, s)) == -1
	    || !LI(curnode)->charexists[c = LI(curnode)->maptable[c] >> 24 & 
	    0xFF]) {
		oldcurrent = current;
		current.font = specialfont;
		oldnode = curnode;
		curnode = fonttolist(MountTable[current.font], 
		current.pointsize);
		if (curnode && curnode->localinfo && (c = 
		lookup(LI(curnode)->maptable, LI(curnode)->maptablesize, s))
		!= -1 && LI(curnode)->charexists[c = LI(curnode)->maptable[c]
		>> 24 & 0xFF]) {
		    endoflist(&FontList, curnode);
		    fontselected = FALSE;
		    putchar('\r');
		    printf("%s00000%s", TAB, ENDCMD);
		    printf("%s00000%s", JUSTIFYMARGIN, ENDCMD);
		    realhpxl = realvpxl = 0;
		    switchedtospecial = TRUE;
		} else {
		    current = oldcurrent;
		    curnode = oldnode;
		    continue;
		}
	    }
	    adjust(&realhpxl, &realvpxl, virthpxl, virtvpxl);
	    if (!(curnode->flags & LOADED)) {
		if (!makeroom(FontList, MaxBlocks, &CurRamBlocks, CurRomBlocks,
		curnode->blocksize)) {
		    fprintf(stderr, 
		    "%s: could not free %d blocks for font %s\n", Whoami,
		    curnode->blocksize, LI(curnode)->pathname);
		    goto trytocontinue2;
		}
		if (!download(curnode))
		    goto trytocontinue2;
		realhpxl = realvpxl = 0;
		adjust(&realhpxl, &realvpxl, virthpxl, virtvpxl);
	    }
	    if (!fontselected) {
		printf("%s%d%s", DEFFONT, curnode->qmsnumber, ENDCMD);
		fontselected = TRUE;
	    }
trytocontinue2:	    
	    if (LI(curnode)->bitmapsize[0][c] != 0 &&
	    LI(curnode)->bitmapsize[1][c] != 0) {
		if (c == '^' || c <= 32 || c >= 127)
		    printf("%s%02X", SPECIAL, c);
		else
		    (void)putchar(c);
		realhpxl += LI(curnode)->widths[c];
	    }
	    if (switchedtospecial) {		/* Switch back */
		switchedtospecial = FALSE;
		current = oldcurrent;
		curnode = oldnode;
		fontselected = FALSE;
	    }
	    break;
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	    virthpxl += (c - '0') * 10 + (cgetchar() - '0');
	    c = cgetchar();
	    if (!curnode || !curnode->localinfo || !LI(curnode)->charexists[c])
	    {
		oldcurrent = current;
		current.font = specialfont;
		oldnode = curnode;
		curnode = fonttolist(MountTable[current.font], 
		current.pointsize);
		if (curnode && curnode->localinfo && 
		LI(curnode)->charexists[c]) {
		    endoflist(&FontList, curnode);
		    fontselected = FALSE;
		    putchar('\r');
		    printf("%s00000%s", TAB, ENDCMD);
		    printf("%s00000%s", JUSTIFYMARGIN, ENDCMD);
		    realhpxl = realvpxl = 0;
		    switchedtospecial = TRUE;
		} else {
		    current = oldcurrent;
		    curnode = oldnode;
		    continue;
		}
	    }
	    adjust(&realhpxl, &realvpxl, virthpxl, virtvpxl);
	    if (!(curnode->flags & LOADED)) {
		if (!makeroom(FontList, MaxBlocks, &CurRamBlocks, 
		CurRomBlocks, curnode->blocksize)) {
		    fprintf(stderr, 
		    "%s: could not free %d blocks for font %s\n", Whoami,
		    curnode->blocksize, LI(curnode)->pathname);
		    goto trytocontinue3;
		}
		if (!download(curnode))
		    goto trytocontinue3;
		realhpxl = realvpxl = 0;
		adjust(&realhpxl, &realvpxl, virthpxl, virtvpxl);
	    }
	    if (!fontselected) {
		printf("%s%d%s", DEFFONT, curnode->qmsnumber, ENDCMD);
		fontselected = TRUE;
	    }
trytocontinue3:
	    if (LI(curnode)->bitmapsize[0][c] != 0 && 
	    LI(curnode)->bitmapsize[1][c] != 0) {
		if (c == '^' || c <= 32 || c >= 127)
		    printf("%s%02X", SPECIAL, c);
		else
		    (void)putchar(c);
		realhpxl += LI(curnode)->widths[c];
	    }
	    if (switchedtospecial) {		/* Switch back */
		switchedtospecial = FALSE;
		current = oldcurrent;
		curnode = oldnode;
		fontselected = FALSE;
	    }
	    break;
	case 'p':		/* Begin a new page */
	    (void)getinteger(stdin);	/* Ignore page number */
	    realhpxl = realvpxl = virthpxl = virtvpxl = 0;
	    printf("%s%05d%s", TAB, 0, ENDCMD);
	    printf("%s%05d%s", JUSTIFYMARGIN, 0, ENDCMD);
	    if (!firstpage)
	    	fputs(FORMFEED, stdout), NumPages++;
	    else
	    	firstpage = FALSE;
	    break;
	case 'w':		/* Appears between words - ignore */
	    break;
	case 'H':		/* Absolute horizontal */
	    virthpxl = getinteger(stdin);
	    break;
	case 'h':		/* Relative horizontal */
	    virthpxl += getinteger(stdin);
	    break;
	case 'V':		/* Absolute vertical */
	    virtvpxl = getinteger(stdin);
	    break;
	case 'v':		/* Relative vertical */
	    virtvpxl += getinteger(stdin);
	    break;
	case 'n':		/* At end of line */
	    (void)getinteger(stdin);	/* Amount of space before line */
	    (void)getinteger(stdin);	/* Amount of space after line */
	    break;
	case '{':		/* Push stack */
	    tempstack = (struct Stack *)malloc(sizeof(struct Stack));
	    tempstack->fontstate.pointsize = current.pointsize;
	    tempstack->fontstate.font = current.font;
	    tempstack->h = virthpxl, tempstack->v = virtvpxl;
	    (void)push((char *)&tempstack);
	    break;
	case '}':		/* Pop stack */
	    tempstack = (struct Stack *)pop();
	    current.pointsize = tempstack->fontstate.pointsize;
	    current.font = tempstack->fontstate.font;
	    virthpxl = tempstack->h, virtvpxl = tempstack->v;
	    curnode = fonttolist(MountTable[current.font], current.pointsize);
	    endoflist(&FontList, curnode);
	    free((char *)tempstack);
	    fontselected = FALSE;
	    putchar('\r');	/* Ends the pass */
	    printf("%s00000%s", TAB, ENDCMD);
	    printf("%s00000%s", JUSTIFYMARGIN, ENDCMD);
	    realhpxl = realvpxl = 0;
	    break;
	case '#':		/* Comment */
	    while (cgetchar() != '\n')
	    	;
	    break;
	case 'D':		/* Graphics drawing commands */
	    switch (c = cgetchar()) {
	    case 'l':		/* Line */
		adjust(&realhpxl, &realvpxl, virthpxl, virtvpxl);
		start.x = virthpxl, start.y = virtvpxl;
		end.x = start.x + getinteger(stdin), end.y = start.y + 
		getinteger(stdin);
		/* I don't know what this next character is.  It is not
		 * documented.
		 */
		while ((c = cgetchar()) != '\n');
		    ;
		fputs(VECTORON, stdout);
		printf("%s%05d:%05d", PENUP, 
		ROUND(virthpxl/(double)RESOLUTION*1000), 
		ROUND(virtvpxl/(double)RESOLUTION*1000));
		printf("%s03", PENWIDTH);
		printf("%s%05d:%05d", PENDOWN, ROUND(end.x / RESOLUTION *
		1000), ROUND(end.y / RESOLUTION * 1000));
		fputs(VECTOROFF, stdout);
		printf("%s00000%s00000%s\r", TAB, JUSTIFYMARGIN, ENDCMD);
		virthpxl = ROUND(end.x), virtvpxl = ROUND(end.y);
		realhpxl = realvpxl = 0;
		break;
	    case 'c':		/* Circle */
		d = getinteger(stdin);
		printf("%s%c%05d%c%05d%05d%03d%03d%02d%s", ARC, 
		virthpxl+.5*d>=0.0?'+':'-', ROUND((virthpxl+.5*d)/RESOLUTION
		*1000.0),
		virtvpxl>=0?'+':'-', ROUND(virtvpxl/(double)RESOLUTION*1000.0),
		ROUND(.5*d/RESOLUTION*1000.0),
		0,0,3,ENDCMD);
		printf("%s00000%s00000%s", TAB, JUSTIFYMARGIN, ENDCMD);
		realhpxl = realvpxl = 0;
		virthpxl += d;
		break;
	    case 'e':		/* Ellipse */
		/* Ellipses satisfy the equation (x^2)/(a^2)+(y^2)/(b^2)=1
		 * where 'a' is the length of the semimajor axis and 'b' is
		 * the length of the semiminor axis.  We divide each quadrant
		 * into 200 line segments and connect the line segments.
		 */
		a = getinteger(stdin) / 2.0 / RESOLUTION, /* In inches */
		b = getinteger(stdin) / 2.0 / RESOLUTION;
		center.x = virthpxl / (double)RESOLUTION + a,
		center.y = virtvpxl / (double)RESOLUTION;
		cur.x = a, cur.y = 0.0; /* cur and new are */
		fputs(VECTORON, stdout);	/* relative to the origin */
		printf("%s03", PENWIDTH);
		for (n = 1; n <= 200; n++, cur = new) {
		    new.x = a*cos((n/200.0)*(PI/2.0)),
		    new.y = b*sin((n/200.0)*(PI/2.0));
		    /* First quadrant */
		    printf("%s%05d:%05d", PENUP,
		    ROUND((center.x+cur.x)*1000.0),
		    ROUND((center.y+cur.y)*1000.0));
		    printf("%s%05d:%05d", PENDOWN,
		    ROUND((center.x+new.x)*1000.0),
		    ROUND((center.y+new.y)*1000.0));
		    /* Second quadrant */
		    printf("%s%05d:%05d", PENUP,
		    ROUND((center.x-cur.x)*1000.0),
		    ROUND((center.y+cur.y)*1000.0));
		    printf("%s%05d:%05d", PENDOWN,
		    ROUND((center.x-new.x)*1000.0),
		    ROUND((center.y+new.y)*1000.0));
		    /* Third quadrant */
		    printf("%s%05d:%05d", PENUP,
		    ROUND((center.x-cur.x)*1000.0),
		    ROUND((center.y-cur.y)*1000.0));
		    printf("%s%05d:%05d", PENDOWN,
		    ROUND((center.x-new.x)*1000.0),
		    ROUND((center.y-new.y)*1000.0));
		    /* Fourth quadrant */
		    printf("%s%05d:%05d", PENUP,
		    ROUND((center.x+cur.x)*1000.0),
		    ROUND((center.y-cur.y)*1000.0));
		    printf("%s%05d:%05d", PENDOWN,
		    ROUND((center.x+new.x)*1000.0),
		    ROUND((center.y-new.y)*1000.0));
		}
		fputs(VECTOROFF, stdout);
		printf("%s00000%s00000\r", TAB, JUSTIFYMARGIN, ENDCMD);
		virthpxl += ROUND(2.0 * a * RESOLUTION);
		realhpxl = realvpxl = 0;
		break;
	    case 'a':		/* Arc */
		start.x = virthpxl, start.y = virtvpxl;
		center.x = virthpxl + getinteger(stdin), center.y = virtvpxl
		+ getinteger(stdin);
		end.x = center.x + getinteger(stdin), end.y = center.y +
		getinteger(stdin);
		radius = sqrt(pow(start.x - center.x, 2.0) + pow(start.y -
		center.y, 2.0));
		/* Ditroff uses reverse cartesian coordinate system */
		anglestart = atan2(start.x - center.x, center.y - start.y);
		angleend = atan2(end.x - center.x, center.y - end.y);
		anglestart -= PI / 2.0;
		angleend -= PI / 2.0;
		if (anglestart < 0)
		    anglestart += 2 * PI;
	        if (angleend < 0)
		    angleend += 2 * PI;
		decimalanglestart = angleend / PI * 500.0;
		decimalangleend = anglestart / PI * 500.0;
		printf("%s%c%05d%c%05d%05d%03d%03d%02d%s", DECIMALARC,
		center.x>0?'+':'-', ROUND(center.x/RESOLUTION*1000.0),
		center.y>0?'+':'-', ROUND(center.y/RESOLUTION*1000.0),
		ROUND(radius / RESOLUTION * 1000.0),
		ROUND(decimalanglestart)>=1000?0:ROUND(decimalanglestart),
		ROUND(decimalangleend)>=1000?0:ROUND(decimalangleend),
		3, ENDCMD);
		printf("%s00000%s00000%s", TAB, JUSTIFYMARGIN, ENDCMD);
		realhpxl = realvpxl = 0;
		virthpxl = ROUND(end.x), virtvpxl = ROUND(end.y);
		break;
	    case '~':		/* Squiggly line */
		(void)cgets(extension);
		s = extension;
		n = 0;
		controlpoints[n][0] = virthpxl;
		controlpoints[n][1] = virtvpxl;
		controlpoints[n][2] = 0.0;
		while (s = extractinteger(s, &c)) {
		    if (!(s = extractinteger(s, &d))) {
			fprintf(stderr, 
			"%s: odd number of spline points given\n", Whoami);
			break;
		    }
		    n++;
		    controlpoints[n][0] = controlpoints[n-1][0] + c;
		    controlpoints[n][1] = controlpoints[n-1][1] + d;
		    controlpoints[n][2] = 0.0;	/* Ignore z axis */
		}
	   	spline(controlpoints, n, curvepoints, 499, 3); /* 2nd degree */
		fputs(VECTORON, stdout);
		printf("%s%05d:%05d", PENUP, 
		ROUND(curvepoints[0][0]/RESOLUTION*1000.0),
		ROUND(curvepoints[0][1]/RESOLUTION*1000.0));
		printf("%s03", PENWIDTH);
		for (c = 1; c <= 499; c++)
		    printf("%s%05d:%05d", PENDOWN, 
		    ROUND(curvepoints[c][0]/RESOLUTION*1000.0),
		    ROUND(curvepoints[c][1]/RESOLUTION*1000.0));
		fputs(VECTOROFF, stdout);
		printf("%s00000%s00000%s\r", TAB, JUSTIFYMARGIN, ENDCMD);
		virthpxl = ROUND(curvepoints[n][0]), virtvpxl = 
		ROUND(curvepoints[n][1]);
		realhpxl = realvpxl = 0;
		break;
	    default:
		fprintf(stderr, "%s: invalid ditroff draw command %c\n",
		Whoami, c);
		fputs(FORMFEED, stdout), NumPages++;
		cleanup(FontList, 2);
		break;
	    }
	    break;
	case 'x':		/* Device dependent commands */
	    switch (c = getnonblank(stdin)) {
	    case 'i':		/* Init - do nothing */
		while (!isspace(cgetchar()))	/* Find next blank */
			;
		break;
	    case 'T':		/* Name of typesetter. -T option */
		if (!EQ(DEVNAME, s = getstring(stdin))) {
		    fprintf(stderr,"%s: %s input given to ditroff filter\n",
		    Whoami, s);
		    cleanup(FontList, 2);
		}
		break;
	    case 'r':		/* Resolution */
		while (!isspace(getchar()))
		    ;
		if (getinteger(stdin) != RESOLUTION) {
		    fprintf(stderr, "%s: ditroff input resolution != %d\n",
		    Whoami, RESOLUTION);
		    cleanup(FontList, 2);
		}
		/* Order of evaluation is immaterial */
		if (getinteger(stdin) != 1 || getinteger(stdin) != 1) {
		    fprintf(stderr, 
		    "%s: incorrect ditroff input increments\n", Whoami);
		    cleanup(FontList, 2);
		}
		break;
	    case 'p':		/* Pause */
		while (!isspace(cgetchar()))
		    ;
		break;
	    case 's':		/* Stop */
		while ((c = getchar()) != EOF)	    /* Standard I/O getchar */
		    ;
		callcleanup();
		break;
	    case 't':		/* Trailer */
		while (!isspace(cgetchar()))
		    ;
		break;
	    case 'f':		/* Load font into mount table */
		while (!isspace(cgetchar()))
		    ;
		c = getinteger(stdin);
		s = getstring(stdin);
		(void)strcpy(MountTable[c], s);
		/* The special font must be named S */
		if (EQ(s, "S"))
		    specialfont = c;
		if (!findpkfont(s, 10))	/* Just check for font, any size. */
		    fprintf(stderr,
		    "%s: could not find font %s at any magnification\n",
		    Whoami, s);
		break;
	    case 'H':		/* Height - QMS can't do */
		while (!isspace(cgetchar()))
		    ;
		(void)getinteger(stdin);
		break;
	    case 'S':		/* Slant - QMS can't do */
		while (!isspace(cgetchar()))
		    ;
		(void)getinteger(stdin);
		break;
	    case 'X':		/* Extensions */
		(void)cgets(extension);
		adjust(&realhpxl, &realvpxl, virthpxl, virtvpxl);
		Pxxxstring = Xxxstring = extension;
		Origin[0] = virthpxl, Origin[1] = virtvpxl;
		(void)yyparse();
		fputs(QUICON, stdout);
		if (Orientation == 'P')
		    fputs(PORTRAIT, stdout);
	        else
		    fputs(LANDSCAPE, stdout);
		printf("%s%c%c", TEXTPROC, '0', '0');
		printf("%s00000%05d", INITMARGVERT, Orientation == 'P' ?
		11 * 1000 : (int)(8.5 * 1000));
		printf("%s00000%05d", INITMARGHORZ, Orientation == 'P' ?
		(int)(8.5 * 1000) : 11 * 1000);
		printf("%s0000", CHARSPACING);
		realhpxl = realvpxl = 0;
		break;
	    default:
		fprintf(stderr, "%s: invalid ditroff x command %c\n", Whoami,
		c);
		fputs(FORMFEED, stdout), NumPages++;
		cleanup(FontList, 2);
		break;
	    }
	    break;
	default:
	    fprintf(stderr, "%s: invalid ditroff command %c\n", Whoami, c);
	    fputs(FORMFEED, stdout), NumPages++;
	    cleanup(FontList, 2);
	    break;
	}
    }
}	    

/* Returns a pointer to a node on the FontList with the matching name, 
 * pointsize and orientation or NULL if not found.
 */
struct FontNode *getnodebyname(head, name, ptsize, or)
struct FontNode	*head;
char		*name;
int		ptsize;
int		or;
{
    struct FontNode *p;

    for (p = head; p; p = p->next)
        if (p->localinfo && LI(p)->pointsize == ptsize && (p->flags & PORT
	? 'P' : 'L') == or && EQ(LI(p)->name, name))
	    return p;
    return NULL;
}

/* Adds a new font to the font list.  All the fields in the LocalInfo
 * structure are filled in.  If the font is loaded on startup, the blocksize
 * is left as is; otherwise, the blocksize is estimated.
 */
struct FontNode *fonttolist(font, pointsize)
char	*font;
int	pointsize;
{
    long	fontstructoffset; /* Offset at beginning of font structure */
    long 	funnycharoffset;/* Offset before funny_char_index_table */
    long	ftell();
    int		qmsfontnum, mag, ds, i, c, fontindex, encodecompare();
    short	size, index;
    char	*fullfontpath, fontname[21], sfontname[21], *tail(), *malloc(),
    		descfilename[81], *findpkfont();
    Boolean	extractinfo();
    void	seteoffunction(), pkeofsocleanup(), desceofsocleanup(),
    		troffeofsocleanup();
    FILE	*f;
    struct dev		devheader;
    struct font		fontheader;
    struct FontNode	*fn, *getfontnode();
    struct FontInfo	*fi, *getfontinfo();

    if (strlen(font) == 0 || pointsize == 0)	/* One of the two has not */
        return NULL;				/* been filled in yet. */
    /* If this works, we don't need to search the font directory */
    if (fn = getnodebyname(FontList, font, pointsize, Orientation))
	return fn;
    if (!(fullfontpath = findpkfont(font, pointsize)))
    	return NULL;			/* Try to continue */
    qmsfontnum = getnumfromtable(tail(fullfontpath));
    assert(qmsfontnum > 0);
    if (!(fn = getfontnode(FontList, qmsfontnum, Orientation))) {
	bzero((char *)(fn = (struct FontNode *)malloc((unsigned)
	sizeof(struct FontNode))), sizeof(struct FontNode));
        fn->next = FontList, FontList = fn;
    }
    if (fn->localinfo)			/* This font is already on the list */
    	return fn;
    else
    	bzero(fn->localinfo = malloc((unsigned)sizeof(struct LocalInfo)),
	sizeof(struct LocalInfo));
    fn->flags |= RAM;
    if (Orientation == 'P')
    	fn->flags |= PORT;
    fn->qmsnumber = qmsfontnum;
    (void)strcpy(LI(fn)->name, font);
    (void)strcpy(LI(fn)->pathname, fullfontpath);
    (void)extractinfo(tail(LI(fn)->pathname), fontname, sfontname, &ds, &mag);
    LI(fn)->pointsize = ROUND(mag / (double)RESOLUTION * UNITWIDTH);
    (void)strcpy(EOFName, fullfontpath);
    seteoffunction(pkeofsocleanup);
    if (!(fi = getfontinfo(fullfontpath, (EQ(Model, "QMS800") ||
    EQ(Model, "QMS1500")) && Orientation == 'P' || (!EQ(Model, "QMS800") && 
    !EQ(Model, "QMS1500")) && Orientation == 'L' ? 'X' : 'Y'))) {
	FontList = FontList->next;	/* Bad PK file or not readable */
	fprintf(stderr, "%s: could not open or bad PK file %s\n", Whoami,
	fullfontpath);
	free(fn->localinfo), free((char *)fn);
	return NULL;
    }
    if (!(fn->flags & PRELOADED))
    	fn->blocksize = fi->blocksize;
    seteoffunction(troffeofsocleanup);
    LI(fn)->qmsheight = fi->qmsheight;
    LI(fn)->qmsbaseline = fi->qmsbaseline;
    for (i = 0; i < 256; i++) {
	if (fi->chararray[i].tfm > 0 || (fi->chararray[i].w > 0 &&
	fi->chararray[i].h > 0))
	    LI(fn)->charexists[i] = TRUE;
	else
	    LI(fn)->charexists[i] = FALSE;
	LI(fn)->widths[i] = ROUND(fi->chararray[i].tfm * ((fi->ds / 
	(double)FIX) / FIX) / PPI * RESOLUTION * ((double)LI(fn)->pointsize
	/ UNITWIDTH));
	LI(fn)->bitmapsize[0][i] = fi->chararray[i].w;
	LI(fn)->bitmapsize[1][i] = fi->chararray[i].h;
    }
    (void)sprintf(descfilename, "%s/%s", FONTDIR, "DESC.out");
    LI(fn)->maptablesize = 0;
    if (!(f = fopen(descfilename, "r"))) { /* Couldn't access DESC.out! */
	static Boolean printed = FALSE;

	if (!printed) {		/* Only print error message once */
	    fprintf(stderr, "%s: could not open %s file \n", Whoami,
	    descfilename);
	    printed = TRUE;
	}
	return fn;
    }
    (void)strcpy(EOFName, descfilename);
    seteoffunction(desceofsocleanup);
    (void)cfread((char *)&devheader, sizeof(struct dev), 1, f);
    (void)cfread((char *)&size, sizeof(short), 1, f);
    while (size)
    	(void)cfread((char *)&size, sizeof(short), 1, f);
    funnycharoffset = ftell(f);
    assert(devheader.nchtab <= 160);
    (void)fseek(f, (long)devheader.nchtab * sizeof(short) + devheader.lchname, 
    1);
    for (fontstructoffset = ftell(f); (i = fread((char *)&fontheader, 
    sizeof(struct font), 1, f)) == 1 && !EQ(LI(fn)->name, fontheader.namefont);
    (void)fseek(f, 3L * fontheader.nwfont + 96 + devheader.nchtab, 1), 
    fontstructoffset = ftell(f))
    	;
    if (i != 1) {	/* font not found */
	(void)fclose(f);
	seteoffunction(troffeofsocleanup);
	return fn;
    }
    for (i = 0; i < devheader.nchtab; i++) {
	(void)fseek(f, fontstructoffset + sizeof(struct font) + 3 * 
	fontheader.nwfont + 96 + i, 0);
	if ((fontindex = cgetc(f)) == 0)
	    continue;
	(void)fseek(f, funnycharoffset + i * sizeof(short), 0);
	(void)cfread((char *)&index, sizeof(short), 1, f);
	(void)fseek(f, funnycharoffset + devheader.nchtab * sizeof(short) + 
	index, 0);
	/* The special strings are encoded, put in the maptable and 
	 * sorted for fast lookup using a binary search.  The encoding must
	 * preserve the uniqueness of the strings.  Assuming special
	 * sequences are 3 bytes or less, we can put the bytes of the strings
	 * in the 3 low order bytes of the maptable[] word and use
	 * the high order byte to store the position in the font.
	 */
	LI(fn)->maptable[LI(fn)->maptablesize] = 0;
	while ((c = cgetc(f)) != '\0')
	    LI(fn)->maptable[LI(fn)->maptablesize] = 
	    LI(fn)->maptable[LI(fn)->maptablesize] << 8 | c;
	LI(fn)->maptable[LI(fn)->maptablesize] &= 0xFFFFFF;/*Keep low 3 bytes*/
	(void)fseek(f, fontstructoffset + sizeof(struct font) + 2 * 
	fontheader.nwfont + fontindex, 0);
	LI(fn)->maptable[LI(fn)->maptablesize] |= cgetc(f) << 24; /* Put font*/
	LI(fn)->maptablesize++;				/* pos in top byte */
    }
    (void)fclose(f);
    seteoffunction(troffeofsocleanup);
    qsort((char *)(LI(fn)->maptable), LI(fn)->maptablesize, sizeof(long),
    encodecompare);
    return fn;
}

/* Returns the full pathname of the font whose size most closely matches that
 * passed as a parameter.  Returns NULL if it can't open the directory or it
 * cannot find the font at any magnification.  The return value is static and
 * overwritten with each call.
 */
char *findpkfont(fontname, pointsize)
char	*fontname;
int	pointsize;
{
    int		    closestnumber = MAX_INTEGER;
    int		    desiredmagnification = ROUND(RESOLUTION * pointsize /
    		    (double)UNITWIDTH);
    static char	    returnvalue[101];
    DIR		    *dirp;
    struct direct   *direntry;
    char	    dirfname[81], dirsfname[81];
    Boolean	    extractinfo();
    int		    dirdsize, dirmag;

    returnvalue[0] = '\0';
    if (dirp = opendir(PKDir)) {
	for (direntry = readdir(dirp); direntry; direntry = readdir(dirp)) {
	    if (direntry->d_namlen < 5 || !extractinfo(direntry->d_name,
	    dirfname, dirsfname, &dirdsize, &dirmag) || !EQ(dirfname,
	    fontname))
		continue;
	    if (ABS(dirmag - desiredmagnification) < closestnumber) {
		closestnumber = ABS(dirmag - desiredmagnification);
		(void)sprintf(returnvalue, "%s/%s", PKDir,
		direntry->d_name);
	    }
	}
	closedir(dirp);
    }
    if (strlen(returnvalue) > 0)
	return returnvalue;
    return NULL;
}

/* Adjusts printer coordinates so they correspond to ditroff's coordinates.  It
 * adjusts the real coordinates if necessary.
 */
void adjust(realh, realv, virth, virtv)
int *realh, *realv, virth, virtv;
{
    if (*realh != virth) {
	printf("%s%05d%s", TAB, ROUND(virth / (double)RESOLUTION * 1000.0),
	ENDCMD);
	*realh = virth;
    }
    if (*realv != virtv) {
	printf("%s%05d%s", JUSTIFYMARGIN, ROUND(virtv / (double)RESOLUTION *
	1000.0), ENDCMD);
	*realv = virtv;
    }
}

/* Compares two encodings of troff special sequences */
int encodecompare(one, two)
long	*one, *two;
{
    return (*one & 0xFFFFFF) == (*two & 0xFFFFFF) ? 0 : (*one & 0xFFFFFF)
    < (*two & 0xFFFFFF) ? -1 : 1;
}

/* Looks up a special sequence in the maptable and returns the position
 * found.  It returns -1 if not found.  This routine uses a binary search.
 */
int lookup(table, numvalues, key)
long	table[];
int	numvalues;
char	*key;
{
    int		start = 0, end = numvalues - 1, midpoint;
    long	keyencoded;		/* An encoded version of the key */
    char	*p;

    for (p = key, keyencoded = 0; p && *p; p++)
	keyencoded = (keyencoded << 8) | *p;
    while (start <= end) {
	midpoint = (start + end) / 2;
	if ((keyencoded & 0xFFFFFF) == (table[midpoint] & 0xFFFFFF))
	    return midpoint;
	if ((keyencoded & 0xFFFFFF) > (table[midpoint] & 0xFFFFFF))
	    start = midpoint + 1;
	else
	    end = midpoint - 1;
    }
    return -1;
}

/* Downloads a font */
Boolean download(fn)
struct FontNode *fn;
{
    struct CharInfo	*ci;
    char		*tail(), *strcpy();
    void		resetpkfile(), seteoffunction(),
    			downloadpkeofsocleanup(), troffeofsocleanup();
    int			downloadinterrupt(), callcleanup();
    int			downloadtype, result, i, truebyteswide, bytestooutput,
    			row, bytes;

    if (!fn || fn->flags & LOADED || !(fn->flags & RAM) || (fn->flags & PORT ?
    'P' : 'L') != Orientation)
    	return FALSE;
    resetpkfile();
    downloadtype = ((EQ(Model, "QMS800") || EQ(Model, "QMS1500")) && 
    Orientation == 'P' || (!EQ(Model, "QMS800") && !EQ(Model, "QMS1500")) 
    && Orientation == 'L' ? 'X' : 'Y');
    DownLoadFNum = fn->qmsnumber;
    (void)sigblock(SIGINT);	/* Temporarily block interrupts */
    SigStruct.sv_handler = downloadinterrupt;
    (void)sigvec(SIGINT, &SigStruct, (struct sigvec *)NULL);
    (void)strcpy(EOFName, LI(fn)->pathname);
    seteoffunction(downloadpkeofsocleanup);
    /* #defining ASCIILOAD causes ASCII characters to be used for the download
     * sequence.  Twice as many characters must be sent but it is useful for
     * looking at the output when debugging.  Also, you can actually see the
     * bitmaps in the output.  ASCII loading must be used when you can't get
     * hardware flow control to work.  Far out.
     */
#ifdef ASCIILOAD
    fputs(FREEFORM, stdout);
#else
    fputs(EIGHTBITON, stdout);
#endif
    printf("%s%05d%c0%-4.4s%03d%03dT", DOWNLOAD, fn->qmsnumber, Orientation,
    tail(LI(fn)->pathname), LI(fn)->qmsheight, LI(fn)->qmsbaseline);
    (void)sigsetmask(sigblock(0) & ~SIGINT);	/* Unblock interrupts */
    while ((result = getnextcharinfo(LI(fn)->pathname, downloadtype, &ci)) !=
    1)
    	switch (result) {
	case 2:
	    fprintf(stderr, "%s: could not open %s\n", Whoami, 
	    LI(fn)->pathname);
	    return FALSE;
	case 3:
	    fprintf(stderr, "%s: %s is not a PK file\n", Whoami, 
	    LI(fn)->pathname);
	    return FALSE;
	case 4:
	    fprintf(stderr, "%s: %s PK version is incorrect\n", Whoami,
	    LI(fn)->pathname);
	    return FALSE;
	case 0:			/* Ok. Got a bitmap */
	    if (ci->h == 0 || ci->w == 0)
		continue;
	    putchar(',');
	    printf("%02X%03d", ci->cc, LI(fn)->widths[ci->cc]);
	    if (downloadtype == 'X') {
		printf("%03d%03d", ci->h, ci->w);
		printf("%c%03d", LI(fn)->qmsbaseline - ci->voff >= 0 ? '+'
		: '-', ABS(LI(fn)->qmsbaseline - ci->voff));
		printf("%c%03d", -ci->hoff >= 0 ? '+' : '-', ABS(-ci->hoff));
	    } else {
		printf("%03d%03d", ci->w, ci->h);
		printf("%c%03d", -ci->hoff >= 0 ? '+' : '-', ABS(ci->hoff));
		printf("%c%03d", (LI(fn)->qmsheight - LI(fn)->qmsbaseline) -
		(ci->h - ci->voff) >= 0 ? '+' : '-', ABS((LI(fn)->qmsheight
                - LI(fn)->qmsbaseline) - (ci->h - ci->voff)));
		i = ci->w, ci->w = ci->h, ci->h = i; /* Swaparoo! */
	    }
	    truebyteswide = ci->w + 7 >> 3;	/* >> divides by 8 */
	    bytestooutput = (ci->w + 15 >> 4) * 2; /* >> divides by 16 */
#ifdef ASCIILOAD
	    for (row = 0; row < ci->h; row++) {
#else
	    for (row = 0; row < ci->h; row++)
#endif
		for (bytes = 0; bytes < bytestooutput; bytes++) {
		    if (bytes == truebyteswide - 1)
		    	*(ci->bitmap + row * truebyteswide + bytes) &=
			0xFF<<7-(ci->w+7)%8;
		    if (bytes < truebyteswide) {
			i = *(ci->bitmap + row * truebyteswide + bytes) &
			0xFF;
#ifdef ASCIILOAD
			printf("%c%c", "0123456789ABCDEF"[i >> 4],
			"0123456789ABCDEF"[i & 0xF]);
#else
			if (i == '^')
			    printf("^^");
			else
			    (void)putchar(i);
#endif
		    } else
#ifdef ASCIILOAD
		        printf("00");
#else
		        (void)putchar(0);
#endif
		}
#ifdef ASCIILOAD
	        putchar('\n');
	    }
#endif
	    break;
	}	/* End switch */
#ifdef ASCIILOAD
    printf("%s%s", ENDCMD, FREEOFF);
#else
    printf("%s%s", ENDCMD, EIGHTBITOFF);
#endif
    putchar('\r');	/* Ends the pass */
    (void)sigblock(SIGINT);
    printf("%s00000%s", TAB, ENDCMD);
    printf("%s00000%s", JUSTIFYMARGIN, ENDCMD);
    SigStruct.sv_handler = callcleanup;
    (void)sigvec(SIGINT, &SigStruct, (struct sigvec *)NULL);
    seteoffunction(troffeofsocleanup);
    CurRamBlocks += fn->blocksize;
    fn->flags |= LOADED;
    (void)sigsetmask(sigblock(0) & ~SIGINT);
    return TRUE;
}

/* The following are routines called when a SIGINT is received or EOF is
 * unexpectedly encountered when reading a file.
 */
simplyexit()
{
    exit(SUCCEED);
}

callcleanup()
{
    void cleanup();

    fputs(FORMFEED, stdout), NumPages++;
    cleanup(FontList, SUCCEED);	/* Interrupting a program is not an error */
}

void troffeofsocleanup()
{
    fprintf(stderr, "%s: unexpected EOF when reading ditroff file\n", Whoami);
    fputs(FORMFEED, stdout), NumPages++;
    cleanup(FontList, 2);
}

void pkeofsocleanup()
{
    fprintf(stderr, "%s: unexpected EOF when reading PK file %s\n", Whoami,
    EOFName);
    fputs(FORMFEED, stdout), NumPages++;
    cleanup(FontList, 2);
}

void desceofsocleanup()
{
    fprintf(stderr, "%s: unexpected EOF when reading DESC.out file %s\n",
    Whoami, EOFName);
    fputs(FORMFEED, stdout), NumPages++;
    cleanup(FontList, 2);
}

downloadinterrupt()
{
    fputs(ENDCMD, stdout);
#ifdef ASCIILOAD
    fputs(FREEOFF, stdout);
#else
    fputs(EIGHTBITOFF, stdout);
#endif
    printf("%s%05d%c%s", DOWNLOAD, DownLoadFNum, Orientation, ENDCMD);
    callcleanup();
}

void downloadpkeofsocleanup()
{
    fputs(ENDCMD, stdout);
#ifdef ASCIILOAD
    fputs(FREEOFF, stdout);
#else
    fputs(EIGHTBITOFF, stdout);
#endif
    printf("%s%05d%c%s", DOWNLOAD, DownLoadFNum, Orientation, ENDCMD);
    pkeofsocleanup();
}

#include "xxx.c"