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