#ifndef lint
static char sccsid[] = "@(#)dsan.c	1.16 (UKC) 21/10/87";
#endif
/*
 *	dsan
 *	driver for Sanders Variflex Dot matrix printer
 *	Martin Guy. UKC. August 1984.
 *	"Adapted" from dlas.c
 */

/*
 *	output language from troff:
 *	all numbers are character strings
 *
 *	sn	size in points
 *	fn	font as number from 1-n
 *	cx	ascii character x
 *	Cxyz	funny char xyz. terminated by white space
 *	Hn	go to absolute horizontal position n
 *	Vn	go to absolute vertical position n (down is positive)
 *	hn	go n units horizontally (relative)
 *	vn	ditto vertically
 *	nnc	move right nn, then print c (exactly 2 digits!)
 *			(this wart is an optimization that shrinks output file
 *			 size about 35% and run-time about 15% while preserving
 *			 ascii-ness)
 *	Dt..\n	draw operation 't':
 *			Dl x y		line from here by x,y
 *			Dc d		circle of diameter d with left side here
 *			De x y		ellipse of axes x,y with left side here
 *			Da x y r	arc counter-clockwise by x,y of radius r
 *			D~ x y x y ...	wiggly line by x,y then x,y ...
 *	nb a	end of line (information only)
 *			b = space before line, a = after
 *	p	new page begins -- set v to 0
 *	#...\n	comment
 *	x ...\n	device control functions:
 *			x i	init
 *			x T s	name of device is s
 *			x r n h v	resolution is n/inch
 *				h = min horizontal motion, v = min vert
 *			x p	pause (can restart)
 *			x s	stop -- done for ever
 *			x t	generate trailer
 *			x f n s	font position n contains font s
 *			x H n	set character height to n
 *			x S n	set slant to N
 *
 *	Subcommands like "i" are often spelled out like "init".
 */

/*
 * We have two groups of routines here; those which deal with the virtual typesetter (which just set variables)
 * and those which send codes to the actual device. The virtual machine's characteristics are that it stores
 * motions and font changes until they are actually needed to set a character and when it sets a character
 * its printhead does not auto-advance, which the sanders' does, but ditroff does not expect.

 * main		Process arguments, sets signal traps and calls outlist() and conv()
 * outlist	Process the argument from -o into ranges of pages to print

 * conv		Read Ditroff intermediate language and calls hmot() vmot() hgoto() vgoto() put1() put1s()
 *		 	setsize() setfont() t_page() t_newline() devctrl()
 * devctrl 	Read more DIL and calls fileinit() t_reset() loadfont() t_charht() t_slant()

 * fileinit	Read DESC.out into dev pstab chtab and chname
 * loadfont	Read %s.out into fontbase, fitab, widthtab and codetab [n]

+* abortjob	Activated by signals to graunch a job to a halt. Calls done().
+* done		eject printout and exit.
 * t_charht t_slant Unimplemented setting of character height (?) and slant
+* t_init	Send codes to initialise device
+* t_page	Send codes to throw a new page
 * t_line	Draw line on device
 * t_size	Convert index into pstab into actual point size
 * t_font	Return conversion of font number as string (1,2,3,4) into integer (great!)
 * t_reset	The code SAYS stop/pause the typesetter, but that's not what dcan/dlas do with it.
 * t_wrapup	Finish job
 * t_trailer	Generate tail banner
 * banner	Generate banner page
 * hgoto hmot	Do Horizontal absolute and relative printhead motions on virtual machine
 * vgoto vmot	Vertical ditto
+* put1		Print a character on the virtual machine; calls setfont()
 * put1s	Ditto, but the character is not a single ascii code but a two-letter one like "ul" calls put1()
 * setsize	Set point size on virtual machine
 * setfont	Set font (virtual)

+* fontflush	Output codes to set real font to virtual one
+* ocmd		Output a printable character to device (a #define)
 * error	error-message printing function.

+* Routines which output device codes
 */

/* spare1 field of struct Font (a char) is B for a bold font, anything else for regular.
 *
 * spare1 field of struct dev (a short) is the actual width of the box rule character \(br.
 *	Troff assumes br is zero-width so we keep up the pretense here by doing a little horizontal motion before and after.
 *	In the width tables br (of which there should only be one) must have a stated width of 0. The real width is in dev.spare1.
 */

