[net.sources] Part 2 of 4 - PostScript programs

stephenf@elecvax.eecs.unsw.oz (Stephen Frede) (05/21/86)

# This is a set of programs which produce PostScript. Included are
# a back end for ditroff, a program which accepts plain text, and a
# program which accepts images.
# Also included are ditroff font width files and some miscellaneous
# PostScript scripts.
# For more details see the file READ_ME in the first part.
# There are 4 parts.
# Stephen Frede	University of New South Wales, Sydney, Australia
# 
# ACSnet:	stephenf@elecvax.oz	ARPA:	stephenf%elecvax.oz@seismo
# CSnet:	stephenf@elecvax.oz@csnet-relay.csnet
# UUCP:	{seimso,ubc-vision,ukc,mcvax,prlb2}!munnari!elecvax.oz!stephenf
# ------------------------------------------------------------------------

# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# src/tpscript/hash.c src/tpscript/pcom.c src/tpscript/sfont2.c src/tpscript/spline.c src/tpscript/stringdefs.c src/tpscript/tpscript.c

echo x - src/tpscript/hash.c
cat > "src/tpscript/hash.c" << '//E*O*F src/tpscript/hash.c//'
#include	"tpscript.h"
#include	"hash.h"

/*
 * the non-acsii character names are now hashed and the elements of the
 * hash structure also contain information about whether this particular
 * character can be directly printed where troff thinks it is going
 * or whether it needs special massaging or even contruction from other
 * characters.
 */


HASH_ELEMENT	*hash_free = NOHASH,	/* points to next available entry */
		*hash_tab[HASH_SIZE];	/* starting entries */

hash_init()
{
	register	SPECIAL_NAME	*snp;
	register	HASH_ELEMENT	*hep, **hpp;
	register	int		i;
	char				*s;

	hash_free = (HASH_ELEMENT *)emalloc( ncharname * sizeof(HASH_ELEMENT));

	for( i = 0 ; i < ncharname ; i++ ) {
		s = &charname[ chartab[ i ] ];
		hpp = & hash_tab[ hash_it(s) ];
			/*
			 * skip through the entries already with this hash value
			 */
		while( *hpp != NOHASH )
			hpp = &( (*hpp)->hash_next );
		*hpp = hep = hash_free++;
		hep->hash_next = NOHASH;
		hep->hash_index = i;
		hep->hash_special = NOSPECIAL;	/* assume nothing special */
			/*
			 * now find if it is one of the special names
			 */
		for ( snp = &specnames[0] ; snp->troff_name != NULL ; snp++ ) {
			if ( strcmp( s, snp->troff_name ) == 0 ) {
				hep->hash_special = snp;
				break;
			}
		}
	}

}

dumphash( n )
	int n;
{
	register	int i;

	if ( n >= 0 && n < HASH_SIZE )
		dhsh( n );
	else
		for ( i = 0 ; i < HASH_SIZE ; i++ )
			dhsh( i );
}

dhsh( i )
	int	i;
{
	register	HASH_ELEMENT *hep;

	hep = hash_tab[i];
	fprintf( stderr, "hashed at %d:\n\t", i );
	while ( hep != NOHASH )
	{
		fprintf( stderr, "%s ",
			&( charname[ chartab[ hep->hash_index ] ] ));
		hep = hep->hash_next;
	}
	fprintf( stderr, "\n" );
}

	/*
	 * hashing function
	 */
hash_it( s )
	register	char	*s;
{
	register	int	i;

	i = 0;
	while( *s )
		i += *s++;
	return( i % HASH_SIZE );
}


putspec(specstr)
char	*specstr;
{
	register HASH_ELEMENT		*hep;
	register int			i = 0,
					n;
	register struct fontdesc	*fd;

	fd = tfp.fp_font;
	for ( hep = hash_tab[ hash_it( specstr ) ] ; hep != NOHASH ; hep = hep->hash_next )
	{
		if ( strcmp ( specstr, &charname[ chartab[ hep->hash_index ] ] ) == 0 )
			break;
	}
	if ( hep == NOHASH )
	{
		sprintf(errbuf, "special character '%s' unknown", specstr);
		error(ERR_WARN, errbuf);
		return;
	}

	i = hep->hash_index;
	i += NASCPRINT;			/* skip ordinary chars */
			/* check std font */
	if((n = fd->f_fitab[i] & BMASK) != 0)
		; 		/* ok - must be in current font */
				/* otherwise check special font */
	else if( (spcfnt1 != NOFONTDESC) && (n = spcfnt1->f_fitab[i] & BMASK) != 0)
	{
		fd = spcfnt1;
	}
				/* try the locally defined special font */
	else if( (spcfnt2 != NOFONTDESC) && (n = spcfnt2->f_fitab[i] & BMASK) != 0)
	{
		fd = spcfnt2;
	}
	else
	{
		sprintf(errbuf,
			"Special char '%s' not in current (%d) or special fonts\n",
			specstr, currtfont);
		error(ERR_WARN, errbuf);
		return;
	}
	width_pending += GETWIDTH( fd, n );

	if ( hep->hash_special != NOSPECIAL )
	{
		register	SPECIAL_NAME	*snp;

		setfont( FALSE );	/* ensure size is setup */
		CLOSEWORD();		/* may or may not have been done
					** in setfont()
					*/

		snp = hep->hash_special;
		if ( ( snp->sn_flags & SN_DEFINED ) == 0 )
		{
			snp->sn_flags |= SN_DEFINED;
			fprintf( postr, "\n/C%s { %s } def\n",
				snp->troff_name, snp->definition );
			if ( snp->sn_flags & SN_ANY_MULTIPLE )
				putmultdef( snp );
#ifdef notdef
			if ( snp->sn_flags & SN_FRACTION )
				putfractdef( snp );
			else if ( snp->sn_flags & SN_BRK_ROUNDING )
				putbrackdef( snp );
			else if ( snp->sn_flags & SN_BRACKET )
				putbrackdef( snp );
#endif
		}
		fprintf( postr, "\nC%s", snp->troff_name );
		return;
	}

	if( fd != tfp.fp_font )
	{
		struct fontdesc	*font;

		font = tfp.fp_font;
		tfp.fp_font = fd;
		putch(fd->f_codetab[n] & BMASK);
		tfp.fp_font = font;
	}
	else
	{
		putch(fd->f_codetab[n] & BMASK );
	}
}

	/*
	 * setup definition used in more than one special char
	 * - e.g. Cfrac used for \(12, \(14 etc
	 * first time through we scan through the special names list for
	 * a definition of the pseudo-name "fraction" which is used for the
	 * others
	 */
putmultdef( snp )
	register	SPECIAL_NAME *snp;
{
	register	SPECIAL_NAME *mnp;
	register	int	type;


	type = snp->sn_flags & SN_ANY_MULTIPLE;

	for ( mnp = &multdefs[0] ; mnp->troff_name != (char *)0 ; mnp++ ) {
		if ( type == (mnp->sn_flags & SN_ANY_MULTIPLE ) )
		{
			if ( ( mnp->sn_flags & SN_DEFINED ) == 0 )
			{
				fprintf( postr, "\n/C%s{%s}def",
						mnp->troff_name, mnp->definition);
				mnp->sn_flags |= SN_DEFINED;
			}
			break;
		}
	}
}


resetspcl()
{
	register	SPECIAL_NAME	*snp;

	for ( snp = &specnames[0] ; snp->troff_name != (char *)0 ; snp++ )
		snp->sn_flags &= ~SN_DEFINED;
	for ( snp = &multdefs[0] ; snp->troff_name != (char *)0 ; snp++ )
		snp->sn_flags &= ~SN_DEFINED;
}
//E*O*F src/tpscript/hash.c//

