/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred."
 *
 */
/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: daps.c 1.2 89/10/10 $@(#)roff:troff.d/devaps/daps.c	2.4"
char *xxxvers = "@(#)roff:troff.d/devaps/daps.c	2.4";



/****************************************************************************
 *																			*
 *		This is the post-processor for the APS-5 phototypesetter. The 		*
 *	language that is accepted by this program is produced by the new device	*
 *	independent troff, and consists of the following statements,			*
 *																			*
 *																			*
 *		sn			set the point size to n									*
 *		fn			set the typesetter font to the one in position n		*
 *		cx			output the ASCII character x 							*
 *		Cxyz		output the code for the special character xyz. This		*
 *					command is 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 from current position			*
 *		vn			go n units vertically from current position				*
 *		nnc			move right nn units, then print the character c. This	*
 *					command expects exactly two digits followed by the		*
 *					character c.											*
 *		w			paddable word space - no action needed					*
 *		nb a		end of line ( information only - no action needed )		*
 *		pn			begin page n											*
 *		Dt ...\n	draw operation 't':										*
 *																			*
 *						Dl x y		line from here to x,y					*
 *						Dc d		circle of diameter d, left side here	*
 *						De x y		ellipse of axes x,y, left side here		*
 *						Da u v x y	arc 									*
 *						D~ x y x y	wiggly line by x,y then x,y				*
 *																			*
 *		x ... \n	device control functions:								*
 *																			*
 *						x i			initialize the typesetter				*
 *						x T s		name of device is s						*
 *						x r n h v	resolution is n units per inch. h is	*
 *									min horizontal motion, v is min vert.	*
 *									motion in machine units.				*
 *						x p			pause - can restart the typesetter		*
 *						x s			stop - done forever						*
 *						x t			generate trailer - no-op for the APS	*
 *						x f n s		load font position n with tables for 	*
 *									font s. Referring to font n now means	*
 *									font s.									*
 *						x H n		set character height to n				*
 *						x S n		set character slant to n				*
 *																			*
 *						Subcommands like i are often spelled out as "init"	*
 *																			*
 *		To get the post-processor running properly on your system, you may	*
 *	have to make one or more of changes:									*
 *																			*
 *			Choose the appropriate description of your typesetter. These	*
 *			values include the type of lens and the maximum master range	*
 *			for your fonts. The values that you will need to adjust are		*
 *			macros and defined constants located at the start of the		*
 *			daps.h file.													*
 *																			*
 *			Make sure the variable 'typesetter' is properly initialized		*
 *			to the APS-5 typesetter file on your system. If you are not		*
 *			going to have daps directly drive the typesetter, you may 		*
 *			want to set it to the null file, and/or initialize 'tf' to		*
 *			be stdout. (file daps.g)										*
 *																			*
 *			Make sure that the accounting file pathname 'tracct' is the		*
 *			the accounting file that you want. If no accounting is to be	*
 *			done then initialize it to the null string. (file daps.g)		*
 *																			*
 *			Check to make sure that 'fontdir' is the directory that			*
 *			contains the devaps directory where your font tables are		*
 *			located. (file daps.g)											*
 *																			*
 *			If there are no characters on your font disk that need any		*
 *			adjustment in their vertical placement, then make sure that		*
 *			the conditional compilation flag ADJUST is undefined. I would	*
 *			recommend that you start this way to see what your font disk	*
 *			really looks like. (file daps.h)								*
 *																			*
 *																			*
 ****************************************************************************/






#include	<stdio.h>
#include	<ctype.h>
#include	<signal.h>

#include	"aps.h"					/* APS-5 opcode definitions */
#include	"dev.h"					/* font and dev structure declarations */

#include	"daps.h"				/* constant and macro definitions */
#include	"daps.g"				/* global variable definitions */





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


main(argc, argv)


	int		argc;
	char	*argv[];


{


	/********************************************************************
	 *																	*
	 *		This is the main program for the APS-5 post-processor. It 	*
	 *	is responsible for calling the sequence of routines that are	*
	 *	needed to translate troff's new typesetter independent output	*
	 *	language into a form that will be understood by the APS-5		*
	 *	phototypesetter.												*
	 *																	*
	 ********************************************************************/



	get_options(argc, argv);		/* process the option list */
	if ( tf != stdout )				/* need to open the file */
		out_file();
	init_signals();					/* set up signal handling */
	acct_file();					/* open the accounting file */
	process_input(argc, argv);		/* translate the input files */
	account();						/* make sure we charge this guy */
	done();							/* finish up this job and reset the APS */

}	/* End of main */


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


get_options(argc, argv)


	int		argc;
	char	*argv[];


{


	int		save;					/* used to adjust arg_index */
	int		v_step;					/* vertical step in -V option */

	double	atof();					/* used in getting beam cutoff postion */

	int ch;
	int i, sharpsign;
	extern char *optarg;
	extern int optind;


	/********************************************************************
	 *																	*
	 *		This is the routine that processes the command line option	*
	 *	list. The macro SET_ARGS uses the global variable arg_index to	*
	 *	properly initialize the local argc and argv values, while the	*
	 *	macro COUNT_ARGS adjusts the value of arg_index to account for	*
	 *	the number of arguments that were just processed.				*
	 *																	*
	 *		The options that are currently available in this driver 	*
	 *	are,															*
	 *																	*
	 *				-f dir	- 	use dir as the font directory			*
	 *				-F dir	-	same									*
	 *																	*
	 *				-t		-	use standard output						*
	 *																	*
	 *				-r		-	report the number of pages				*
	 *																	*
	 *				-A		-	do accounting even if there is no real	*
	 *							accounting file. If tracct is NULL then	*
	 *							the accounting information is written	*
	 *							to stderr. This is the way things are	*
	 *							done on our APS.						*
	 *																	*
	 *				-b		-	report whether typesetter is busy or	*
	 *							not. Nothing is printed.				*
	 *																	*
	 *				-w		-	wait until typesetter is available,		*
	 *							then procees the job.					*
	 *																	*
	 *				-o[str]	-	process only this list of pages. The	*
	 *							list may contain single pages or page	*
	 *							ranges, where the latter consists of 	*
	 *							a pair of pages separated by -.			*
	 *																	*
	 *				-s[num]	-	stop processing every num pages, and	*
	 *							HALT the typesetter.					*
	 *																	*
	 *				-v[num] -	use num as the maximum vertical step	*
	 *							size up or down the page. The argument	*
	 *							num is interpreted as 10ths of a point.	*
	 *																	*
	 *				-h[str]	-	use str as the string to be printed		*
	 *							in the header.							*
	 *																	*
	 *				-H[str]	-	use str as the pathname of the file		*
	 *							whose first line contains the string	*
	 *							to be printed in the header.			*
	 *																	*
	 *				-d[str] -	toggle the debug flags for each number	*
	 *							contained in the string str. If str		*
	 *							contains the character '*' then all of	*
	 *							the debug flags will be toggled.		*
	 *																	*
	 *				-D[str] -	dump all of the debug information into	*
	 *							file str. If this option is not used	*
	 *							then the debugging stuff is written to	*
	 *							stderr.									*
	 *																	*
	 *				-L[str] -	use the file str as the log file for	*
	 *							all error messages. If this option is	*
	 *							not used then all error messages will	*
	 *							be written to stderr.					*
	 *																	*
	 *				-I		-	ignore all FATAL errors. This option is	*
	 *							only to be used for debugging - it may	*
	 *							cause a core file to be written.		*
	 *																	*
	 ********************************************************************/



	SET_ARGS(save);					/* MACRO - adjust internal argc and argv */

	while ((ch = getopt(argc, argv, "f:F:trAbwo:s:h:H:d:D:L:Iv:c:#"))
		!= EOF) {				/*read options list*/

		switch ( ch )  {					/* check option */

			case 'f':								/* font directory */
			case 'F':
						fontdir = optarg;
						break;

			case 't':								/* use standard output */
						tf = stdout;
						break;

			case 'r':								/* print page report */
						report = YES;
						break;

			case 'A':								/* do accounting! */
						if ( privelege == ON )
							x_stat |= DO_ACCT;
						break;

			case 'b':								/* check if busy or not */
						busyflag = ON;
						break;

			case 'w':								/* wait til APS is free */
						waitflag = ON;
						break;

			case 'o':								/* process page list */
						outlist(optarg);
						break;

			case 's':								/* stop every spage(s) */
						spage = atoi(optarg);
						if ( spage <= 0 )			/* illegal page number */
							spage = 9999;
						break;

			case 'h':								/* banner is in argument */
						banner = optarg;
						print_banner = YES;
						break;

			case 'H':								/* banner is in file */
						ban_file(optarg);
						print_banner = YES;
						break;

			case 'd':								/* selective debug */
						debug_select(optarg);
						break;

			case 'D':								/* set up debug file */
						debug_file(optarg);
						break;

			case 'L':								/* set up log file */
						log_file(optarg);
						break;

			case 'I':								/* ignore fatal errors */
						if ( privelege == ON )
							ignore = YES;
						break;

			case 'v':								/* set max vertical step */
						v_step = atoi(optarg);
						if ( v_step != 0 )
							vert_step = ( v_step > 0 ) ? v_step
													   : -v_step;
						if ( vert_step > MAX_INT )	/* its too big */
							vert_step = MAX_INT;
						break;

			case 'c':								/* set beam cutoff */
						if ( (cutoff = atof(optarg)) <= 0 )
							cutoff = CUTOFF;
						break;

		case '#':
			sharpsign = 1;
			break;
			default:								/* didn't find it */
						error(NON_FATAL, "illegal option %c", argv[1][1]);
						break;

		}	/* End of switch */

	}	/* End while */

	argc -= optind - 1;
	COUNT_ARGS(save);				/* MACRO - adjust arg_index */
	if (sharpsign == 1) {
		fprintf(stderr, "report = %d, x_stat = %o\n",
			report, x_stat);
		fprintf(stderr, "busyflag = %d, waitflag = %d, ignore = %d\n",
			busyflag, waitflag, ignore);
		fprintf(stderr, "fontdir = %s, spage = %d, banner = %s\n",
			fontdir, spage, banner);
		fprintf(stderr, "v_step = %d, cutoff = %g\n",
			v_step, cutoff);
		for (i=0; i<nolist; i+=2)
			fprintf (stderr, "olist[%d] is %d; olist[%d] is %d.\n",
				i, olist[i], i+1, olist[i+1]);
		for (i=0; i<MAX_DEBUG; i+=4)
			fprintf(stderr, "debug[%d] = %d, %d, %d, %d\n",
				i,debug[i],debug[i+1],debug[i+2],debug[i+3]);
		if (fp_debug != stderr)
			fprintf(stderr, "fp_debug is %x\n", fp_debug);
		if (fp_error != stderr)
			fprintf(stderr, "fp_error is %x\n", fp_error);
		if (tf != stdout)
			fprintf(stderr, "tf is %x\n", tf);
	}

}	/* End of get_options */


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


process_input(argc, argv)


	int		argc;		
	char	*argv[];


{


	FILE	*fp_in;					/* input file descriptor */
	int 	save;					/* used to adjust arg_index before exit */



	/********************************************************************
	 *																	*
	 *		This routine is called by main to handle the processing		*
	 *	of the input files from the command line. If there were no		*
	 *	files specified in the call then the post-processor will read	*
	 *	from the standard input. Otherwise it will process all of the	*
	 *	input files in the list, concatenating the output from each		*
	 *	one onto the output file. The only convention that is used for	*
	 *	input file names is that the character '-' as a file name will	*
	 *	cause the driver to read from the standard input, provided it	*
	 *	isn't the first 'file' in the list of input files. It would		*
	 *	probably be better if we chose some other character or sequence	*
	 *	of characters for this purpose.									*
	 *																	*
	 ********************************************************************/



	SET_ARGS(save);						/* MACRO - adjust argc and argv */

	if ( argc <= 1 ) conv(stdin);		/* no more args - use stdin */
	else  {								/* read input file list */
		while ( --argc > 0 )  {			/* rest of the args are input files */

			if ( strcmp(*++argv, "-") == 0 )	/* use standard input */
				fp_in == stdin;
			else if ( (fp_in = fopen(*argv, "r")) == NULL )  {		
					error(FATAL, "can't open input file %s", *argv);
					continue;			/* in case we ignore this error */
			}	/* End else */

			conv(fp_in);				/* translate the file */
			if ( fp_in != stdin )		/* probably don't need it anymore */
				fclose(fp_in);

		}	/* End of while */
	}	/* End else */

	COUNT_ARGS(save);					/* MACRO - adjust arg_index */

}	/* End of process_input */


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


init_signals()


{


	int		wrap_up();					/* signal handling routine */
	int		float_err();				/* handles floating point errors */



	/********************************************************************
	 *																	*
	 *		This routine is called by main to set up the appropriate	*
	 *	handling of external signals for the post-processor. As 		*
	 *	currently written interrupts, quits and hangups are all either	*
	 *	ignored or processed by the routine wrap_up().					*
	 *																	*
	 ********************************************************************/



	signal(SIGFPE, float_err);			/* catch floating point errors */

	if ( signal(SIGINT, wrap_up) == SIG_IGN )  {	/* ignoring interrupts */
		signal(SIGINT, SIG_IGN);					/* so reset SIGINT */
		signal(SIGQUIT, SIG_IGN);					/* and ignore the rest */
		signal(SIGHUP, SIG_IGN);
	} else {										/* wrap_up() handles them */
		signal(SIGQUIT, wrap_up);
		signal(SIGHUP, wrap_up);
	}	/* End if */

}	/* End of init_signals */


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


debug_select(str)


	char	*str;						/* string of debug flags */


{


	int		index;						/* single debug flag to toggle */
	int		i;							/* for loop index */



	/********************************************************************
	 *																	*
	 *		This routine is called by main() when it finds the -d		*
	 *	option.	The parameter str is a pointer to a string of comma		*
	 *	separated tokens from the command line that specify which of 	*
	 *	the debug flags is to be toggled. As currently implemented the	*
	 *	tokens in str may consist of numbers, which specify the actual	*
	 *	flag to be toggled, or the character '*', which stands for all	*
	 *	of the available debug flags.									*
	 *																	*
	 ********************************************************************/



	while ( *str )  {

		if ( isdigit(*str) )  {			/* have a single debug flag */
			STR_CONVERT(str, index);	/* MACRO - get the debug flag */
			if ( index >= 0  &&  index < MAX_DEBUG )
				TOGGLE(debug[index]);	/* MACRO - toggle it */
		} else if ( *str == '*' )		/* toggle all the debug flags */
			for ( i = 0; i < MAX_DEBUG; i++ )
				TOGGLE(debug[i]);		/* MACRO */

		if ( *str != '\0' ) str++;		/* skip the comma */

	}	/* End while */

}	/* End of debug_select */


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


debug_file(str)


	char	*str;						/* debug file pathname */


{



	/********************************************************************
	 *																	*
	 *		This routine is called by get_options() when it finds the	*
	 *	-D option in the command line. The parameter str is a pointer	*
	 *	to the pathname of the file to be used for all of the debugging	*
	 *	output. If the -D option is not specified then by default all	*
	 *	the debug output is written to stderr.							*
	 *																	*
	 ********************************************************************/



	if ( (fp_debug = fopen(str, "w")) == NULL )  {
		fp_debug = stderr;
		error(NON_FATAL, "can't open debug file %s", str);
	}	/* End if */

}	/* End of debug_file */


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


log_file(str)


	char	*str;						/* log file pathname */


{



	/********************************************************************
	 *																	*
	 *		This routine is called to open the log file for the APS-5	*
	 *	post-processor. The pathname of the file is passed in the		*
	 *	parameter str when this routine is called from get_options().	*
	 *	If the log file isn't specified then the post-processor will	*
	 *	write all of it's error messages to stderr.						*
	 *																	*
	 ********************************************************************/



	if ( (fp_error = fopen(str, "a")) == NULL )  {
		fp_error = stderr;
		error(FATAL, "can't open log file %s", str);
	}	/* End if */

}	/* End of log_file */


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


acct_file()


{



	/********************************************************************
	 *																	*
	 *		This routine is called to open the accounting file whose	*
	 *	pathname is pointed to by the variable tracct. If there is no	*
	 *	pathname in tracct then nothing is done in this routine, while	*
	 *	if we are unable to open the accounting file then an error		*
	 *	message is printed out and we quit.								*
	 *																	*
	 ********************************************************************/



	if ( *tracct )  {					/* we have an accnt file pathname */

		if ( (fp_acct = fopen(tracct, "a")) == NULL )  {	/* so open it */
			fp_acct = stderr;			/* couldn't open accounting file */
			x_stat |= NO_ACCTFILE;		/* indicate this in the exit status */
			error(FATAL, "unable to open accounting file");
			exit(x_stat);				/* in case we ignore this error */
		}	/* End if */
		x_stat |= DO_ACCT;				/* accounting needs to be done */

	}	/* End if */

}	/* End of acct_file */


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


ban_file(str)


	char	*str;						/* banner file pathname */


{


	FILE	*fp_ban;					/* banner file descriptor */



	/********************************************************************
	 *																	*
	 *		This routine is called from get_options() to read the 		*
	 *	banner string from the first line of the file whose pathname is	*
	 *	contained in the string str.									*
	 *																	*
	 ********************************************************************/



	if ( (fp_ban = fopen(str, "r")) == NULL )  {
		error(NON_FATAL, "can't open the banner file %s", str);
		return;
	}	/* End if */

	GET_LINE(fp_ban, ban_buf);			/* MACRO - banner is first line only */
	banner = ban_buf;					/* t_banner() prints string *banner */

	fclose(fp_ban);						/* shouldn't need this file again */

}	/* End of ban_file */


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


out_file()


{



	/********************************************************************
	 *																	*
	 *		This routine is called from the main program to open the	*
	 *	file pointed to by typesetter.									*
	 *																	*
	 ********************************************************************/



	do  {
		tf = fopen(typesetter, "w");	/* typesetter output file */
		if ( busyflag == ON )  {		/* report status and then exit */
			printf(tf == NULL ? "Busy.\n" : "Available.\n");
			exit(0);
		}	/* End if */

		if ( tf == NULL )  {			/* didn't open on last try */
			if ( waitflag == OFF )  {	/* he doesn't want to wait */
				error(NON_FATAL, "can't open typesetter");
				exit(NO_OUTFILE);
			}	/* End if */
			else sleep(60);				/* try again later */
		}	/* End if */
	}  while ( tf == NULL );

}	/* End of out_file */


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


outlist(str)


	char	*str;						/* string of pages to process */


{


	int		start, stop;				/* page range end points */
	int		i;							/* loop index - debug only */



	/********************************************************************
	 *																	*
	 *		This routine is called when the -o option is read in		*
	 *	the command line. The parameter str points to a list of page	*
	 *	numbers to be processed. This list consists single pages or 	*
	 *	page ranges, separated by commas. A page range is specified by	*
	 *	separating two page numbers by the character '-'. In this case	*
	 *	all pages in this closed interval will be processed by daps.	*
	 *																	*
	 ********************************************************************/



	while ( *str  &&  nolist < MAX_OUTLIST - 2 )  {

		start = 0;

		if ( isdigit(*str) )				/* page number should begin here */
			STR_CONVERT(str,start);			/* MACRO - get left end point */
		else start = -9999;					/* use lowest possible page */

		stop = start;						/* in case it is a single page */

		if ( *str == '-' )  {				/* have a page range */
			str++;							/* so skip the minus sign */
			if ( isdigit(*str) )			/* page number begins here */
				STR_CONVERT(str,stop);		/* MACRO - get right end point */
			else stop = 9999;				/* use largest possible page */
		}	/* End if */

		if ( start > stop )
			error(FATAL,"illegal range %d-%d",start,stop);

		olist[nolist++] = start;			/* save the page range */
		olist[nolist++] = stop;

		if ( *str != '\0' ) str++;			/* skip the comma */

	}	/* End while */

	olist[nolist] = 0;						/* terminate the page list */

	if ( *str )							/* too many pages for olist array */
		error(NON_FATAL, "skipped pages %s", str);

	if ( debug[1] )							/* dump the olist[] array */
		for ( i = 0; i < nolist; i += 2 )
			fprintf(fp_debug,"%3d  %3d\n", olist[i], olist[i+1]);

}	/* End of outlist */


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


error(kind, str, a1, a2, a3, a4, a5, a6, a7)


	int		kind;					/* kind of error ie. FATAL or NON_FATAL */
	char	*str;					/* pointer to message to be printed */


{


	/********************************************************************
	 *																	*
	 *		This routine is called when the post-processor has found	*
	 *	an internal error. The parameter kind has the value FATAL or	*
	 *	NON_FATAL, and accordingly determines whether processing will	*
	 *	continue or not. The parameter str is a pointer to the error	*
	 *	message that is to be printed. All the remaining parameters		*
	 *	are the arguments that may be referenced in the control string	*
	 *	str.															*
	 *																	*
	 *		The global variable ignore is initialized to NO in the		*
	 *	file daps.globals, and can be set to YES by using the -I option	*
	 *	in the command line. This will allow the post-processor to		*
	 *	continue after a normally FATAL error has been encountered. 	*
	 *	This is only a debugging feature and should not generally be	*
	 *	used.															*
	 *																	*
	 ********************************************************************/



	fprintf(fp_error, "daps: ");
	if ( (kind == NON_FATAL) && (line_number > 0) )
		fprintf(fp_error, "warning - ");
	fprintf(fp_error, str, a1, a2, a3, a4, a5, a6, a7);

	if ( line_number > 0 )
		fprintf(fp_error, " ( line = %ld )", line_number);
	fprintf(fp_error, "\n");

	if ( ignore == YES  &&  privelege == ON )
		return;

	if ( kind == FATAL )			/* can't ignore this error */
		wrap_up();					/* so quit */

}	/* End of error */


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


done()


{



	/********************************************************************
	 *																	*
	 *		This routine is called to do the final processing for the	*
	 *	current job. If there is to be any accounting for this job we	*
	 *	need to be sure that the account function is called first		*
	 *	because there is nothing more we can do when we get here.		*
	 *																	*
	 ********************************************************************/



	if ( tf == NULL ) 					/* Nowhere to write */
		exit(x_stat | NO_OUTFILE);		/* so set the bit in x_stat and quit */

	t_reset('s');						/* get APS ready for the next job */
	exit(x_stat);						/* quit with status x_stat */

}	/* End of done */


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


float_err(sig)


	int		sig;						/* signal number - not used */


{



	/********************************************************************
	 *																	*
	 *		Called when a floating point error has been detected.		*
	 *	Needed because we want to make sure we exit gracefully if a		*
	 *	users job would normally dump a core file.						*
	 *																	*
	 ********************************************************************/



	error(FATAL, "floating point exception");

}	/* End of float_err */


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


wrap_up(sig)


	int		sig;						/* signal number - not used */


{



	/********************************************************************
	 *																	*
	 *		This routine is called to make sure that all the necessary	*
	 *	stuff is done when the driver finishes its job because of some	*
	 *	external signal or because it encountered a FATAL syntax error.	*
	 *																	*
	 ********************************************************************/



	account();							/* keep some kind of record for this job */
	done();								/* get the APS ready for next job */

}	/* End of wrap_up */


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


conv(fp)


	register FILE	*fp;				/* input file descriptor */


{


	register int	ch;					/* first character of the command */
	int		c;							/* used only as a character */
	int		n;							/* general purpose integer variable */
	char	str[100], buf[300];			/* buffers for fscanf and fgets */



	/********************************************************************
	 *																	*
	 *		This is the main interpreter for the post-processor. It is	*
	 *	called from routine process_input with the single parameter		*
	 *	fp, which is the file descriptor for the current input file.	*
	 *																	*
	 *		The global variable line_number is used to keep track of	*
	 *	the current line in the input file fp. Its value is adjusted	*
	 *	in both conv() and devcntrl() and it is used in the routine		*
	 *	error() when error messages are written to the file fp_error.	*
	 *																	*
	 *		The bits in the global variable x_stat are used to keep 	*
	 *	track of the progress of the post-processor, and when the 		*
	 *	program exits it will return x_stat as its termination status.	*
	 *																	*
	 *																	*
	 *		NOTE - In order to improve the speed of this routine we may	*
	 *	want to declare more register variables. When we do this we 	*
	 *	need to be sure that the macros that are being used will accept	*
	 *	register variables as arguments. For example if a macro takes	*
	 *	the address of one of its arguments, then we can't assign this	*
	 *	variable to a register.											*
	 *																	*
	 ********************************************************************/



	x_stat |= FILE_STARTED;					/* indicate this in x_stat */
	line_number = 1;						/* line in current input file */

	while ( (ch = getc(fp) ) != EOF )  {

		switch ( ch )  {					/* ch determines the command */

			case 'w':						/* don't do anything for these */
			case ' ':
			case  0:
						break;

			case '\n':						/* just increment line_number */
						line_number++;
						break;

			case '0':						/* two motion digits and a char */
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
						GET_DIG(fp, c);		/* MACRO - get the second digit */
						hmot((ch - '0') * 10 + c - '0');

				/* Be careful - we need to fall through here */

			case 'c':						/* single ASCII character */
						GET_CHAR(fp, c);	/* MACRO - read the character */
						put1(c);			/* output c's APS-5 code */
						break;

			case 'h':						/* relative horizontal motion */
			case 'H':						/* absolute horizontal motion */
			case 'v':						/* relative vertical motion */
			case 'V':						/* absolute vertical motion */
			case 's':						/* set point size */
			case 'p':						/* start a new page */
						GET_INT(fp, n);		/* MACRO - first get an integer */
						switch( ch )  {		/* and then process the command */

							case 'h':	hmot(n);
										break;

							case 'H':	hgoto(n);
										break;

							case 'v':	vmot(n);
										break;

							case 'V':	vgoto(n);
										break;

							case 's':	setsize(t_size(n));
										break;

							case 'p':	t_page(n);
										break;

						}	/* End switch */
						break;

			case 'C':						/* process special char string */
			case 'f':						/* set font */
						GET_STR(fp, str);	/* MACRO - first get a string */
						if ( ch == 'C' )
							 put1s(str);
						else setfont(t_font(str));
						break;

			case 'x':						/* device control function */
						devcntrl(fp);
						break;

			case 'D':						/* drawing operation */
			case 't':						/* text string upto newline */
						GET_LINE(fp, buf);	/* MACRO - get rest of the line */
						if ( ch == 'D' )
							 drawfunct(buf, fp);
						else t_text(buf);
						line_number++;		/* finished with this line */
						break;

			case 'n':						/* end of line */
			case '#':						/* comment */
						SKIP_LINE(fp,c);	/* MACRO - skip rest of this line */
						if ( ch == 'n' )
							t_newline();
						line_number++;		
						break;

			default:						/* illegal command - quit */
						error(FATAL,"unknown input character 0%o %c", ch, ch);
						break;				/* in case we ignore this error */

		}	/* End switch */

	}	/* End while */

	x_stat &= ~FILE_STARTED;				/* turn off FILE_STARTED bit in x_stat */

}	/* End of conv */


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


drawfunct(buf, fp)


	char	buf[];						/* drawing command */
	FILE	*fp;


{


	int		n1, n2, n3, n4;				/* values are set in the MACROS */



	/********************************************************************
	 *																	*
	 *		This routine interprets the drawing functions that are 		*
	 *	provided by troff. The array buf[] has been filled in by the	*
	 *	function conv(), and it contains the drawing command line from	*
	 *	the input file. 												*
	 *																	*
	 ********************************************************************/



	switch ( buf[0] )  {					/* process the command */

		case 'l':							/* draw a line */
					SCAN2(buf+1, n1, n2);	/* MACRO - get two integers */
					drawline(n1, n2, ".");
					break;

		case 'c':							/* draw a circle */
					SCAN1(buf+1, n1);		/* MACRO - get one integer */
					drawcirc(n1);
					break;

		case 'e':							/* draw an ellipse */
					SCAN2(buf+1, n1, n2);	/* MACRO - get two integers */
					drawellip(n1, n2);
					break;

		case 'a':							/* draw an arc */
					SCAN4(buf+1,n1,n2,n3,n4);	/* MACRO - get four integers */
					drawarc(n1, n2, n3, n4);
					break;

		case '~':							/* draw spline curve */
					drawwig(buf+1);
					break;

#ifdef PLOT
		case 'p':							/* plot these points */
					plot_points(buf+1, fp);
					break;
#endif
		default:							/* don't understand the command */
					error(FATAL, "unknown drawing function %s", buf);
					break;					/* in case we ignore this error */

	}	/* End switch */

}	/* End of drawfunct */


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


devcntrl(fp)


	FILE	*fp;						/* input file descriptor */


{


	int		c;							/* character used in SKIP_LINE */
	int		n;							/* integer used in GET_INT */
	char	str[20];					/* used to hold different strings */
	char	file[50];					/* load from this font file - maybe */
	char	buf[50];					/* buffer used in GET_LINE etc. */



	/********************************************************************
	 *																	*
	 *		This is the interpreter for the device control language		*
	 *	that is produced by the new troff. The parameter fp is the file	*
	 *	descriptor for the current input file.							*
	 *																	*
	 ********************************************************************/



	GET_STR(fp, str);						/* read command from input file */

	switch ( str[0] )  {					/* str[0] determines the command */

		case 'i':							/* initialize the device */
					fileinit();				/* read data from DESC.out */
					t_init();				/* initialize the typesetter */
					if ( print_banner == YES )	 /* print the job's banner */
						t_banner();
					break;

		case 'T':							/* set device name */
					GET_STR(fp, devname);	/* MACRO - get device string */
					if ( strcmp(devname, "aps") != SAME_STR )
						error(FATAL, "illegal typesetter %s", devname);
					break;

		case 't':							/* trailer - do nothing on APS-5 */
					break;

		case 'p':							/* pause - we can restart */
		case 's':							/* stop - done with this job */
					t_reset(str[0]);		/* reset the typesetter */
					break;

		case 'r':							/* set resolution */
					GET_INT(fp, res);		/* MACRO - get one integer */
					hcutoff = cutoff * res;	/* beam cutoff in device units */
					break;

		case 'f':							/* load a font */
					GET_INT(fp, n);			/* MACRO - put font number in n */
					GET_STR(fp, str);		/* MACRO - put font name in str */
					GET_LINE(fp, buf);		/* MACRO - use this filename */
					ungetc('\n', fp);		/* put '\n' back for SKIP_LINE */
					file[0] = 0;			/* in case there is no file name */
					SCAN_STR(buf, file);	/* MACRO - may have a file name */
					loadfont(n, str, file);	
					break;

		case 'H':							/* set character height */
					GET_INT(fp, n);			/* MACRO - read vertical point size */
					t_charht(t_size(n));
					break;

		case 'S':							/* set character slant */
					GET_INT(fp, n);			/* MACRO - read APS slant angle */
					t_slant(n);				/* set the APS slant to n */
					last_req_slant = n;		/* and remember this angle */
					break;

		default:							/* don't understand the command */
					error(FATAL, "unknown device command %c", str[0]);
					break;					/* in case we ignore this error */

	}	/* End switch */

	SKIP_LINE(fp, c);						/* finished with this line */
	line_number++;

}	/* End of devcntrl */


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


t_init()


{


	int		i;							/* for loop variable */



	/********************************************************************
	 *																	*
	 *		This routine is called from devcntrl() when the command		*
	 *	x init is read. It produces the instructions that initialize	*
	 *	the APS, and then it sets up the picture drawing variables		*
	 *	drawdot and drawsize.											*
	 *																	*
	 *																	*
	 *		NOTE - the opcode STRTJOB is defined to be octal 272 in the	*
	 *	file aps.h, although on our APS-5 typesetter it is one of the	*
	 *	reserved but not implemented opcodes. 							*
	 *																	*
	 ********************************************************************/



	PUTC(STRTJOB, tf);					/* MACRO - output STRTJOB opcode */
	putint(1);							/* dummy argument for STRTJOB? */
	PUTC(STRTPG, tf);					/* MACRO - start page 0 */
	putint(0);

	hpos = vpos = 0;					/* initialize page coordinates */

	for ( i = 0; i < nchtab; i++ )		/* find drawing character index */
		if ( strcmp(&chname[chtab[i]], "l.") == SAME_STR )
			break;

	if ( i < nchtab )  {				/* found it in the table */
		drawdot = i + 128;				/* so use these values for drawing */
		drawsize = 1;
	} else  {							/* didn't find it - use default */
		drawdot = '.';
		drawsize = 3;
	}	/* End if */

}	/* End of t_init */


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


t_banner()


{


	int		i;							/* while loop counter */
	char 	*bp;						/* temp pointer to banner string */



	/********************************************************************
	 *																	*
	 *		This routine is called from devcntrl() when the device		*
	 *	initialization command is read. It is responsible for printing	*
	 *	the job's header. This includes checking and then writing out	*
	 *	the string pointed to by banner. The user may set this string	*
	 *	by using either the -h or the -H options on the command line.	*
	 *																	*
	 ********************************************************************/



	output = ON;						/* ON for routine put1() etc. */
	bp = banner;						/* point to start of banner string */
	i = 0;								/* characters looked at so far */

	while ( *bp )						/* check at most BAN_LENGTH chars */
		if ( *bp++ < ' '  ||  ++i > BAN_LENGTH )	/* unprintable or too long */
			*(--bp) = '\0';				/* so last char is as far as we go */

	setsize(t_size(BAN_SIZE));			/* use font 1 and point size BAN_SIZE */

	vmot(VSPACE0);						/* space down for banner cut marks */
	t_text(cut_marks);					/* print first set of cut marks */
	hmot(HSPACE0);						/* space right for more cut marks */
	t_text(cut_marks);					/* print second set of cut marks */

	vmot(VSPACE1);						/* skip this far before banner */
	hgoto(HSPACE1);						/* indent */
	t_text(ban_sep);					/* start of banner */

	vmot(VSPACE2);						/* space down for banner string */
	hgoto(HSPACE2);						/* indent */
	t_text(banner);						/* print the user's banner */

	vmot(VSPACE3);						/* skip down for next separator */
	hgoto(HSPACE3);						/* indent */
	t_text(ban_sep);					/* print the second separator */

	vmot(VSPACE4);						/* skip down for banner cut marks */
	hgoto(HSPACE4);						/* position for first cut marks */
	t_text(cut_marks);					/* print first set of cut marks */
	hmot(HSPACE0);						/* skip right for second cut marks */
	t_text(cut_marks);					/* print second set */
	
	hpos = vpos = 0;					/* reset these guys again */
	cur_vpos = max_vpos = 0;			/* user doesn't own the banner */
	print_banner = NO;					/* don't print it again */
	output = OFF;						/* output is controlled in t_page() */

}	/* End of t_banner */


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


t_page(n)


{


	int		i;							/* for loop index */



	/********************************************************************
	 *																	*
	 *		This routine is called from conv() to do the work that is	*
	 *	necessary when we begin a new page. The STRTPG command to the	*
	 *	APS-5 invokes the RESET command which sets the font to 0000,	*
	 *	the master range to 1, and sets the oblique mode to normal and	*
	 *	the oblique angle to 14 degrees. To reset the font and range	*
	 *	properly we call setsize(), while if we were in oblique mode	*
	 *	the variable aps_slant will be non-zero and so in this case		*
	 *	we call routine t_slant() with aps_slant as the parameter.		*
	 *																	*
	 ********************************************************************/



	if ( output == ON  &&  ++scount >= spage )  {	/* reached stop page */
		t_reset('p');					/* so reset the APS and then HALT */
		scount = 0;						/* and start counting over again */
	}	/* End if */

	vpos = 0;							/* we are at the top of new page */
	output = ON;						/* enable output in put1() etc. */
	last_slant = POS_SLANT;				/* this will be the stored angle */

	PUTC(STRTPG, tf);					/* MACRO - start page n */
	putint(n);
	++pageno;							/* update user's page number */

	setsize(size);						/* reset both the font and size */
	if ( aps_slant )					/* then the APS was in oblique mode */
		t_slant(aps_slant);				/* so go back to previous slant */

	if ( nolist == 0 ) return;			/* no -o option, so print every page */
	output = OFF;						/* otherwise -o option was specified */
	for ( i = 0; i < nolist; i += 2 )	/* so check page pairs in olist[] */
		if ( n >= olist[i]  &&  n <= olist[i+1] )  {
			output = ON;				/* enable output for this page */
			break;
		}	/* End if */

}	/* End of t_page */


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


t_newline()


{



	/********************************************************************
	 *																	*
	 *		This routine is called from conv() when it has read the		*
	 *	start new line command. This command has the form "n a b" where	*
	 *	a and b are integers that we can safely ignore for the APS-5.	*
	 *																	*
	 ********************************************************************/



	hpos = 0;							/* return to left margin */

}	/* End of t_newline */


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


t_size(n)


	int		n;							/* convert this point size */


{

	int		i;							/* for loop index */



	/********************************************************************
	 *																	*
	 *		This routine is called to convert the point size n to an	*
	 *	internal size, which is defined as one plus the index of the	*
	 *	least upper bound for n in the array pstab[]. If n is larger	*
	 *	than all the entries in pstab[] then nsizes is returned.		*
	 *																	*
	 *																	*
	 *		NOTE - this routine expects the entries in pstab[] to be in	*
	 *	increasing numerical order, but it doesn't require this list to	*
	 *	be terminated by a 0 point size entry.							*
	 *																	*
	 ********************************************************************/



	if ( n >= pstab[nsizes-1] )			/* greater than all entries */
		return(nsizes);					/* so use largest internal size */

	for ( i = 0; n > pstab[i]; i++ )	/* otherwise find the LUB for n */
		;
	return(i+1);						/* internal size is i+1 */

}	/* End of t_size */


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


t_charht(n)


	int		n;							/* set height to this internal size */


{


	int		max;						/* max internal size for current range */



	/********************************************************************
	 *																	*
	 *		This routine is called by devcntrl() to set the height of	*
	 *	the characters that are being printed to the internal size		*
	 *	specified by the parameter n. If the requested size is too 		*
	 *	large for the current range then an error message is written	*
	 *	and the requested size is set to the maximum allowed in the		*
	 *	current range. Since the APS-5 apparently allows us to decrease	*
	 *	the height as far as we want, no lower limit checks are made on	*
	 *	the requested size. The global variable range is set by the		*
	 *	routine setfont() to the current master range that is being		*
	 *	used.															*
	 *																	*
	 ********************************************************************/



	if ( range < 1  ||  range > MAX_RANGE )  {	/* something is wrong here */
		error(FATAL, "illegal master range %d", range);
		return;							/* in case this error is ignored */
	}	/* End if */

	max = upper_limit(range);			/* internal upper limit for range */

	if ( n > max )  {					/* requested size is too big */
		error(NON_FATAL, "size %d too large for range %d", pstab[n-1], range);
		n = max;						/* reset n to largest allowed size */
	}	/* End if */

	PUTC(VSIZE, tf);					/* MACRO - set character height */
	putint(10 * pstab[n-1]);			/* vertical size - in decipoints */

}	/* End of t_charht */


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


upper_limit(n)


	int		n;							/* find upper limit for this range */


{


	int		bsize;						/* master range base size */
	int		max;						/* maximum point size for range n */
	int		max_internal;				/* maximum internal size for range n */



	/********************************************************************
	 *																	*
	 *		This routine is called by t_charht() and possibly others to	*
	 *	find the maximum internal size that is allowed for master range	*
	 *	n. The value returned to the caller is the largest internal		*
	 *	size that is allowed in this range.								*
	 *																	*
	 ********************************************************************/



	bsize = BASE_SIZE(n);				/* base size for master range n */
	max = bsize;						/* max point size if n > 3 */
	if ( n <= 3 )						/* can scale these ranges up */
		max = SCALE_UP(bsize);			/* to this limit on our APS-5 */

	max_internal = t_size(max);			/* first try at max internal size */
	if ( pstab[max_internal -1] > max )	/* take next lower internal size */
		max_internal--;

	return(max_internal);

}	/* End of upper_limit */


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


t_slant(n)


	int		n;							/* set the APS slant to this value */


{



	/********************************************************************
	 *																	*
	 *		Called to set the slant angle to the value of the parameter	*
	 *	n. On the APS-5 we can only set positive or negative 14 degree	*
	 *	slants, even though in TROFF any slant angle can be requested.	*
	 *	The global variable last_slant is the value of the last angle	*
	 *	that was stored in the APS-5 using either the SETOBLIQUE or		*
	 *	STRTPG commands, while aps_slant is the last angle we set in	*
	 *	this routine.													*
	 *																	*
	 *		Originally we only set the oblique angle if last_slant		*
	 *	was not equal to n. This really didn't work too well, because	*
	 *	appaerntly the APS-5 resets the oblique angle to +14 degrees	*
	 *	when it returns to normal mode.									*
	 *																	*
	 ********************************************************************/



	if ( n != 0 )  {					/* need to slant type being set */

		n = ( n > 0 ) ? POS_SLANT		/* use +14 degree slant */
					  : NEG_SLANT;		/* otherwise slant at -14 degrees */

		PUTC(SETOBLIQUE, tf);			/* MACRO - store new angle n */
		putint(10 * n);					/* APS expects 10 times the angle */
		last_slant = n;					/* remember this stored angle */

		PUTC(XOBLIQUE, tf);				/* MACRO - execute oblique mode */

	}	/* End if */
	else PUTC(XNORMAL, tf);				/* MACRO - otherwise use normal mode */

	aps_slant = n;						/* angle that type is being set at */

}	/* End of t_slant */


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


t_font(str)


	char	*str;						/* convert this string to font number */


{


	int		n;							/* integer value for number in str */



	/********************************************************************
	 *																	*
	 *		This routine is called from conv() to convert the ASCII		*
	 *	string *str to an integer that represents a legal font number.	*
	 *	If the resulting number is outside the allowed range for fonts	*
	 *	on this typesetter then an error message is printed out and		*
	 *	the program is aborted.											*
	 *																	*
	 ********************************************************************/



	n = atoi(str);						/* font number */
	if ( n < 0  ||  n > nfonts )  {		/* illegal font - abort */
		error(FATAL, "illegal font number %d", n);
		n = font;						/* in case we don't quit on an error */
	}	/* End if */

	return(n);							/* legal value so return it */

}	/* End of t_font */


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


t_text(str)


	char	*str;						/* typeset this string of characters */


{


	int		ch;							/* internal character variable */
	char	buf[4];						/* buffer used for special chars */



	/********************************************************************
	 *																	*
	 *		This routine is called by conv() to process the text string	*
	 *	that is in the array str. Characters are read from str and		*
	 *	written to the output file until the end of the string is		*
	 *	reached.														*
	 *																	*
	 *		After the character has been put in the output file the		*
	 *	current horizontal position is adjusted by a call to hmot()		*
	 *	using the global variable lastw as the parameter. lastw is set	*
	 *	in put1() and represents the width of the last character that	*
	 *	was printed.													*
	 *																	*
	 ********************************************************************/



	if ( debug[2] )
		fprintf(fp_debug,"input string = %s\n", str);

	if ( output == OFF ) return;		/* not doing output on this page */

	while ( (ch = *str++) != '\n'  &&  ch != '\0' )  {

		if ( ch == '\\' )  {			/* this is a special char sequence */
			switch ( ch = *str++ )  {	/* so check the next character */

				case '(':				/* special troff character sequence */
							buf[0] = *str++;
							buf[1] = *str++;
							buf[2] = '\0';
							put1s(buf);	
							break;

				case '\\':				/* backslash character */
				case 'e':
							put1('\\');
							break;

				default:				/* illegal character sequence */
							error(FATAL,"illegal character sequence \\%c", ch);
							break;

			}	/* End switch */
		} else put1(ch);				/* otherwise it is a simple character */
		hmot(lastw);					/* beam has moved right lastw units */

		if ( debug[3] )
			fprintf(fp_debug,"char = %c  width = %d\n", ch, lastw);

	}	/* End while */

}	/* End of t_text */


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


t_reset(ch)


	int		ch;							/* pause or stop */


{


	int		n;							/* for loop variable */
	long	dist;						/* distance to end of job */
	int		opcode;						/* ENDJOB or HALT */



	/********************************************************************
	 *																	*
	 *		This routine is called to produce the typesetter commands	*
	 *	that are required at the end of a job. If the parameter ch has	*
	 *	the value 's' then we have reached the end of the current job	*
	 *	and so we output the ENDJOB opcode, otherwise ch should be		*
	 *	equal to 'p', and so we produce the HALT command. This enables	*
	 *	the operator to restart the current job by pressing the PROCEED	*
	 *	button on the front panel, while if he hits the INITIAL button	*
	 *	the current job will be terminated.								*
	 *																	*
	 *		If we have finished this job, but we are not currently at	*
	 *	position max_vpos, we step the job forward to this position so	*
	 *	that the next job begins on unexposed paper.					*
	 *																	*
	 ********************************************************************/



	output = ON;						/* probably not needed here? */

	if ( ((dist = max_vpos - cur_vpos) > 0) && (ch == 's') )  {
		dist += (pstab[nsizes-1] / 2) * 10;		/* try to get whole last line */
		while ( dist > 0 )  {			/* go to the end of the job */
			vmot(dist > MAX_INT ? MAX_INT : dist);
			dist -= (dist > MAX_INT) ? MAX_INT : dist;
		}	/* End while */
	}	/* End if */

	PUTC(STRTPG, tf);					/* MACRO - output STRTPG opcode */
	putint(9000+pageno);				/* can't possibly be a page number */
	
	for ( n = 0; n < 10; n++ )			/* flush out APS internal buffer */
		PUTC(APSNOOP, tf);				/* MACRO - put out a few no-op's */

	opcode = ( ch == 'p' ) ? HALT		/* may want to restart this job */
						   : ENDJOB;	/* done with this guy */

	PUTC(opcode, tf);					/* MACRO - stop */

	fflush(tf);							/* flush any buffered output */

}	/* End of t_reset */


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


hflush()


{



	/********************************************************************
	 *																	*
	 *		This routine is called in put1() and t_push() to make sure 	*
	 *	that the two variables hpos and htrue aren't too different. If	*
	 *	they differ by more than the constant SLOP then a tab is set 	*
	 *	and then executed to position the beam properly.				*
	 *																	*
	 *		The variable hpos is the current horizontal position as 	*
	 *	determined by troff, while htrue is the horizontal position as	*
	 *	calculated by the post-processor.								*
	 *																	*
	 ********************************************************************/



	if ( output == OFF ) return;		/* not doing output for this page */

	if ( abs(hpos - htrue) > SLOP )  {	/* positions are too different */
		PUTC(SETTAB, tf);				/* MACRO - so set a tab */
		putint(hpos);					/* to the current value of hpos */
		PUTC(XTAB, tf);					/* MACRO - then execute the tab */
		htrue = hpos;					/* the positions are the same now */
	}	/* End if */

}	/* End of hflush */


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


hmot(n)


	int		n;							/* move this far from here */


{



	/********************************************************************
	 *																	*
	 *		This routine is called from conv() to handle a relative		*
	 *	horizontal motion of n units. If n is positive then we move to	*
	 *	the right on the current line. If the final horizontal position	*
	 *	hpos is negative then something has gone wrong and so we print	*
	 *	out an error message and if we return from error() we set hpos	*
	 *	to 0.															*
	 *																	*
	 ********************************************************************/



	if ( (hpos += n ) < 0 || hpos > hcutoff )  {		/* bad beam position */
		error(FATAL, "illegal horizontal position %d", hpos);
		hpos = 0;						/* in case we ignore this error */
	}	/* End if */

}	/* End of hmot */


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


hgoto(n)


	int		n;							/* move to this horizontal position */


{



	/********************************************************************
	 *																	*
	 *		This routine is called by conv() to set the absolute		*
	 *	horizontal position of the beam to the position n, where n		*
	 *	must be a positive integer.										*
	 *																	*
	 ********************************************************************/



	if ( (hpos = n) < 0 || hpos > hcutoff )  {		/* bad beam position */
		error(FATAL, "illegal horizontal position %d", hpos);
		hpos = 0;						/* in case we ignore this error */
	}	/* End if */

}	/* End of hgoto */


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


vgoto(n)


	int		n;							/* final absolute vert position */


{



	/********************************************************************
	 *																	*
	 *		This routine is called from conv() to position the beam at	*
	 *	the absolute vertical position n. The unit used in all of the	*
	 *	APS absolute spacing commands is 1/10 of a point.				*
	 *																	*
	 *																	*
	 *		NOTE - it is important to check that a job doesn't try to	*
	 *	write on a previous job by using the vertical spacing commands.	*
	 *	Currently this check is made in the routine vmot().				*
	 *																	*
	 ********************************************************************/



	vmot(n - vpos);						/* move n-vpos units from here */

}	/* End of vgoto */


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


vmot(n)


	int		n;							/* move n units vertically from here */


{


	int		i;							/* for loop variable */
	int		sign;						/* sign of the requested motion */
	int		dist;						/* distance left to move */


	/********************************************************************
	 *																	*
	 *		This routine is called to move the vertical position of the	*
	 *	beam n units from the current position. The global variable 	*
	 *	cur_vpos is the typesetter's current vertical position as 		*
	 *	measured from the start of the job. If the user has requested	*
	 *	a relative vertical motion that would make cur_vpos negative	*
	 *	we print an error message and then abort. The variable max_vpos	*
	 *	is the maximum vertical position that this job has reached. It 	*
	 *	is used to calculate the amount of paper that has been used.	*
	 *																	*
	 ********************************************************************/



	if ( output == OFF ) return;		/* not doing output on this page */

	if ( cur_vpos + n < 0 )  {			/* trying to write on last job! */
		error(FATAL, "can't backup past start of job");
		return;							/* in case we ignore this error */
	}	/* End if */

	sign = ( n > 0 ) ? 1 : -1;			/* up or down the page */
	dist = sign * n;					/* total distance to move */

	while ( dist > 0 )  {				/* not done yet */
		PUTC(VSPABS, tf);				/* MACRO - absolute vert motion */
		putint(sign * ((dist > vert_step) ? vert_step : dist));
		dist -= ( dist > vert_step ) ? vert_step : dist ;
	}	/* End while */

	vpos += n;							/* record our new vertical position */
	cur_vpos += n;
	if ( cur_vpos > max_vpos )			/* this is the farthest we have gone */
		max_vpos = cur_vpos;

}	/* End of vmot */


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


put1s(s)


	char	*s;							/* print this special character */


{


	int		i;							/* for loop index */



	/********************************************************************
	 *																	*
	 *		This routine is called to produce the typesetter output		*
	 *	for the special character string pointed to by s. All of the	*
	 *	special characters are listed in the charset portion of the 	*
	 *	typsetter's DESC file, and the program makedev reads this part	*
	 *	of the DESC file and produces two tables called chtab[] and		*
	 *	chname[]. The character array chname[] contains the special		*
	 *	character strings separated by '\0', while chtab[i] gives us 	*
	 *	the starting position in chname[] for the special character i.	*
	 *																	*
	 *																	*
	 *		NOTE - Since we just do a sequential search of the strings	*
	 *	in chname[] when looking for a special character, the speed of	*
	 *	the post-processor could be improved if we had the most common	*
	 *	special characters at the start of the list in the DESC file.	*
	 *	However before doing this we should find out how troff does 	*
	 *	the lookup.														*
	 *																	*
	 ********************************************************************/



	if ( output == OFF ) return;		/* not doing output for this page */

	for ( i = 0; i < nchtab; i++ )		/* lookup the special character */
		if ( strcmp(&chname[chtab[i]], s) == SAME_STR )
			break;
	
	if ( i < nchtab )  {				/* found it */
#ifdef ADJUST
		t_adjust(s);					/* adjust vertical positions */
#endif
		put1(i + 128);					/* special characters start at 128 */
	}	/* End if */
	else								/* didn't find it - abort */
		if ( newfile(s, pstab[size-1]) )
			error(FATAL, "special character %s not found", s);

	if ( debug[4] )  {
		fprintf(fp_debug,"string = %s  ", s);
		fprintf(fp_debug,"index = %d\n", i);
	}	/* End if */

}	/* End of put1s */


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


#ifdef ADJUST


t_adjust(s)


	char	*s;							/* look for this string */


{


	int		i;							/* for loop index */
	int		incr;						/* incrment v_adjust by this much */



	/********************************************************************
	 *																	*
	 *		Called from put1s() to look for the string *s in adj_tbl[]	*
	 *	and if found it is used to set the appropriate vertical for the	*
	 *	character.														*
	 *																	*
	 ********************************************************************/


	v_adjust = 0;

	for ( i = 0; adj_tbl[i] != '\0'; i++ )
		if ( strcmp(s, adj_tbl[i]) == 0 )  {
			incr = (vadjustment[i] < 0) ? -1 : 1;
			v_adjust = (vadjustment[i] * pstab[size-1]) /dev.unitwidth + incr;
			break;
		}	/* End if */

}	/* End of t_adjust */

#endif


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


put1(c)


	int		c;							/* print this character */


{


	register int	i;					/* c's index in font data structures */
	register int	k;					/* look for c on this font */
	int		j;							/* lookup failed on this many fonts */
	int		k1;							/* if not found check this font next */
	int		code;						/* APS-5 code for this character */
	int		old_font;					/* original font number (1..nfonts) */
	int		old_range;					/* original master range setting */
	int		last_range;					/* last master range setting */
	int		old_slant;					/* original device slant angle */



	/********************************************************************
	 *																	*
	 *		This routine is responsible for producing the output codes	*
	 *	for the characters to be printed by the APS. The integer c		*
	 *	is the ASCII code for the character if c < 128. Otherwise it 	*
	 *	refers to one of troff's special character sequences. Since we	*
	 *	are not concerned with unprintable ASCII characters we subtract	*
	 *	32 from c to get the right index to use when we do the lookup	*
	 *	in array fitab[][]. If the character isn't found on the current	*
	 *	font then we search all the remaining fonts, starting with the	*
	 *	first special font. If we haven't found the character after we	*
	 *	search the last font then an error message is written out and	*
	 *	we quit. Since we can't be guarenteed that font position 0 has	*
	 *	been loaded with a valid font, we need to make sure that the	*
	 *	circular search skips this position.							*
	 *																	*
	 *		If we find the character c, but it has an APS code that is	*
	 *	larger than 128, then the makedev program has encoded some		*
	 *	extra information in this field. Therefore we call the routine	*
	 *	special_case() to decode this info, and return the correct APS	*
	 *	code for c.														*
	 *																	*
	 ********************************************************************/



	if ( output == OFF ) return;		/* not doing output on this page */

	old_font = font;					/* may find c on a different font */
	old_range = last_range = range;		/* and in a different master range */
	old_slant = aps_slant;				/* and may change the slant for c */

	k = font;							/* while loop looks for c on font k */
	k1 = smnt - 1;						/* get the next font from this guy */
	j = 0;								/* c not found on this many fonts */

	c -= 32;							/* tables don't include unprintable chars */

	if ( c <= 0 )  {					/* c is a space or unprintable */
		if ( c < 0 )   					/* can't be in any of our font tables */
			error(FATAL, "non-printable character 0%o\n", c+32);

		lastw = (widthtab[font][0] & BMASK) * pstab[size-1] / dev.unitwidth;
		return;							/* lastw = space width (see t_text()) */
	}	/* End if */


	/* Look for character c in current font and then on the special fonts */


	while ( (j < nfonts + 1) && ((i = fitab[k][c] & BMASK) == 0) )  {
		k = (k1++) % nfonts + 1;		/* now check all other fonts */
		j++;
	}	/* End while */


	/* If j > nfonts or i == 0 then char c not found. no-op if code = 0 */


	if ( j > nfonts || i == 0 || (code = codetab[k][i] & BMASK) == 0 )  {
		if ( i == 0  ||  j > nfonts )	
			if ( c+32 < 128 || newfile(&chname[chtab[c+32-128]], pstab[size-1]) )
				error(FATAL, "character 0%o not found", c+32);
		return;
	}	/* End if */

	if ( aps_font != fontname[k].number )	/* probably need a new font */
		if ( (code < 129) || (fontbase[font]->spare1 != fontbase[k]->spare1) )
			CHANGE_FONT(k, last_range);

	if ( code > 128  &&  fontbase[k]->spare1 )  {	/* got some more stuff to do */
		code = special_case(i, k);		/* this is the real APS code for c */
		last_range = range;				/* in case the range was changed */
	}	/* End if */

#ifdef ADJUST

	if ( v_adjust != 0 )
		vmot(v_adjust);

#endif

	hflush();							/* get to right horizontal position */
	PUTC(code, tf);						/* MACRO - then print the character */

#ifdef ADJUST

	if ( v_adjust != 0 )  {
		vmot(-v_adjust);
		v_adjust = 0;
	}	/* End if */

#endif

	if ( (last_range != old_range) || (aps_slant != old_slant) )
		CHANGE_FONT(old_font, last_range);	/* MACRO - back to the old font */

	lastw = ((widthtab[k][i] & BMASK) * pstab[size-1] + dev.unitwidth/2) / dev.unitwidth;
	htrue += lastw;						/* approximate right side of char */

}	/* End of put1 */


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


putint(n)


	int		n;							/* write out this integer */


{



	/********************************************************************
	 *																	*
	 *		This routine is called to write the integer n out in the	*
	 *	last two bytes of a type two command on the APS-5 typesetter.	*
	 *																	*
	 ********************************************************************/



	PUTC(n >> 8, tf);					
	PUTC(n, tf);						

}	/* End of putint */


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


setsize(n)


	int		n;							/* new internal size */


{



	/********************************************************************
	 *																	*
	 *		This routine is called to set the current internal size to	*
	 *	the value n, and then output the commands needed to change the	*
	 *	APS-5 point size. The internal size n is an index into the		*
	 *	array pstab[], which contains the actual point size that is to	*
	 *	be used.														*
	 *																	*
	 *																	*
	 *		NOTE - This routine always calls setfont in case the range	*
	 *	has changed, but it may be the case that the range doesn't		*
	 *	really change even though the point size has changed. Always 	*
	 *	making this call definitely produces some unnecessary APS-5		*
	 *	code. Probably fix it in setfont routine.						*
	 *																	*
	 ********************************************************************/



	if ( output == OFF ) return;		/* not doing output on this page */

	if ( n <= 0  ||  n > nsizes )  {	/* internal size out of range */
		error(FATAL, "illegal internal size %d", n);
		n = size;						/* in case we return from this error */
	}	/* End if */

	size = n;							/* must preceed call to setfont() */
	change_font(font);					/* in case the range has changed */
	PUTC(HVSIZE, tf);					/* MACRO - set the new point size */
	putint(10 * pstab[n-1]);			/* APS expects 10 times the size */

}	/* End of setsize */


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


setfont(n)


	int		n;							/* new font's internal number */


{


	int		oldrange;					/* current master range */



	/********************************************************************
	 *																	*
	 *		This routine is called to set the current typesetter font	*
	 *	to the one that corresponds to the parameter n. These internal	*
	 *	font numbers are determined by the way the font tables are set	*
	 *	up for the typesetter in the DESC file.							*
	 *																	*
	 *		Our APS-5 typesetter typically has only master ranges 1, 2,	*
	 *	and 3. The actual font that we want to set is determined by the	*
	 *	current value of the global variable size. Remember that this	*
	 *	value is an internal size, which is an index into the array		*
	 *	pstab[]. The following table gives the point sizes that can be	*
	 *	used in the different master ranges,							*
	 *																	*
	 *																	*
	 *					MASTER RANGE 1:   0 to 12						*
	 *					MASTER RANGE 2:  12 to 24						*
	 *					MASTER RANGE 3:  24 to 48						*
	 *					MASTER RANGE 4:  48 to 96						*
	 *																	*
	 *																	*
	 *	although any range can actually print characters up to size,	*
	 *																	*
	 *																	*
	 *						1.5 * limit - 1								*
	 *																	*
	 *																	*
	 *	where the value of limit is obtained from the above table.		*
	 *																	*
	 ********************************************************************/



	if ( output == OFF ) return;		/* not doing output for this page */

	if ( n < 0  ||  n > nfonts )  {
		error(FATAL, "requested font number %d out of range", n);
		n = font;						/* in case we ignore this error */
	}	/* End if */

	oldrange = range;					/* new range may be different */
	CHANGE_FONT(n, oldrange);			/* change the APS font */
	font = n;							/* current internal font number */

}	/* End of setfont */


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


change_font(n)


	int		n;							/* new font's internal number */


{



	int		angle;						/* slant angle - used in SETSLANT */
	int		max_range;					/* largest range allowed on font n */
	int		max_size;					/* largest size allowed in max_range */
	char	f_spare1;					/* value of font flags */



	/********************************************************************
	 *																	*
	 *		This routine is called to change the font that the APS is	*
	 *	currently setting type in. Some extra stuff is done if the font	*
	 *	has any of its special flags set.								*
	 *																	*
	 ********************************************************************/



	range = get_range(pstab[size-1]);	/* probably the new range */

	f_spare1 = fontbase[n]->spare1;		/* get special case font flags */
	if ( f_spare1 & RANGE_BIT )  {		/* have a maximum range for font n */

		max_range = DECODE(f_spare1, RANGE_VAL, TWO_BITS);	/* decode this range */
		if ( range > max_range )  {		/* current range is too big */
			max_size = upper_limit(max_range);
			if ( size > max_size )		/* can't blow max_range up to size */
				error(FATAL, "size %d too big for range %d", pstab[size-1], max_range);
			range = max_range;			/* use the biggest possible range */
		}	/* End if */

	}	/* End if */

	if ( f_spare1 & SLANT_BIT )			/* this font has a default slant */
		SETSLANT(f_spare1, angle);		/* MACRO - so use it */
	else if ( aps_slant != last_req_slant )		/* back to the last requested slant */
		t_slant(last_req_slant);


	PUTC(FONT, tf);						/* MACRO - set the new font and range */
	if ( range < 3 )					/* APS expects a positive number */
		 putint(fontname[n].number + range-1);
	else putint(-(fontname[n].number + range-1));

	aps_font = fontname[n].number;		/* font that the APS is using */

}	/* End of change_font */


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


t_fp(n, s, si)


	int		n;							/* update this font position */
	char	*s;							/* font's external name (eg. R) */
	char	*si;						/* font number used by the APS */


{



	/********************************************************************
	 *																	*
	 *		This routine is called to update the data structures that	*
	 *	are maintained by the post-processor to keep track of fonts.	*
	 *	The parameter n is the font position that we are going to		*
	 *	update, while s and si are the font's external and internal		*
	 *	names.															*
	 *																	*
	 ********************************************************************/



	fontname[n].name = s;
	fontname[n].number = atoi(si);
	fontname[n].nwfont = fontbase[n]->nwfont;

}	/* End of t_fp */


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


account()


{


	char	user[L_cuserid];			/* user's login name */
	float	pages;						/* will charge for this many pages */



	/********************************************************************
	 *																	*
	 *		This is the accounting routine for the post-processor. It	*
	 *	may have to be changed for your particular system.				*
	 *																	*
	 *		NOTE - since the variable res can be changed we better use	*
	 *	the constant RES to determine how many pages were printed.		*
	 *																	*
	 ********************************************************************/



	paper = max_vpos;						/* used this much paper */
	pages = paper / (RES * PAGE_LENGTH);	/* pages that we think were used */

	if ( x_stat & DO_ACCT )  {				/* got accounting to do */
		cuserid(user);
		fprintf(fp_acct, " user = %-10s", user);
		fprintf(fp_acct, " paper = %-10.1f", pages);
		x_stat &= ~DO_ACCT;					/* done the important accounting */
		fprintf(fp_acct, "exit status = 0%-6o", x_stat);
		if ( tf == stdout )
			fprintf(fp_acct, "  ??");
		fprintf(fp_acct, "\n");
	}	/* End if */

	if ( report == YES )
		fprintf(stderr, " %-3.1f pages\n", pages);

}	/* End of account */


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


special_case(index, font)


	int		index;						/* char position in tables */
	int		font;						/* char info is on this font */


{


	int		old_range;					/* saved value of current range */
	int		old_font;					/* present APS-5 font number */
	int		req_font;					/* requested APS-5 font number */
	int		max_range;					/* max allowed range for character */
	int		max_size;					/* max size allowed for max_range */
	int		angle;						/* special char angle to set */
	int		code;						/* old code - with bits set */



	/********************************************************************
	 *																	*
	 *		This routine is called by put1() to handle the special any	*
	 *	special case characters for the APS-5. 							*
	 *																	*
	 *		Taken on its own, this is a very confusing routine, but one	*
	 *	which I found from my experience with the APS-5 at Murray Hill	*
	 *	was definitely needed. I'll try to give a brief, but hopefully	*
	 *	complete explination here.										*
	 *																	*
	 *		The additions that I made here needed to be completely		*
	 *	transparent to troff, and so the only field in the font tables	*
	 *	that could be changed was the actual charater code, since troff	*
	 *	can't possibly need this stuff (it's device independent!). On	*
	 *	the APS-5, character codes lie between 1 and 128 (inclusive) -	*
	 *	anything else is an actual function code. Therefore when we are	*
	 *	ready to print a character in put1(), and we find that its code	*
	 *	is larger than 128 we know something more has to be done. This	*
	 *	routine is then called to try to make sense out of the code,	*
	 *	do any of the special stuff required for this character, and	*
	 *	finially return its real code. This extra stuff can include		*
	 *	changing the character's slant, ensuring that the current		*
	 *	master range is not larger than a given value (max_range), and	*
	 *	finially getting the actual character from a completely			*
	 *	different font (alternate_font). What we actually do for each	*
	 *	special character is determined by its code in the current		*
	 *	set of font tables. This code is produced by a special version	*
	 *	of the makedev program, which I changed to interpret some 		*
	 *	extra fields in the ASCII font tables.					 		*
	 *																	*
	 *																	*
	 *		NOTE - This extra stuff is costly, and should only be used	*
	 *	when absolutely necessary. The alternate font stuff can			*
	 *	cause many extra font changes, which in turn will force extra	*
	 *	disk accesses. We found this to be one of the main reasons for	*
	 *	slow jobs on our APS, not to mention the extra wear on the disk	*
	 *	drive.															*
	 *																	*
	 ********************************************************************/



	if ( fontname[font].number != alt_tables )	/* don't have font's table */
		load_alt(font);							/* load font's '.add' tables */

	code = codetab[font][index] & BMASK;	/* encoded character information */
	old_range = range;						/* current typesetter range */
	old_font = aps_font;					/* quick change - fix it later */
	req_font = old_font;

	if ( code & SLANT_BIT )				/* special slant for this character */
		SETSLANT(code, angle);			/* MACRO - set slant to encoded value */

	if ( code & RANGE_BIT )  {			/* maximum range specified */

		max_range = DECODE(code, RANGE_VAL, TWO_BITS);
		range = get_range(pstab[size-1]);	/* get range for current size */
		if ( range > max_range )  {		/* check current size first */
			max_size = upper_limit(max_range);
			if ( size > max_size )		/* too big for max_range - abort */
				error(FATAL, "size %d too large for range %d", pstab[size-1], max_range);
			else range = max_range;		/* set the range */
		}	/* End if */

	}	/* End if */

	if ( code & FONT_BIT )				/* alternate font specified */
		req_font = alt_font[index];		/* APS-5 alternate font number */

	if ( (old_range != range) || (req_font != old_font) )  {	

		aps_font = req_font;
		PUTC(FONT, tf);					/* set new font */
		if ( range < 3 )				/* output positive number */
			putint(req_font + range - 1);
		else putint(-(req_font + range - 1));
		if ( range != old_range )  {	/* Need to set the size */
			PUTC(HVSIZE, tf);			/* MACRO - set the point size */
			putint(10 * pstab[size-1]);
		}	/* End if */

	}	/* End if */

	return(alt_code[index] & BMASK);

}	/* End of special_case */


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


get_range(n)


	int		n;							/* find the range for this point size */


{


	int		val;						/* return this as the range */
	int		msize;						/* size for master range val */



	/********************************************************************
	 *																	*
	 *		This routine is called to return the range corresponding to	*
	 *	the point size n. Note that n is not an internal size for this	*
	 *	routine.														*
	 *																	*
	 ********************************************************************/



	val = 0;

	while ( ++val <= MAX_RANGE )  {
		msize = BASE_SIZE(val);			/* base size for master range val */
		if ( (val == MAX_RANGE) && (val <= 3) )		/* scale it up */
			msize = SCALE_UP(msize);
		if ( n <= msize ) return(val);	/* found the correct master range */
	}	/* End while */

	error(FATAL, "size %d too large", n);
	return(1);							/* in case we ignore this error */

}	/* End of get_range */


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


fileinit()


{


	int		fin;						/* input file descriptor */
	int		nw;							/* number of font table entries */
	int		i;							/* for loop index */
	char	*malloc();					/* memory allocation routine */
	char	*filebase;					/* pointer to memory block */
	char	*p;							/* pointer used to set up tables */
	char	temp[60];					/* pathname of the DESC.out file */



	/********************************************************************
	 *																	*
	 *		This routine is responsible for reading the DESC.out file	*
	 *	for the APS-5 typesetter, and then initializing the appropriate	*
	 *	data structures for the device and all of the default fonts		*
	 *	that were mentioned in the typesetter description file DESC.	*
	 *																	*
	 *		First the DESC.out file is opened and the structure dev is	*
	 *	initialized from the first part of the file. This structure 	*
	 *	then contains all the information needed to enable us to finish	*
	 *	the initialization process. Next the rest of the file is read	*
	 *	in and the device tables pstab[], chtab[], and chname[] are set	*
	 *	up. After this has been done we enter a loop which handles the	*
	 *	initialization for all of the fonts that were mentioned in the	*
	 *	DESC file. Finially we allocate enough memory so that font		*
	 *	position 0 can be loaded with the tables from any valid font 	*
	 *	in the library.													*
	 *																	*
	 *																	*
	 *		NOTE - the program 'makedev' creates all of the '.out' 		*
	 *	files in the troff font library. This routine quite obviously	*
	 *	depends on how 'makedev' has written these files. In addition	*
	 *	the troff program also reads the '.out' files and it too 		*
	 *	expects the same format as we do here! To really understand		*
	 *	this routine you need to look at 'makedev.c' and the ASCII		*
	 *	tables in the troff font library.								*
	 *																	*
	 ********************************************************************/



	sprintf(temp, "%s/dev%s/DESC.out", fontdir, devname);
	if ( (fin = open(temp, 0)) < 0 )	/* file didn't open - abort */
		error(FATAL, "can't open tables for %s", temp);

	READ(fin, &dev, sizeof(struct dev));	/* init dev structure */

	nsizes = dev.nsizes;				/* number of point sizes specified */
	nchtab = dev.nchtab;				/* number of char table entries */
	nfonts = dev.nfonts;				/* number of default fonts */

	if ( nfonts > NFONT )				/* need to redefine NFONT */
		error(FATAL, "internal error - constant NFONT too small");

	filebase = malloc(dev.filesize);	/* space for rest of the file */
	READ(fin, filebase, dev.filesize);	/* read in the rest of the file */

	pstab = (short *) filebase;			/* point size list is first */
	chtab = pstab + nsizes + 1;			/* next comes chtab list */
	chname = (char *) (chtab + dev.nchtab);	/* then the char table list */

	p = chname + dev.lchname;			/* start of font table info */

	for (i = 1; i <= nfonts; i++)  {	/* set up default font tables */

		fontbase[i] = (struct Font *) p;
		nw = *p & BMASK;				/* width count comes first */

		if (smnt == 0  &&  fontbase[i]->specfont == 1)
			smnt = i;					/* this is first special font */

		p += sizeof(struct Font);		/* font structure was written first */
		widthtab[i] = p;				/* next is this font's width table */
		codetab[i] = p + 2 * nw;		/* skip kern to get the code table */
		fitab[i] = p + 3 * nw;			/* last is the font index table */

		p += 3 * nw + dev.nchtab + 128 - 32;	/* point to next font */

		t_fp(i, fontbase[i]->namefont, fontbase[i]->intname);

		if ( debug[5] ) fontprint(i);	/* dump font tables */

	}	/* End for */

	if ( smnt == 0 ) 					/* no special fonts specified */
		smnt = nfonts + 1;				/* used in routine put1() */

	fontbase[0] = (struct Font *) malloc(3*255 + dev.nchtab + (128-32) + sizeof(struct Font));
	widthtab[0] = (char *) fontbase[0] + sizeof(struct Font);
	fontbase[0]->nwfont = 255;
	close(fin);

}	/* End of fileinit */


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


fontprint(i)


	int		i;							/* index of font to print out */


{


	int		j;							/* for loop variable */
	int		n;							/* number of width entries */
	int		pos;						/* position of char from fitab array */
	int		count;						/* count of characters in this font */



	/********************************************************************
	 *																	*
	 *		This is a debugging routine that is used to dump all of the	*
	 *	information about font i that was loaded from the '.out' file.	*
	 *	It is called from fileinit() when the debug flag 5 is turned 	*
	 *	on, and from loadfont when flag 6 is set.						*
	 *																	*
	 ********************************************************************/



	n = fontbase[i]->nwfont & BMASK;	/* number of width entries */

	fprintf(fp_debug, "\nDUMP FOR FONT %s ", fontbase[i]->namefont);
	fprintf(fp_debug, "   FONT POSITION = %d\n\n", i);
	fprintf(fp_debug, "  font structure data:\n");
	fprintf(fp_debug, "\t\tfont.nwfont = %d\n", fontbase[i]->nwfont & BMASK);
	fprintf(fp_debug, "\t\tfont.specfont = %d\n", fontbase[i]->specfont & BMASK);
	fprintf(fp_debug, "\t\tfont.ligfont = %d\n", fontbase[i]->ligfont & BMASK);
	fprintf(fp_debug, "\t\tfont.spare1 = %d\n", fontbase[i]->spare1 & BMASK);
	fprintf(fp_debug, "\t\tfont.intname = %s\n\n", fontbase[i]->intname);

	fprintf(fp_debug, "  CHAR     WIDTH      CODE     INDEX\n");
	count = 0;

	for (j = 0; j < dev.nchtab + 128 - 32; j++)  {	 

		if ( (pos = fitab[i][j] & BMASK) != 0 )  {
			count++;
			if ( j >= 96 )					/* special chars start at 128-32 */
				 fprintf(fp_debug, "%5s", &chname[chtab[j-96]]);
			else fprintf(fp_debug, "%5c", (j + 32) & BMASK);
			fprintf(fp_debug, "%10d", widthtab[i][pos] & BMASK);
			fprintf(fp_debug, "%10d", codetab[i][pos] & BMASK);
			fprintf(fp_debug, "%10d", j);
			if ( alt_tables == fontname[i].number )		/* print extra info */
				fprintf(fp_debug, "%10d%10d", alt_code[pos] & BMASK, alt_font[pos]);
			fprintf(fp_debug,"\n");
		}	/* End if */

	}	/* End for */

	fprintf(fp_debug, "\n CHARACTER COUNT FOR THIS FONT = %d\n", count);
	fprintf(fp_debug, "\n\n");

}	/* End of fontprint */


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


loadfont(num, name, fdir)


	int		num;						/* font position to load */
	char	*name;						/* name of the font to load */
	char	*fdir;						/* name of directory to load from? */


{


	int		fin;						/* font file descriptor */
	int		nw;							/* width entries in new font file */
	int		norig;						/* width entries in old font file */
	char	temp[60];					/* path name of font file */



	/********************************************************************
	 *																	*
	 *		This routine is called to load the font position num with	*
	 *	the data for the font name. If the string fdir is not null then	*
	 *	it is taken as the pathname of the directory that contains the	*
	 *	'.out' file for the font that we are to load, otherwise we load	*
	 *	this data from the standard font library.						*
	 *																	*
	 ********************************************************************/



	if ( num < 0  ||  num > NFONT )		/* illegal font number - abort */
		error(FATAL, "illegal fp command %d %s", num, name);

	if ( strcmp(name, fontbase[num]->namefont) == SAME_STR )	/* in already */
		return;

	if ( fdir == NULL  ||  fdir[0] == '\0' )	/* no alternate directory */
		 sprintf(temp, "%s/dev%s/%s.out", fontdir, devname, name);
	else sprintf(temp, "%s/%s.out", fdir, name);

	if ( (fin = open(temp, 0)) < 0 )			/* open the font file */
		error(FATAL, "can't open font table %s", temp);

	norig = fontbase[num]->nwfont & BMASK;	/* 'space' available in pos num */
	READ(fin, fontbase[num], 3*norig + nchtab+128-32 + sizeof(struct Font));
	nw = fontbase[num]->nwfont & BMASK;		/* 'space' needed for new font */
	if ( nw > norig )						/* new font is too big - abort */
		error(FATAL, "font %s too big for position %d", name, num);

	close(fin);
	widthtab[num] = (char *) fontbase[num] + sizeof(struct Font);
	codetab[num] = (char *) widthtab[num] + 2 * nw;
	fitab[num] = (char *) widthtab[num] + 3 * nw;
	t_fp(num, fontbase[num]->namefont, fontbase[num]->intname);

	fontbase[num]->nwfont = norig;			/* save size of position num */

	if ( debug[6] ) fontprint(num);			/* dump font position num */

}	/* End of loadfont */


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


load_alt(font)


	int		font;						/* load '.add' tables for this font */


{


	int		nw;							/* number of width entries for font */
	int		fin;						/* font file descriptor */
	char	cmd[60];					/* room for font's .add file */



	/********************************************************************
	 *																	*
	 *		This routine is called to read in the '.add' file for the	*
	 *	font that is loaded in the position specified by the parameter	*
	 *	font. These '.add' files are created by the special version of	*
	 *	the makedev program, which reads and interprets several extra	*
	 *	fields in the APS font tables. Included in the '.add' files are	*
	 *	the alternate font table and the APS-5 character code table.	*
	 *																	*
	 ********************************************************************/



	sprintf(cmd, "%s/dev%s/%s.add", fontdir, devname, fontname[font].name);

	if ( (fin = open(cmd, 0)) < 0 )		/* couldn't open the file */
		error(FATAL, "can't open file %s", cmd);

	nw = fontname[font].nwfont & BMASK;			/* number of width entries */

	READ(fin, alt_font, nw * sizeof(alt_font[0]));
	READ(fin, alt_code, nw);			/* read alternate code table */

	close(fin);
	alt_tables = fontname[font].number;	/* read in tables for this font */

	if ( debug[7] )						/* print all the font information */
		fontprint(font);

}	/* End of load_alt */


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