/* Conditional Compilation Options
 *	FRICTION Set page length to 12" - 5/288ths if using friction feed
 *	PROOF	Include code to do the -P option. Removed to save ribbons.
 *	DEBUG	Include bits for -d option. Produces eNORMOUS crap in log file
 *
 *	values of dbg (number following -d option):
 *	1	description of everything the prog puts out as it goes along. BIG.
 *	2	dump fonts as read in from description files
 *	4	print analysis of DELIVERY string used in tail banner
 *	8	list of pages to output (-o option)
 *	16	Head auto-advance info. Even  B I G G E R.
 */

#include	<stdio.h>
#include	<signal.h>
#include	<math.h>
#include	<ctype.h>
#include	<sys/types.h>
#include	<sys/time.h>
#include	<errno.h>
#include	"dev.h"

#define	NFONT	10
#define VEPS	288	/* Actual resolution of Sanders in steps per inch */
#define BOLD	6	/* Degree of emboldening */
#define NOBOLD	0

#ifdef FRICTION
# define RESET "!RS!!FL3451!!FW7920!!LM0!!RM0!!VMOFF!" /* commands to reset the printer to a useable state */
#else
# define RESET "!RS!!FL3456!!FW7920!!LM0!!RM0!!VMOFF!" /* commands to reset the printer to a useable state */
#endif
		/*	RS	Reset (Not that it seems to do much)
		 *	FL3456	Form length = ( 12" x 288 == 3456 )
				- 5.5 (fiddle).  -5 -> text creeps down page
				(The Sanders Inch != the Imperial Inch)
		 *	FW7920	Form width = 8.25" x 960
		 *	LM0 RM0	Blap left and right margins
		 *	VMOFF	Don't honour vertical margins
		 */

/* Print Quality */
#define DRAFT	-1	/* A single printing pass */
#define REGULAR	0	/* Normal mode Regular, Overprinted Bold */
#ifdef PROOF
# undef PROOF
# define PROOF	1	/* Overprinted Regular, Smudged Bold */
#endif
int	quality	= REGULAR;

int	output	= 0;	/* 0 => skip, 1 => print it */
int	nolist	= 0;	/* output page list if > 0 */
int	olist[20];	/* pairs of page numbers */

struct dev dev;
struct Font *fontbase[NFONT+1];
short	*pstab;		/* USED IN draw.o */
int	nsizes	= 1;
int	nfonts;
int	paperwidth;
int	smnt;		/* index of first special font */
int	nchtab;
char	*chname;
short	*chtab;
char	*fitab[NFONT+1];/* font index table: position of char i on this font */
			/* zero if not there */
unsigned char *widthtab[NFONT+1];	/* Widths of the characters */
char	*codetab[NFONT+1];	/* device codes */
short	boxrulewid;		/* the real printing width of a box rule char */

#define	BMASK	0377
#ifdef DEBUG
int	dbg	= 0;
#endif
long	lineno	= 0;
#define RES	960
int	res;			/* resolution assumed when generated */
FILE	*tf	= stdout;	/* output file pointer */
char	*fontdir	= "/usr/lib/font";
char	*username;		/* for banner */
char	*jobnumber;
char	*jobname;
char	*host;
int	acctpages	= 0;
char	devname[20]	= "san";

extern int DX, DY, maxdots;	/* USED IN draw.o */

int	hpos = 0, vpos = 0;	/* current internal x,y pos */

#define hgoto(n)	hpos = (n)
#define	hmot(n)		hpos += (n)
#define vgoto(n)	vpos = (n)
#define	vmot(n)		vpos += (n)

int abortjob();	/* must declare to use in signal() call */

