Plan 9 from Bell Labs’s /usr/web/sources/extra/mpm/mpm.bundle

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


From cse.psu.edu!9fans-admin Thu Feb 14 12:53:21 EST 2002
# To unbundle, run this file
echo README
sed 's/.//' >README <<'//GO.SYSIN DD README'
-An experiment in page makeup for troff output...
-
--mpm is a version of standard -ms that causes extra
-information for vertical justification and figure
-placement to be included in troff output.  Commands that
-have been augmented to provide paddable space are
-
-	.SH and .NH
-	.PP and .LP	no space if \n(PD is 0; normally .nr PD 0.3v; leave at least 1u
-	.IP and .QP	also
-	.EQ and .EN
-	.TS and .TE	no space if \n(TS is 0; normally .nr TS 0.5v
-	.PS and .PE
-	.P1 and .P2	display programs in CW font
-	.DS and .DE
-	.QS and .QE
-
-Other commands, registers, strings, etc.:
-
-	.SP n		explicit paddable space, just like .sp n.
-			generally you should ALWAYS use .SP instead of .sp.
-			if you need exactly a given vertical space, you can say
-				.SP 3i exactly
-			this space won't be padded.
-	.Tm words	prints "words" and the output page number on stderr
-			sorry about the spelling; -ms pre-empted .TM
-	.NE n		like .ne.  note: does not cause a break
-
-			Others may be added as the need arises.
-
-	.nr FO n	Set the page length.  This value is the bottom of
-			the text on the page; a bottom title may lie below.
-			default is 10i (== 10 inches).
-	%o, %e		are strings containing odd and even page titles
-	%#		is the current page number (often useless)
-	.PT		is a macro invoked at the top of each "page";
-			it will normally use %e, %o and %#.  There is also
-			a .BT for page bottoms if desired.
-	.BP		force a page break
-	.FL		force all waiting figures out before any more running text
-	.1C, .2C	multiple columns;  number registers CW and GW set
-			the column and gutter width if you don't like the default.
-			absent a .FC command, all two-column contents collect
-			together on the page
-	.FC		freeze current two-column contents and start afresh.
-			necessary if you want to switch between 1 and 2 column
-			text and keep the relative order among them.
-
-Usage is some variant of
-
-	... | troff -mpm
-
-/usr/lib/tmac/pm is the page-justifier itself;  it is called automatically
-by the -mpm macro package.  If you are installing this yourself, you will
-have to edit the 2nd line of tmac.pm to arrange that pm is called directly
-from troff.
-
-There are several lines in tmac.pm that say
-	.so /n/coma/usr/bwk/...
-You should delete these;  they are placeholders for some experiments.
-
-If you use -mm, you are more or less out of luck, although we will be
-happy to provide a crude and incomplete program that purports to convert
--mm to -ms.  It may suggest what you need but it won't do the job.
-
-To compile pm, you need a C++ compiler, preferably release 2.0 or later.
-Put the .c and .h files in a directory, and type
-	make
-This process may well fail.  The usual cause is that different systems
-put function declarations in different header files, and C++ insists that
-all functions be properly declared.  You can almost always get through this
-part by adding function declarations.  The most likely offender is malloc;
-a line like
-	extern char *malloc(int);
-near the top of slug.c will solve this one.
-
-
-Bugs, etc.:
-
-	not all -ms commands have been decorated;  in particular,
-	the rich variety of document types (TM, CSTR, etc.,) is not
-	really supported.
-
-	there are problems with funny first pages and troff input
-	that moves back up the page.
-
-	multiple columns:  only .2C is available.  The program does not check
-	whether something is wide or narrow:  user has responsibility to mark
-	which with .1C or .2C.
-
-	headings are a bit tricky if you want things like
-	running titles that include the current section title.
-	normally a two-pass procedure using .Tm is needed.
-	
-	It's a pain to force a blank vertical space of specified height.
-	Try this:
-		.de x
-		\v'\\$1'\0\h'-\w'\0'u'\c
-		..
-		.x 2.5i
-
-
-If you want to roll your own, the following components are
-included in pm's "command language".  They are inserted in
-the troff output in the form of "x X ..." commands, which
-are created either by \X'...' or by the .X macro in -mpm.
-Look at how they are used in /usr/lib/tmac/tmac.pm for examples.
-
-
-BS n	breakable stream	n = min # lines that must appear on page
-				use:  PP, LP, IP, ...
-
-US	unbreakable stream	use:  KS/KE, DS/DE, TS/TE, EQ/EN, PS/PE, etc.
-
-BF v	breakable float		v = preferred vertical location of box center
-				use:  FS/FE
-				use two successive BF's to give two preferences
-
-UF v	unbreakable float	v = preferred vertical location of box center
-				use:  KF/KE
-				use two successive UF's to give two preferences
-
-PT	page title		use:  user has absolute control between PT and END
-				no SP's or other pm commands inside are processed
-
-BT	bottom title		use:  user has absolute control between BT and END
-
-END	end			end a US, BF, UF, PT, or BT
-				all constructs nest, but a float within another float
-				or a US block will not float within or outside the block
-
-NE n	need			break page if a VBOX of height n would not fit on page
-				use:  .NE n
-
-SP n	space			paddable space of n
-				use:  .SP n
-
-PARM NP v			top of pm text at v
-	new page
-
-PARM FO v			bottom of pm text at v
-	footer			length of text on page = FO-NP
-
-PARM PL v			physical page ends at v
-	page length		default = FO + NP
-
-PARM MF x			tolerance to prevent padding
-	minimum fullness	default = 0.9
-
-PARM CT x			tolerance for two-column operation
-	column tolerance	default = 0.5
-
-PARM DBG x			debugging flag
-
-TM str	message			.Tm words prints <pageno> <tab> <words> on stderr
-
-MC n o	multiple column		n columns, offset o.
-				Only 1 and 2 columns will work.
-
-CMD BP	break page		force page break
-
-CMD FL	flush			force all queued figures out before any more
-				stream material is output
-
-CMD FC	freeze columns		force out current two-column contents;
-				start a fresh one
-
-Something like this will probably have to be added:
-
-NC	new column		HARD!
-
-Known botches in the existing implementation of pm:
-
-If a footnote is split across two pages, any associated separator line
-will not be copied.  If there are multiple footnotes on one page, there
-will be multiple separators too.  -mpm's .FS macro does not provide a
-separator.  If you want a separator line, put it in explicitly with
-a call to the .FA macro.
-
-There are not enough settable parameters;  in particular, the
-way to control the height is a botch.
-
-
-Historical note:  There is a simpler version of pm and -mpm
-called pj and -mpj that only does vertical justification of
-pages that have already been laid out by conventional means.
-This simpler version may be adequate, but it is no longer
-supported and memory of how it works is growing dim.
//GO.SYSIN DD README
echo misc.c
sed 's/.//' >misc.c <<'//GO.SYSIN DD misc.c'
-#include	"misc.h"
-
-char	errbuf[200];
-char	*progname;
-int	wantwarn = 0;
-
-int	dbg	= 0;
-// dbg = 1 : dump slugs
-// dbg = 2 : dump ranges
-// dbg = 4 : report function entry
-// dbg = 8 : follow queue progress
-// dbg = 16: follow page fill progress
//GO.SYSIN DD misc.c
echo misc.h
sed 's/.//' >misc.h <<'//GO.SYSIN DD misc.h'
-#include	<stdio.h>
-#include	<stdlib.h>
-#include	<math.h>
-#include	<ctype.h>
-#include	<string.h>
-
-extern char	errbuf[];
-extern char	*progname;
-extern int	linenum;
-extern int	wantwarn;
-
-// #define	ERROR	fflush(stdout), fprintf(stderr, "%s: ", progname), fprintf(stderr,
-// #define	FATAL	), exit(1)
-// #define	WARNING	)
-
-#define	ERROR	fprintf(stdout, "\n#MESSAGE TO USER:  "), sprintf(errbuf,
-#define	FATAL	), fputs(errbuf, stdout), \
-		fprintf(stderr, "%s: ", progname), \
-		fputs(errbuf, stderr), \
-		fflush(stdout), \
-		exit(1)
-#define	WARNING	), fputs(errbuf, stdout), \
-		wantwarn ? \
-			fprintf(stderr, "%s: ", progname), \
-			fputs(errbuf, stderr) : 0, \
-		fflush(stdout)
-
-#define	eq(s,t)	(strcmp(s,t) == 0)
-
-inline	max(int x, int y)	{ return x > y ? x : y; }
-inline	min(int x, int y)	{ return x > y ? y : x; }
-inline	abs(int x)		{ return (x >= 0) ? x : -x; }
-
-extern int	dbg;
-
-extern int	pn, userpn;		// actual and user-defined page numbers
-extern int	pagetop, pagebot;	// printing margins
-extern int	physbot;		// physical bottom of the page
//GO.SYSIN DD misc.h
echo mkfile
sed 's/.//' >mkfile <<'//GO.SYSIN DD mkfile'
-</$objtype/mkfile
-
-TARG=aux/pm
-OFILES=misc.$O\
-	slug.$O\
-	range.$O\
-	queue.$O\
-	page.$O\
-
-HFILES=misc.h\
-
-BIN=/$objtype/bin
-</sys/src/cmd/mkone
-CC=c++/$CC
-LD=c++/$LD
-CFLAGS=
-
-slug.$O:	slug.h
-range.$O:	range.h slug.h
-queue.$O:	page.h range.h slug.h
-page.$O:	page.h range.h slug.h
-
-test:V:	$O.out
-	tryout $O.out
//GO.SYSIN DD mkfile
echo page.c
sed 's/.//' >page.c <<'//GO.SYSIN DD page.c'
-#include	"misc.h"
-#include	"slug.h"
-#include	"range.h"
-#include	"page.h"
-
-const	MAXRANGES	= 1000;
-static range *ptemp[MAXRANGES];		// for movefloats()
-
-static void swapright(int n)		// used by movefloats()
-{
-	range *t = ptemp[n];
-	ptemp[n] = ptemp[n+1];
-	ptemp[n+1] = t;
-	ptemp[n]->setaccum( ptemp[n+1]->accum() -
-			    ptemp[n+1]->rawht() + ptemp[n]->rawht() );
-	ptemp[n+1]->setaccum( ptemp[n]->accum() + ptemp[n+1]->rawht() );
-}
-
-// Figure out the goal position for each floating range on scratch,
-// and move it past stream ranges until it's as close to its goal as possible.
-static void movefloats(stream *scratch, double scale)
-{
-	const int Huge = 100000;
-	for (int nranges = 0; scratch->more(); scratch->advance())
-		ptemp[nranges++] = scratch->current();
-	scratch->freeall();
-	ufrange rtemp;
-	ptemp[nranges] = &rtemp;
-	rtemp.setgoal(Huge);
-	int accumV = 0;				// compute accum values and
-	for (int i = 0; i < nranges; i++) {	// pick closest goal for floats
-		ptemp[i]->pickgoal(accumV, scale);
-		ptemp[i]->setaccum(accumV += ptemp[i]->rawht());
-	}
-	int j;					// index for inner loop below:
-	for (i = nranges; --i >= 0; )		// stably sort floats to bottom
-		for (j = i; j < nranges; j++)
-			if (ptemp[j]->goal() > ptemp[j+1]->goal())
-				swapright(j);
-			else
-				break;
-	if (dbg & 16)
-		printf("#movefloats:  before floating, from bottom:\n");
-	for (i = nranges; --i >= 0; ) {		// find topmost float
-		if (ptemp[i]->goal() == NOGOAL)
-			break;
-		if (dbg & 16)
-			printf("# serialno %d goal %d height %d\n",
-				ptemp[i]->serialno(), ptemp[i]->goal(),
-				ptemp[i]->rawht());
-	}					// i+1 is topmost float
-	for (i++ ; i < nranges; i++)		// move each float up the page
-		for (j = i; j > 0; j--)		// as long as closer to its goal
-			if (ptemp[j]->goal()
-			  <= ptemp[j-1]->accum() + ptemp[j]->rawht()/2
-			  && ptemp[j-1]->goal() == NOGOAL)
-				swapright(j-1);
-			else
-				break;
-	if (ptemp[nranges] != &rtemp)
-		ERROR "goal sentinel has disappeared from movefloats" FATAL;
-	for (i = 0; i < nranges; i++)		// copy sorted list back
-		scratch->append(ptemp[i]);
-}
-
-// Traverse the leaves of a tree of ranges, filtering out only SP and VBOX.
-static range *filter(generator *g)
-{
-	range *r;
-	while (r = g->next())
-		if (r->isvbox() || r->issp())
-			break;
-	return r;
-}
-
-// Zero out leading and trailing spaces; coalesce adjacent SP's.
-static void trimspace(stream *scratch)
-{
-	range *r, *prevr = 0;
-	for (generator g = scratch; (r = filter(&g)) != 0 && r->issp(); prevr = r)
-		r->setheight(0);		// zap leading SP
-	for ( ; (r = filter(&g)) != 0; prevr = r)
-		if (r->issp())
-			if (prevr && prevr->issp()) {
-						// coalesce adjacent SPs
-				r->setheight(max(r->rawht(), prevr->height()));
-				prevr->setheight(0);
-			} else			// a VBOX intervened
-				r->setheight(r->rawht());
-	if (prevr && prevr->issp())		// zap *all* trailing space
-		prevr->setheight(0);		// (since it all coalesced
-						// into the last one)
-}
-
-// Pad the non-zero SP's in scratch so the total height is wantht.
-// Note that the SP values in scratch are not the raw values, and
-// indeed may already have been padded.
-static void justify(stream *scratch, int wantht)
-{
-	range *r;
-	int nsp = 0, hsp = 0;
-
-	int adjht = scratch->height();
-					// Find all the spaces.
-	for (generator g = scratch; r = g.next(); )
-		if (r->issp() && r->height() > 0) {
-			nsp++;
-			hsp += r->height();
-		}
-	int excess = wantht - adjht;
-	if (excess < 0)
-		ERROR "something on page %d is oversize by %d\n",
-			userpn, -excess WARNING;
-	if (dbg & 16)
-		printf("# justify %d: excess %d nsp %d hsp %d adjht %d\n",
-			userpn, excess, nsp, hsp, adjht);
-	if (excess <= 0 || nsp == 0)
-		return;
-					// Redistribute the excess space.
-	for (g = scratch; r = g.next(); )
-		if (r->issp() && r->height() > 0) {
-			int delta = (int) ((float)(r->height()*excess)/hsp + 0.5);
-			if (dbg & 16)
-				printf("# pad space %d by %d: hsp %d excess %d\n",
-					r->height(), delta, hsp, excess);
-			r->setheight(r->height() + delta);
-		}
-}
-
-// If r were added to s, would the height of the composed result be at most maxht?
-int wouldfit(range *r, stream *s, int maxht)
-{
-	if (r->rawht() + s->rawht() <= maxht)
-		return 1;		// the conservative test succeeded
-	stream scratch;			// local playground for costly test
-	for (stream cd = *s; cd.more(); cd.advance())
-		scratch.append(cd.current());
-	scratch.append(r);
-	movefloats(&scratch, ((double) scratch.rawht())/maxht);
-	trimspace(&scratch);
-	int retval = scratch.height() <= maxht;
-	scratch.freeall();
-	return retval;
-}
-
-// If s1 were added to s, would the height of the composed result be at most maxht?
-// The computational structure is similar to that above.
-int wouldfit(stream *s1, stream *s, int maxht)
-{
-	if (s1->rawht() + s->rawht() <= maxht)
-		return 1;
-	stream scratch;
-	for (stream cd = *s; cd.more(); cd.advance())
-		scratch.append(cd.current());
-	for (cd = *s1; cd.more(); cd.advance())
-		scratch.append(cd.current());
-	movefloats(&scratch, ((double) scratch.rawht())/maxht);
-	trimspace(&scratch);
-	int retval = scratch.height() <= maxht;
-	scratch.freeall();
-	return retval;
-}
-
-// All of stream *s is destined for one column or the other; which is it to be?
-void multicol::choosecol(stream *s, int goalht)
-{
-	stream *dest;
-	if (!leftblocked && wouldfit(s, &(column[0]), goalht))
-		dest = &(column[0]);
-	else {
-		dest = &(column[1]);
-		if (!s->current()->floatable())
-					// a stream item is going into the right
-					// column, so no more can go into the left.
-			leftblocked = 1;
-	}
-	for (stream cd = *s; cd.more(); cd.advance())
-		dest->append(cd.current());
-}
-
-double coltol = 0.5;
-
-// Try, very hard, to put everything in the multicol into two columns
-// so that the total height is at most htavail.
-void multicol::compose(int defonly)
-{
-	if (!nonempty()) {
-		setheight(0);
-		return;
-	}
-	scratch.freeall();		// fill scratch with everything destined
-					// for either column
-	for (stream cd = definite; cd.more(); cd.advance())
-		scratch.append(cd.current());
-	if (!defonly)
-		for (cd = *(currpage->stage); cd.more(); cd.advance())
-			if (cd.current()->numcol() == 2)
-				scratch.append(cd.current());
-	scratch.restoreall();		// in particular, floatables' goals
-	int rawht = scratch.rawht();
-	int halfheight = (int)(coltol*rawht);
-					// choose a goal height
-	int maxht = defonly ? halfheight : htavail;
-secondtry:
-	for (int i = 0; i < 2; i++)
-		column[i].freeall();
-	leftblocked = 0;
-	cd = scratch;
-	while (cd.more()) {
-		queue ministage;	// for the minimally acceptable chunks
-		ministage.freeall();	// that are to be added to either column
-		while (cd.more() && !cd.current()->issentinel()) {
-			ministage.enqueue(cd.current());
-			cd.advance();
-		}
-		choosecol(&ministage, maxht);
-		if (cd.more() && cd.current()->issentinel())
-			cd.advance();	// past sentinel
-	}
-	if (height() > htavail && maxht != htavail) {
-					// We tried to balance the columns, but
-					// the result was too tall.  Go back
-					// and try again with the less ambitious
-					// goal of fitting the space available.
-		maxht = htavail;
-		goto secondtry;
-	}
-	for (i = 0; i < 2; i++) {
-		movefloats(&(column[i]), ((double) column[i].rawht())/currpage->pagesize);
-		trimspace(&(column[i]));
-	}
-	if (dbg & 32) {
-		printf("#multicol::compose: htavail %d maxht %d dv %d\n",
-			htavail, maxht, height());
-		dump();
-	}
-	if (defonly)
-		stretch(height());
-}
-
-// A sequence of two-column ranges waits on the stage.
-// So long as the page's skeleton hasn't changed--that is, the maximum height
-// available to the two-column chunk is the same--we just use the columns that
-// have been built up so far, and choose a column into which to put the stage.
-// If the skeleton has changed, however, then we may need to make entirely
-// new decisions about which column gets what, so we recompose the whole page.
-void multicol::tryout()
-{
-	if (htavail == prevhtavail)
-		choosecol(currpage->stage, htavail);
-	else
-		currpage->compose(DRAFT);
-	prevhtavail = htavail;
-}
-
-// Make both columns the same height.
-// (Maybe this should also be governed by minfull,
-// to prevent padding very underfull columns.)
-void multicol::stretch(int wantht)
-{
-	if (wantht < height())
-		ERROR "page %d: two-column chunk cannot shrink\n", userpn FATAL;
-	for (int i = 0; i < 2; i++)
-		justify(&(column[i]), wantht);
-	if (dbg & 16)
-		printf("#col hts: left %d right %d\n",
-			column[0].height(), column[1].height());
-}
-
-// Report an upper bound on how tall the current two-column object is.
-// The (possibly composed) heights of the two columns give a crude upper
-// bound on the total height.  If the result is more than the height
-// available for the two-column object, then the columns are each
-// composed to give a better estimate of their heights.
-int multicol::height()
-{
-	int retval = max(column[0].height(), column[1].height());
-	if (retval < htavail)
-		return retval;
-	for (int i = 0; i < 2; i++) {
-		movefloats(&(column[i]), ((double) column[i].height())/currpage->pagesize);
-		trimspace(&(column[i]));
-	}
-	return max(column[0].height(), column[1].height());
-}
-
-void multicol::dump()
-{
-	printf("####2COL dv %d\n", height());
-	printf("# left column:\n");
-	column[0].dump();
-	printf("# right column:\n");
-	column[1].dump();
-}
-
-// From the head of queue qp, peel off a piece whose raw height is at most space.
-int peeloff(stream *qp, int space)
-{
-	stream *s1 = qp->current()->children();
-	if (!(s1 && s1->more() && s1->current()->height() <= space))
-					// in other words, either qp's head is
-					// not nested, or its first subrange
-		return 0;		// is also too big, so we give up
-	qp->split();
-	s1 = qp->current()->children();
-	stream *s2 = qp->next()->children();
-	while (s2->more() && s2->current()->rawht() <= space) {
-		s1->append(s2->current());
-		space -= s2->current()->rawht();
-		s2->advance();
-	}
-	return 1;
-}
-
-// There are four possibilities for consecutive calls to tryout().
-// If we're processing a sequence of single-column ranges, tryout()
-// uses the original algorithm: (1) conservative test; (2) costly test;
-// (3) split a breakable item.
-// If we're processing a sequence of double-column ranges, tryout()
-// defers to twocol->tryout(), which gradually builds up the contents
-// of the two columns until they're as tall as they can be without
-// exceeding twocol->htavail.
-// If we're processing a sequence of single-column ranges and we
-// get a double-column range, then we use compose() to build a
-// skeleton page and set twocol->htavail, the maximum height that
-// should be occupied by twocol.
-// If we're processing a sequence of double-column ranges and we
-// get a single-column range, then we should go back and squish
-// the double-column chunk as short as possible before we see if
-// we can fit the single-column range.
-void page::tryout()
-{
-	if (!stage->more())
-		ERROR "empty stage in page::tryout()\n" FATAL;
-	int curnumcol = stage->current()->numcol();
-	if (dbg & 32) {
-		printf("#page::tryout(): ncol = %d, prevncol = %d; on stage:\n",
-			curnumcol, prevncol);
-		stage->dump();
-		printf("#END of stage contents\n");
-	}
-	switch(curnumcol) {
-	default:
-		ERROR "unexpected number of columns in tryout(): %d\n",
-			stage->current()->numcol() FATAL;
-		break;
-	case 1:
-		if (prevncol == 2)
-			compose(FINAL);
-		if (wouldfit(stage, &definite, pagesize - twocol->height()))
-			commit();
-		else if (stage->current()->breakable() || blank()
-			&& peeloff(stage,
-				pagesize - (definite.height() + twocol->height()))) {
-			// first add the peeled-off part that fits
-			adddef(stage->dequeue());
-			// then send the rest back for later
-			stage->current()->setbreaking();
-			welsh();
-		} else if (blank()) {
-			stage->current()->rdump();
-			ERROR "A %s is too big to continue.\n",
-			stage->current()->typename() FATAL;
-		} else
-			welsh();
-		break;
-	case 2:
-		if (prevncol == 1)
-			compose(DRAFT);
-		else
-			twocol->tryout();
-		if (scratch.height() <= pagesize)
-			commit();
-		else
-			welsh();
-		break;
-	}
-	prevncol = curnumcol;
-}
-
-// To compose the page, we (1) fill scratch with the stuff that's meant to
-// go on the page; (2) compose scratch as best we can; (3) set the maximum
-// height available to the two-column part of the page; (4) have the two-
-// column part compose itself.
-// In the computation of twocol->htavail, it does not matter that
-// twocol->height() is merely an upper bound, because it is merely being
-// subtracted out to give the exact total height of the single-column stuff.
-void page::compose(int final)
-{
-	makescratch(final);
-	int adjht = scratch.rawht();
-	if (dbg & 16)
-		printf("# page %d measure %d\n", userpn, adjht);
-	movefloats(&scratch, ((double) adjht)/pagesize);
-	trimspace(&scratch);
-	twocol->htavail = pagesize - (scratch.height() - twocol->height());
-	twocol->compose(final);
-	adjht = scratch.height();
-	if (dbg & 16)
-		printf("# page %d measure %d after trim\n", userpn, adjht);
-}
-
-// Fill the scratch area with ranges destined for the page.
-// If defonly == 0, then add anything that's on stage--this is a trial run.
-// If defonly != 0, use only what's definitely on the page.
-void page::makescratch(int defonly)
-{
-	scratch.freeall();
-	for (stream cd = definite; cd.more(); cd.advance())
-		scratch.append(cd.current());
-	if (!defonly)
-		for (cd = *stage; cd.more(); cd.advance())
-			if (cd.current()->numcol() == 1)
-				scratch.append(cd.current());
-	if (twocol->nonempty())
-		scratch.append(twocol);
-}
-
-// Accept the current contents of the stage.
-// If the stage contains two-column ranges, add a sentinel to indicate the end
-// of a chunk of stage contents.
-void page::commit()
-{
-	if (dbg & 4)
-		printf("#entering page::commit()\n");
-	int numcol = 0;
-	while (stage->more()) {
-		numcol = stage->current()->numcol();
-		adddef(stage->dequeue());
-	}
-	if (numcol == 2)
-		adddef(new sentrange);
-}
-
-// Send the current contents of the stage back to its source.
-void page::welsh()
-{
-	if (dbg & 4)
-		printf("#entering page::welsh()\n");
-	while (stage->more()) {
-		range *r = stage->dequeue();
-		r->enqueue(ANDBLOCK);
-	}
-}
-
-enum { USonly = 1 };
-
-// So long as anything is eligible to go onto the page, keep trying.
-// Once nothing is eligible, compose and justify the page.
-void page::fill()
-{
-	while (stage->prime())
-		stage->pend();
-	compose(FINAL);
-	if (dbg & 16)
-		scratch.dump();
-	if (anymore()) {
-		int adjht = scratch.height();
-		if (adjht > minfull*pagesize) {
-			justify(&scratch, pagesize);
-			adjht = scratch.height();
-			int stretchamt = max(pagesize - adjht, 0);
-			twocol->stretch(twocol->height() + stretchamt);
-					// in case the page's stretchability lies
-					// entirely in its two-column part
-		} else
-			ERROR "page %d only %.0f%% full; will not be adjusted\n",
-				userpn, 100*(double) adjht/pagesize WARNING;
-	}
-}
-
-void page::adddef(range *r)
-{
-	if (dbg & 4)
-		printf("#entering page::adddef()\n");
-	switch (r->numcol()) {
-	case 1:	definite.append(r);
-		break;
-	case 2: twocol->definite.append(r);
-		break;
-	default: ERROR "%d-column range unexpected\n", r->numcol() FATAL;
-	}
-}
-
-int multicol::print(int cv, int col)
-{
-	if (col != 0)
-		ERROR "multicolumn output must start in left column\n" FATAL;
-	int curv = cv, maxv = cv;	// print left column
-	for ( ; column[0].more(); column[0].advance()) {
-		curv = column[0].current()->print(curv, 0);
-		maxv = max(maxv, curv);
-	}
-	curv = cv;			// print right column
-	for ( ; column[1].more(); column[1].advance()) {
-		curv = column[1].current()->print(curv, 1);
-		maxv = max(maxv, curv);
-	}
-	return maxv;
-}
-
-void page::print()
-{
-	static int tops = 1, bots = 1;
-	if (!scratch.more()) {
-		ERROR "## Here's what's left on squeue:\n" WARNING;
-		squeue.dump();
-		ERROR "## Here's what's left on bfqueue:\n" WARNING;
-		bfqueue.dump();
-		ERROR "## Here's what's left on ufqueue:\n" WARNING;
-		ufqueue.dump();
-		ERROR "page %d appears to be empty\n", userpn WARNING;
-		fflush(stderr), fflush(stdout), exit(0);
-					// something is very wrong if this happens
-	}
-	printf("p%d\n", userpn);	// print troff output page number
-	if (ptlist.more()) {		// print page header
-		ptlist.current()->print(0, 0);
-		ptlist.advance();
-	} else if (tops) {
-		ERROR "ran out of page titles at %d\n", userpn WARNING;
-		tops = 0;
-	}
-	int curv = 0;
-	printf("V%d\n", curv = pagetop);// print page contents
-	for ( ; scratch.more(); scratch.advance()) {
-		curv = scratch.current()->print(curv, 0);
-	}
-	if (btlist.more()) {		// print page footer
-		btlist.current()->print(0, 0);
-		btlist.advance();
-	} else if (bots) {
-		ERROR "ran out of page bottoms at %d\n", userpn WARNING;
-		bots = 0;
-	}
-	printf("V%d\n", physbot);	// finish troff output page
-}
-
-int	pagetop	= 0;		// top printing margin
-int	pagebot = 0;		// bottom printing margin
-int	physbot = 0;		// physical bottom of page
-
-double minfull = 0.9;		// minimum fullness before padding
-
-int	pn	= 0;		// cardinal page number
-int	userpn	= 0;		// page number derived from PT slugs
-
-static void makepage()
-{
-	page pg(pagebot - pagetop);
-	++pn;
-	userpn = ptlist.more() ? ptlist.current()->pn() : pn;
-	pg.fill();
-	pg.print();
-}
-
-static void conv(FILE *fp)
-{
-	startup(fp);		// read slugs, etc.
-	while (anymore())
-		makepage();
-	lastrange->print(0, 0);	// trailer
-	checkout();		// check that everything was printed
-}
-
-main(int argc, char **argv)
-{
-	static FILE *fp = stdin;
-	progname = argv[0];
-	while (argc > 1 && argv[1][0] == '-') {
-		switch (argv[1][1]) {
-		case 'd':
-			dbg = atoi(&argv[1][2]);
-			if (dbg == 0)
-				dbg = ~0;
-			break;
-		case 'm':
-			minfull = 0.01*atof(&argv[1][2]);
-			break;
-		case 'c':
-			coltol = 0.01*atof(&argv[1][2]);
-			break;
-		case 'w':
-			wantwarn = 1;
-			break;
-		}
-		argc--;
-		argv++;
-	}
-	if (argc <= 1)
-		conv(stdin);
-	else
-		while (--argc > 0) {
-			if (strcmp(*++argv, "-") == 0)
-				fp = stdin;
-			else if ((fp = fopen(*argv, "r")) == NULL)
-				ERROR "can't open %s\n", *argv FATAL;
-			conv(fp);
-			fclose(fp);
-		}
-	exit(0);
-}
//GO.SYSIN DD page.c
echo page.h
sed 's/.//' >page.h <<'//GO.SYSIN DD page.h'
-extern queue	squeue;			// the three queues on which ranges reside
-extern queue	bfqueue;
-extern queue	ufqueue;
-
-extern double minfull;
-
-extern double coltol;
-
-int anymore();
-
-// The following is used in some calls to range::enqueue(int = 0).
-#define ANDBLOCK 1
-
-class page;
-
-enum { DRAFT = 0, FINAL = 1 };
-
-// The mergestream currpage->stage serves as a staging area for page makeup:
-// when primed, it contains a minimal acceptable chunk of input ranges.
-// The page must either take or leave everything that's on stage.
-class mergestream : public queue {
-	page	*currpage;		// current page that's accepting stuff
-  public:
-	mergestream(page *cp)	{ currpage = cp; unblock(); }
-	void	unblock();
-	int	prime();		// stage next legal chunk
-	void	pend();			// process pending chunk on stage
-};
-
-// The multicol currpage->twocol is the two-column piece of the page to which
-// two-column ranges are currently being added.
-// The page sets htavail to indicate how tall it is allowed to become.
-// All ranges on definite must be placed when the multicol is printed.
-// Each of these definite ranges also resides on one of column[0] and [1],
-// which represent the current best guess about how to divide definite
-// between the two columns.
-class multicol : public range {
-	page	*currpage;		// current page that's accepting stuff
-	stream	definite;		// definitely on page
-	stream	scratch;		// for trial compositions
-	stream	column[2];		// left (0) and right (1) columns
-	int	leftblocked;		// OK to add to left column?
-	int	htavail;		// max possible ht, set by page::tryout()
-	int	prevhtavail;		// max 2-colht last time we added something
-	friend	page;
-public:
-	multicol(page *cp)	{ currpage = cp;
-				leftblocked = 0;
-				htavail = 0;
-				prevhtavail = -1;
-				setgoal(NOGOAL); }
-					// the two-column piece behaves as part
-					// of the stream of single-column input.
-	int	numcol()	{ return 1; }
-	int	nonempty()	{ return definite.more(); }
-	void	choosecol(range *, int);// add first arg to one or other column
-	void	choosecol(stream*, int);// add *all ranges on first arg*
-					// to one or other column
-					// NOT the same as a mapcar of the
-					// preceding function over the ranges
-					// on the first argument!
-	void	compose(int);		// divide into two columns
-	void	tryout();		// decide which column gets stage contents
-	void	stretch(int);		// justify both columns to given height
-	int	print(int curv, int col);
-	int	height();		// an upper bound on actual height
-	int	rawht()		{ return max(column[0].rawht(), column[1].rawht()); }
-	void	reheight(int *cv, int *mv)
-				{ *cv += height(); *mv = max(*mv, *cv); }
-	void	dump();
-	int	isvbox()	{ return nonempty(); }	// during trimspace()
-};
-
-// These sentinel ranges are used to separate the ranges on twocol::definite
-// into the chunks in which they came from the staging area.
-// Thus, they preserve the results of the computation that was done to prime
-// page::stage.
-class sentrange : public range {
-  public:
-	sentrange()		{ }
-	int	numcol()	{ return 2; }
-	int	issentinel()	{ return 1; }
-};
-
-class page {
-	int	pagesize;		// allowed maximum height
-	int	prevncol;		// was last item tried 1- or 2-column?
-	int	vsince;			// how many vboxes from "current" BS
-					// (to avoid putting a single line on
-					// a page with a very large floatable)
-	stream	definite;		// definitely on page, in input order
-	stream	scratch;		// playground in which to alter page
-	void	cmdproc();		// process any of several commands
-	void	parmproc();		// process any of several parameters
-	void	tryout();		// see whether current stage contents fit
-	void	compose(int);		// float and trim current page contents
-	void	makescratch(int);	// fill scratch area
-	void	commit();		// accept the items on stage
-	void	welsh();		// reject the items on stage
-	void	adddef(range *r);	// add to one of the definite queues
-					// (definite or twocol->definite)
-  public:
-	mergestream *stage;
-	friend	mergestream;
-	multicol *twocol;
-	friend multicol;
-	page(int p)	{ pagesize = p;
-			prevncol = 1;
-			vsince = 0;
-			stage = new mergestream(this);
-			twocol = new multicol(this); }
-	~page()	{ definite.freeall(); scratch.freeall(); }
-	void	fill();
-	int	blank()	{ return !definite.more() && !twocol->definite.more();}
-	void	print();
-};
-
-// functions in page.c
-main(int, char **);
//GO.SYSIN DD page.h
echo queue.c
sed 's/.//' >queue.c <<'//GO.SYSIN DD queue.c'
-#include	"misc.h"
-#include	"slug.h"
-#include	"range.h"
-#include	"page.h"
-
-queue	squeue;
-queue	bfqueue;
-queue	ufqueue;
-
-// We use the stream function current() to access a queue's head.
-// Thus, queue member curr should always point to its first range.
-void queue::check(char *whence)
-{
-	if (dbg & 8) {
-		char *p;
-		if (this == &squeue)
-			p = "squeue";
-		else if (this == &bfqueue)
-			p = "bfqueue";
-		else if (this == &ufqueue)
-			p = "ufqueue";
-		else
-			p = "weird queue";
-		printf("#checking %s\n", p);
-	}
-	if (first != curr)
-		ERROR "check(%s): first != curr, line %d\n", whence, curr->rp->lineno() FATAL;
-}
-
-// When ranges are told to enqueue themselves, they are being rejected from the
-// stage back onto their original queues.
-// They reset any parameters that may have been altered by staging or trial
-// composition.
-
-void	range::enqueue(int block)
-{
-	squeue.enqueue(this);
-	if (block)
-		squeue.block();
-}
-
-void	ufrange::enqueue(int block)
-{
-	restore();			// both goal positions
-	ufqueue.enqueue(this);
-	if (block)
-		ufqueue.block();
-}
-
-void	bfrange::enqueue(int block)
-{
-	restore();			// both goal positions
-	bfqueue.enqueue(this);
-	if (block)
-		bfqueue.block();
-}
-
-int anymore()
-{
-	return !(squeue.empty() && ufqueue.empty() && bfqueue.empty());
-}
-
-void mergestream::unblock()
-{
-	squeue.unblock();
-	bfqueue.unblock();
-	ufqueue.unblock();
-}
-
-// Fill the staging area with a minimal chunk of input ranges.
-int mergestream::prime()
-{
-	if (dbg & 4)
-		printf("#entering mergestream::prime()\n");
-	if (!empty())
-		return 1;
-	int brkok = 1;			// is it OK to break after the last
-					// VBOX that was added to the stage?
-	int needheight = -1;		// minimum acceptable height of the
-					// chunk being constructed on stage
-	// If the range at the head of any queue is breaking,
-	// deal with it first.
-	if (squeue.more() && squeue.current()->breaking())
-		enqueue(squeue.dequeue());
-	else if (bfqueue.more() && (bfqueue.current()->breaking() ||
-		(bfqueue.serialno() < squeue.serialno())))
-		enqueue(bfqueue.dequeue());
-	else if (ufqueue.more() && (ufqueue.current()->breaking() ||
-		(ufqueue.serialno() < squeue.serialno())))
-		enqueue(ufqueue.dequeue());
-	else while (squeue.more()) {
-		// Fill the stage with enough ranges to be a valid chunk.
-		range *r = squeue.dequeue();
-		if (r->isvbox()) {	// VBOX
-			if (dbg & 16)
-				printf("#VBOX: !empty: %d; brkok: %d; vsince: %d\n",
-					!empty(), brkok, currpage->vsince);
-			if (!empty()	// there's something there
-				&& brkok
-					// it's OK to break here
-				&& currpage->vsince >= 2
-					// enough stream has gone onto this page
-				&& rawht() >= needheight
-					// current need has been satisfied
-				) {
-					// the stage already contains enough
-					// ranges, so this one can wait
-				r->enqueue();
-				break;
-			} else {
-				if (r->rawht() > 0) {
-					++currpage->vsince;
-					brkok = r->brkafter();
-				}
-				enqueue(r);
-			}
-		} else if (r->isnested() || r->issp()) {	// US, SP
-			if (!empty() && rawht() >= needheight) {
-					// enough already, wait
-				r->enqueue();
-				break;
-			}
-			currpage->vsince = 0;
-			enqueue(r);
-			if (height() >= needheight)
-				break;
-		} else if (r->isneed()) {	// NE
-			if (!empty() && rawht() >= needheight) {
-					// not currently working on an unsatisfied NEed 
-				r->enqueue();
-				break;
-			}
-					// deal with overlapping NEeds
-			needheight = rawht() + max(needheight - rawht(), r->needht());
-			enqueue(r);
-		} else if (r->forceflush() == NO) {
-			enqueue(r);
-		} else if (r->forceflush() == YES) {
-			currpage->vsince = 0;
-			if (!empty()) {
-					// ready or not, r must wait
-				r->enqueue();
-				break;
-			}
-			enqueue(r);
-			break;
-		} else
-			ERROR "unexpected  %s[%s] in prime(), line %d\n",
-				r->typename(), r->headstr(), r->lineno() FATAL;
-	}
-	return more();			// 0 if nothing was staged
-}
-
-void page::cmdproc()
-{
-	if (stage->next())
-		ERROR "more than a single command on bsqueue\n" FATAL;
-	switch (stage->current()->cmdtype()) {
-	case FC:	// freeze the current 2-column range and start a new one
-		adddef(stage->dequeue());
-		twocol->compose(FINAL);
-		adddef(twocol);
-		twocol = new multicol(this);
-		break;
-	case BP:	// force a page break
-		adddef(stage->dequeue());
-		squeue.block();
-		break;
-	case FL:	// flush out all floatables that precede this range:
-			// no more stream input allowed until they're past
-		if (stage->serialno() > ufqueue.serialno() ||
-			stage->serialno() > bfqueue.serialno()) {
-			range *r = stage->dequeue();
-			r->enqueue(ANDBLOCK);
-		} else
-			adddef(stage->dequeue());
-		break;
-	default:
-		stage->current()->dump();
-		ERROR "unknown command\n" FATAL;
-	}
-}
-
-void page::parmproc()
-{
-	if (stage->next())
-		ERROR "more than a single parameter on bsqueue\n" FATAL;
-	switch (stage->current()->parmtype()) {
-	case NP:	// page top margin
-		if (blank())
-			pagetop = stage->current()->parm();
-		pagesize = pagebot - pagetop;
-		break;
-	case FO:
-		if (blank())
-			pagebot = stage->current()->parm();
-		pagesize = pagebot - pagetop;
-		break;
-	case PL:
-		if (blank())
-			physbot = stage->current()->parm();
-		break;
-	case MF:
-		minfull = 0.01*stage->current()->parm();
-		break;
-	case CT:
-		coltol = 0.01*stage->current()->parm();
-		break;
-	case WARN:
-		wantwarn = stage->current()->parm();
-		break;
-	case DBG:
-		dbg = stage->current()->parm();
-		break;
-	default:
-		stage->current()->dump();
-		ERROR "unknown parameter\n" FATAL;
-	}
-	adddef(stage->dequeue());
-}
-
-// Process the contents of the staging area; a relic that used to do more.
-void mergestream::pend()
-{
-	if (dbg & 4)
-		printf("#entering mergestream::pend()\n");
-	if (!more())
-		return;
-	if (current()->iscmd())
-		currpage->cmdproc();
-	else if (current()->isparm())
-		currpage->parmproc();
-	else
-		currpage->tryout();
-}
//GO.SYSIN DD queue.c
echo range.c
sed 's/.//' >range.c <<'//GO.SYSIN DD range.c'
-#include	<math.h>
-#include	"misc.h"
-#include	"slug.h"
-#include	"range.h"
-
-void sprange::reheight(int *cv, int *mv)
-{
-	if (*cv != *mv)
-		ERROR "slug %d: an imbedded SP, line %d\n",
-			first->serialno(), first->lineno() WARNING;
-	*cv += dv;
-	*mv = max(*mv, *cv);
-}
-
-void sprange::rerawht(int *cv, int *mv)
-{
-	*cv += rawht();
-	*mv = max(*mv, *cv);
-}
-
-void nestrange::restore()
-{
-	subrange->restoreall();
-}
-
-void stream::freeall()	// not a destructor;  called explicitly
-{
-	strblk *p, *q;
-	for (p = first; p; p = q) {
-		q = p->next;
-		delete p;
-	}
-	first = last = curr = 0;
-}
-
-void stream::dump()
-{
-	for (stream s = *this; s.more(); s.advance())
-		s.current()->dump();
-}
-
-void stream::rdump()
-{
-	for (stream s = *this; s.more(); s.advance())
-		s.current()->rdump();
-}
-
-int stream::restoreall()
-{
-	for (stream s = *this; s.more(); s.advance())
-		s.current()->restore();
-	return measure(this);
-}
-
-range *stream::append(range *r)
-{
-	if (last == 0)
-		curr = first = last = new strblk;
-	else {
-		last->next = new strblk;
-		last = last->next;
-		if (curr == 0)
-			curr = last;
-	}
-	last->next = 0;
-	return last->rp = r;
-}
-
-void stream::split()	// duplicate current() range
-{
-	strblk *s2 = new strblk;
-	range *r2 = curr->rp->clone();
-	s2->rp = r2;
-	s2->next = curr->next;
-	if (last == curr)
-		last = s2;
-	curr->next = s2;
-	curr->rp->killkids();		// children only in the 2nd one
-	// r2->crosslink(r1);
-}
-
-int stream::height()
-{
-	stream s = *this;
-	for (int h = 0; s.more(); s.advance())
-		h += s.current()->height();
-	return h;
-}
-
-int stream::rawht()
-{
-	stream s = *this;
-	for (int h = 0; s.more(); s.advance())
-		h += s.current()->rawht();
-	return h;
-}
-
-int measure(stream *sp)		// record high-water mark of stream
-{				// sets nested stream heights
-	stream s = *sp;
-	int curv, maxv;
-	for (maxv = curv = 0; s.more(); s.advance())
-		s.current()->reheight(&curv, &maxv);
-	return maxv;
-}
-
-int rawmeasure(stream *sp)
-{
-	stream s = *sp;
-	int curv, maxv;
-	for (maxv = curv = 0; s.more(); s.advance())
-		s.current()->rerawht(&curv, &maxv);
-	return maxv;
-}
-
-void nestrange::rdump()
-{
-	dump();
-	if (subrange)
-		subrange->rdump();
-}
-
-void nestrange::killkids()
-{
-	subrange = new stream;
-}
-
-int nestrange::print(int curv, int col)
-{
-	int ocurv = curv;
-	first->slugout(col);
-	for (stream s = *subrange; s.more(); s.advance())
-		curv = s.current()->print(curv, col);
-	return ocurv + height();
-}
-
-#define macroclone(rangetype) range *rangetype::clone() {\
-	rangetype *t = new rangetype;\
-	*t = *this;\
-	return t; }
-
-macroclone(usrange);
-macroclone(ufrange);
-macroclone(bfrange);
-
-#undef macroclone
-
-#define macropickgoal(rangetype) void rangetype::pickgoal(int acv, double scale) {\
-	if (scale > 1) {\
-		goalV = (int)(scale*goalV);\
-		goal2 = (int)(scale*goal2);\
-	}\
-	if (abs(acv - goalV) > abs(acv-goal2))\
-		goalV = goal2; }
-
-macropickgoal(ufrange)
-macropickgoal(bfrange)
-
-#undef macropickgoal
-
-range *generator::next()
-{
-	range *r;
-	if (child) {
-		if (r = child->next())
-			return r;
-		delete child;
-		child = 0;
-	}
-	if (!s.more())
-		return 0;
-	r = s.current();
-	if (r->isnested())
-		child = new generator(r->children());
-	s.advance();
-	return r;
-}
-
-range *queue::enqueue(range *r)
-{
-	if (dbg & 8)
-		printf("#entering queue::enqueue()\n");
-	check("queue::enqueue");
-	if (!last || last->rp->serialno() < r->serialno())	// common case
-		return append(r);
-	if (dbg & 8)
-		printf("#queue::enqueue() pushing back\n");
-	newguy = new strblk;
-	newguy->rp = r;
-	if (r->serialno() < first->rp->serialno()) {
-		newguy->next = first;
-		curr = first = newguy;
-		return newguy->rp;
-	}
-	if (dbg & 8)
-		printf("#queue::enqueue() searching down queue\n");
-	for (curr = first;
-		next() && next()->serialno() < r->serialno();
-		curr = curr->next)
-		;
-	newguy->next = curr->next;
-	curr->next = newguy;
-	curr = first;			// restore important queue condition
-	return newguy->rp;
-}
-
-range *queue::dequeue()
-{
-	if (dbg & 8)
-		printf("#entering queue::dequeue()\n");
-	check("queue::dequeue");
-	curr = first->next;
-	range *retval = first->rp;
-	delete first;
-	first = curr;
-	if (!curr)
-		last = 0;
-	return retval;
-}
-
-// ================================================================================
-
-//	functions that munge the troff output stored in slugs[]
-
-// ================================================================================
-
-static void doprefix(FILE *fp) // copy 1st "x" commands to output
-{
-	int c;
-
-	while ((c = getc(fp)) != EOF) {
-		if (c != 'x') {
-			ungetc(c, fp);
-			break;
-		}
-		putchar(c);
-		do {
-			putchar(c = getc(fp));
-		} while (c != '\n');
-		linenum++;
-	}
-//	printf("x font 1 R\n");	// horrible kludge: ensure a font for first f1 command 
-}
-
-#define	DELTASLUGS	15000
-
-static slug	*slugs = 0;
-static int	nslugs = 0;	// slugs has nslugs slots
-static slug	*slugp = 0;	// next free slug in slugs
-
-static void readslugs(FILE *fp)
-{
-	if ((slugs = (slug *) malloc((nslugs = DELTASLUGS)*sizeof(slug))) == NULL)
-		ERROR "no room for %d-slug array\n", nslugs FATAL;
-	slugp = slugs;
-	for (slugp = slugs; ; slugp++) {
-		if (slugp >= slugs+nslugs-2) {
-			int where = slugp - slugs;
-			if ((slugs = (slug *) realloc((char *) slugs, (nslugs += DELTASLUGS)*sizeof(slug))) == NULL)
-				ERROR "no room for %d slugs\n", nslugs FATAL;
-			ERROR "now slug array can hold %d slugs\n", nslugs WARNING;
-			slugp = slugs + where;
-		}
-		*slugp = getslug(fp);
-		if (slugp->type == EOF)
-			break;
-	}
-	*++slugp = eofslug();
-	printf("# %d slugs\n", slugp-slugs);
-}
-
-static slug *findend(slug *sp)
-{
-	for (slug *p = sp; p->type == sp->type; p++)	// skip runs
-		;				// espec UF UF UF 
-	for ( ; p < slugp; p++)
-		switch (p->type) {
-		case US:
-		case UF:
-		case BF:
-		case PT:
-		case BT:
-			p = findend(p);
-			break;
-		case END:
-			return p;
-		}
-	ERROR "walked past EOF in findend looking for %d (%s), line %d\n",
-		sp->type, sp->typename(), sp->lineno() FATAL;
-	return sp;
-}
-
-static int markp(int i, int n, int parm)
-{	// should VBOX i of n be marked to brevent breaking after it?
-	if (i >= n-1)
-		return 0;
-	return i <= parm-2 || i >= n-parm;
-}
-
-static void markbreak(slug *p)
-{
-	// Mark impermissible breakpoints in BS's.
-	// The parm field of a VBOX is >0 if we shouldn't break after it.
-	int parm;		// how many lines must stay on page
-	int goahead = 1;	// true until we see the next BS
-	int nowmark = 0;	// true when we should be marking
-	int n = 0;
-	while (p->type == BS)
-		parm = p++->parm;	// latest BS parm applies
-	slug *op = p;
-	while (goahead) {
-		switch (p->type) {
-		case VBOX:		// count VBOXes so second pass knows
-			if (p->dv > 0)	// knows how far to end of BS
-				n++;
-			break;
-		case US:		// mark around EQ/EN, etc.
-			nowmark = 1;
-			p = findend(p);
-			break;
-		case UF:		// but not around floats, PTs, and BTs
-		case BF:
-		case PT:
-		case BT:
-			p = findend(p);
-			break;
-		case SP:		// naked SP:  probable macro botch
-			nowmark = 1;	// mark around it anyhow
-			break;
-		case BS:		// beginning of next paragraph
-		case END:		// probable macro botch
-		case EOF:
-			goahead = 0;	// stop work after marking
-			nowmark = 1;
-		default:
-			break;
-		}
-		p++;
-		if (nowmark) {
-			int i = 0;	// VBOX counter for second pass
-			while (op < p) {
-				switch (op->type) {
-				case VBOX:
-					if (op->dv > 0)
-						op->parm = markp(i, n, parm);
-					i++;
-					break;
-				case US:	// caused second pass to begin
-				case SP:
-				case BS:
-				case END:
-				case EOF:
-					op = p;
-					break;
-				case UF:	// skip on this pass too
-				case BF:
-				case PT:
-				case BT:
-					op = findend(op);
-					break;
-				default:
-					break;
-				}
-			op++;
-			}
-			if (i != n)
-				ERROR "markbreak failed : i %d n %d\n",
-					i, n WARNING;
-			op = p;
-			nowmark = n = 0;
-		}
-	}
-}
-
-static void fixslugs()		// adjust bases and dv's, set parameters, etc.
-{
-	slug *prevV = 0;
-	for (slug *p = slugs; p < slugp; p++) {
-		if (p->type == VBOX) {
-			prevV = p;
-			continue;
-		}
-		if (p->base != 0) {
-			ERROR "%s slug (type %d) has base = %d, line %d\n",
-				p->typename(), p->type, p->base, p->lineno() WARNING;
-		}
-		if ((p->type == SP) || (p->type == NE))
-			continue;
-		if (p->type == PAGE)
-			prevV = 0;
-		if (p->dv != 0)
-			if (prevV) {
-				prevV->base = max(prevV->base, p->dv);
-				p->dv = 0;
-			} else {
-				ERROR "s slug (type %d) has dv = %d, line %d\n",
-					p->typename(), p->type, p->dv, p->lineno() WARNING;
-			}
-	}
-	prevV = 0;
-	int firstNP = 0, firstFO = 0, firstPL = 0;
-	for (p = slugs; p < slugp; p++) {
-		switch (p->type) {
-		// adjust the dv in a sequence of VBOXes
-		// by subtracting from each the base of the preceding VBOX
-		case VBOX:
-			if (prevV)
-				p->dv -= prevV->base;
-			prevV = p;
-			break;
-		case SP:
-			p->dv = max(p->dv, 0);
-			break;
-		case PAGE:
-			p->neutralize();
-			prevV = 0;
-			break;
-		// record only first "declarations" of Page Top and bottom (FO);
-		case PARM:
-			switch (p->parm) {
-			case NP:
-				if (firstNP++ == 0)
-					pagetop = p->parm2;
-				p->neutralize();
-				break;
-			case FO:
-				if (firstFO++ == 0)
-					pagebot = p->parm2;
-				p->neutralize();
-				break;
-			case PL:
-				if (firstPL++ == 0)
-					physbot = p->parm2;
-				p->neutralize();
-				break;
-			}
-			break;
-		// things that begin groups; not US, which should nest properly
-		case UF:
-		case BF:
-			while ((p+1)->type == p->type) {
-							// join adjacent identical
-				(p+1)->parm2 = p->parm;	// parm is latest
-							// parm2 is previous
-				p->neutralize();	// so it's not seen later
-				p++;
-			}
-			break;
-		// none of the above
-		case US:
-		case PT:
-		case BT:
-		case BS:
-		case END:
-		case TM:
-		case COORD:
-		case NE:
-		case MC:
-		case CMD:
-		case EOF:
-			break;
-		default:
-			ERROR "Unknown slug type %d in fixslugs, line %d\n",
-				p->type, p->lineno() WARNING;
-			break;
-		}
-	}
-	int pagesize = pagebot - pagetop;
-	if (pagesize == 0)
-		ERROR "Page dimensions not declared\n" FATAL;
-	if (physbot == 0)
-		physbot = pagebot + pagetop;
-	printf("# page top %d bot %d size %d physbot %d\n",
-		pagetop, pagebot, pagesize, physbot);
-	for (p = slugs; p < slugp; p++) {
-		switch (p->type) {
-		// normalize float parameters
-		case BF:
-		case UF:
-					// primary goal
-			p->parm = max(min(p->parm-pagetop, pagesize), 0);
-					// secondary goal
-			p->parm2 = max(min(p->parm2-pagetop, pagesize), 0);
-			break;
-		// normalize need parameters
-		case NE:
-			p->dv = max( min(p->dv, pagesize), 0);
-			break;
-		// mark permissible breaks
-		case BS:
-			markbreak(p);
-			break;
-		}
-		if (dbg & 1)
-			p->dump();
-	}
-}
-
-void checkout()
-{
-	for (slug *p = slugs; p < slugp; p++)
-		switch (p->type) {
-		case PT:
-		case BT:
-			p = findend(p);
-			break;
-		case SP:
-		case VBOX:
-			if (p->seen != 1)
-				ERROR "%s slug %d seen %d times\n",
-					p->typename(), p->serialno(),
-					p->seen WARNING;
-			break;
-		}
-}
-
-eofrange *lastrange;
-stream	ptlist, btlist;
-
-static slug *makeranges(slug *p, stream *s, int level)
-{
-	stream *t;
-
-	for ( ; p < slugp; p++)
-		switch (p->type) {
-		case VBOX:
-			s->append(new vboxrange(p));
-			break;
-		case SP:
-			s->append(new sprange(p));
-			break;
-		case BS:
-			s->append(new bsrange(p));
-			break;
-		case US:
-			s->append(new usrange(p, t = new stream));
-			p = makeranges(p+1, t, level+1);
-			break;
-		case BF:
-			s->append(new bfrange(p, t = new stream));
-			p = makeranges(p+1, t, level+1);
-			break;
-		case UF:
-			s->append(new ufrange(p, t = new stream));
-			p = makeranges(p+1, t, level+1);
-			break;
-		case PT:
-			ptlist.append(new ptrange(p, t = new stream));
-			p = makeranges(p+1, t, level+1);
-			break;
-		case BT:
-			btlist.append(new btrange(p, t = new stream));
-			p = makeranges(p+1, t, level+1);
-			break;
-		case END:
-			s->append(new endrange(p));
-			return p;
-		case TM:
-			s->append(new tmrange(p));
-			break;
-		case COORD:
-			s->append(new coordrange(p));
-			break;
-		case NE:
-			if (level) {
-				ERROR "Nested NE commands are ignored, line %d\n",
-					p->lineno() WARNING;
-				p->dv = 0;
-			}
-			s->append(new nerange(p));
-			break;
-		case MC:
-			s->append(new mcrange(p));
-			break;
-		case CMD:
-			if (level)
-				ERROR "Nested command ignored, line %d\n",
-					p->lineno() WARNING;
-			s->append(new cmdrange(p));
-			break;
-		case PARM:
-			if (level)
-				ERROR "Nested parameter ignored, line %d\n",
-					p->lineno() WARNING;
-			s->append(new parmrange(p));
-			break;
-		case EOF:
-			lastrange = new eofrange(p);
-			return 0;
-		}
-	return p;
-}
-
-static queue text;			// unexamined input ranges; the real data
-
-void startup(FILE *fp)
-{
-	doprefix(fp);			// peel off 'x' commands
-	readslugs(fp);			// read everything into slugs[]
-	fixslugs();			// measure parameters and clean up
-	makeranges(slugs, &text, 0);	// add range superstructure
-	measure(&text);		// heights of nested things
-	rawmeasure(&text);
-	while (text.more()) {
-		range *r = text.dequeue();
-		if (dbg & 2)
-			r->dump();
-		r->enqueue();
-	}
-}
//GO.SYSIN DD range.c
echo range.h
sed 's/.//' >range.h <<'//GO.SYSIN DD range.h'
-const	NOGOAL = -1;
-
-class stream;
-
-enum primeflush { NO, YES, EXPECTED, UNEXPECTED };	// mergestream::prime()
-
-// Ranges do two things.  They interpose a layer between slugs and the rest
-// of the program; this is important because of the grossness of the slug
-// data structure (made necessary by its origins in troff output).  Ranges also
-// group together other ranges into meaningful chunks like unbreakable stream
-// objects, floatable objects, and page headers and footers.
-// Member function height() returns a range's height as of the latest composition.
-// Member function rawht() returns the range's original height in the input.
-class range {
-  protected:
-	slug	*first;		// earliest slug in range
-	int	accumV;		// accumulated V to this point
-  public:
-	range()		{ first = 0; accumV = 0; }
-	range(slug *p)	{ first = p; accumV = 0; }
-	char	*headstr()		{
-		return first ? first->headstr() : ""; }
-	char	*typename()		{ return first->typename(); }
-	int	serialno()		{ return first->serialno(); }
-	int	lineno()		{ return first->lineno(); }
-	virtual void	dump()		{ first->dump(); }
-	virtual void	rdump()		{ dump(); }
-	virtual int	print(int cv, int col)	{
-		first->slugout(col); return cv; }
-	virtual int	floatable()	{ return 0; }
-	virtual int	brkafter()	{ return 1; }
-	virtual int	isnested()	{ return 0; }
-	virtual int	issp()		{ return 0; }
-	virtual int	isvbox()	{ return 0; }
-	virtual int	isneed()	{ return 0; }
-	virtual int	iscmd()		{ return 0; }
-	virtual int	cmdtype()	{ return -1; }
-	virtual int	isparm()	{ return 0; }
-	virtual int	parmtype()	{ return -1; }
-	virtual int	parm()		{ return -1; }
-	virtual int	breakable()	{ return 0; }
-	virtual int	forceflush()	{ return UNEXPECTED; }
-	virtual int	pn()		{ return 0; }
-	virtual stream	*children()	{ return 0; }	// see page::peeloff()
-	virtual void	killkids()	{ }
-	virtual void	enqueue(int = 0);
-	virtual int	height()	{ return 0; }
-	virtual int	rawht()		{ return 0; }
-	virtual int	needht()	{ return 0; }
-	virtual void	reheight(int *, int *)	{ }
-	virtual void	rerawht(int *, int *)	{ }
-	virtual void	setheight(int) { }
-	virtual void	restore()	{ }		// goals of floatables
-	virtual int	goal()		{ return NOGOAL; }
-	int		accum()		{ return accumV; }
-	void		setaccum(int n)	{ accumV = n; }
-	virtual	void	setgoal(int)	{ }
-	virtual void	pickgoal(int, double)	{ }
-	virtual int	numcol()	{ return first->numcol(); }
-	virtual int	issentinel()	{ return 0; }
-	virtual range	*clone()	{ return 0; }
-	virtual int	breaking()	{ return 0; }
-	virtual void	setbreaking()	{ }
-};
-
-class vboxrange : public range {
-	int	dv;		// inherited from slug
-	int	base;		// inherited from slug
-	int	brk;		// 0 => ok to break after, 1 => no break 
-  public:
-	vboxrange(slug *p) : range(p) { dv = p->dv; base = p->base; brk = p->parm; }
-	void	dump() {
-		printf("#### VBOX brk? %d dv %d ht %d\n", brk, dv, dv+base); }
-	int	print(int cv, int col) {
-		printf("V%d\n", cv += dv); first->slugout(col); return cv+base; }
-	int	brkafter()		{ return !brk; }
-	int	isvbox()		{ return 1; }
-	int	forceflush()		{ return NO; }
-	int	height()		{ return dv + base; }
-	int	rawht()			{ return first->dv + first->base; }
-	void	reheight(int *cv, int *mv) {
-		*cv += dv+base; *mv = max(*mv, *cv); }
-	void	rerawht(int *cv, int *mv) {
-		*cv += rawht(); *mv = max(*mv, *cv); }
-};
-
-class sprange : public range {
-	int dv;
-  public:
-	sprange(slug *p) : range(p) { dv = first->dv; }
-	void	dump() {
-		printf("#### SP dv %d (originally %d)\n", dv, first->dv); }
-	int	print(int cv, int col)	{
-		first->slugout(col); return cv + dv; }
-	int	issp()			{ return 1; }
-	int	forceflush()		{ return YES; }
-	int	height()		{ return dv; }
-	int	rawht()			{ return first->dv; }
-	void	reheight(int *, int *);
-	void	rerawht(int *, int *);
-	void	setheight(int n)	{ dv = n; }
-};
-
-class tmrange : public range {
-  public:
-	tmrange(slug *p) : range(p)	{ }
-	int	forceflush()		{ return NO; }
-	int	print(int cv, int col)	{ first->slugout(col); return cv; }
-};
-
-class coordrange : public range {
-  public:
-	coordrange(slug *p) : range(p)	{ }
-	int	forceflush()		{ return NO; }
-	int	print(int cv, int col)
-		{ first->slugout(col); printf(" Y %d\n", cv); return cv; }
-};
-
-class nerange : public range {
-  public:
-	nerange(slug *p) : range(p)	{ }
-	int	isneed()		{ return 1; }
-	int	forceflush()		{ return YES; }
-	int	needht()		{ return first->dv; }
-};
-
-class mcrange : public range {
-  public:
-	mcrange(slug *p) : range(p)	{ }
-	int	forceflush()		{ return YES; }
-};
-
-class cmdrange : public range {
-  public:
-	cmdrange(slug *p) : range(p)	{ }
-	int	iscmd()			{ return 1; }
-	int	forceflush()		{ return YES; }
-	int	cmdtype()		{ return first->parm; }
-};
-
-class parmrange : public range {
-  public:
-	parmrange(slug *p) : range(p)	{ }
-	int	isparm()		{ return 1; }
-	int	forceflush()		{ return YES; }
-	int	parmtype()		{ return first->parm; }
-	int	parm()			{ return first->parm2; }
-};
-
-class bsrange : public range {
-  public:
-	bsrange(slug *p) : range(p)	{ }
-	int	forceflush()		{ return NO; }
-	int	print(int cv, int col)	{ first->slugout(col); return cv; }
-};
-
-class endrange : public range {
-  public:
-	endrange(slug *p) : range(p)	{ }
-	int	forceflush()		{ return UNEXPECTED; }
-};
-
-class eofrange : public range {
-  public:
-	eofrange(slug *p) : range(p)	{ }
-	int	forceflush()		{ return UNEXPECTED; }
-};
-
-extern eofrange *lastrange;	// the EOF block (trailer, etc.) goes here
-
-int measure(stream *);
-int rawmeasure(stream *);
-
-// A nestrange packages together a sequence of ranges, its subrange.
-// Other parts of the program reach in and alter the dimensions of
-// some of these ranges, so when the height of a range is requested
-// it is computed completely afresh.
-// (Note:  the alternative, of keeping around many copies of ranges
-// with different dimensions, was abandoned because of the difficulty
-// of ensuring that exactly one copy of each original range would be
-// output.)
-class nestrange : public range {
-  protected:
-	stream	*subrange;
-	int isbreaking;
-	int rawdv;
-  public:
-	nestrange() : range()	{ subrange = 0; isbreaking = 0; rawdv = -1; }
-	nestrange(slug *p, stream *s) : range(p)
-				{ subrange = s; isbreaking = 0; rawdv = -1; }
-	void	rdump();
-	virtual void restore();
-	stream	*children()	{ return subrange; }
-	void	killkids();
-	int	height()	{ return measure(subrange); }
-	int	rawht()		{ if (rawdv < 0 || isbreaking) rawdv = rawmeasure(subrange);
-					return rawdv; }
-	void	reheight(int *cv, int *mv) {
-			*mv += measure(subrange); *cv = max(*mv, *cv); }
-	void	rerawht(int *cv, int *mv) {
-			*mv += rawht(); *cv = max(*mv, *cv); }
-	int	isnested()	{ return 1; }
-	int	forceflush()	{ return EXPECTED; }
-	int	print(int cv, int col);
-	int	breaking()	{ return isbreaking; }
-	void	setbreaking()	{ isbreaking++; }
-};
-
-class usrange : public nestrange {
-  public:
-	usrange()	{ }
-	usrange(slug *p, stream *s) : nestrange(p, s) {}
-	void dump() { printf("#### US	dv %d\n", height()); }
-	range	*clone();
-};
-
-class ufrange : public nestrange {
-	int	goalV, goal2;
-  public:
-	ufrange()	{ }
-	ufrange(slug *p, stream *s) : nestrange(p, s) {
-		goalV = p->parm; goal2 = p->parm2; }
-	void 	dump() { printf("#### UF   dv %d goal %d goal2 %d\n",
-		height(), goalV, goal2); }
-	int	floatable()	{ return 1; }
-	void	enqueue(int = 0);
-	range	*clone();
-	int	goal()		{ return goalV; }
-	void	setgoal(int n)	{ goalV = goal2 = n; }
-	void	pickgoal(int acv, double scale);
-	void	restore()	{ goalV = first->parm; goal2 = first->ht; }
-};
-
-class bfrange : public nestrange {
-	int	goalV, goal2;
-  public:
-	bfrange()	{ }
-	bfrange(slug *p, stream *s) : nestrange(p, s) {
-		goalV = p->parm; goal2 = p->parm2; }
-	void 	dump() { printf("#### BF   dv %d goal %d goal2 %d\n",
-		height(), goalV, goal2); }
-	int	floatable()	{ return 1; }
-	void	enqueue(int = 0);
-	range	*clone();
-	int	goal()		{ return goalV; }
-	void	setgoal(int n)	{ goalV = goal2 = n; }
-	void	pickgoal(int acv, double scale);
-	void	restore()	{ goalV = first->parm; goal2 = first->parm2; }
-	int	breakable()	{ return 1; }	// can be broken
-};
-
-class ptrange : public nestrange {
-	int	pgno;
-  public:
-	int	pn()	{ return pgno; }
-	ptrange(slug *p, stream *s) : nestrange(p, s) { pgno = p->parm; }
-	void 	dump() { printf("#### PT   pgno %d dv %d\n", pgno, height()); }
-};
-
-class btrange : public nestrange {
-	int	pgno;
-  public:
-	btrange(slug *p, stream *s) : nestrange(p, s) { pgno = p->parm; }
-	void 	dump() { printf("#### BT   pgno %d dv %d\n", pgno, height()); }
-};
-
-// A stream is a sequence of ranges; we use this data structure a lot
-// to traverse various sequences that crop up in page-making.
-class stream {
-  protected:
-public:
-	struct strblk {		// ranges are linked by these blocks
-		strblk	*next;
-		range	*rp;
-	};
-	strblk	*first;
-	strblk	*last;
-	strblk	*curr;
-  public:
-	stream()		{ curr = last = first = 0; }
-	stream(range *r)	{ curr = last = first = new strblk;
-					last->rp = r; last->next = 0; }
-	void	freeall();	// note:  not a destructor
-	void	dump();		// top level
-	void	rdump();	// recursive
-	int	restoreall();
-	range	*current()	{ return curr->rp; }
-	range	*next()		{ return curr && curr->next ? curr->next->rp : 0; }
-	void	advance()	{ curr = curr->next; }
-	range	*append(range *r);
-	void	split();
-	int	more()		{ return curr && curr->rp; }
-	int	height();
-	int	rawht();
-};
-
-// A generator iterates through all the ranges of a stream
-// (not just the root ranges of nestranges).
-class generator {
-	stream	s;
-	generator *child;
-  public:
-	generator()		{ child = 0; }
-	generator(stream *sp)	{ s = *sp; child = 0; }
-	range	*next();
-};
-
-extern stream	ptlist, btlist;		// page titles
-
-#define INFINITY 1000001
-
-// A queue is a distinguished kind of stream.
-// It keeps its contents in order by the serial numbers of the ranges.
-// A queue can be blocked from dequeuing something to indicate
-// that it's not worth considering the queue again on a given page.
-class queue : public stream {
-	strblk	*newguy;
-  protected:
-	int	blocked;
-	void	check(char *);
-  public:
-	queue() : blocked(0)	{ }
-	range	*enqueue(range *r);
-	range	*dequeue();
-	void	block()		{ blocked = 1; }
-	void	unblock()	{ blocked = 0; }
-	int	more()		{ return !blocked && stream::more(); }
-	int	empty()		{ return !stream::more(); }
-	int	serialno()	{ return empty() ? INFINITY : current()->serialno(); }
-};
-
-// functions in range.c
-void checkout();
-void startup(FILE *);
//GO.SYSIN DD range.h
echo slug.c
sed 's/.//' >slug.c <<'//GO.SYSIN DD slug.c'
-#include	"misc.h"
-#include	"slug.h"
-#include	<libc.h>
-#include	<math.h>
-
-static char	*bufptr(int);
-
-void slug::coalesce()
-{
-	(this+1)->dp = dp;	// pretty grimy, but meant to ensure
-				// that all output goes out.
-			// maybe it has to skip over PT's;
-			// some stuff is getting pushed inside PT..END
-}
-
-void slug::neutralize()
-{
-	switch (type) {
-	case PAGE:
-	case UF:
-	case BF:
-	case PARM:
-		type = NEUTRAL;
-		coalesce();
-		break;
-	default:
-		ERROR "neutralized %d (%s) with %s\n",
-			type, typename(), headstr() WARNING;
-		break;
-	}
-}
-
-void slug::dump()	// print contents of a slug
-{
-	printf("# %d %-4.4s parm %d dv %d base %d s%d f%d H%d\n#\t\t%s\n",
-		serialno(), typename(), parm, dv, base,
-		size, font, hpos, headstr());
-}
-
-char *slug::headstr()
-{
-	const HEADLEN = 65;
-	static char buf[2*HEADLEN];
-	int j = 0;
-	char *s = bufptr(dp);
-	int n = (this+1)->dp - dp;
-	if (n >= HEADLEN)
-		n = HEADLEN;
-	for (int i = 0; i < n; i++)
-		switch (s[i]) {
-			case '\n':
-			case '\t':
-			case '\0':
-			case ' ':
-				break;
-			default:
-				buf[j++] = s[i];
-				break;
-		}
-	buf[j] = 0;
-	return buf;
-}
-
-static char *strindex(char s[], char t[])	// index of earliest t[] in s[]
-{
-	for (int i = 0; s[i] != '\0'; i++) {
-		int j, k;
-		for (j = i, k = 0; t[k]!='\0' && s[j] == t[k]; j++, k++)
-			;
-		if (k > 0 && t[k] == '\0')
-			return s+i;
-	}
-	return 0;
-}
-
-void slug::slugout(int col)
-{
-	static numout = 0;
-	if (seen++)
-		ERROR "%s slug #%d seen %d times [%s]\n",
-			typename(), serialno(), seen, headstr() WARNING;
-	if (type == TM) {
-		char *p;
-		if (p = strindex(bufptr(dp), "x X TM "))
-			p += strlen("x X TM ");		// skip junk
-		else
-			ERROR "strange TM [%s]\n", headstr() FATAL;
-		fprintf(stderr, "%d\t", userpn);	// page # as prefix
-		for ( ; p < bufptr((this+1)->dp); p++)
-			putc(*p, stderr);
-	} else if (type == COORD) {
-		for (char *p = bufptr(dp); p < bufptr((this+1)->dp) && *p != '\n'; p++)
-			putc(*p, stdout);
-		printf(" # P %d X %d", userpn, hpos + col*offset);
-		return;
-	} else if (type == VBOX) {
-		if (numout++ > 0)	// BUG??? might miss something
-			printf("s%d\nf%d\n", size, font);
-		printf("H%d\n", hpos + col*offset);
-	}
-	fwrite(bufptr(dp), sizeof(char), (this+1)->dp - dp, stdout);
-}
-
-char *slug::typename()
-{
-	static char buf[50];
-	char *p = buf;		// return value
-	switch(type) {
-	case EOF:	p = "EOF"; break;
-	case VBOX:	p = "VBOX"; break;
-	case SP:	p = "SP"; break;
-	case BS:	p = "BS"; break;
-	case US:	p = "US"; break;
-	case BF:	p = "BF"; break;
-	case UF:	p = "UF"; break;
-	case PT:	p = "PT"; break;
-	case BT:	p = "BT"; break;
-	case END:	p = "END"; break;
-	case NEUTRAL:	p = "NEUT"; break;
-	case PAGE:	p = "PAGE"; break;
-	case TM:	p = "TM"; break;
-	case COORD:	p = "COORD"; break;
-	case NE:	p = "NE"; break;
-	case CMD:	p = "CMD"; break;
-	case PARM:	p = "PARM"; break;
-	default:	sprintf(buf, "weird type %d", type);
-	}
-	return p;
-}
-
-// ================================================================================
-
-// 	troff output-specific functions
-
-// ================================================================================
-
-const	DELTABUF = 500000;		// grow the input buffer in chunks
-
-static char	*inbuf = 0;		// raw text input collects here
-static int	ninbuf = 0;		// byte count for inbuf
-static char	*inbp = 0;		// next free slot in inbuf
-int		linenum = 0;		// input line number
-
-static inline void addc(int c) { *inbp++ = c; }
-
-static void adds(char *s)
-{
-	for (char *p = s; *p; p++)
-		addc(*p);
-}
-
-static char *getutf(FILE *fp)	// get 1 utf-encoded char (might be multiple bytes)
-{
-	static char buf[100];
-	char *p = buf;
-
-	for (*p = 0; (*p++ = getc(fp)) != EOF; ) {
-		*p = 0;
-		if (mblen(buf, sizeof buf) > 0)	// found a valid character
-			break;
-	}
-	return buf;
-}
-
-static char *bufptr(int n) { return inbuf + n; }  // scope of inbuf is too local
-
-static inline int wherebuf() { return inbp - inbuf; }
-
-static char *getstr(char *p, char *temp)
-{		// copy next non-blank string from p to temp, update p
-	while (*p == ' ' || *p == '\t' || *p == '\n')
-		p++;
-	if (*p == '\0') {
-		temp[0] = 0;
-		return(NULL);
-	}
-	while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
-		*temp++ = *p++;
-	*temp = '\0';
-	return(p);
-}
-
-/***************************************************************************
-   bounding box of a circular arc             Eric Grosse  24 May 84
-
-Conceptually, this routine generates a list consisting of the start,
-end, and whichever north, east, south, and west points lie on the arc.
-The bounding box is then the range of this list.
-    list = {start,end}
-    j = quadrant(start)
-    k = quadrant(end)
-    if( j==k && long way 'round )  append north,west,south,east
-    else
-      while( j != k )
-         append center+radius*[j-th of north,west,south,east unit vectors]
-         j += 1  (mod 4)
-    return( bounding box of list )
-The following code implements this, with simple optimizations.
-***********************************************************************/
-
-static int quadrant(double x, double y)
-{
-	if (     x>=0.0 && y> 0.0) return(1);
-	else if( x< 0.0 && y>=0.0) return(2);
-	else if( x<=0.0 && y< 0.0) return(3);
-	else if( x> 0.0 && y<=0.0) return(4);
-	else			   return 0;	/* shut up lint */
-}
-
-static double xmin, ymin, xmax, ymax;	// used by getDy
-
-static void arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc)
-		/* start, end, center */
-{		/* assumes center isn't too far out */
-	double r;
-	int j, k;
-	printf("#start %g,%g, end %g,%g, ctr %g,%g\n", x0,y0, x1,y1, xc,yc);
-	y0 = -y0; y1 = -y1; yc = -yc;	// troff's up is eric's down
-	x0 -= xc; y0 -= yc;	/* move to center */
-	x1 -= xc; y1 -= yc;
-	xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1;
-	xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1;
-	r = sqrt(x0*x0 + y0*y0);
-	if (r > 0.0) {
-		j = quadrant(x0,y0);
-		k = quadrant(x1,y1);
-		if (j == k && y1*x0 < x1*y0) {
-			/* viewed as complex numbers, if Im(z1/z0)<0, arc is big */
-			if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r;
-			if( xmax <  r) xmax =  r; if( ymax <  r) ymax =  r;
-		} else {
-			while (j != k) {
-				switch (j) {
-				case 1: if( ymax <  r) ymax =  r; break; /* north */
-				case 2: if( xmin > -r) xmin = -r; break; /* west */
-				case 3: if( ymin > -r) ymin = -r; break; /* south */
-				case 4: if( xmax <  r) xmax =  r; break; /* east */
-				}
-				j = j%4 + 1;
-			}
-		}
-	}
-	xmin += xc; ymin += yc; ymin = -ymin;
-	xmax += xc; ymax += yc; ymax = -ymax;
-}
-
-
-static int getDy(char *p, int *dx, int *maxv)
-				// figure out where we are after a D'...'
-{
-	int x, y, x1, y1;	// for input values
-	char temp[50];
-	p++;		// get to command letter
-	switch (*p++) {
-	case 'l':	// line
-		sscanf(p, "%d %d", dx, &y);
-		return *maxv = y;
-	case 'a':	// arc
-		sscanf(p, "%d %d %d %d", &x, &y, &x1, &y1);
-		*dx = x1 - x;
-		arc_extreme(0, 0, x+x1, y+y1, x, y);	// sets [xy][max|min]
-		printf("#arc bounds x %g, %g; y %g, %g\n",
-			xmin, xmax, ymin, ymax);
-		*maxv = (int) (ymin+0.5);
-		return y + y1;
-	case '~':	// spline
-		for (*dx = *maxv = y = 0; (p=getstr(p, temp)) != NULL; ) {
-						// above getstr() gets x value
-			*dx += atoi(temp);
-			p = getstr(p, temp);	// this one gets y value
-			y += atoi(temp);
-			*maxv = max(*maxv, y);	// ok???
-			if (*p == '\n' || *p == 0)	// input is a single line;
-				break;			// don't walk off end if realloc
-		}
-		return y;
-	case 'c':	// circle, ellipse
-		sscanf(p, "%d", dx);
-		*maxv = *dx/2;		// high water mark is ht/2
-		return 0;
-	case 'e':
-		sscanf(p, "%d %d", dx, &y);
-		*maxv = y/2;		// high water mark is ht/2
-		return 0;
-	default:	// weird stuff
-		return 0;
-	}
-}
-
-static int serialnum = 0;
-
-slug eofslug()
-{
-	slug ret;
-	ret.serialnum = serialnum;
-	ret.type = EOF;
-	ret.dp = wherebuf();
-	return ret;
-}
-
-slug getslug(FILE *fp)
-{
-	if (inbuf == NULL) {
-		if ((inbuf = (char *) malloc(ninbuf = DELTABUF)) == NULL)
-			ERROR "no room for %d character input buffer\n", ninbuf FATAL;
-		inbp = inbuf;
-	}
-	if (wherebuf() > ninbuf-5000) {
-		// this is still flaky -- lines can be very long
-		int where = wherebuf();	// where we were
-		if ((inbuf = (char *) realloc(inbuf, ninbuf += DELTABUF)) == NULL)
-			ERROR "no room for %d character input buffer\n", ninbuf FATAL;
-		ERROR "grew input buffer to %d characters\n", ninbuf WARNING;
-		inbp = inbuf + where;	// same offset in new array
-	}
-	static int baseV = 0;	// first V command of preceding slug
-	static int curV = 0, curH = 0;
-	static int font = 0, size = 0;
-	static int baseadj = 0;
-	static int ncol = 1, offset = 0;	// multi-column stuff
-	char str[1000], str2[1000], buf[3000], *p;
-	int firstV = 0, firstH = 0;
-	int maxV = curV;
-	int ocurV = curV, mxv = 0, dx = 0;
-	int sawD = 0;		// > 0 if have seen D...
-	slug ret;
-	ret.serialnum = serialnum++;
-	ret.type = VBOX;	// use the same as last by default
-	ret.dv = curV - baseV;
-	ret.hpos = curH;
-	ret.base =  ret.parm = ret.parm2 = ret.seen = 0;
-	ret.font = font;
-	ret.size = size;
-	ret.dp = wherebuf();
-	ret.ncol = ncol;
-	ret.offset = offset;
-	ret.linenum = linenum;	// might be low
-
-	for (;;) {
-		int c, m, n;	// for input values
-		int sign;		// hoisted from case 'h' below
-		switch (c = getc(fp)) {
-		case EOF:
-			ret.type = EOF;
-			ret.dv = 0;
-			if (baseadj)
-				printf("# adjusted %d bases\n", baseadj);
-			printf("# %d characters, %d lines\n", wherebuf(), linenum);
-			return ret;
-		case 'V':
-			fscanf(fp, "%d", &n);
-			if (firstV++ == 0) {
-				ret.dv = n - baseV;
-				baseV = n;
-			} else {
-				sprintf(buf, "v%d", n - curV);
-				adds(buf);
-			}
-			curV = n;
-			maxV = max(maxV, curV);
-			break;
-		case 'H':		// absolute H motion
-			fscanf(fp, "%d", &n);
-			if (firstH++ == 0) {
-				ret.hpos = n;
-			} else {
-				sprintf(buf, "h%d", n - curH);
-				adds(buf);
-			}
-			curH = n;
-			break;
-		case 'h':		// relative H motion
-			addc(c);
-			sign = 1;
-			if ((c = getc(fp)) == '-') {
-				addc(c);
-				sign = -1;
-				c = getc(fp);
-			}
-			for (n = 0; isdigit(c); c = getc(fp)) {
-				addc(c);
-				n = 10 * n + c - '0';
-			}
-			curH += n * sign;
-			ungetc(c, fp);
-			break;
-		case 'x':	// device control: x ...
-			addc(c);
-			fgets(buf, (int) sizeof(buf), fp);
-			linenum++;
-			adds(buf);
-			if (buf[0] == ' ' && buf[1] == 'X') {	// x X ...
-				if (2 != sscanf(buf+2, "%s %d", str, &n))
-					n = 0;
-				if (eq(str, "SP")) {	// X SP n
-					ret.type = SP;	// paddable SPace
-					ret.dv = n;	// of height n
-				} else if (eq(str, "BS")) {
-					ret.type = BS;	// Breakable Stream
-					ret.parm = n;	// >=n VBOXES on a page
-				} else if (eq(str, "BF")) {
-					ret.type = BF;	// Breakable Float
-					ret.parm = ret.parm2 = n;
-							// n = pref center (as UF)
-				} else if (eq(str, "US")) {
-					ret.type = US;	// Unbreakable Stream
-					ret.parm = n;
-				} else if (eq(str, "UF")) {
-					ret.type = UF;	// Unbreakable Float
-					ret.parm = ret.parm2 = n;
-							// n = preferred center
-							// to select several,
-							// use several UF lines
-				} else if (eq(str, "PT")) {
-					ret.type = PT;	// Page Title
-					ret.parm = n;
-				} else if (eq(str, "BT")) {
-					ret.type = BT;	// Bottom Title
-					ret.parm = n;
-				} else if (eq(str, "END")) {
-					ret.type = END;
-					ret.parm = n;
-				} else if (eq(str, "TM")) {
-					ret.type = TM;	// Terminal Message
-					ret.dv = 0;
-				} else if (eq(str, "COORD")) {
-					ret.type = COORD;// page COORDinates
-					ret.dv = 0;
-				} else if (eq(str, "NE")) {
-					ret.type = NE;	// NEed to break page
-					ret.dv = n;	// if <n units left
-				} else if (eq(str, "MC")) {
-					ret.type = MC;	// Multiple Columns
-					sscanf(buf+2, "%s %d %d",
-						str, &ncol, &offset);
-					ret.ncol = ncol;
-					ret.offset = offset;
-				} else if (eq(str, "CMD")) {
-					ret.type = CMD;	// CoMmaNd
-					sscanf(buf+2, "%s %s", str2, str);
-					if (eq(str, "FC"))	// Freeze 2-Col
-						ret.parm = FC;
-					else if (eq(str, "FL"))	// FLush
-						ret.parm = FL;
-					else if (eq(str, "BP"))	// Break Page
-						ret.parm = BP;
-					else ERROR "unknown command %s\n",
-						str WARNING;
-				} else if (eq(str, "PARM")) {
-					ret.type = PARM;// PARaMeter
-					sscanf(buf+2, "%s %s %d", str2, str, &ret.parm2);
-					if (eq(str, "NP"))	// New Page
-						ret.parm = NP;
-					else if (eq(str, "FO"))	// FOoter
-						ret.parm = FO;
-					else if (eq(str, "PL")) // Page Length
-						ret.parm = PL;
-					else if (eq(str, "MF")) // MinFull
-						ret.parm = MF;
-					else if (eq(str, "CT")) // ColTol
-						ret.parm = CT;
-					else if (eq(str, "WARN")) //WARNings?
-						ret.parm = WARN;
-					else if (eq(str, "DBG"))// DeBuG
-						ret.parm = DBG;
-					else ERROR "unknown parameter %s\n",
-						str WARNING;
-				} else
-					break;		// out of switch
-				if (firstV > 0)
-					ERROR "weird x X %s in mid-VBOX\n",
-						str WARNING;
-				return ret;
-			}
-			break;
-		case 'n':	// end of line
-			fscanf(fp, "%d %d", &n, &m);
-			ret.ht = n;
-			ret.base = m;
-			getc(fp);	// newline
-			linenum++;
-			sprintf(buf, "n%d %d\n", ret.ht, ret.base);
-			adds(buf);
-			if (!firstV++)
-				baseV = curV;
-			// older incarnations of this program used ret.base
-			// in complicated and unreliable ways;
-			// example:  if ret.ht + ret.base < ret.dv, ret.base = 0
-			// this was meant to avoid double-counting the space
-			// around displayed equations; it didn't work
-			// Now, we believe ret.base = 0, otherwise we give it
-			// a value we have computed.
-			if (ret.base == 0 && sawD == 0)
-				return ret;	// don't fiddle 0-bases
-			if (ret.base != maxV - baseV) {
-				ret.base = maxV - baseV;
-				baseadj++;
-			}
-			if (ret.type != VBOX)
-				ERROR "%s slug (type %d) has base = %d\n",
-					ret.typename(), ret.type, ret.base WARNING;
-			return ret;
-		case 'p':	// new page
-			fscanf(fp, "%d", &n);
-			ret.type = PAGE;
-			curV = baseV = ret.dv = 0;
-			ret.parm = n;	// just in case someone needs it
-			return ret;
-		case 's':	// size change snnn
-			fscanf(fp, "%d", &size);
-			sprintf(buf, "s%d\n", size);
-			adds(buf);
-			break;
-		case 'f':	// font fnnn
-			fscanf(fp, "%d", &font);
-			sprintf(buf, "f%d\n", font);
-			adds(buf);
-			break;
-		case '\n':
-			linenum++;
-			/* fall through */
-		case ' ':
-			addc(c);
-			break;
-		case '0': case '1': case '2': case '3': case '4':
-		case '5': case '6': case '7': case '8': case '9':
-			// two motion digits plus a character
-			addc(c);
-			n = c - '0';
-			addc(c = getc(fp));
-			curH += 10 * n + c - '0';
-			adds(getutf(fp));
-			if (!firstV++)
-				baseV = curV;
-			break;
-		case 'c':	// single ascii character
-			addc(c);
-			adds(getutf(fp));
-			if (!firstV++)
-				baseV = curV;
-			break;
-		case 'C':	// Cxyz\n
-		case 'N':	// Nnnn\n
-			addc(c);
-			while ((c = getc(fp)) != ' ' && c != '\n')
-				addc(c);
-			addc(c);
-			if (!firstV++)
-				baseV = curV;
-			linenum++;
-			break;
-		case 'D':	// draw function: D.*\n
-			sawD++;
-			p = bufptr(wherebuf());	// where does the D start
-			addc(c);
-			while ((c = getc(fp)) != '\n')
-				addc(c);
-			addc(c);
-			if (!firstV++)
-				baseV = curV;
-			ocurV = curV, mxv = 0, dx = 0;
-			curV += getDy(p, &dx, &mxv);	// figure out how big it is
-			maxV = max(max(maxV, curV), ocurV+mxv);
-			curH += dx;
-			linenum++;
-			break;
-		case 'v':	// relative vertical vnnn
-			addc(c);
-			if (!firstV++)
-				baseV = curV;
-			sign = 1;
-			if ((c = getc(fp)) == '-') {
-				addc(c);
-				sign = -1;
-				c = getc(fp);
-			}
-			for (n = 0; isdigit(c); c = getc(fp)) {
-				addc(c);
-				n = 10 * n + c - '0';
-			}
-			ungetc(c, fp);
-			curV += n * sign;
-			maxV = max(maxV, curV);
-			addc('\n');
-			break;
-		case 'w':	// word space
-			addc(c);
-			break;
-		case '#':	// comment
-			addc(c);
-			while ((c = getc(fp)) != '\n')
-				addc(c);
-			addc('\n');
-			linenum++;
-			break;
-		default:
-			ERROR "unknown input character %o %c (%50.50s)\n",
-				c, c, bufptr(wherebuf()-50) WARNING;
-			break;
-		}
-	}
-}
//GO.SYSIN DD slug.c
echo slug.h
sed 's/.//' >slug.h <<'//GO.SYSIN DD slug.h'
-enum slugtypes {
-	NONE,		// can't happen
-	VBOX,		// Vertical Box -- printable stuff
-	SP,		// paddable SPace
-	BS,		// start Breakable Stream
-	US,		// start Unbreakable Stream
-	BF,		// start Breakable Float
-	UF,		// start Unbreakable Float
-	PT,		// start Page Top material (header)
-	BT,		// start page BoTtom material (footer)
-	END,		// ENDs of groups
-	NEUTRAL,	// NEUTRALized slugs can do no harm (cf. CIA)
-	PAGE,		// beginning of PAGE in troff input
-	TM,		// Terminal Message to appear during output
-	COORD,		// output page COORDinates
-	NE,		// NEed command
-	MC,		// Multiple-Column command
-	CMD,		// misc CoMmanDs:  FC, FL, BP
-	PARM,		// misc PARaMeters:  NP, FO
-	LASTTYPE	// can't happen either
-};
-
-enum cmdtypes {
-	FC,	// Freeze 2-Column material
-	FL,	// FLush all floats before reading more stream
-	BP	// Break Page
-};
-
-enum parmtypes {
-	NP,	// distance of top margin from page top (New Page)
-	FO,	// distance of bottom margin from page top (FOoter)
-	PL,	// distance of physical page bottom from page top (Page Length)
-	MF,	// minimum fullness required for padding
-	CT,	// tolerance for division into two columns
-	WARN,	// warnings to stderr?	
-	DBG	// debugging flag
-};
-
-class slug {
-	int	serialnum;
-	int	dp;		// offset of data for this slug in inbuf
-	int	linenum;	// input line number (approx) for this slug
-	short	font;		// font in effect at slug beginning
-	short	size;		// size in effect at slug beginning
-	short	seen;		// 0 until output
-	short	ncol;		// number of columns (1 or 2)
-	short	offset;		// horizontal offset for 2 columns
-  public:
-	short	type;		// VBOX, PP, etc.
-	short	parm;		// parameter
-	short	base;		// "depth" of this slug (from n command)
-	int	hpos;		// abs horizontal position
-	int	dv;		// height of this slug above its input Vpos
-	union {
-		int	ht;	// "height" of this slug (from n command)
-		int	parm2;	// second parameter, since only VBOXes have ht
-	};
-	friend	slug getslug(FILE *);
-	friend	void checkout();
-	friend	slug eofslug();
-	void	coalesce();	// with next slug in array slugs[]
-	void	neutralize();	// render this one a no-op
-	void	dump();		// dump its contents for debugging
-	char	*headstr();	// string value of text
-	void	slugout(int);	// add the slug to the output
-	char	*typename();	// printable slug type
-	int	serialno()	{ return serialnum; }
-	int	numcol()	{ return ncol; }
-	int	lineno()	{ return linenum; }
-};
-
-// functions in slug.c
-slug	eofslug();
-slug	getslug(FILE *);
//GO.SYSIN DD slug.h
echo tmac.pm
sed 's/.//' >tmac.pm <<'//GO.SYSIN DD tmac.pm'
-.\" 10/22/92 activate next line before installing
-.pi /$objtype/bin/aux/pm
-.
-.		\" IZ - initialization
-.de IZ
-.fp 1 R			\" force a font out into prefix
-.nr PS 10		\" point size
-.nr VS 12		\" line spacing
-.ps \\n(PS
-.ie \\n(VS>=41 .vs \\n(VSu
-.el .vs \\n(VSp
-.nr LL 6i		\" line length
-.ll \\n(LLu
-.nr LT \\n(.l		\" title length
-.lt \\n(LTu
-.if !\\n(HM .nr HM 1i   \" top of page
-.if !\\n(FM .nr FM 1i	\" footer margin
-.if !\\n(FO .nr FO \\n(.p-\\n(FM	\" bottom of page
-.			\" to set text ht to N, set FO to N + \n(HM.  default is 10i
-.pl 32767u		\" safety first: big pages for pm
-.if !\\n(PO .nr PO \\n(.ou	\" page offset
-.nr PI 5n		\" .PP paragraph indent
-.nr QI 5n		\" .QS indent
-.nr DI 5n		\" .DS indent
-.nr PD 0.3v		\" paragraph vertical separation
-.nr TS 0.5v		\" space around tables
-.nr Kf 0.5v		\" space around .KF/.KE
-.nr Ks 0.5v		\" space around .KS/.KE
-.
-.nr P1 .4i		\" indent for .P1/.P2
-.nr dP 1		\" delta point size for programs in .P1/.P2
-.nr dV 2p		\" delta vertical for programs
-.nr dT 8		\" delta tab stop for programs
-.nr DV .5v		\" space before start of program
-.nr IP 0		\" ?
-.nr IR 0		\" ?
-.nr I1 \\n(PIu
-.ev 1
-.if !\\n(FL .nr FL \\n(LLu	\" footnote length
-.ll \\n(FLu
-.ps 8			\" text size & leading in footnote
-.vs 10p
-.ev
-.if \\*(CH .ds CH "\(hy \\\\n(PN \(hy
-.ds # #\\\\n(.c \\\\n(.F
-.
-.
-.ME	\" initialize date strings
-.rm ME
-.	\"  accents:  \*'e \*`e \*:u \*^e \*~n \*va \*,c
-.ds ' \h'\w'e'u*4/10'\z\(aa\h'-\w'e'u*4/10'
-.ds ` \h'\w'e'u*4/10'\z\(ga\h'-\w'e'u*4/10'
-.ds : \\v'-0.6m'\\h'(1u-(\\\\n(.fu%2u))*0.13m+0.00m'\\z.\\h'0.2m'\\z.\\h'-((1u-(\\\\n(.fu%2u))*0.13m+0.20m)'\\v'0.6m'
-.ds ^ \\\\k:\\h'-\\\\n(.fu+1u/2u*2u+\\\\n(.fu-1u*0.13m+0.06m'\\z^\\h'|\\\\n:u'
-.ds ~ \\\\k:\\h'-\\\\n(.fu+1u/2u*2u+\\\\n(.fu-1u*0.13m+0.06m'\\z~\\h'|\\\\n:u'
-.ds v \\\\k:\\\\h'+\\\\w'e'u/4u'\\\\v'-0.6m'\\\\s6v\\\\s0\\\\v'0.6m'\\\\h'|\\\\n:u'
-.ds , \\\\k:\\\\h'\\\\w'c'u*0.4u'\\\\z,\\\\h'|\\\\n:u'
-..
-.
-.
-.		\" SP - generate paddable space
-.de SP
-.br
-.nr X 1v
-.if \\n(.$ .nr X \\$1v
-.ie '\\$2'exactly' \{\
-\v'\\nXu'\ \h'-\w'\ 'u'\c
-.sp \\$1\}
-.el .X "SP \\nX \\$2"
-..
-.		\" NE - need space on this page
-.de NE
-.nr X 1v
-.if \\n(.$ .nr X \\$1v
-.X "NE \\nX \\$2"
-..
-.		\" BP, FL, FC - begin page, flush figures, flush column
-.de BP
-.br
-.X CMD BP
-..
-.de FL
-.br
-.X CMD FL
-..
-.de FC
-.br
-.X CMD FC
-..
-.		\" X - generate an x X ... command in the output
-.de X
-....ie '\\n(.z'' \\!x X \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
-....el \\!.X "\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
-...
-.if !'\\n(.z'' .if \\n(.$=1 \\!.X "\\$1
-.if !'\\n(.z'' .if \\n(.$=2 \\!.X "\\$1 \\$2
-.if !'\\n(.z'' .if \\n(.$=3 \\!.X "\\$1 \\$2 \\$3
-.if !'\\n(.z'' .if \\n(.$>3 \\!.X "\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
-.if '\\n(.z'' .if \\n(.$=1 \\!x X \\$1 \\*#
-.if '\\n(.z'' .if \\n(.$=2 \\!x X \\$1 \\$2 \\*#
-.if '\\n(.z'' .if \\n(.$=3 \\!x X \\$1 \\$2 \\$3 \\*#
-.if '\\n(.z'' .if \\n(.$=4 \\!x X \\$1 \\$2 \\$3 \\$4 \\*#
-.if '\\n(.z'' .if \\n(.$>4 \\!x X \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 \\*#
-..
-.		\" DA - force date
-.de DA
-.if \\n(.$ .ds DY \\$1 \\$2 \\$3 \\$4
-.ds CF \\*(DY
-..
-.		\" ND - set new or no date
-.de ND
-.ds DY \\$1 \\$2 \\$3 \\$4
-.rm CF
-..
-.de ME		\" ME - set month strings
-.if \\n(mo-0 .ds MO January
-.if \\n(mo-1 .ds MO February
-.if \\n(mo-2 .ds MO March
-.if \\n(mo-3 .ds MO April
-.if \\n(mo-4 .ds MO May
-.if \\n(mo-5 .ds MO June
-.if \\n(mo-6 .ds MO July
-.if \\n(mo-7 .ds MO August
-.if \\n(mo-8 .ds MO September
-.if \\n(mo-9 .ds MO October
-.if \\n(mo-10 .ds MO November
-.if \\n(mo-11 .ds MO December
-.if \\n(dw-0 .ds DW Sunday
-.if \\n(dw-1 .ds DW Monday
-.if \\n(dw-2 .ds DW Tuesday
-.if \\n(dw-3 .ds DW Wednesday
-.if \\n(dw-4 .ds DW Thursday
-.if \\n(dw-5 .ds DW Friday
-.if \\n(dw-6 .ds DW Saturday
-.if "\\*(DY"" .ds DY \\*(MO \\n(dy, 19\\n(yr
-..
-.		\" FP - font position for a family
-.de FP
-.if '\\$1'palatino'\{\
-.	fp 1 PA
-.	fp 2 PI
-.	fp 3 PB
-.	fp 4 PX\}
-.if '\\$1'century'\{\
-.	ie '\\*(.T'202'\{\
-.		fp 1 NR Centsb
-.		fp 2 NI CentI
-.		fp 3 NB CentB
-.		fp 4 NX CentBI\}
-.	el \{\
-.		fp 1 NR
-.		fp 2 NI
-.		fp 3 NB
-.		fp 4 NX\}\}
-.if '\\$1'helvetica'\{\
-.	fp 1 H
-.	fp 2 HI
-.	fp 3 HB
-.	fp 4 HX\}
-.if '\\$1'bembo'\{\
-.	ie '\\*(.T'202'\{\
-.		fp 1 B1 Bembo
-.		fp 2 B2 BemboI
-.		fp 3 B3 BemboB
-.		fp 4 B4 BemboBI\}
-.	el \{\
-.		fp 1 B1
-.		fp 2 B2
-.		fp 3 B3
-.		fp 4 B4\}\}
-.if '\\$1'optima'\{\
-.	fp 1 R Optima
-.	fp 2 I OptimaI
-.	fp 3 B OptimaB
-.	fp 4 BI OptimaBI\}
-.if '\\$1'souvenir'\{\
-.	fp 1 R Souvenir
-.	fp 2 I SouvenirI
-.	fp 3 B SouvenirB
-.	fp 4 BI SouvenirBI\}
-.if '\\$1'melior'\{\
-.	fp 1 R Melior
-.	fp 2 I MeliorI
-.	fp 3 B MeliorB
-.	fp 4 BI MeliorBI\}
-.if '\\$1'times'\{\
-.	fp 1 R
-.	fp 2 I
-.	fp 3 B
-.	fp 4 BI\}
-..
-.		\" TL - title
-.de TL
-.br
-.if !\\n(1T .BG
-....hy 0
-.ft 3
-.ps \\n(PS+2p
-.vs \\n(VS+2p
-.ll \\n(LLu
-.ce 100		\" turned off in .RT
-.sp .5i
-..
-.		\" AU - remember author(s)
-.de AU
-.ft 1
-.ps \\n(PS
-.ie \\n(VS>=41 .vs \\n(VSu
-.el .vs \\n(VSp
-.SP .5
-..
-.		\" AI - author's institution
-.de AI
-.SP .25
-.ft 2
-..
-.		\" AB - begin abstract
-.de AB
-.nr AB 1	  \" we're in abstract
-.if !\\n(1T .BG
-.ft 1
-.ps \\n(PS
-.vs \\n(VSp
-.ce
-.in +\\n(.lu/12u
-.ll -\\n(.lu/12u
-.SP 1
-.ie \\n(.$ \\$1
-.el ABSTRACT
-.SP .75 
-.RT
-..
-.		\" AE - end of abstract
-.de AE
-.br
-.nr AB 0
-.in 0
-.ll \\n(LLu
-.ps \\n(PS
-.ie \\n(VS>=41 .vs \\n(VSu
-.el .vs \\n(VSp
-.SP
-..
-.		\" 2C - 2 columns
-.de 2C
-.MC 2
-..
-.		\" 1C - 1 column
-.de 1C
-.MC 1
-..
-.		\" MC - multiple columns
-.de MC
-.br
-.if \\n(1T .RT
-.if \\n(1T .NP
-.if !\\n(OL .nr OL \\n(LL
-.if \\n(CW=0 .nr CW \\n(LL*7/15
-.if \\n(GW=0 .nr GW \\n(LL-(2*\\n(CW)
-.nr x \\n(CW+\\n(GW
-.if "\\$1"" .MC 2
-.if \\$1=1 \{\
-.	X MC 1 0
-.	nr LL \\n(OLu\}
-.if \\$1=2 \{\
-.	X MC 2 \\nx
-.	nr LL \\n(CWu\}
-.ll \\n(LLu
-.if \\$1>2 .tm -mpm can't handle more than two columns
-.if \\n(1T .RT
-..
-.		\" TS - table start, TE - table end;  also TC, TQ, TH
-.de TS
-.br
-.if !\\n(1T .RT
-.SP \\n(TSu TS
-.X "US TS
-.if \\$1H .TQ
-.nr IX 1
-..
-.de TC
-.nr TZ \\n(.lu
-.if \\n(.$ .nr TZ \\$1n
-.ta \\n(TZuR
-..
-.de TD
-.LP
-.nr TZ 0
-..
-.de TQ
-.di TT
-.nr IT 1
-..
-.de TH
-.if \\n(.d>0.5v \{\
-.	nr T. 0
-.	T# 0\}
-.di
-.nr TQ \\n(.i
-.nr HT 1
-.in 0
-.mk #a
-.mk #b
-.mk #c
-.mk #d
-.mk #e
-.mk #f
-.TT
-.in \\n(TQu
-.mk #T
-..
-.		\" TE - table end
-.de TE
-.nr IX 0
-.if \\n(IT .if !\\n(HT \{\
-.	di
-.	nr EF \\n(.u
-.	nf
-.	TT
-.	if \\n(EF .fi\}
-.nr IT 0
-.nr HT 0
-.rm a+ b+ c+ d+ e+ f+ g+ h+ i+ j+ k+ l+ n+ m+
-.rr 32 33 34 35 36 37 38 40 79 80 81 82
-.rr a| b| c| d| e| f| g| h| i| j| k| l| m|
-.rr a- b- c- d- e- f- g- h- i- j- k- l- m-
-.X "END US TE
-.SP \\n(TSu TE
-.bp
-..
-.		\" EQ - equation, breakout and display
-.de EQ
-.nr EF \\n(.u
-.rm EE
-.nr LE 1	\" 1 is center
-.ds EL \\$1
-.if "\\$1"L" \{\
-.	ds EL \\$2
-.	nr LE 0\}
-.if "\\$1"C" .ds EL \\$2
-.if "\\$1"R" \{\
-.	ds EL \\$2 \" 2 is right adjust
-.	nr LE 2\}
-.if "\\$1"I" \{\
-.	nr LE 0
-.	if "\\$3"" .ds EE \\h'|10n'
-.	el .ds EE \\h'\\$3'
-.	ds EL \\$2\}
-.if \\n(YE .nf
-.di EZ
-..
-.		\" EN - end of equation
-.de EN 
-.br
-.di
-.rm EZ
-.nr ZN \\n(dn
-.if \\n(ZN .if !\\n(YE .LP
-.if !\\n(ZN .if !"\\*(EL"" .nr ZN 1
-.if \\n(ZN \{\
-.	SP .5v EQ
-.	X "US EQ"\}
-'pc
-.if \\n(BD .nr LE 0 \" don't center if block display or mark/lineup
-.if \\n(MK \{\
-.	if \\n(LE=1 .ds EE \\h'|10n'
-.	nr LE 0\}
-'lt \\n(.lu
-.if !\\n(EP .if \\n(ZN \{\
-.	if \\n(LE=1 .tl \(ts\(ts\\*(10\(ts\\*(EL\(ts
-.	if \\n(LE=2 .tl \(ts\(ts\(ts\\*(10\\*(EL\(ts
-.	if !\\n(LE \{\
-.		if !\\n(BD .tl \(ts\\*(EE\\*(10\(ts\(ts\\*(EL\(ts
-.		if \\n(BD .if \\n(BD<\\w\(ts\\*(10\(ts .nr BD \\w\(ts\\*(10\(ts
-.		if \\n(BD \!\\*(10\\t\\*(EL\}\}
-.if \\n(EP .if \\n(ZN \{\
-.	if \\n(LE=1 .tl \(ts\\*(EL\(ts\\*(10\(ts\(ts
-.	if \\n(LE=2 .tl \(ts\\*(EL\(ts\(ts\\*(10\(ts
-.	if !\\n(LE \{\
-.		if !\\n(BD .tl \(ts\\*(EL\\*(EE\\*(10\(ts\(ts\(ts
-.		if \\n(BD .if \\n(BD<\\w\(ts\\*(10\(ts .nr BD \\w\(ts\\*(10\(ts
-.		if \\n(BD \!\\h'-\\\\n(.iu'\\*(EL\\h'|0'\\*(10\}\}
-'lt \\n(LLu
-'pc %
-.if \\n(YE .if \\n(EF .fi
-.if \\n(ZN .X "END US EQ"
-.if \\n(ZN .SP .5v EN
-.if \\n(ZN .bp
-..
-.		\" PS - start picture
-.de PS			\" $1 is height, $2 is width, in inches
-.br
-.nr X 0.35v
-.if \\$1>0 .X "SP \\nX PS"
-.ie \\$1>0 .nr $1 \\$1
-.el .nr $1 0
-.X "US PS \\$1
-.in (\\n(.lu-\\$2)/2u
-..
-.		\" PE - end of picture
-.de PE
-.in
-.X "END US PE
-.nr X .65v
-.if \\n($1>0 .X "SP \\nX PE"
-.bp
-..
-.de IS	\" for -mpm only
-.KS
-..
-.de IE
-.KE
-.bp
-..
-.		\" NP - new page
-.de NP
-.ev 2
-.bp
-.if \\n(KF=0 \{\
-.	nr PX \\n(.s
-.	nr PF \\n(.f
-.	nr PV \\n(.v
-.	lt \\n(LTu
-.	ps \\n(PS
-.	vs \\n(PS+2
-.	ft 1
-.	if \\n(PO .po \\n(POu	\" why isn't this reset???
-.	PT \\$1
-.	bp
-.	rs
-.	BT
-.	bp
-.	nr %# +1
-.	ps \\n(PX
-.	vs \\n(PVu
-.	ft \\n(PF \}
-.ev
-..
-.
-.ds %e .tl '\\*(LH'\\*(CH'\\*(RH'
-.ds %o .tl '\\*(LH'\\*(CH'\\*(RH'
-.ds %E .tl '\\*(LF'\\*(CF'\\*(RF'
-.ds %O .tl '\\*(LF'\\*(CF'\\*(RF'
-.
-.		\" PT - page title
-.de PT
-.nr PN \\n(%#
-.X "PT \\n(%#
-.sp \\n(HMu/2u
-.if \\n(OL .lt \\n(OLu		\" why isn't this reset???
-.if \\n(BT>0 .if \\n(%#%2 \\*(%o
-.if \\n(BT>0 .if !\\n(%#%2 \\*(%e
-.if \\n(BT=0 .tl '\0'''		\" put out something or spacing is curdled
-.X "END PT \\n(%#
-..
-.		\" BT - bottom title
-.de BT
-.X "BT \\n(%#
-.sp |\\n(FMu/2u+\\n(FOu-1v
-.if \\n(%#%2 \\*(%O
-.if !\\n(%#%2 \\*(%E
-.nr BT \\n(BT+1
-.X "END BT \\n(%#
-..
-.		\" KS - non-floating keep
-.de KS
-.br
-.if "\\n(.z"" .NP  \" defends poorly against including ht of page stuff in diversion for .B1
-.X "US KS 0
-.nr KS +1
-.SP \\n(Ksu
-..
-.		\" KF - floating keep
-.de KF
-.ev 1
-.br
-.if \\n(KS>0 .tm KF won't work inside KS, line \\n(.c, file \\n(.F
-.if \\n(KF>0 .tm KF won't work inside KF, line \\n(.c, file \\n(.F
-.nr KF 1
-.nr 10 0
-.	if !'\\$1'' .nr 10 \\$1u
-.	if '\\$1'bottom' .nr 10 \\n(FOu-1u
-.	if '\\$1'top' .nr 10 \\n(HM
-.	if \\n(10 .X "UF \\n(10 KF"
-.	if !\\n(10 .X "UF \\n(HM KF"
-.	nr X \\n(FOu-2u
-.	if \\n(10 .X "UF \\n(10 KF"
-.	if !\\n(10 .X "UF \\nX KF"
-.nr SJ \\n(.u
-.ps \\n(PS
-.if \\n(VS>40 .vs \\n(VSu
-.if \\n(VS<=39 .vs \\n(VSp
-.ll \\n(LLu
-.lt \\n(LTu
-.SP \\n(Kfu
-..
-.		\" KE - end of KS/KF
-.de KE
-.bp
-.ie \\n(KS>0 \{\
-.	SP \\n(Ksu
-.	X "END US KS
-.	nr KS -1 \}
-.el .ie \\n(KF>0 \{\
-.	SP \\n(Kfu
-.	nr KF 0
-.	X "END UF KF"
-.	if \\n(SJ .fi
-.	ev \}
-.el .tm .KE without preceding .KS or .KF, line \\n(.c, file \\n(.F
-..
-.
-.		\" DS - display. .DS C center; L left-adjust; I indent (default)
-.de DS		\"  $2 = amount of indent
-.KS
-.nf
-.\\$1D \\$2 \\$1
-.ft 1
-.if !\\n(IF \{\
-.	ps \\n(PS
-.	if \\n(VS>40 .vs \\n(VSu
-.	if \\n(VS<=39 .vs \\n(VSp\}
-..
-.de D
-.ID \\$1
-..
-.de CD
-.XD
-.ce 1000
-..
-.de ID
-.XD
-.if \\n(.$=0 .in +\\n(DIu
-.if \\n(.$=1 .if "\\$1"I" .in +\\n(DIu
-.if \\n(.$=1 .if !"\\$1"I" .in +\\$1n
-.if \\n(.$>1 .in +\\$2n
-.....in +0.5i
-.....if \\n(.$ .if !"\\$1"I" .if !"\\$1"" .in \\n(DIu
-.....if \\n(.$ .if !"\\$1"I" .if !"\\$1"" .in +\\$1n
-..
-.de LD
-.XD
-..
-.de XD
-.nf
-.nr OI \\n(.i
-.SP \\n(DVu
-..
-.		\" BD - block display: save everything, then center it.
-.de BD
-.XD
-.nr BD 1
-.nf
-.in \\n(OIu
-.di DD
-..
-.		\" DE - display end
-.de DE
-.ce 0
-.if \\n(BD>0 .XF
-.nr BD 0
-.in \\n(OIu
-.SP \\n(DVu
-.KE
-.fi
-..
-.		\" XF - finish a block display to be recentered.
-.de XF
-.di
-.if \\n(dl>\\n(BD .nr BD \\n(dl
-.if \\n(BD<\\n(.l .in (\\n(.lu-\\n(BDu)/2u
-.nr EI \\n(.l-\\n(.i
-.ta \\n(EIuR
-.nf
-.DD
-.in \\n(OIu
-..
-.
-.
-.		\" SH - (unnumbered) section heading
-.de SH
-.RT
-.nr X 1v
-.nr Y 3v
-.if \\n(1T .NP
-.if \\n(1T .X "NE \\nY SH"	\" should these be reversed, change Y to 4v
-.if \\n(1T .X "SP \\nX SH
-.ft 3
-..
-.		\" NH - numbered heading
-.de NH
-.RT
-.nr X 1v
-.nr Y 3v
-.if \\n(1T .NP
-.if \\n(1T .X "NE \\nY NH"	\" should these be reversed, change Y to 4v
-.if \\n(1T .X "SP \\nX NH
-.ft 3
-.nr NS \\$1
-.if !\\n(.$ .nr NS 1
-.if !\\n(NS .nr NS 1
-.nr H\\n(NS +1
-.if !\\n(NS-4 .nr H5 0
-.if !\\n(NS-3 .nr H4 0
-.if !\\n(NS-2 .nr H3 0
-.if !\\n(NS-1 .nr H2 0
-.if !\\$1 .if \\n(.$ .nr H1 1
-.ds SN \\n(H1.
-.if \\n(NS-1 .as SN \\n(H2.
-.if \\n(NS-2 .as SN \\n(H3.
-.if \\n(NS-3 .as SN \\n(H4.
-.if \\n(NS-4 .as SN \\n(H5.
-\\*(SN
-..
-.		\" RT - reset at beginning of each PP, LP, etc.
-.de RT
-.if !\\n(AB .if !\\n(1T .BG
-.ce 0
-.if !\\n(AB .if !\\n(KF .if !\\n(IF .if !\\n(IX .if !\\n(BE .di
-.if \\n(QP \{\
-.	ll +\\n(QIu
-.	in -\\n(QIu
-.	nr QP -1\}
-.if !\\n(AB \{\
-.	ll \\n(LLu\}
-.if !\\n(IF .if !\\n(AB \{\
-.	ps \\n(PS
-.	ie \\n(VS>=41 .vs \\n(VSu
-.	el .vs \\n(VSp\}
-.ie \\n(IP \{\
-.	in \\n(I\\n(IRu
-.	nr IP -1\}
-.el .if !\\n(IR \{\
-.	nr I1 \\n(PIu
-.	nr I2 0
-.	nr I3 0
-.	nr I4 0
-.	nr I5 0\}
-.if !\\n(AB .ft 1
-.ta 5n 10n 15n 20n 25n 30n 35n 40n 45n 50n 55n 60n 65n 70n 75n 80n
-.fi
-..
-.		\" BG - begin, execute at first TL, AB, NH, SH, PP, etc.
-.de BG		\"	IZ has been called, so registers have some value
-.br
-.if \\n(CW>0 .if \\n(LL=0 .nr LL \\n(CW+\\n(CW+\\n(GW
-.ll \\n(LLu
-.lt \\n(LLu
-.po \\n(POu
-.nr YE 1		\" ok to cause break in .EQ (earlier ones won't)
-.ev 0
-.hy 14
-.ev
-.ev 1
-.hy 14
-.ev
-.ev 2
-.hy 14
-.ev
-.nr 1T 1
-.X "PARM NP \\n(HM
-.X "PARM FO \\n(FO
-.if !\\n(%# .nr %# 1
-..
-.		\" PP - paragraph
-.de PP
-.RT
-.if \\n(1T .NP
-.if \\n(1T .X "SP \\n(PD PP"
-.if \\n(1T .X "BS 2 PP"
-.ti +\\n(PIu
-..
-.		\" LP - left aligned paragraph
-.de LP
-.RT
-.if \\n(1T .NP
-.if \\n(1T .X "SP \\n(PD LP"
-.if \\n(1T .X "BS 2 LP"
-..
-.		\" IP - indented paragraph
-.de IP
-.RT
-.if !\\n(IP .nr IP +1
-.if \\n(1T .NP
-.if \\n(1T .X "SP \\n(PD PP"
-.if \\n(1T .X "BS 2 IP"
-.nr IU \\n(IR+1
-.if \\n(.$>1 .nr I\\n(IU \\$2n+\\n(I\\n(IRu
-.if \\n(I\\n(IU=0 .nr I\\n(IU \\n(PIu+\\n(I\\n(IRu
-.in \\n(I\\n(IUu
-.nr TY \\n(TZ-\\n(.i
-.nr JQ \\n(I\\n(IU-\\n(I\\n(IR
-.ta \\n(JQu \\n(TYuR
-.if \\n(.$ \{\
-.ti \\n(I\\n(IRu
-\&\\$1\t\c\}
-..
-.		\" QP - quoted paragraph (within IP)
-.de QP
-.RT
-.if \\n(1T .NP
-.if \\n(1T .X "SP \\n(PD QP"
-.if \\n(1T .X "BS 2 QP"
-.nr QP 1
-.in +\\n(QIu
-.ll -\\n(QIu
-.ti \\n(.iu
-..
-.		\" RS - prepare for double indenting
-.de RS
-.nr IS \\n(IP
-.RT
-.nr IP \\n(IS
-.nr IU \\n(IR
-.nr IR +1
-.if !\\n(I\\n(IR .nr I\\n(IR \\n(I\\n(IU+\\n(PIu
-.in \\n(I\\n(IRu
-.nr TY \\n(TZ-\\n(.i
-.ta \\n(TYuR
-..
-.		\" RE - retreat to the left
-.de RE
-.nr IS \\n(IP
-.RT
-.nr IP \\n(IS
-.if \\n(IR>0 .nr IR -1
-.in \\n(I\\n(IRu
-..
-.		\" B - bold font
-.de B
-.nr PQ \\n(.f
-.ft 3
-.if \\n(.$ \&\\$1\\f\\n(PQ\\$2
-..
-.		\" BI - bold italic
-.de BI
-.nr PQ \\n(.f
-.ft 4
-.if \\n(.$ \&\\$1\\f\\n(PQ\\$2
-..
-.		\" R - Roman font
-.de R
-.nr PQ \\n(.f
-.ft 1
-.if \\n(.$ \&\\$1\f\\n(PQ\\$2
-..
-.		\" I - italic font
-.de I
-.nr PQ \\n(.f
-.ft 2
-.if \\n(.$ \&\\$1\^\f\\n(PQ\\$2
-..
-.		\" CW - constant width font from -ms
-.de CW
-.nr PQ \\n(.f
-.if \\n(.$=0 .ft CW
-.if \\n(.$>0 \%\&\\$3\f(CW\\$1\\f\\n(PQ\\$2
-..
-.de IT		\" ditto to italicize argument
-.nr Sf \\n(.f
-\%\&\\$3\f2\\$1\f\\n(Sf\&\\$2
-..
-.		\" TA - tabs set in ens or chars
-.de TA
-.ta \\$1n \\$2n \\$3n \\$4n \\$5n \\$6n \\$7n \\$8n \\$9n
-..
-.		\" SM - make smaller size
-.de SM
-.ie \\n(.$ \&\\$3\s-2\\$1\s0\\$2
-.el .ps -2
-..
-.		\" LG - make larger size
-.de LG
-.ie \\n(.$ \&\\$3\s+2\\$1\s0\\$2
-.el .ps +2
-..
-.		\" NL - return to normal size
-.de NL
-.ps \\n(PS
-..
-.		\" FS - begin footnote
-.de FS
-.if \\n(IF>0 .tm .FS within .FS/.FE, line \\n(.c, file \\n(.F
-.if \\n(KF>0 .tm .FS won't work inside .KF, line \\n(.c, file \\n(.F
-.if \\n(KS>0 .tm .FS won't work inside .KS, line \\n(.c, file \\n(.F
-.nr IF 1
-.ev 1
-.ps \\n(PS-2
-.ie \\n(VS>=41 .vs \\n(VSu-2p
-.el .vs \\n(VSp-2p
-.ll \\n(LLu
-.br
-.nr X \\n(FOu
-.X "BF \\nX FS
-.SP .3v
-....FA	\" deleted by authority of cvw, 10/17/88
-..
-.		\" FE - end footnote
-.de FE
-.if !\\n(IF .tm .FE without .FS, line \\n(.c, file \\n(.F
-.br
-.X "END BF FE
-.bp
-.ev
-.nr IF 0
-..
-.		\" FA - the line for a footnote
-.de FA
-\l'1i'
-.br
-..
-.		\" Tm - message to be passed on
-.de Tm
-.ev 2
-.if \\n(.$=1 .X "TM \\$1
-.if \\n(.$=2 .X "TM \\$1 \\$2
-.if \\n(.$=3 .X "TM \\$1 \\$2 \\$3
-.if \\n(.$=4 .X "TM \\$1 \\$2 \\$3 \\$4
-.if \\n(.$=5 .X "TM \\$1 \\$2 \\$3 \\$4 \\$5
-.if \\n(.$=6 .X "TM \\$1 \\$2 \\$3 \\$4 \\$5 \\$6
-.if \\n(.$=7 .X "TM \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7
-.if \\n(.$=8 .X "TM \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
-.if \\n(.$=9 .X "TM \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
-.br
-.ev
-..
-.de MH
-AT&T Bell Laboratories
-Murray Hill, New Jersey 07974
-..
-.de HO
-AT&T Bell Laboratories
-Holmdel, New Jersey 07733
-..
-.de WH
-AT&T Bell Laboratories
-Whippany, New Jersey 07981
-..
-.de IH
-AT&T Bell Laboratories
-Naperville, Illinois 60540
-..
-.		\" UL - underline argument, don't italicize
-.de UL
-\\$1\l'|0\(ul'\\$2
-..
-.		\" UX - print $2 UNIX $1
-.de UX
-.ie \\n(UX \\$2\s-1UNIX\s0\\$1
-.el \{\
-\\$2\s-1UNIX\\s0\\$1\(rg
-.nr UX 1\}
-..
-.		\" QS - start quote
-.de QS
-.br
-.LP
-.in +\\n(QIu
-.ll -\\n(QIu
-..
-.		\" QE - end quote
-.de QE
-.br
-.ll +\\n(QIu
-.in -\\n(QIu
-.LP
-..
-.		\"  B1 - begin boxed stuff
-.de B1
-.br
-.di BB
-.nr BC 0
-.if "\\$1"C" .nr BC 1
-.nr BE 1
-..
-.		\" B2 - end boxed stuff
-.de B2 
-.br
-.nr BI 1n
-.if \\n(.$>0 .nr BI \\$1n
-.di
-.nr BE 0
-.nr BW \\n(dl
-.nr BH \\n(dn
-.ne \\n(BHu+\\n(.Vu
-.nr BQ \\n(.j
-.nf
-.ti 0
-.if \\n(BC>0 .in +(\\n(.lu-\\n(BWu)/2u
-.in +\\n(BIu
-.ls 1
-.BB
-.ls
-.in -\\n(BIu
-.nr BW +2*\\n(BI
-.sp -1
-\l'\\n(BWu\(ul'\L'-\\n(BHu'\l'|0\(ul'\h'|0'\L'\\n(BHu'
-.if \\n(BC>0 .in -(\\n(.lu-\\n(BWu)/2u
-.if \\n(BQ .fi
-.br
-..
-.		\" BX - boxed stuff
-.de BX
-\(br\|\\$1\|\(br\l'|0\(rn'\l'|0\(ul'
-..
-.
-.	\" macros for programs, etc.
-.
-.ig
-	programs are displayed between .P1/.P2 pairs
-	default is to indent by 1/2 inch, nofill, dP smaller
-	.P1 x causes an indent of x instead.
-
-	.P3 can be used to specify optional page-break points
-	inside .P1/.P2
-..
-.
-.		\" P1 - start of program
-.de P1
-.nr $1 \\n(P1
-.if \\n(.$ .nr $1 \\$1n
-.br
-.X "SP \\n(DV P1"
-.X "US P1"
-.in \\n($1u
-.nf
-.nr v \\n(.v
-.ps -\\n(dP
-.vs -\\n(dVu
-.ft CW
-.nr t \\n(dT*\\w'x'u
-.ta 1u*\\ntu 2u*\\ntu 3u*\\ntu 4u*\\ntu 5u*\\ntu 6u*\\ntu 7u*\\ntu 8u*\\ntu 9u*\\ntu 10u*\\ntu 11u*\\ntu 12u*\\ntu 13u*\\ntu 14u*\\ntu
-..
-.		\" P2 - end of program
-.de P2
-.br
-.ps \\n(PS
-.vs \\nvu
-.ft 1
-.in
-.X "END US P1
-.X "SP \\n(DV P2"
-.fi
-..
-.		\" P3 - provides optional unpadded break in P1/P2
-.de P3
-.nr x \\n(DV
-.nr DV 0
-.P2
-.P1 \\n($1u
-.nr DV \\nx
-..
-.de [
-[
-..
-.de ]
-]
-..
-.IZ
-.rm IZ
-.so /sys/lib/tmac/tmac.srefs
//GO.SYSIN DD tmac.pm


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.