|  | 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	class 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	class mergestream; | 
|  | multicol *twocol; | 
|  | friend class 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 | 
|  | int main(int, char **); |