main(argc, argv)
char *argv[];
{
	--argc; ++argv;
	while (argc > 0 ) {
		if (argv[0][0] == '-') {
			switch (argv[0][1]) {
			case 'u':
				username = argv[1];
				argv++; argc--;
				break;
			case 'n':
				jobname = argv[1];
				argv++; argc--;
				break;
			case 'j':
				jobnumber = argv[1];
				argv++; argc--;
				break;
			case 'h':
				host = argv[1];
				argv++; argc--;
				break;
			case 'o':
				outlist(&argv[0][2]);
				break;
#ifdef DEBUG
			case 'd':
				dbg = atoi(&argv[0][2]);
				if (dbg == 0) dbg = -1;	/* I hope they know what they just asked for! */
				tf = stdout;
				break;
#endif
			case 'D':
				quality = DRAFT;
				break;
#ifdef PROOF
			case 'P':
				quality = PROOF;
				break;
#endif
			}
		}
		argc--;
		argv++;
	}

	(void) signal(SIGINT, abortjob);
	(void) signal(SIGHUP, abortjob);
	(void) signal(SIGQUIT, abortjob);

#ifdef DEBUG
	if (dbg) error(0, "DSAN DEBUGGING STARTS");    /* not really an error */
#endif
	conv(stdin);
	t_wrapup();
	account();
	done();
}

/*
 *	Process list of page numbers to be printed
 */
outlist(s)
char *s;
{
	int n1, n2;

	nolist = 0;
	while (*s) {
		n1 = 0;
		if (isdigit(*s))
			do
				n1 = 10 * n1 + *s++ - '0';
			while (isdigit(*s));
		else
			n1 = -9999;
		n2 = n1;
		if (*s == '-') {
			s++;
			n2 = 0;
			if (isdigit(*s))
				do
					n2 = 10 * n2 + *s++ - '0';
				while (isdigit(*s));
			else
				n2 = 9999;
		}
		olist[nolist++] = n1;
		olist[nolist++] = n2;
		if (*s != '\0')
			s++;
	}
	olist[nolist] = 0;
#ifdef DEBUG
	if (dbg&8)
		fputs("Output Page list\nFrom To\n", stderr);
		for (i=0; i<nolist; i += 2)
			fprintf(stderr,"%3d  %3d\n", olist[i], olist[i+1]);
#endif
}

in_olist(n)
int n;
{
	int i;

	if (nolist == 0)
		return(1);	/* everything is included */
	for (i = 0; i < nolist; i += 2)
		if (n >= olist[i] && n <= olist[i+1])
			return(1);
	return(0);
}

conv(fp)
register FILE *fp;
{
	register int c, k;
	int n;
	char str[100], buf[300];

	while ((c = getc(fp)) != EOF) {
		switch (c) {
		case '\n':	/* when input is text */
		case ' ':
		case 0:		/* occasional noise creeps in */
			break;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			/* two motion digits plus a character */
			hmot((c-'0')*10 + getc(fp)-'0');
			put1(getc(fp));
			break;
		case 'c':	/* single ascii character */
			put1(getc(fp));
			break;
		case 'C':
			(void) fscanf(fp, "%s", str);
			put1s(str);
			break;
		case 'D':	/* draw function */
			(void) fgets(buf, sizeof(buf), fp);
 			error(0, "Drawing functions not supported");
			break;
		case 's':
			(void) fscanf(fp, "%d", &n);	/* ignore fractional sizes */
			setsize(t_size(n));
			break;
		case 'f':
			(void) fscanf(fp, "%s", str);
			setfont(t_font(str));
			break;
		case 'H':	/* absolute horizontal motion */
			/* (void) fscanf(fp, "%d", &n); */
			while ((c = getc(fp)) == ' ')
				;
			k = 0;
			do {
				k = 10 * k + c - '0';
			} while (isdigit(c = getc(fp)));
			(void) ungetc(c, fp);
			hgoto(k);
			break;
		case 'h':	/* relative horizontal motion */
			/* (void) fscanf(fp, "%d", &n); */
			while ((c = getc(fp)) == ' ')
				;
			k = 0;
			do {
				k = 10 * k + c - '0';
			} while (isdigit(c = getc(fp)));
			(void) ungetc(c, fp);
			hmot(k);
			break;
		case 'w':	/* word space */
			break;
		case 'V':
			(void) fscanf(fp, "%d", &n);
			vgoto(n);
			break;
		case 'v':
			(void) fscanf(fp, "%d", &n);
			vmot(n);
			break;
		case 'p':	/* new page */
			(void) fscanf(fp, "%d", &n);
			t_page(n);
			break;
		case 'n':	/* end of line */
			while (getc(fp) != '\n')
				;
			t_newline();
			break;
		case '#':	/* comment */
			while (getc(fp) != '\n')
				;
			break;
		case 'x':	/* device control */
			devcntrl(fp);
			break;
		default:
			error(0, "unknown input character %o %c near line %ld",
								c, c, lineno);
			done();
		}
	}
}

