/*	Copyright (c) 1990 UNIX System Laboratories, Inc.	*/
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF     	*/
/*	UNIX System Laboratories, Inc.                     	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/


#ident	"@(#)ucbtroff:troff.d/devaps/daps.c	1.1.1.1"

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

		PROPRIETARY NOTICE (Combined)

This source code is unpublished proprietary information
constituting, or derived under license from AT&T's UNIX(r) System V.
In addition, portions of such source code were derived from Berkeley
4.3 BSD under license from the Regents of the University of
California.



		Copyright Notice 

Notice of copyright on this source code product does not indicate 
publication.

	(c) 1986,1987,1988,1989  Sun Microsystems, Inc
	(c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
	          All rights reserved.
********************************************************************/ 

char *xxxvers = "@(#)ucbtroff:troff.d/devaps/daps.c	1.1";



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


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