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