echo x - src/tpscript/pcom.c
cat > "src/tpscript/pcom.c" << '//E*O*F src/tpscript/pcom.c//'
/* LINTLIBRARY */
/*
 *	pcom.c
 *
 *	Some common code for programs which generate postscript.
 *
 *	We assume that the lineprinter software brackets jobs with EOF,
 *	and possibly sets jobname etc., so we don't do it ourselves.
 *	The program that calls these routines should do any necessary
 *	scaling first.
 *	We will set the following variables:
 *	/pgtop		top of page 
 *	/stm		job start time in ms
 *	/spg		pages at start of job
 *	/jobname	string - name of current job
 *	We define the following postscript routines:
 *	/mf		enable manual feed
 *	/af		enable auto feed
 *	/ps		print string on output stream
 *	/a4		set a4 page size	(Apple LaserWriter Only)
 *
 */

#include	"pscript.h"
#include	<stdio.h>
#if	VERBOSE
#include	<time.h>
#if	AUSAM
#include	<passwd.h>
#else	AUSAM
#include	<pwd.h>
#endif	AUSAM
#if	SYSV
#include	<sys/utsname.h>
#endif	SYSV
#if	UNSW
#include	<table.h>
#endif	UNSW
#endif	VERBOSE

	/* output stream on which postscript appears */
FILE		*postr = NULL;

long	time();
char	*ctime(),
	*getuser(),
	*systemid();

char	*ptstr[] = {
	"",		/* default - usually determined by paper tray */
	"letter",
	"legal",
	"note",
#ifdef	ALW
	"a4",		/* on Apple LaserWriter ONLY */
#endif	ALW
};

char	*pcom0tab[] = {
	"initmatrix",
#ifdef	ALW
	"/a4 [ [300 72 div 0 0 -300 72 div -52 3436 ] 292 3365",
	"{statusdict /jobstate (printing) put 0 setblink",
	"margins exch 157 add exch 245 add 8 div round cvi frametoroket",
	"statusdict /jobstate (busy) put 1 setblink} /framedevice load",
	"60 45 {dup mul exch dup mul add 1.0 exch sub} /setscreen load",
	"{} /settransfer load /initgraphics load /erasepage load ] cvx",
	"statusdict begin bind end readonly def",
#endif	ALW
	(char *)0
};

char	*pcom1tab[] = {
#if	VERBOSE
	/* job start time in ms */
	"/stm usertime def",
	/* remember total pages used */
	"/pgc statusdict begin pagecount end def",
#endif	VERBOSE
	/* move origin up page so bottom is bottom of imageable region */
	"clippath pathbbox pop pop exch pop 0 exch translate",
	/* save top of page */
	"clippath pathbbox /pgtop exch def pop pop pop",
#if	VERBOSE
	/* routine to print strings on output stream */
	"/ps { print flush } def",
#endif
	/* routine to print a page and begin a new one */
	/* the restore/save pair is important - it ensures that VM garbage
	 * collection is done at least once every page. The user program
	 * (ie lpscript, tpscript, etc.) MUST call endinit() as the
	 * last part of its initialisation - this prints an initial
	 * "save".
	 */
	"/page { copypage erasepage restore save home } def",
	/* routine to initialise a path */
	"/home { newpath 0 pgtop moveto } def",
	/* routines to select manual or auto feed */
	"/mf { statusdict /manualfeed true put",
#ifdef	ALW
	/* fix for manual feed bug (see p. 29 Appendix D Inside LaserWriter) */
	" usertime 5000 add { dup usertime lt { pop exit } if } loop",
#endif	ALW
	" } def",
	"/af { statusdict /manualfeed false put } def",
	(char *)0
};

pcomminit(scale, rotation, papertype, manualfeed, font, title, creator)
float	rotation,
	scale;
int	papertype;
bool	manualfeed;
char	*font,
	*title,
	*creator;
{
	register char	**ptab;
	long		clock;
#if	VERBOSE
	char		*user;
	char		jobname[100];
#endif	VERBOSE

	fprintf(postr, "%%!PS-Adobe-1.0\n");
	time(&clock);
#if	VERBOSE
	user = getuser();
	if(title)
		strcpy(jobname, title);
	else
	{
		strcpy(jobname, user);
		strcat(jobname, "/");
		strcat(jobname, creator);
	}
	fprintf(postr, "%%%%Title: %s\n", jobname);
#endif	VERBOSE
	fprintf(postr, "%%%%DocumentFonts: %s\n",
		font ? font : "");
	fprintf(postr, "%%%%Creator: %s\n", creator);
	fprintf(postr, "%%%%CreationDate: %s", ctime(&clock));
	fprintf(postr, "%%%%Pages: (atend)\n");
#if	VERBOSE
	fprintf(postr, "%%%%For: %s\n", user);
#endif	VERBOSE
	fprintf(postr, "%%%%EndComments\n");
	ptab = pcom0tab;
	while(*ptab)
		fprintf(postr, "%s\n", *ptab++);
	fprintf(postr, "%s\n", ptstr[papertype]);
	if(rotation != PD_ROTATION)
		fprintf(postr, "%.1f rotate\n", rotation);
	if(scale != 1.0 && scale != 0.0)
		fprintf(postr, "%.4f dup scale\n", scale);
	ptab = pcom1tab;
	while(*ptab)
		fprintf(postr, "%s\n", *ptab++);
#if	VERBOSE
	fprintf(postr, "/jobname (%s) def\n", jobname);
	fprintf(postr, "userdict /jobname jobname put\n");
	fprintf(postr, "( :: '%s' :: job starts\\n) ps\n", jobname);
#endif	VERBOSE
	fprintf(postr, "%s\n", manualfeed ? "mf" : "af");
}

endinit()
{
	/* this save is for the restore/save pairs in page */
	fprintf(postr, "\nsave\n");
	/* all variable assignments are now local to a page */

	/* initialise current path - move to top of page */
	fprintf(postr, "home\n");
	fprintf(postr, "%%%%EndProlog\n");
}

pch(ch)
int	ch;
{
	if(ch < ' ' || ch > '~')
		fprintf(postr, "\\%03o", ch);
	else
	{
		if(ch == '(' || ch == ')' || ch == '\\')
			putc('\\', postr);
		putc(ch, postr);
	}
}

pcommfinish(pages, fonts)
int pages;
char *fonts;
{
	fprintf(postr, "\n%%%%Trailer\n");
#if	VERBOSE
	fprintf(postr, "jobname ps (: Job finished:\\n) ps\n");
	fprintf(postr, "(\\ttime (s) = ) ps usertime stm sub 1000 div ==\n");
	fprintf(postr, "(\\tpages = ) ps statusdict begin pagecount end pgc sub == flush\n");
	if(pages >= 0)
		fprintf(postr, "%%%%Pages: %d\n", pages);
#endif	VERBOSE
	if(fonts)
		fprintf(postr, "%%%%DocumentFonts: %s\n");
}

#if	VERBOSE
char *
getuser()
{
	char		*lname;
	static char	username[100];
#if	AUSAM
	char		sbuf[SSIZ];
	struct pwent	pe;
	extern int	getpwlog();

	pe.pw_limits.l_uid = getuid();
	if(getpwlog(&pe, sbuf, sizeof(sbuf)) == PWERROR)
		lname = (char *)0;
	else
		lname = pe.pw_strings[LNAME];
	pwclose();
	if(lname == (char *)0)
		lname = "?";
	strcpy(username, lname);

#else	/* ! AUSAM */
	extern	char		*getlogin();
	extern	struct passwd	*getpwuid();
	struct	passwd		*pwdp;

	if ( ( lname = getlogin() ) != NULL )
		strcpy( username, lname );
	else
	{
		if ( ( pwdp = getpwuid( getuid() ) ) == (struct passwd *)0 )
			sprintf( username, "User%d", getuid() );
		else
			strcpy( username, pwdp->pw_name );
	}
#endif	/* ! AUSAM */
	strcat(username, "@");
	strcat(username, systemid());
	return(username);
}