/*
 *	Interpret device control functions
 */
devcntrl(fp)
FILE *fp;
{
        char str[20], str1[50], buf[50];
	int c, n;

	(void) fscanf(fp, "%s", str);
	switch (str[0]) {	/* crude for now */
	case 'i':	/* initialize */
		fileinit();
		t_init(0);
		break;
	case 'T':	/* device name */
		(void) fscanf(fp, "%s", devname);
		if (strcmp(devname,"san") != 0) {
			error(0, "Input file prepared for %s instead of for sanders", devname);
			_exit(0); /* DON'T flush stdout buffers */
		}
		break;
	case 't':	/* trailer */
		t_trailer();
		break;
	case 'p':	/* pause -- can restart */
		t_reset('p');
		break;
	case 's':	/* stop */
		t_reset('s');
		break;
	case 'r':	/* resolution assumed when prepared */
		(void) fscanf(fp, "%d", &res);
		if (res != RES) {
			error(0, "resolution wrong (%d)", res);
			exit(0);	/* Don't reprint */
		}
		break;
	case 'f':	/* font used */
		(void) fscanf(fp, "%d %s", &n, str);
		(void) fgets(buf, sizeof buf, fp);	/* in case there's a filename */
		(void) ungetc('\n', fp);	/* fgets goes too far */
		str1[0] = 0;	/* in case there's nothing to come in */
		(void) sscanf(buf, "%s", str1);
		loadfont(n, str, str1);
		break;
	case 'H':	/* char height */
		(void) fscanf(fp, "%d", &n);
		t_charht(n);
		break;
	case 'S':	/* slant */
		(void) fscanf(fp, "%d", &n);
		t_slant(n);
		break;
	}
	while ((c = getc(fp)) != '\n')	/* skip rest of input line */
		if (c == EOF)
			break;
}

/*
 *	Read in font and code files etc.
 */
fileinit()
{
	int i, fin;
	char *malloc(), *filebase;
	char temp[60];

	/* open table for device,
	/* read in resolution, size info, font info, etc.
	/* and set params
	*/
	(void) sprintf(temp, "%s/dev%s/DESC.out", fontdir, devname);
	if ((fin = open(temp, 0)) < 0) {
		error(0, "can't open tables for %s near line %ld", temp, lineno);
		done();
	}
	(void) read(fin, (char *)&dev, sizeof(struct dev));
	nfonts = dev.nfonts;
	nsizes = dev.nsizes;
	nchtab = dev.nchtab;
	paperwidth = dev.paperwidth;
	boxrulewid = dev.spare1;
	filebase = malloc((unsigned)dev.filesize);	/* enough room for whole file */
	(void) read(fin, filebase, dev.filesize);	/* all at once */
	pstab = (short *) filebase;
	chtab = pstab + nsizes + 1;
	chname = (char *) (chtab + dev.nchtab);
	for (i = 0; i <= nfonts; i++) {
		fontbase[i] = NULL;
		widthtab[i] = NULL; /* seperate because of different types */
		codetab[i] = fitab[i] = NULL;
	}
	(void) close(fin);
}

/*
 *	Debugging print of font i (0,...)
 */
#ifdef DEBUG
fontprint(i)
{
	int j, n;
	char *p;

	fprintf(stderr,"font %d:\n", i);
	p = (char *) fontbase[i];
	n = fontbase[i]->nwfont & BMASK;
	fprintf(stderr,"base=0%o, nchars=%d, spec=%d, name=%s, widtab=0%o, fitab=0%o, spare1=%c\n",
		p, n, fontbase[i]->specfont, fontbase[i]->namefont, widthtab[i], fitab[i], fontbase[i]->spare1);
	fputs("widths:\n",stderr);
	for (j=0; j <= n; j++) {
		fprintf(stderr," %3d", widthtab[i][j] & BMASK);
		if (j % 20 == 19) putc('\n',stderr);
	}
	fputs("\ncodetab:\n",stderr);
	for (j=0; j <= n; j++) {
		fprintf(stderr," %3d", codetab[i][j] & BMASK);
		if (j % 20 == 19) putc('\n',stderr);
	}
	fputs("\nfitab:\n",stderr);
	for (j=0; j <= dev.nchtab + 128-32; j++) {
		fprintf(stderr," %3d", fitab[i][j] & BMASK);
		if (j % 20 == 19) putc('\n',stderr);
	}
	putc('\n',stderr);
}
#endif

