wkj | 5f1cf8e | 2004-05-16 07:56:41 +0000 | [diff] [blame] | 1 | const int NOGOAL = -1; |
| 2 | |
| 3 | class stream; |
| 4 | |
| 5 | enum primeflush { NO, YES, EXPECTED, UNEXPECTED }; // mergestream::prime() |
| 6 | |
| 7 | // Ranges do two things. They interpose a layer between slugs and the rest |
| 8 | // of the program; this is important because of the grossness of the slug |
| 9 | // data structure (made necessary by its origins in troff output). Ranges also |
| 10 | // group together other ranges into meaningful chunks like unbreakable stream |
| 11 | // objects, floatable objects, and page headers and footers. |
| 12 | // Member function height() returns a range's height as of the latest composition. |
| 13 | // Member function rawht() returns the range's original height in the input. |
| 14 | class range { |
| 15 | protected: |
| 16 | slug *first; // earliest slug in range |
| 17 | int accumV; // accumulated V to this point |
| 18 | public: |
| 19 | range() { first = 0; accumV = 0; } |
| 20 | range(slug *p) { first = p; accumV = 0; } |
rsc | 05f5d46 | 2006-02-17 18:21:29 +0000 | [diff] [blame^] | 21 | virtual ~range() { } |
wkj | 5f1cf8e | 2004-05-16 07:56:41 +0000 | [diff] [blame] | 22 | char *headstr() { |
| 23 | return first ? first->headstr() : (char*)""; } |
| 24 | char *typename() { return first->typename(); } |
| 25 | int serialno() { return first->serialno(); } |
| 26 | int lineno() { return first->lineno(); } |
| 27 | virtual void dump() { first->dump(); } |
| 28 | virtual void rdump() { dump(); } |
| 29 | virtual int print(int cv, int col) { |
| 30 | first->slugout(col); return cv; } |
| 31 | virtual int floatable() { return 0; } |
| 32 | virtual int brkafter() { return 1; } |
| 33 | virtual int isnested() { return 0; } |
| 34 | virtual int issp() { return 0; } |
| 35 | virtual int isvbox() { return 0; } |
| 36 | virtual int isneed() { return 0; } |
| 37 | virtual int iscmd() { return 0; } |
| 38 | virtual int cmdtype() { return -1; } |
| 39 | virtual int isparm() { return 0; } |
| 40 | virtual int parmtype() { return -1; } |
| 41 | virtual int parm() { return -1; } |
| 42 | virtual int breakable() { return 0; } |
| 43 | virtual int forceflush() { return UNEXPECTED; } |
| 44 | virtual int pn() { return 0; } |
| 45 | virtual stream *children() { return 0; } // see page::peeloff() |
| 46 | virtual void killkids() { } |
| 47 | virtual void enqueue(int = 0); |
| 48 | virtual int height() { return 0; } |
| 49 | virtual int rawht() { return 0; } |
| 50 | virtual int needht() { return 0; } |
| 51 | virtual void reheight(int *, int *) { } |
| 52 | virtual void rerawht(int *, int *) { } |
| 53 | virtual void setheight(int) { } |
| 54 | virtual void restore() { } // goals of floatables |
| 55 | virtual int goal() { return NOGOAL; } |
| 56 | int accum() { return accumV; } |
| 57 | void setaccum(int n) { accumV = n; } |
| 58 | virtual void setgoal(int) { } |
| 59 | virtual void pickgoal(int, double) { } |
| 60 | virtual int numcol() { return first->numcol(); } |
| 61 | virtual int issentinel() { return 0; } |
| 62 | virtual range *clone() { return 0; } |
| 63 | virtual int breaking() { return 0; } |
| 64 | virtual void setbreaking() { } |
| 65 | }; |
| 66 | |
| 67 | class vboxrange : public range { |
| 68 | int dv; // inherited from slug |
| 69 | int base; // inherited from slug |
| 70 | int brk; // 0 => ok to break after, 1 => no break |
| 71 | public: |
| 72 | vboxrange(slug *p) : range(p) { dv = p->dv; base = p->base; brk = p->parm; } |
| 73 | void dump() { |
| 74 | printf("#### VBOX brk? %d dv %d ht %d\n", brk, dv, dv+base); } |
| 75 | int print(int cv, int col) { |
| 76 | printf("V%d\n", cv += dv); first->slugout(col); return cv+base; } |
| 77 | int brkafter() { return !brk; } |
| 78 | int isvbox() { return 1; } |
| 79 | int forceflush() { return NO; } |
| 80 | int height() { return dv + base; } |
| 81 | int rawht() { return first->dv + first->base; } |
| 82 | void reheight(int *cv, int *mv) { |
| 83 | *cv += dv+base; *mv = max(*mv, *cv); } |
| 84 | void rerawht(int *cv, int *mv) { |
| 85 | *cv += rawht(); *mv = max(*mv, *cv); } |
| 86 | }; |
| 87 | |
| 88 | class sprange : public range { |
| 89 | int dv; |
| 90 | public: |
| 91 | sprange(slug *p) : range(p) { dv = first->dv; } |
| 92 | void dump() { |
| 93 | printf("#### SP dv %d (originally %d)\n", dv, first->dv); } |
| 94 | int print(int cv, int col) { |
| 95 | first->slugout(col); return cv + dv; } |
| 96 | int issp() { return 1; } |
| 97 | int forceflush() { return YES; } |
| 98 | int height() { return dv; } |
| 99 | int rawht() { return first->dv; } |
| 100 | void reheight(int *, int *); |
| 101 | void rerawht(int *, int *); |
| 102 | void setheight(int n) { dv = n; } |
| 103 | }; |
| 104 | |
| 105 | class tmrange : public range { |
| 106 | public: |
| 107 | tmrange(slug *p) : range(p) { } |
| 108 | int forceflush() { return NO; } |
| 109 | int print(int cv, int col) { first->slugout(col); return cv; } |
| 110 | }; |
| 111 | |
| 112 | class coordrange : public range { |
| 113 | public: |
| 114 | coordrange(slug *p) : range(p) { } |
| 115 | int forceflush() { return NO; } |
| 116 | int print(int cv, int col) |
| 117 | { first->slugout(col); printf(" Y %d\n", cv); return cv; } |
| 118 | }; |
| 119 | |
| 120 | class nerange : public range { |
| 121 | public: |
| 122 | nerange(slug *p) : range(p) { } |
| 123 | int isneed() { return 1; } |
| 124 | int forceflush() { return YES; } |
| 125 | int needht() { return first->dv; } |
| 126 | }; |
| 127 | |
| 128 | class mcrange : public range { |
| 129 | public: |
| 130 | mcrange(slug *p) : range(p) { } |
| 131 | int forceflush() { return YES; } |
| 132 | }; |
| 133 | |
| 134 | class cmdrange : public range { |
| 135 | public: |
| 136 | cmdrange(slug *p) : range(p) { } |
| 137 | int iscmd() { return 1; } |
| 138 | int forceflush() { return YES; } |
| 139 | int cmdtype() { return first->parm; } |
| 140 | }; |
| 141 | |
| 142 | class parmrange : public range { |
| 143 | public: |
| 144 | parmrange(slug *p) : range(p) { } |
| 145 | int isparm() { return 1; } |
| 146 | int forceflush() { return YES; } |
| 147 | int parmtype() { return first->parm; } |
| 148 | int parm() { return first->parm2; } |
| 149 | }; |
| 150 | |
| 151 | class bsrange : public range { |
| 152 | public: |
| 153 | bsrange(slug *p) : range(p) { } |
| 154 | int forceflush() { return NO; } |
| 155 | int print(int cv, int col) { first->slugout(col); return cv; } |
| 156 | }; |
| 157 | |
| 158 | class endrange : public range { |
| 159 | public: |
| 160 | endrange(slug *p) : range(p) { } |
| 161 | int forceflush() { return UNEXPECTED; } |
| 162 | }; |
| 163 | |
| 164 | class eofrange : public range { |
| 165 | public: |
| 166 | eofrange(slug *p) : range(p) { } |
| 167 | int forceflush() { return UNEXPECTED; } |
| 168 | }; |
| 169 | |
| 170 | extern eofrange *lastrange; // the EOF block (trailer, etc.) goes here |
| 171 | |
| 172 | int measure(stream *); |
| 173 | int rawmeasure(stream *); |
| 174 | |
| 175 | // A nestrange packages together a sequence of ranges, its subrange. |
| 176 | // Other parts of the program reach in and alter the dimensions of |
| 177 | // some of these ranges, so when the height of a range is requested |
| 178 | // it is computed completely afresh. |
| 179 | // (Note: the alternative, of keeping around many copies of ranges |
| 180 | // with different dimensions, was abandoned because of the difficulty |
| 181 | // of ensuring that exactly one copy of each original range would be |
| 182 | // output.) |
| 183 | class nestrange : public range { |
| 184 | protected: |
| 185 | stream *subrange; |
| 186 | int isbreaking; |
| 187 | int rawdv; |
| 188 | public: |
| 189 | nestrange() : range() { subrange = 0; isbreaking = 0; rawdv = -1; } |
| 190 | nestrange(slug *p, stream *s) : range(p) |
| 191 | { subrange = s; isbreaking = 0; rawdv = -1; } |
| 192 | void rdump(); |
| 193 | virtual void restore(); |
| 194 | stream *children() { return subrange; } |
| 195 | void killkids(); |
| 196 | int height() { return measure(subrange); } |
| 197 | int rawht() { if (rawdv < 0 || isbreaking) rawdv = rawmeasure(subrange); |
| 198 | return rawdv; } |
| 199 | void reheight(int *cv, int *mv) { |
| 200 | *mv += measure(subrange); *cv = max(*mv, *cv); } |
| 201 | void rerawht(int *cv, int *mv) { |
| 202 | *mv += rawht(); *cv = max(*mv, *cv); } |
| 203 | int isnested() { return 1; } |
| 204 | int forceflush() { return EXPECTED; } |
| 205 | int print(int cv, int col); |
| 206 | int breaking() { return isbreaking; } |
| 207 | void setbreaking() { isbreaking++; } |
| 208 | }; |
| 209 | |
| 210 | class usrange : public nestrange { |
| 211 | public: |
| 212 | usrange() { } |
| 213 | usrange(slug *p, stream *s) : nestrange(p, s) {} |
| 214 | void dump() { printf("#### US dv %d\n", height()); } |
| 215 | range *clone(); |
| 216 | }; |
| 217 | |
| 218 | class ufrange : public nestrange { |
| 219 | int goalV, goal2; |
| 220 | public: |
| 221 | ufrange() { } |
| 222 | ufrange(slug *p, stream *s) : nestrange(p, s) { |
| 223 | goalV = p->parm; goal2 = p->parm2; } |
| 224 | void dump() { printf("#### UF dv %d goal %d goal2 %d\n", |
| 225 | height(), goalV, goal2); } |
| 226 | int floatable() { return 1; } |
| 227 | void enqueue(int = 0); |
| 228 | range *clone(); |
| 229 | int goal() { return goalV; } |
| 230 | void setgoal(int n) { goalV = goal2 = n; } |
| 231 | void pickgoal(int acv, double scale); |
| 232 | void restore() { goalV = first->parm; goal2 = first->ht; } |
| 233 | }; |
| 234 | |
| 235 | class bfrange : public nestrange { |
| 236 | int goalV, goal2; |
| 237 | public: |
| 238 | bfrange() { } |
| 239 | bfrange(slug *p, stream *s) : nestrange(p, s) { |
| 240 | goalV = p->parm; goal2 = p->parm2; } |
| 241 | void dump() { printf("#### BF dv %d goal %d goal2 %d\n", |
| 242 | height(), goalV, goal2); } |
| 243 | int floatable() { return 1; } |
| 244 | void enqueue(int = 0); |
| 245 | range *clone(); |
| 246 | int goal() { return goalV; } |
| 247 | void setgoal(int n) { goalV = goal2 = n; } |
| 248 | void pickgoal(int acv, double scale); |
| 249 | void restore() { goalV = first->parm; goal2 = first->parm2; } |
| 250 | int breakable() { return 1; } // can be broken |
| 251 | }; |
| 252 | |
| 253 | class ptrange : public nestrange { |
| 254 | int pgno; |
| 255 | public: |
| 256 | int pn() { return pgno; } |
| 257 | ptrange(slug *p, stream *s) : nestrange(p, s) { pgno = p->parm; } |
| 258 | void dump() { printf("#### PT pgno %d dv %d\n", pgno, height()); } |
| 259 | }; |
| 260 | |
| 261 | class btrange : public nestrange { |
| 262 | int pgno; |
| 263 | public: |
| 264 | btrange(slug *p, stream *s) : nestrange(p, s) { pgno = p->parm; } |
| 265 | void dump() { printf("#### BT pgno %d dv %d\n", pgno, height()); } |
| 266 | }; |
| 267 | |
| 268 | // A stream is a sequence of ranges; we use this data structure a lot |
| 269 | // to traverse various sequences that crop up in page-making. |
| 270 | class stream { |
| 271 | protected: |
| 272 | public: |
| 273 | struct strblk { // ranges are linked by these blocks |
| 274 | strblk *next; |
| 275 | range *rp; |
| 276 | }; |
| 277 | strblk *first; |
| 278 | strblk *last; |
| 279 | strblk *curr; |
| 280 | public: |
| 281 | stream() { curr = last = first = 0; } |
| 282 | stream(range *r) { curr = last = first = new strblk; |
| 283 | last->rp = r; last->next = 0; } |
| 284 | void freeall(); // note: not a destructor |
| 285 | void dump(); // top level |
| 286 | void rdump(); // recursive |
| 287 | int restoreall(); |
| 288 | range *current() { return curr->rp; } |
| 289 | range *next() { return curr && curr->next ? curr->next->rp : 0; } |
| 290 | void advance() { curr = curr->next; } |
| 291 | range *append(range *r); |
| 292 | void split(); |
| 293 | int more() { return curr && curr->rp; } |
| 294 | int height(); |
| 295 | int rawht(); |
| 296 | }; |
| 297 | |
| 298 | // A generator iterates through all the ranges of a stream |
| 299 | // (not just the root ranges of nestranges). |
| 300 | class generator { |
| 301 | stream s; |
| 302 | generator *child; |
| 303 | public: |
| 304 | generator() { child = 0; } |
| 305 | generator(stream *sp) { s = *sp; child = 0; } |
| 306 | range *next(); |
| 307 | }; |
| 308 | |
| 309 | extern stream ptlist, btlist; // page titles |
| 310 | |
rsc | 05f5d46 | 2006-02-17 18:21:29 +0000 | [diff] [blame^] | 311 | #undef INFINITY |
wkj | 5f1cf8e | 2004-05-16 07:56:41 +0000 | [diff] [blame] | 312 | #define INFINITY 1000001 |
| 313 | |
| 314 | // A queue is a distinguished kind of stream. |
| 315 | // It keeps its contents in order by the serial numbers of the ranges. |
| 316 | // A queue can be blocked from dequeuing something to indicate |
| 317 | // that it's not worth considering the queue again on a given page. |
| 318 | class queue : public stream { |
| 319 | strblk *newguy; |
| 320 | protected: |
| 321 | int blocked; |
| 322 | void check(char *); |
| 323 | public: |
| 324 | queue() : blocked(0) { } |
| 325 | range *enqueue(range *r); |
| 326 | range *dequeue(); |
| 327 | void block() { blocked = 1; } |
| 328 | void unblock() { blocked = 0; } |
| 329 | int more() { return !blocked && stream::more(); } |
| 330 | int empty() { return !stream::more(); } |
| 331 | int serialno() { return empty() ? INFINITY : current()->serialno(); } |
| 332 | }; |
| 333 | |
| 334 | // functions in range.c |
| 335 | void checkout(); |
| 336 | void startup(FILE *); |