char *
systemid()
{
	static char	sysname[100] = "";

#if	UNSW
	getaddr(G_SYSNAME, sysname);
#endif	UNSW

#if	BSD
	gethostname( sysname, sizeof(sysname) );
#endif	BSD

#if	SYSV
	struct utsname	un;

	uname(&un);
	strcpy(sysname, un.nodename);
#endif	SYSV

	return(sysname);
}
#endif	VERBOSE
//E*O*F src/tpscript/pcom.c//

echo ln src/tpscript/pcom.c src/opscript/pcom.c
ln src/tpscript/pcom.c src/opscript/pcom.c
echo x - src/tpscript/sfont2.c
cat > "src/tpscript/sfont2.c" << '//E*O*F src/tpscript/sfont2.c//'
/*
 * sfont2.c
 * routines for manipulating special font 2 (the characters generated
 * specifically to match troff requirements - because kludging the available
 * bracket chars could never be made to work completely properly)
 */

#include	"tpscript.h"

#include	"sfont2defs.h"

struct spcdefs {
	short	s2_index;	/* index in encoding structure == value in width tables */
	short	s2_width;	/* width of char as in width tables */
	char	*s2_name;	/* troff 2(?)-char name */
	char	*s2_def;	/* the postscript definition to build the char */
};

		/*
		 * if chars are wider than this then you cannot use the
		 * default C.setC procedure for calling setcachedevice
		 * This comes about because, while I could just call
		 * setcachedevice with the values in FontBBox, I assume that
		 * I will save caching memory if i dont use a value which is
		 * twice as big as I really need for most chars.
		 * ... But do I really save anything since the definition is
		 * now a bit bigger? - probably I do, since the space defined
		 * by the bounding box at 10 pt is about 2400 pixels
		 * (~ 300 bytes)
		 */
#define	DFLT_CACHE_WIDTH	50

	/*
	 * note - I really should look up the width in the width tables and
	 * just use that but when we run s2init() we havent read in the
	 * tables yet - and while about it i should probably read in the
	 * index value as well.
	 */

typedef	struct	spcdefs	S2DEF;

S2DEF	s2defs[]  = {
	{ 0101, 50, "bv", "C.bv" },
	{ 0102, 50, "lt", DEF_lt },
	{ 0103, 50, "lk", DEF_lk },
	{ 0104, 50, "lb", DEF_lb },
	{ 0105, 50, "rt", DEF_rt },
	{ 0106, 50, "rk", DEF_rk },
	{ 0107, 50, "rb", DEF_rb },
	{ 0110, 50, "lc", DEF_lc },
	{ 0111, 50, "lf", DEF_lf },
	{ 0112, 50, "rc", DEF_rc },
	{ 0113, 50, "rf", DEF_rf },
	{ 0114, 0, "br",  DEF_br },
	{ 0115, 50, "rn", DEF_rn },
	{ 0116, 100, "ci", DEF_ci },
	{ 0117, 17,  "||", DEF_sp_6 },
	{ 0120, 8,   "^^", DEF_sp_12 },
	{ 0121, 80,  "r1", DEF_r1 },
	{ 0122, 80,  "r2", DEF_r2 },
	{ 0, 0,    "",   "" }
};
#define	NUM_S2DEFS	(sizeof s2defs/sizeof(S2DEF) -1)

	/*
	 * bracket font initialisation
	 */
char	*bf_0init[] = {
	"/BracketFontDict 9 dict def /$workingdict 10 dict def",
	"BracketFontDict begin",
		/* locally defined font */
	"/FontType 3 def",
		/* give it a name like the built-in fonts */
	"/FontName (Bracket) cvn def",
		/* the standard matrix for font definitions */
	"/FontMatrix [ 0.001 0 0 0.001 0 0] def",
	"/FontBBox [ -50 -250 1000 1000 ] def",
		/* will be a sparse array so fill initally with .notdef */
	"/Encoding 256 array def 0 1 255 { Encoding exch /.notdef put } for",
	"Encoding",
	(char *) 0 };
		/* at this point we put the real encoding and the procedures for
		* building the chars
		*/

		/*
		 * predefined common routines for inclusion in CharProcs
		 */
char	*bf_CPinit[] = {
			/*
			 * define setcachedevice with default width passed
			 * as argument (on stack)
			 */
	"/setC { 0 -50 -250 500 1000 setcachedevice} def",
			/*
			 * common stuff for solid vert bar (\(bv)
			 * also used for floor and ceilings
			 */
	"/C.bv {220 -250 moveto 0 1000 rlineto",
		"60 0 rlineto 0 -1000 rlineto fill } def",
			/*
			 * stuff for horiz bar for ceiling
			 * X coord passed as arg
			 */
	"/C.barc { 750 moveto 180 0 rlineto 0 -60 rlineto -180 0 rlineto fill } def",
			/*
			 * stuff for horiz bar for floor
			 */
	"/C.barf { -250 moveto 180 0 rlineto 0 60 rlineto -180 0 rlineto fill } def",
			/*
			 * common routine for drawing the end parts of brackets
			 * e.g. \(lt, \(lb
			 * start with starting x,y on stack
			 * draw line and then curve to tip
			 */
	"/C.brk.end { 1 setlinewidth moveto rlineto rcurveto",
			/* back to start, over by width of line */
			/* draw line and then curve to near tip */
		"reversepath 60 0 rlineto rlineto rcurveto fill } def",

			/*
			 * set the linewidth rounded to the nearest pixel
			 * We need a dummy y entry as well but ignore it.
			 * The reason for using the X rounded value is only
			 * relevant when ( x-scale != y-scale ):
			 * since troff keeps char width constant and scales
			 * the height up/down, we also scale relative to the
			 * width.
			 */
	"/C.setl {dup dtransform exch round exch idtransform pop setlinewidth } def",

	(char *) 0 };

#define	N_CP_PROCS	6	/* number of extra common procs */

		/*
		 * the mandatory BuildChar routine for the font description
		 */
char	*bf_1init[] = {
	"/BuildChar",
	"{",
	"	$workingdict begin",
	"	/charcode exch def",
	"	/fontdict exch def",
	"	fontdict /CharProcs get begin",
	"	fontdict /Encoding get",
	"	charcode get load",
	"	gsave",
	"	0 setlinecap 0 setgray newpath",
	"	exec",
	"	grestore",
	"	end end",
	"} def end",
	"/BracketFont BracketFontDict definefont pop",
	(char *) 0 };

		/*
		 * setup the definitions for building our own special font
		 * for the characters that the laserwriter does not do
		 * properly
		 */
s2init()
{
	register	char	**ptab;
	register	S2DEF	*s2p;

	ptab = bf_0init;
	while(*ptab)
		fprintf(postr, "%s\n", *ptab++);
			/*
			 * now put the entries in the encoding table
			 */
	for ( s2p = &s2defs[0] ; s2p->s2_index != 0 ; s2p++ )
		fprintf( postr, "dup %d /C%s put\n",
				s2p->s2_index, s2p->s2_name);
			/*
			 * define the CharProcs dictionary which contains
			 * the procedures required by BuildChar to construct
			 * the characters
			 */
	fprintf( postr, "pop\n/CharProcs %d dict dup begin\n",
			NUM_S2DEFS + N_CP_PROCS );

	ptab = bf_CPinit;
	while(*ptab)
		fprintf(postr, "%s\n", *ptab++);

	for ( s2p = &s2defs[0] ; s2p->s2_index != 0 ; s2p++ )
	{
		if ( s2p->s2_width == DFLT_CACHE_WIDTH )
			fprintf( postr, "/C%s {\n%d setC\n%s\n} def\n",
				s2p->s2_name,
				(int)(s2p->s2_width * respunits),
					/* this is unwise because it assumes
					 * unitwidth 10 in DESC file
					 */
				s2p->s2_def);
		else
			fprintf( postr,
				"/C%s {\n%d 0 -50 -250 %d 1000 setcachedevice\n%s\n} def\n",
				s2p->s2_name,
				(int)(s2p->s2_width * respunits),
				(int)(s2p->s2_width * respunits),
				s2p->s2_def);
	}
	fputs( "end def\n", postr);
	ptab = bf_1init;
	while(*ptab)
		fprintf(postr, "%s\n", *ptab++);
}