/*
 *	Load font info for font s on position n (0,...)
 */
loadfont(n, s, s1)
int n;
char *s, *s1;
{
	char temp[60];
	int fin, nw;

	if (n < 0 || n > NFONT) {
		error(0, "illegal fp command %d %s near line %ld", n, s, lineno);
		done();
	}
	if (fontbase[n] != NULL && strcmp(s, fontbase[n]->namefont) == 0)
		return;
	if (s1 == NULL || s1[0] == '\0')
		(void) sprintf(temp, "%s/dev%s/%s.out", fontdir, devname, s);
	else
		(void) sprintf(temp, "%s/%s.out", s1, s);
	if ((fin = open(temp, 0)) < 0) {
		error(0, "can't open font table %s near line %ld", temp, lineno);
		return;
	}
	if (fontbase[n] != NULL)
		free((char *)fontbase[n]);
	fontbase[n] = (struct Font *) malloc(3*255 + (unsigned)dev.nchtab + (128-32) + sizeof(struct Font));
	if (fontbase[n] == NULL) {
		error(0, "Out of space in loadfont %s near line %ld", s, lineno);
		done();
	}
	(void) read(fin, (char *)fontbase[n], 3*255 + nchtab+128-32 + sizeof(struct Font));
	(void) close(fin);
	if (smnt == 0 && fontbase[n]->specfont == 1)
		smnt = n;
	nw = fontbase[n]->nwfont & BMASK;
	widthtab[n] = (unsigned char *) fontbase[n] + sizeof(struct Font);
	/* has to be unsigned because some chars are >127 wide */
	codetab[n] = (char *) widthtab[n] + 2 * nw;
	fitab[n] = (char *) widthtab[n] + 3 * nw;
	t_fp(n, fontbase[n]->namefont, fontbase[n]->intname);
#ifdef DEBUG
	if (dbg & 2) fontprint(n);
#endif
}

/*	Here beginneth the even more Sanders-dependent stuff	*/

int	font	= 1;		/* internal font number */
int	size	= 1;		/* internal font size USED IN draw.o */
int	dev_font= -1;		/* device font number */
int	dev_hpos = 0,		/* current device x,y pos */
	dev_vpos = 0,
	dev_bold = NOBOLD;	/* emboldening prevalent on device ( = BOLD or NOBOLD ) */
/* int	drawdot	= '.';		/* what draw.o uses for plotting */
/* int	drawsize= 1;		/* and how much it shrinks it by */
/* int	DX	= 2;		/* x-step for drawing in draw.o */
/* int	DY	= 2;		/* ditto y */

#define ocmd(c) { if (c == '!') fputs("!IC!",tf); else (void) putc(c,tf); }

abortjob()
{
	register char *base;

	/* flush stdio buffer without writing out contents,
	 * a munged version of fflush()
	 */
	if ((tf->_flag&(_IONBF|_IOWRT))==_IOWRT
	 && (base=tf->_base)!=NULL && (tf->_ptr-base)>0) {
		tf->_ptr = base;
		tf->_cnt = (tf->_flag&(_IOLBF|_IONBF)) ? 0 : tf->_bufsiz;
	}
	/* make any half-baked commands raise their eyebrows, followed shortly by the users'.
	 * Longest I could think of was the 8-char fontname in !AF2,blahblah! hence 9 rubbish chars.
	 */
	fputs("*********!NL!***Print job aborted***", tf);
	done();
	/* NOTREACHED */
}

done()
{
	fputs("!CR!!FF!",tf);
	banner();
#ifdef DEBUG
	if (dbg)
		error(0, "DSAN DEBUGGING ENDS");	/* not really an error */
#endif
	exit(0);
	/* NOTREACHED */
}

/*
 *	Set character height to n
 */
t_charht(n)
int n;
{
#ifdef lint
	n=n;
#endif
	error(0, "character height not implemented, near line %ld", lineno);
}

/*
 *	Set slant to n
 */
t_slant(n)
int n;
{
#ifdef lint
	n=n;
#endif
	error(0, "slant not implemented, use italics near line %ld", lineno);
}

/*
 *	Record paper use
 */
