Checkpoint: pull in mpm; merge pic from Taj's version of the world
diff --git a/src/cmd/mpm/range.h b/src/cmd/mpm/range.h
new file mode 100644
index 0000000..401035a
--- /dev/null
+++ b/src/cmd/mpm/range.h
@@ -0,0 +1,334 @@
+const int	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*)""; }
+	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 *);