//E*O*F src/tpscript/sfont2.c//

echo x - src/tpscript/spline.c
cat > "src/tpscript/spline.c" << '//E*O*F src/tpscript/spline.c//'
/*
 * spline.c
 * routine for converting troff's idea of splines into
 * the form that the postscript device can easily construct.
 * This means that the actual curves are not precisely those that would
 * be drawn on an other machine, but at least they fit the general
 * description and intention (see Pic manual, section 8).
 * The technique used here takes the starting point, the mid-points
 * of all intermediate lines, and the end point and uses these to construct
 * bezier spline fragments
 * If there are only two defining lines, i.e. two coordinate pairs, we have the
 * minimun requirements and use rcurveto from currentpoint to endpoint with
 * interpolation of the two midpoints of the lines as postcript's control
 * points.
 * With more than this we use several rcurveto instructions; the first and last
 * go from each end to the midpoint of the line second from each end, using
 * the midpoint and other end of the respective end lines as control points.
 * All other segments go from one midpoint to the next using the point where
 * the lines join for both control points
 *
 * It should be possible to get closer approximation by suitably chosing
 * control points at some other fraction of the way along the line,
 * rather than just taking mid- and endpoints. For example, the end
 * sections of multiple curves are not actually tangent at the mid-point
 * at the moment and are a bit lopsided.
 *
 */

#include	"tpscript.h"

#include	<ctype.h>

#define		MAXLINE		512		/* max len of D~ input line */
#define		MAXCOORD	50		/* max num x,y pairs */

		/*
		 * coordinates of each defining point in units, relative
		 * to the starting point
		 * the rounding errors are not cumulative so I don't think
		 * it needs to be floating point
		 */
typedef	struct	{
	int	x,
		y;
} COORD;

static	char	splineformat[] = "\n%d %d %d %d %d %d spln";

draw_spline( istr )
	FILE	*istr;
{
	register	int	nnums;
	register	char	*s;
	register	COORD	*cp;
	char	buf[MAXLINE];
	COORD	coord[MAXCOORD],
		current,
		next;

	fgets(buf, MAXLINE, istr);
	s = buf;
	cp = &coord[0];
		/*
		 * scan the input line for an indeterminate number of dx,dy
		 * pairs - some compilers cark if given sscanf with lots of
		 * args
		 */
	for( ; s < &buf[MAXLINE] && cp < &coord[MAXCOORD] ; cp++ )
	{
		while ( isspace( *s ) )
			s++;
		if ( *s == '\0' )
			break;		/* done */
		cp->x = atoi( s );
		while ( ! isspace( *s ) )
			s++;
		while ( isspace( *s ) )
			s++;
		if ( *s == '\0' )
			break;		/* done */
		cp->y = atoi( s );
		while ( ! isspace( *s ) )
			s++;
	}
	nnums = cp - &coord[0];
		/*
		 * now we go through and change all coordinate references to
		 * be relative to the starting point, rather than relative to
		 * each previous point - this assists conversion to postscript
		 * where each coordinate to an rcurveto is relative to the
		 * starting point (of each rcurveto)
		 * There is a bug in pic which only returns negatively
		 * one unit less than it went forward - this may lead to
		 * accumulating errors in a hideously complicated spline
		 * I dont want to *fix* pic in case that is only a fix(ughh)
		 * for a bug elsewhere
		 */
	for ( cp = &coord[1] ; cp < &coord[nnums] ; cp++ )
	{
		if ( cp->x < 0 )
			cp->x--;
		cp->x += cp[-1].x;
		if ( cp->y < 0 )
			cp->y--;
		cp->y += cp[-1].y;
	}

#ifdef	debug
	fprintf( stderr, "spline with %d coords to ", nnums );
	for ( cp = &coord[0] ; cp < &coord[nnums] ; cp++ )
		fprintf( stderr, "%d %d, ", cp->x, cp->y );
	putc( '\n', stderr);
#endif

	current.x = 0;
	current.y = 0;
		/*
		 * note: the starting point is always relative 0,0, and coord[0]
		 * is the other end of the first line
		 */
	cp = &coord[0];
	if ( nnums < 2 )
	{
		error( ERR_WARN, "Too few points in spline" );
		return;
	}
	if ( nnums == 2 )
	{
			/*
			 * remember: the positive y direction in troff is
			 * negative in postscript
			 */
		fprintf
		(
			postr, splineformat,
			cp->x/2, -(cp->y/2),
			(cp->x + coord[1].x)/2, -(cp->y + coord[1].y)/2,
			coord[1].x, -(coord[1].y)
		);
		return;
	}
		/*
		 * next current point will be middle of second line
		 */
	current.x = (cp->x + coord[1].x) / 2;
	current.y = (cp->y + coord[1].y) / 2;
	fprintf
	(
		postr, splineformat,
		cp->x/2, -(cp->y/2),
		cp->x, -(cp->y),
		current.x, -current.y
	);
	cp++;
	while( --nnums > 2 )
	{
			/*
			 * this defines the end of this curve section and the
			 * start of the following section ( relative to
			 * the starting point)
			 */
		next.x = (cp->x + cp[1].x) / 2;
		next.y = (cp->y + cp[1].y) / 2;
			/*
			 * note that each rcurveto must be done relative to
			 * the new currentpoint, thus the input relative
			 * values need to be adjusted
			 */
		fprintf
		(
			postr, splineformat,
			cp->x - current.x, current.y - cp->y,
			cp->x - current.x, current.y - cp->y,
			next.x - current.x, current.y - next.y
		);
		cp++;
		current = next;		/* set new current point */
	}

		/*
		 * finally we draw the last section
		 */
	fprintf
	(
		postr, splineformat,
		cp->x - current.x, current.y - cp->y,
		(cp->x + cp[1].x)/2 - current.x,
		current.y - (cp->y + cp[1].y)/2,
		cp[1].x - current.x, current.y - cp[1].y
	);
	return;
}

//E*O*F src/tpscript/spline.c//

echo x - src/tpscript/stringdefs.c
cat > "src/tpscript/stringdefs.c" << '//E*O*F src/tpscript/stringdefs.c//'
#include	"hash.h"

#include		"stringdefs.h"

SPECIAL_NAME	specnames[] = {
	{"ru",		DEF_ru,		0, }, 
	{"12",		DEF_12,		SN_FRACTION, },
	{"14",		DEF_14,		SN_FRACTION, },
	{"34",		DEF_34,		SN_FRACTION, },
	{"13",		DEF_13,		SN_FRACTION, },
	{"18",		DEF_18,		SN_FRACTION, },
	{"23",		DEF_23,		SN_FRACTION, },
	{"38",		DEF_38,		SN_FRACTION, },
	{"58",		DEF_58,		SN_FRACTION, },
	{"78",		DEF_78,		SN_FRACTION, },
	{"sr",		DEF_sr,		0, },
	{"sq",		DEF_sq,		0, },
	{"ff",		DEF_ff,		0 },
	{"Fi",		DEF_Fi,		0 },
	{"Fl",		DEF_Fl,		0 },
	{ (char *)0,	(char *)0,	0}
};

SPECIAL_NAME	multdefs[] =
{
	{"fract",	DEF_fract,	SN_FRACTION },
	{ (char *)0,	(char *)0,	0}
};
//E*O*F src/tpscript/stringdefs.c//