account()
{
	/* Do whatever accounting information */
}

/*
 *	Initialise
 */
t_init(re_init)
int re_init;
{
/*	int i; */

	if(!re_init) {
		fputs(RESET, tf);
		fprintf(tf, "!FW%d!", paperwidth);
		/* precise paperlength is too important to trust to these haywire calculations */
		
		if (quality == DRAFT)
			fputs("!DRON!",tf);
#ifdef PROOF
		else if (quality == PROOF) fputs("!BB0!",tf);
#endif
	}
	setsize(t_size(10));
}

/*
 *	Start of page
 */
t_page(num)
int num;
{
	/* we want to put out a form feed before every page except the first */
	static int firstpageout = 1;

	output = in_olist(num);
#ifdef DEBUG
	if(dbg & 1) fprintf(stderr,"%s page %d\n",output?"print":"skip",num);
#endif
	if(output) {
		if (!firstpageout) {
			fputs("!FF!!CR!",tf); /* FF does not do a CR */
		} else firstpageout = 0;
		hpos = vpos = dev_hpos = dev_vpos = 0;
		acctpages++;
	}
}

t_newline()
{
	if (ferror(tf)) { /* in case printer has fallen in a heap */
		error(1, "write fails");
		exit(1);
	}
	hpos = 0;	/* implicit */
	lineno++;	/* for error reporting */
}

/*
 *	Convert n to internal size number
 *	USED IN draw.o
 */
t_size(n)
int n;
{
	int i;

	if(n <= pstab[0]) return(1);
	if(n >= pstab[nsizes-1]) return(nsizes);
	for(i = 0; n > pstab[i]; i++) ;
	return(i + 1);
}

/*
 *	Convert numeric string to internal font number
 */
t_font(s)
char *s;
{
	int n;

	n = atoi(s);
	if(n < 0 || n > nfonts) n = 1;
	return(n);
}

t_reset(c)
char c;
{
	switch(c) {
	case 'p':
		break;
	case 's':
		break;
	}
}

t_wrapup()
{
}

t_trailer()
{
}

/*
 *	br is used to communicate from put1s to xychar the fact that we are 
 *	printing a boxrule character to avoid repeated testing.
 */
 int br = 0;

/*
 *	Put a funny char name (like Fi, bs etc.)
 */
put1s(s)
register char *s;
{
	static int i;

	if(!output) return;
#ifdef DEBUG
	if(dbg & 1) fprintf(stderr,"%s ",s);
#endif
	if(strcmp(s, &chname[chtab[i]]) != 0)
		for(i = 0; i < nchtab; i++)
			/* strcmp here is a big timewaster */
			if (s[0] == chname[chtab[i]] &&
			    s[1] == chname[chtab[i]+1] ) break;
	if(i < nchtab) {
		if (s[0] == 'b' && s[1] == 'r') {
			br = 1; put1(i+128); br = 0;
		} else {
			put1(i + 128);
		}
	}
	else i = 0;
}

/*
 *	Output a char
 *	USED IN draw.o
 */
put1(c)
register int c;
{
	register char *p;
	register int i,j,k;
	int ofont;
	unsigned char code;

	if(!output) return;

	c -= 32;
	if(c <= 0) {
#ifdef DEBUG
		fprintf(stderr,"0%03o non-existent\n",c+32);
#endif
		return;
	}
	k = ofont = font;
	i = fitab[font][c] & BMASK;
	if(i != 0)	/* it's on this font */
		p = codetab[font];
	else if(smnt > 0) {	/* maybe its on the special */
		for(k = smnt, j = 0; j <= nfonts; j++, k = (k + 1) % (nfonts+1))
			if(fontbase[k] && ((i = fitab[k][c] & BMASK) != 0)) {
				p = codetab[k];
				break;
			}
	}
	if(i == 0 || (code = p[i] & BMASK) == 0 || k > nfonts) {
		/* it wasn't there at all */
#ifdef DEBUG
		fprintf(stderr,"0%03o not found\n",c+32);
#endif
		return;
	}
	if(k != font) setfont(k);
#ifdef DEBUG
	if(dbg & 1) fprintf(stderr,isprint(c+32)?"%c %d\n":"0%03o %d\n",c+32,code);
#endif
	if (br) hmot(-boxrulewid/2);
	{
		/* flush horizontal and vertical position */
		register int delta_h, delta_v; /* in machine steps */

		/* check for overflow */
		if (hpos >= paperwidth) {
			hpos %= paperwidth;
		} else {
			while (hpos < 0) {
				hpos += paperwidth;
			}
		}
		if ( (delta_v = (VEPS * vpos / RES) - dev_vpos) != 0) {
			fprintf(tf, "!VF%d!",delta_v);
			dev_vpos += delta_v;
		}
		if ( (delta_h = hpos - dev_hpos) != 0) {
			fprintf(tf, "!FE%d!",delta_h);
			dev_hpos += delta_h;
		}
	}
	if (font != dev_font) {
		fontflush();
	}
	ocmd(code);	/* o/p the code itself */
	if (br) hmot(boxrulewid/2);
	/* Echo Auto-advance in dev_hpos */
	dev_hpos += (br ? boxrulewid : widthtab[k][i]);

	if(font != ofont) setfont(ofont);
}

