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
|
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 *);
|