echo x - src/tpscript/tpscript.c
cat > "src/tpscript/tpscript.c" << '//E*O*F src/tpscript/tpscript.c//'
/*
 *	tpscript.c
 *	Troff post-processor for postscript devices
 *
 *	Original program by Stephen Frede (stephenf@elecvax.oz)
 *		Dept. Comp. Sci., University of NSW, Sydney, Australia.
 *					...!seismo!munnari!elecvax!stephenf
 *
 *	Extensive modifications by Cameron Davidson (probe@mm730.uq.oz)
 *				University of Queensland, Brisbane, Australia
 *
 *	Other changes by Michael Rourke (michaelr@elecvax.oz) UNSW.
 */

/* NOTES:
 *
 * Originally, changes to a new font would not take effect until
 * characters from that font were required to be printed, but this
 * means that commands passed through to postscript directly (via \!!)
 * may end up with the wrong font. So now font changes actually happen
 * when requested (or needed in the case of the special font).
 *
 */

/*	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.
 *			( this is an optimisation that shrinks output file
 *			size by about 35% and run-time by about 15% while
 *			preserving ascii-ness)
 *	w		paddable word space - no action needed
 *	nb a		end of line ( information only - no action needed )
 *			b = space before line, a = space after line
 *	pn		begin page n
 *	in		stipple as no. from 1 to n (BERK).
 *	P		spread ends -- output it (put in by rsort) (BERK).
 *	# ...\n		comment - ignore.
 *	! ...\n		pass through uninterpreted (LOCAL MOD).
 *	Dt ...\n	draw operation 't':
 *
 *		Dl dx dy		line from here to dx, dy
 *		Dc d		circle of diameter d, left side here
 *		De x y		ellipse of axes diameter x,y, left side here
 *		Da dx1 dy1 dx2 dy2	arc counter-clockwise, start here,
 *					centre is dx1, dy1 (relative to start),
 *					end is dx2, dy2 (relative to centre).
 *		D~ x y x y ...	wiggly line (spline) by x,y then x,y ...
 *		Dt d		set line thickness to d pixels (BERK).
 *		Ds d		set line style mask to d (BERK).
 *		Dg x y x y ...	gremlin (BERK).
 *
 *	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
 *		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"
 *
 *	Commands marked "BERK" are berzerkeley extensions.
 *
 */

#include	"tpscript.h"

#define	FONTDIR	"/usr/lib/font"		/* where font directories live */

FILE	*Debug = NULL;		/* debugging stream if non-null */
char	*fontdir = FONTDIR;	/* where the fonts live */
char	*ifile = 0;		/* current input file name */
int	lineno,			/* line no. in current input file */
	npages = 0;			/* no. pages printed so far */
char	device[100],		/* device name, eg "alw" */
	errbuf[100];		/* tmp buffer for error messages */
int	hpos = 0,		/* current horizontal position */
	vpos = 0;		/* current vertical position (rel. TOP pg.) */
int	res,			/* resolution in THINGS/inch */
	hor_res,		/* min horizontal movement (in THINGS) */
	vert_res,		/* min vertical movement (in THINGS) */
	respunits;
float	rotation = 0;		/* page orientation (degrees) */
int	currtfont = DEF_FONT,	/* current font number selected by troff */
	papertype = 		/* paper type (different imageable regions) */
#ifdef	ALW
		PT_A4;
#else
		PT_DEFAULT;
#endif
bool	manualfeed = FALSE;	/* normally auto-feed */

/* due to an obscure bug in ditroff, sometimes no initial 'p' command
 * is generated, so we have to remember if any output has happened
 * to decide if a 'p' causes a page print or not.
 */
bool	firstpage = TRUE;	/* nothing yet printed anywhere */

/* font parameters */
struct	fontparam 
	tfp,		/* current troff font parameters */
	pfp;		/* current postscript font parameters */


/* table of font descriptions */
struct fontdesc 
	*fontd = NOFONTDESC,
	*spcfnt1 = NOFONTDESC,	/* special font */
	*spcfnt2 = NOFONTDESC;	/* special font 2 */

/* font mount table - array of pointers to font descriptions */
struct fontdesc	**fontmount;

/* mapping between troff font names and builtin font names
 * This should go in the internal name part of the font description
 * itself, but there is only 10 bytes allocated (see dev.h).
 */
struct fontmap  fontmap[] = {
	{ "R", "Times-Roman" },
	{ "I", "Times-Italic" },
	{ "B", "Times-Bold" },
	{ "BI", "Times-BoldItalic" },
	{ "S", "Symbol" },
	{ "S2", "BracketFont" },	/* locally defined special font */
	{ "C", "Courier" },
	{ "CW", "Courier" },		/* synonym: constant width */
	{ "CB", "Courier-Bold" },
	{ "CO", "Courier-Oblique" },
	{ "CX", "Courier-BoldOblique" },
	{ "H", "Helvetica" },
	{ "HR", "Helvetica" },		/* two-char name for H */
	{ "HB", "Helvetica-Bold" },
	{ "HO", "Helvetica-Oblique" },
	{ "HX", "Helvetica-BoldOblique" },
	{ (char *)0,	(char *)0 }
};

struct dev	dev;

short	*chartab = NULL;	/* char's index in charname array */
char	*charname = NULL;	/* special character names */
int	ncharname;		/* no. special character names */
int	nfonts = 0;		/* no. of fonts mounted */
int	nfontmount;		/* no. of font mount positions */

	/*
	 * this is the width that the printer will have moved following
	 * the last printed character, if troff then says to move a
	 * different amount we will shift the difference
	 */
int	width_pending	= 0;

bool	word_started	= FALSE;	/* we are in middle of word string */


int		strcmp();
char		*emalloc();
struct fontdesc *findfont();
struct fontmap	*getfmap();

main(argc, argv)
int		argc;
register char	**argv;
{
	register FILE	*istr;
	int		status = 0;
	extern 	double	atof();
#ifdef SPACING
	float		spacing;
#endif SPACING

	strcpy(device, DEF_DEV); /* just in case we get a "Di" before a "DT" */
	argv++;
	while(*argv && **argv == '-')
	{
		char	c;

		(*argv)++;	/* skip the '-' */
		c = **argv;
		(*argv)++;	/* skip the character */
		switch(c)
		{
			case 'D':	/* debug */
				Debug = stderr;
				break;

#ifdef SPACING
			case 'h':
				spacing = atof(*argv);
				break;
#endif SPACING
			case 'r':	/* rotate */
				if(**argv == '\0')
					rotation = 90.0;
				else
					rotation = atof(*argv);
				break;

			case 'S':	/* manual feed */
				manualfeed = TRUE;
				break;

			case 'L':	/* legal paper type */
				papertype = PT_LEGAL;
				break;

			case 't':
				postr = stdout;
				break;

			default:
				break;
		}
		argv++;
	}

	if (postr == NULL)
	{
#ifdef	GRIS
		postr = popen("exec sendfile -AC -aprinter -dbasser -ugris -e\"-R -qd\" -ntroff-alw", "w");
		if (postr == NULL)
			error(ERR_SNARK, "can't popen spooler");
#else	GRIS
		postr = stdout;
#endif	GRIS
	}

	if(! *argv)
	{
		ifile = "stdin";
		process(stdin);
	}
	else while(*argv)
	{
		if((istr=fopen(*argv, "r")) == NULL)
		{
			perror(*argv);
			status++;
		}
		else
		{
			ifile = *argv;
			process(istr);
			fclose(istr);
		}
		argv++;
	}
	if (postr != stdout)
		status += pclose(postr);
	exit(status);
	/* NOTREACHED */
}

