2004-05-16 07:56:41 +00:00
|
|
|
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; }
|
2006-02-17 18:21:29 +00:00
|
|
|
virtual ~range() { }
|
2004-05-16 07:56:41 +00:00
|
|
|
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
|
|
|
|
|
|
2006-02-17 18:21:29 +00:00
|
|
|
#undef INFINITY
|
2004-05-16 07:56:41 +00:00
|
|
|
#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 *);
|