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.cvoid checkout();void startup(FILE *);
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 *);