process(istr)
FILE	*istr;
{
	int	ch;
	char	str[50];
	int	n;
	register int	i;

	lineno = 1;	/* start processing 1st input line */

	while((ch=getc(istr)) != EOF)
	{
			/*
			 * the first switch group can safely be scanned without
			 * having to first ensure the horizontal position is
			 * up to date.
			 */
		switch(ch)
		{
			/* noise */
			case ' ':
			case '\0':
				continue;

			case '\n':
				lineno++;
				continue;

			case '0': case '1': case '2': case '3': case '4':
			case '5': case '6': case '7': case '8': case '9':
				ungetc(ch, istr);
				fscanf(istr, "%2d", &n);

				width_pending -= n;
				hpos += n;

				/* drop through to process the next char */

			case 'c':	/* ascii character */

					/*
					 * if this char and preceeding were
					 * not simply successive chars in the
					 * same word then we need some
					 * horizontal motion to reset position
					 */
				if ( width_pending != 0 )
					hgoto( );

				ch = getc(istr);

				width_pending += GETWIDTH( tfp.fp_font,
					(i = tfp.fp_font->f_fitab[ch - NUNPRINT] ));

				if(ch != ' ')
					putch(tfp.fp_font->f_codetab[i] & BMASK);
				else
					putch(' ');	/* no code for ' ' */
				continue;

			case 'C':	/* troff character */

				if ( width_pending != 0 )
					hgoto( );

				fscanf(istr, "%s", str);
				putspec(str);
				continue;

			case 'h':	/* relative horizontal movement */
				fscanf(istr, "%d", &n);

				/*
				 * we continually accumulate horizontal
				 * motions and all relative requests are
				 * translated into absolute ones.
				 * This avoids accumulation of character
				 * width rounding errors
				 * beyond a single word. (These errors arise
				 * because troff requires widths to be
				 * integral to the unit resolution whereas in
				 * the printer they may be fractional).
				 */

				hpos += n;
				if ( ( width_pending -= n ) != 0 )
					hgoto( );	/* most likely end of word */

				continue;

			case 'w':
				firstpage = FALSE;
				CLOSEWORD();
				continue;

			case 'n':	/* newline */
				fscanf(istr, "%*f %*f");
				width_pending = 0;	/* doesn't matter now */
				continue;

			case 'f':	/* select font no. */
				fscanf(istr, "%d", &n);
				if(n > nfonts || n < 0 || fontmount[n] == NULL)
				{
					sprintf(errbuf, "ERROR: font %d not mounted",
						n);
					error(ERR_WARN, errbuf);
				}
				else
				{
					tfp.fp_font = fontmount[n];
					currtfont = n;
				}
				continue;

			case 's':	/* size in points */
				fscanf(istr, "%d", &n);
				if(n <= 0)
				{
					sprintf(errbuf, "Illegal point size %d\n", n);
					error(ERR_WARN, errbuf);
				}
				else
				{
					tfp.fp_size = n;
					tfp.fp_height = (float) n;
				}
				continue;

			case 'H':	/* absolute horizontal position */

				fscanf(istr, "%d", &hpos);
				hgoto();
				continue;

			case 'V':	/* absolute vertical position */
				fscanf(istr, "%d", &vpos);
				vgoto();
				continue;

			case 'v':	/* relative vertical movement */
				fscanf(istr, "%d", &n);
				vmot(n);
				continue;

		}
			/*
			 * If the input char is in the second group
			 * then we must make sure the printer is positioned
			 * where troff thinks it is
			 * and close any word currently being printed
			 */
		if ( width_pending != 0 )
			hgoto( );
		else
			CLOSEWORD();

		switch(ch)
		{
			case 'x':	/* device control function */
				devcntrl(istr);
				break;

			case 'D':	/* draw */
				draw(istr);
				break;

			case 'p':	/* new page */
				fscanf(istr, "%d", &n);
				page(n);
				break;

			case '#':	/* comment */
				while((ch=getc(istr)) != '\n' && ch != EOF);
				lineno++;
				break;

			case 't':	/* text */
				text(istr);
				break;

# ifdef HASH
			/*
			 * debug - to be manually inserted in input stream if needed
			 * if n >= 0 && n <= HASH_SIZE
			 *	then will print entire hash contents
			 * otherwise will dump just names in hash_tab[n] entry
			 */
			case 'Z':
				fscanf(istr, "%d", &n);
				dumphash( n );
				break;
				
# endif

			case '!':	/* pass through uninterpreted */
				setfont(FALSE);	/* ensure current font is set */
				putc('\n', postr);
				while((ch=getc(istr)) != '\n' && ch != EOF)
					putc(ch, postr);
				break;

			default:
				sprintf(errbuf, "Unknown command '%c'", ch);
				error(ERR_FATAL, errbuf);
		}
	}
}

devcntrl(istr)
FILE	*istr;
{
	char		str[50];
	int		fontn,
			ch;
	float		f;

	fscanf(istr, "%s", str);
	switch(*str)
	{
		case 'i':	/* device initialisation */
			initfonts(device);
			devinit();
			break;

		case 'T':	/* we had better get this before an 'init' */
			fscanf(istr, "%s", device);
			break;

		case 'r':	/* resolution */
			fscanf(istr, "%d %d %d", &res, &hor_res, &vert_res);
			respunits = res / PU_INCH;
			break;

		case 'f':	/* load font */
			fscanf(istr, "%d %s", &fontn, str);
			loadfont(str, fontn);
			break;

		case 's':	/* stop */
			finish(0);
			break;

		case 'p':	/* pause */
			break;

		case 't':	/* trailer */
			break;

		case 'H':	/* character height (in points) */
			fscanf(istr, "%f", &f);
			if(f <= 0 || f > 1000)
			{
				sprintf(errbuf,
					"Illegal character height %.1f", f);
				error(ERR_WARN, errbuf);
			}
			else
				tfp.fp_height = f;
			break;

		case 'S':
			fscanf(istr, "%f", &f);
			if(f < -80 || f > 80)
			{
				sprintf(errbuf, "Illegal character slant %.1f degrees", f);
				error(ERR_WARN, errbuf);
			}
			else
				tfp.fp_slant = f;
			break;

		default:
			sprintf(errbuf, "Unknown device control '%s'", str);
			error(ERR_WARN, errbuf);
			break;
	}
	while((ch=getc(istr)) != '\n' && ch != EOF);	/* skip rest of input line */
	lineno++;
}

error(errtype, errmsg)
int	errtype;
char	*errmsg;
{
	switch(errtype)
	{
		case ERR_WARN:
			fprintf(stderr, "Warning");
			break;

		case ERR_FATAL:
			fprintf(stderr, "Error");
			break;

		case ERR_SNARK:
			fprintf(stderr, "Snark");
			break;
	}
	fprintf(stderr, "\t%s pscript input, line %d of '%s'\n",
		errtype == ERR_SNARK ? "at" : "in",
		lineno, ifile);
	if(errmsg && *errmsg)
		fprintf(stderr, "\t%s\n", errmsg);
	if(errtype != ERR_WARN)
		finish(1);
}

finish(status)
int	status;
{
	page(-1);
	pcommfinish(npages, "");
	if(status != 0)
		fprintf(stderr, "\t... aborted processing\n");
	exit(status);
}

/*
 *	Output the postscript "prologue" that is the start of each program
 *	generated. This sets up definitions, sets the scale to be troff
 *	units, etc.
 *	By convention, single character variables are procedure names,
 *	while multi-character variables are local to procedures.
 */