setsize(n)
int n;
{
	size = n;
}

/*
 *	Font position n now contains font s, internal name si
 */
t_fp(n, s, si)
int n;
char *s,*si;
{
#ifdef lint
	s=s;
#endif
	fprintf(tf, "!AF%d,%s!", n, si);
}

setfont(n)
{
	if(!output) return;
	if(n < 0 || n > NFONT) {
		error(0, "illegal font %d near line %ld", n, lineno);
		done();
	}
	font = n;
}

/*
 *	Cobble together a banner page.
 *	Must leave the printhead at the top of a new page
 */
banner()
{
	time_t clock;
	char *ctime();
	char *delivery;
	char *getenv();
	
	(void) time(&clock);
	if (quality != DRAFT) fputs("!DRON!",tf);
	/* To include pages, n_chars, n_pages, date, uid, (job title?) */
	fprintf(tf,
"!CR!!VF288!!SF0!%d page%s of %s printed for %s (%s) from %s on %15.15s\n\r",
				acctpages, (acctpages==1) ? "" : "s",
				jobname, username, jobnumber, host,
				ctime(&clock)+4);

	if ( (delivery = getenv("DELIVERY")) != NULL && *delivery!=0 )
		fprintf(tf, "Deliver to %s!CR!", delivery);
	fputs("!DROFF!!FF!",tf);
#ifdef DEBUG
	if (dbg & 4)
		fprintf(stderr, "delivery = %d; string = \"%s\"\n", delivery, delivery);
#endif
}

/*
 *	Tell the Sanders about any font changes which have occurred.
 */
fontflush()
{
	register int bold;

	if(font != dev_font) {
#ifdef DEBUG
		if(dbg & 1) fprintf(stderr,"Font -> %d\n",font);
#endif
		fprintf(tf, "!SF%d!", font);
		dev_font = font;
		if ( (bold = (fontbase[font]->spare1 == 'B' ? BOLD : NOBOLD)) != dev_bold ) {
#ifdef PROOF
			if (quality == PROOF) {
				fprintf(tf,"!BB%d!", bold);
			} else
#endif
			{	/* Regular and Draft modes */
				if (bold == NOBOLD)
					fputs("!EB!", tf);
				else	fputs("!BB0!", tf);
			}
			dev_bold = bold;
		}
	}
	/* No size munging - we only have 10-point */
}

/*
 * print an error message, with timestamp and process identification.
 * no more than 10 args can be taken.
 */
/* VARARGS2 */
/* PRINTFLIKE */
error(syserr, format, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
int syserr;	/* whether we should should print system error message */
char *format;
char *a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
{
	time_t clock;			/* for timestamp */
	char *ctime();
	static int pid = 0;		/* process id (cannot be zero) */
	extern int errno;		/* for system error message */
	extern int sys_nerr;
	extern char *sys_errlist[];

	(void) time(&clock);
	if (pid == 0)
		pid = getpid();
	fprintf(stderr, "%15.15s dsan <%d> (%s) ",
					ctime(&clock)+4, pid, username);
	fprintf(stderr, format, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
	if (syserr != 0) {
		fprintf(stderr, " [%s]\n", (errno < 0 && errno >= sys_nerr) ?
					"Unknown error" : sys_errlist[errno]);
	} else {
		putc('\n', stderr);
	}
	(void) fflush(stderr);
}
