aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/mpm/range.h
blob: a1d00dfba42899133626ce58b2ab21acb31507a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
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; }
	virtual ~range() { }
	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

#undef INFINITY
#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 *);