char	*inittab[] = {
	/* initialise current path to non-null */
	"0 0 moveto",
	/* fix to make "joined" lines better */
	"2 setlinecap",
	/* routine for RELATIVE HORIZONTAL RIGHT */
	/* need no more
	"/x { 0 rmoveto } def",
	/* routine for RELATIVE VERTICAL DOWN */
	"/y { neg 0 exch rmoveto } def",
	/* routine for ABSOLUTE HORIZONTAL (rel left edge page) */
	"/X { currentpoint exch pop moveto } def",
	/* routine for ABSOLUTE VERTICAL (rel top of page) */
	"/Y { pgtop exch sub currentpoint pop exch moveto } def",
#ifdef	SPACING
	"/s { currentpoint spacing 0 5 -1 roll ashow moveto } def",
#else
	"/s { show } def",
#endif	SPACING
	"/l { neg rlineto currentpoint stroke moveto } def",
	/* circle - arg is diameter.
	 * Current point is left edge
	 */
	"/c {",
	/* save radius and current position */
	"2 div /rad exch def currentpoint /y0 exch def /x0 exch def",
	/* draw circle */
	"newpath x0 rad add y0 rad 0 360 arc stroke",
	/* move to right edge of circle */
	"x0 rad add rad add y0 moveto",
	" } def",
	/* Arc anticlockwise, currentpoint is start;
	 * args are dx1, dy1 (centre relative to here)
	 * and dx2, dy2 (end relative to centre).
	 */
	"/a {",
	/* save all parameters */
	"/y2 exch neg def /x2 exch def /y1 exch neg def /x1 exch def",
	/* move to centre, push position for moveto after arc */
	"x1 y1 rmoveto currentpoint",
	/* push centre for args to arc */
	"currentpoint",
	/* calculate and push radius */
	"x2 x2 mul y2 y2 mul add sqrt",
	/* start angle */
	"y1 neg x1 neg atan",
	/* end angle */
	"y2 x2 atan",
	/* draw the arc, and move to end position */
	"newpath arc stroke moveto x2 y2 rmoveto",
	"} def",
	/* ellipse - args are x diameter, y diameter;
	 * current position is left edge
	 */
	"/e {",
	/* save x and y radius */
	"2 div /yrad exch def 2 div /xrad exch def",
	/* save current position */
	"currentpoint /y0 exch def /x0 exch def",
	/* translate to centre of ellipse */
	"x0 xrad add y0 translate",
	/* scale coordinate system */
	"xrad yrad scale",
	/* draw the ellipse (unit circle in scaled system) */
	"newpath 0 0 1 0 360 arc",
	/* restore old scale + origin */
	"savematrix setmatrix",
	/* actually draw the ellipse (with unscaled linewidth) */
	"stroke",
	/* move to right of ellipse */
	"x0 xrad add xrad add y0 moveto",
	"} def",
		/*
		 * common procedure for spline curves
		 */
	"/spln {",
		/* setup curve, remember where we are, fill in line,
		** and reset current point
		*/
	"rcurveto currentpoint stroke moveto",
	"} def",
	/* routine to select a font */
	"/ft { /fonttype exch def /xsiz exch def /ysiz exch def /sl exch def",
	" fonttype [ xsiz pt 0 sl sin sl cos div ysiz pt mul ysiz pt 0 0 ]",
	" makefont setfont",
	/* point size also affects linewidth (see Pic user manual, p. 17) */
	" xsiz 1.7 div setlinewidth } def",
	(char *) 0 };


devinit()
{
	register char	**ptab;
	register int	i;

	/* postscript basic units are "1/PU_INCH" inches.
	 * Normally PU_INCH=72, making postscript units points (1/72 inch)
	 * Scale postscript to accept whatever resolution we are given
	 * Typically res=300 for a 300 dot/inch laser printer
	 */
	pcomminit(PU_INCH / (float) res, rotation, papertype, manualfeed, 0,
		(char *)0, "troff->tpscript");
	ptab = inittab;
	while(*ptab)
		fprintf(postr, "%s\n", *ptab++);
	/* conversion back to points for font sizes etc. */
	fprintf(postr, "/pt { %d mul } def\n", respunits);

#if	defined(UQMINMET) && !defined(ALW)
			/* to compensate for "setmargins" */
	fprintf( postr, "\n-90 230 translate\n" );
#endif
	/* All graphics transformations have been done. Save the
	 * transformation matrix
	 */
	fprintf(postr, "/savematrix matrix currentmatrix def\n");
#ifdef SPACING
	/* set increased character spacing (if any) */
	fprintf(postr, "/spacing %.1f pt def\n", spacing);
#endif SPACING

	s2init();	/* initialise special font 2 */

	/* set up font abbreviations */
	for(i=1; i<nfonts+1; i++)
		fprintf(postr, "/f.%s /%s findfont def\n",
			fontd[i].f_extname, fontd[i].f_intname);
	/* select default current font */
	tfp.fp_size = DEF_SIZE;
	tfp.fp_height = (float) DEF_SIZE;
	tfp.fp_slant = 0;
	tfp.fp_font = &fontd[DEF_FONT];
	pfp.fp_font = (struct fontdesc *) NULL;
	setfont(FALSE);

	/* save state */
	endinit();
}


/*
 *	Called when some use of characters or line-drawing
 *	is about to be made, to ensure that the correct font and
 *	line thickness is selected in postscript.
 */
setfont(force)
bool	force;
{

	if(tfp.fp_size == pfp.fp_size &&
		tfp.fp_height == pfp.fp_height &&
		tfp.fp_slant == pfp.fp_slant &&
		tfp.fp_font == pfp.fp_font &&
		! force)
		return;
	CLOSEWORD();
	fprintf(postr, "\n%.1f %.0f %d f.%s ft",
		tfp.fp_slant,
		tfp.fp_height, tfp.fp_size,
		tfp.fp_font->f_extname);
	pfp = tfp;
}

draw(istr)
FILE	*istr;
{
	int	ch;
	int	x, y,
		x1, y1,
		d;

	setfont( FALSE );	/* in case of size change affecting line thickness */

	switch(ch=getc(istr))
	{
		case 'l':
			fscanf(istr, "%d %d", &x, &y);
			fprintf(postr, "\n%d %d l", x, y);
			break;

		case 'c':
			fscanf(istr, "%d", &d);
			fprintf(postr, "\n%d c", d);
			break;

		case 'e':
			fscanf(istr, "%d %d", &x, &y);
			fprintf(postr, "\n%d %d e", x, y);
			break;

		case 'a':
			fscanf(istr, "%d %d %d %d", &x, &y, &x1, &y1);
			fprintf(postr, "\n%d %d %d %d a", x, y, x1, y1);
			break;

		case '~':
			draw_spline( istr );
			break;

		default:
			sprintf(errbuf, "Illegal draw function '%c'", ch);
			error(ERR_WARN, errbuf);
			break;
	}
	while((ch=getc(istr)) != '\n' && ch != EOF);
	lineno++;
}


text(istr)
FILE	*istr;
{
	register int	ch;

	fprintf(postr, "\n(");
	while((ch=getc(istr)) != '\n' && ch != EOF)
		pch(ch);
	fprintf(postr, ")s");
}

page(n)
register int	n;
{
	hpos = 0; vpos = 0;
	/* for each page except the first, print the previous one */
	if(firstpage)
		firstpage = FALSE;
	else
	{
		fprintf(postr, "\npage");
		setfont(TRUE);
		resetspcl();		/* it forgets definitions on next page */
	}
	if(n >= 0)		/* beginning of a new page */
		fprintf(postr, "\n%%%%Page: %d %d\n", n, ++npages);
}

hgoto()
{
	CLOSEWORD();
	width_pending = 0;	/* doesn't matter now */
	fprintf(postr, "\n%d X", hpos);
}

vgoto( )
{
	CLOSEWORD();
	fprintf(postr, "\n%d Y", vpos);
}

vmot(n)
int	n;	/* +'ve is DOWN */
{
	CLOSEWORD();
	fprintf(postr, "\n%d y", n);
	vpos += n;
}

/*
 *	Read the DESC file for the current device. This includes
 *	information about all the common fonts. The format is:
 *
 *		struct dev	(see dev.h)
 *		point size table	(dev.nsizes * sizeof(short))
 *		char index table	(chtab; dev.nchtab * sizeof(short))
 *		char name table		(chname; dev.lchname)
 *
 *	followed by dev.nfonts occurrences of	
 *		struct font	(see dev.h)
 *		width tables		(font.nwfont)
 *		kern tables		(font.nwfont)
 *		code tables		(font.nwfont)
 *		font index table	(dev.nchtab + NASCPRINT)
 */

initfonts(devname)
char	*devname;
{
	register int			i;
	register struct fontdesc	*fd;
	FILE				*fstr;
	char				path[100];

	sprintf(path, "%s/dev%s/DESC.out", fontdir, devname);
	if((fstr=fopen(path, "r")) == NULL)
	{
		sprintf(errbuf, "Can't open '%s' (%s)",
			path, sys_errlist[errno]);
		error(ERR_FATAL, errbuf);
	}
	if(efread((char *)&dev, sizeof(dev), 1, fstr) != 1)
	{
		sprintf(errbuf, "%s: bad format (read dev failed)", path);
		error(ERR_SNARK, errbuf);
	}

	nfonts = dev.nfonts;
	/* nfontmount should be at least nfonts+2 */
	nfontmount = nfonts + 20;
	ncharname = dev.nchtab;
	fontd = (struct fontdesc *)
			emalloc((unsigned)(nfonts+2) * sizeof(struct fontdesc));
	fontmount = (struct fontdesc **)
			emalloc((unsigned)nfontmount * sizeof(struct fontdesc *));

	/* skip point size table */
	efseek(fstr, (int)((dev.nsizes + 1)*sizeof(short)));

	chartab = (short *) emalloc((unsigned)ncharname * sizeof(short));
	efread((char *)chartab, sizeof(* chartab), ncharname, fstr);

	charname = emalloc((unsigned)dev.lchname);
	efread(charname, sizeof(* charname), dev.lchname, fstr);

	hash_init();

	for(i=1; i <= nfonts; i++)
	{
		register int			nw;
		struct font			f;
		struct fontmap			*fm;

		/* read struct font header */
		efread((char *)&f, sizeof(f), 1, fstr);

		nw = (int)(f.nwfont & BMASK);	/* NO sign extension */
		fd = &fontd[i];
		fd->f_nent = nw;

		fd->f_widthtab = emalloc((unsigned)nw);
		fd->f_codetab = emalloc((unsigned)nw);
		fd->f_fitab = emalloc((unsigned)(ncharname+NASCPRINT));
		/* remember if font is special */
		if(f.specfont == 1)
		{
			if(spcfnt1 == NOFONTDESC )
				spcfnt1 = fd;
			else if ( spcfnt2 == NOFONTDESC )
				spcfnt2 = fd;
			else
			{
				sprintf( errbuf,
					"Too many special fonts, %s ignored",
					fd->f_extname );
				error(ERR_WARN, errbuf );
			}
		}

		fm = getfmap(f.namefont);
		if(fm)
		{
			fd->f_intname = fm->fm_intname;
			fd->f_extname = fm->fm_extname;
			fd->f_mounted = TRUE;
		}
		else
			fprintf(stderr, "font name '%s' not known\n",
				f.namefont);

		efread(fd->f_widthtab, sizeof(char), nw, fstr);
		efseek(fstr, 1*nw);	/* skip kern tables */
		efread(fd->f_codetab, sizeof(char), nw, fstr);
		efread(fd->f_fitab, sizeof(char), ncharname+NASCPRINT, fstr);
	}

	fclose(fstr);

	for(i=0; i < nfontmount; i++)
		fontmount[i] = NOFONTDESC;

	/* zeroth font desc entry reserved for "extra" fonts */
	fd = &fontd[0];
	fd->f_intname = "";	/* not NULL */
	fd->f_extname = "";	/* not NULL */
	fd->f_codetab = emalloc((unsigned)MAXCHARS);
	fd->f_fitab = emalloc((unsigned)(ncharname+NASCPRINT));
	fd->f_nent = MAXCHARS;

	/* sentinel fontdesc entry */
	fd = &fontd[nfonts+1];
	fd->f_intname = (char *)NULL;
	fd->f_extname = (char *)NULL;
	fd->f_nent = 0;
	fd->f_codetab = (char *)NULL;
	fd->f_fitab = (char *)NULL;
}

loadfont(extname, fpos)
char	*extname;	/* troff font name */
int	fpos;		/* font position */
{
	register struct fontdesc	*font;

	if(fpos > nfontmount || fpos < 0)
	{
		sprintf(errbuf, "Illegal font mount position %d\n", fpos);
		error(ERR_WARN, errbuf);
		return;
	}
	if ( (font = findfont(extname)) == (struct fontdesc *) NULL )
	{
		sprintf(errbuf, "No such font '%s'\n", extname);
		error(ERR_WARN, errbuf);
		return;
	}
	fontmount[fpos] = font;
}

struct fontmap *
getfmap(extname)
char	*extname;
{
	struct fontmap	*fm;

	fm = fontmap;
	while(fm->fm_intname && strcmp(fm->fm_extname, extname) != 0)
		fm++;
	if(fm->fm_intname)
		return(fm);
	else
		return((struct fontmap *)NULL);
}

#ifndef	UQMINMET

struct fontdesc *
findfont(extname)
char	*extname;
{
	struct fontdesc	*fd;

	fd = fontd;
	while(fd->f_intname && strcmp(fd->f_extname, extname) != 0)
		fd++;
	if(fd->f_intname)
		return(fd);
	else
		return((struct fontdesc *)NULL);
}

#else	UQMINMET
		/*
		 * find font including from possible synonym
		 * - use internal name instead of troff name.
		 * troff names need not uniquely correspond to a given
		 * internal name
		 */
struct fontdesc *
findfont(extname)
char	*extname;
{
	struct fontmap	*fm;
	struct fontdesc	*fd;

	if ( (fm = getfmap( extname )) == (struct fontmap *)NULL )
		return((struct fontdesc *)NULL);
	fd = fontd;
	while(fd->f_intname && strcmp(fd->f_intname, fm->fm_intname) != 0)
		fd++;
	if(fd->f_intname)
		return(fd);
	else
		return((struct fontdesc *)NULL);
}
#endif UQMINMET

char *
emalloc(size)
unsigned size;
{
	char		*malloc();
	register char	*s;

	s = malloc(size);
	if(s == NULL)
	{
		fprintf(stderr, "Ran out of memory allocating %u bytes\n",
			size);
		finish(1);
	}
	return(s);
}

efread(buf, size, nel, istr)
char	*buf;
int	size,
	nel;
FILE	*istr;
{
	register int n;

	if((n=fread(buf, size, nel, istr)) != nel)
		fprintf(stderr, "Bad format font file\n");
	return(n);
}

efseek(istr, offset)
FILE	*istr;
int	offset;
{
	if(fseek(istr, (long)offset, 1) != 0)
		fprintf(stderr, "Snark: Bad seek on font file\n");
}


putch(ch)
int	ch;
{
	setfont(FALSE);	/* ensure correct font */

	if ( word_started == FALSE ) {
		word_started = TRUE;
		putc('(', postr);
	}
	pch(ch);
}
//E*O*F src/tpscript/tpscript.c//

echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
    223    786   4821 hash.c
    270    951   6355 pcom.c
    211   1023   6079 sfont2.c
    193    909   5159 spline.c
     28     90    650 stringdefs.c
   1116   3968  25878 tpscript.c
   2041   7727  48942 total
!!!
wc  src/tpscript/hash.c src/tpscript/pcom.c src/tpscript/sfont2.c src/tpscript/spline.c src/tpscript/stringdefs.c src/tpscript/tpscript.c | sed 's=[^ ]*/==' | diff -b $temp -
exit 0