Checkpoint: pull in mpm; merge pic from Taj's version of the world
This commit is contained in:
parent
c5561c23cf
commit
5f1cf8e6fb
21 changed files with 3878 additions and 39 deletions
613
src/cmd/mpm/range.cc
Normal file
613
src/cmd/mpm/range.cc
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
#include <math.h>
|
||||
#include "misc.h"
|
||||
#include "slug.h"
|
||||
#include "range.h"
|
||||
|
||||
void sprange::reheight(int *cv, int *mv)
|
||||
{
|
||||
if (*cv != *mv)
|
||||
ERROR "slug %d: an imbedded SP, line %d\n",
|
||||
first->serialno(), first->lineno() WARNING;
|
||||
*cv += dv;
|
||||
*mv = max(*mv, *cv);
|
||||
}
|
||||
|
||||
void sprange::rerawht(int *cv, int *mv)
|
||||
{
|
||||
*cv += rawht();
|
||||
*mv = max(*mv, *cv);
|
||||
}
|
||||
|
||||
void nestrange::restore()
|
||||
{
|
||||
subrange->restoreall();
|
||||
}
|
||||
|
||||
void stream::freeall() // not a destructor; called explicitly
|
||||
{
|
||||
strblk *p, *q;
|
||||
for (p = first; p; p = q) {
|
||||
q = p->next;
|
||||
delete p;
|
||||
}
|
||||
first = last = curr = 0;
|
||||
}
|
||||
|
||||
void stream::dump()
|
||||
{
|
||||
for (stream s = *this; s.more(); s.advance())
|
||||
s.current()->dump();
|
||||
}
|
||||
|
||||
void stream::rdump()
|
||||
{
|
||||
for (stream s = *this; s.more(); s.advance())
|
||||
s.current()->rdump();
|
||||
}
|
||||
|
||||
int stream::restoreall()
|
||||
{
|
||||
for (stream s = *this; s.more(); s.advance())
|
||||
s.current()->restore();
|
||||
return measure(this);
|
||||
}
|
||||
|
||||
range *stream::append(range *r)
|
||||
{
|
||||
if (last == 0)
|
||||
curr = first = last = new strblk;
|
||||
else {
|
||||
last->next = new strblk;
|
||||
last = last->next;
|
||||
if (curr == 0)
|
||||
curr = last;
|
||||
}
|
||||
last->next = 0;
|
||||
return last->rp = r;
|
||||
}
|
||||
|
||||
void stream::split() // duplicate current() range
|
||||
{
|
||||
strblk *s2 = new strblk;
|
||||
range *r2 = curr->rp->clone();
|
||||
s2->rp = r2;
|
||||
s2->next = curr->next;
|
||||
if (last == curr)
|
||||
last = s2;
|
||||
curr->next = s2;
|
||||
curr->rp->killkids(); // children only in the 2nd one
|
||||
// r2->crosslink(r1);
|
||||
}
|
||||
|
||||
int stream::height()
|
||||
{
|
||||
int h;
|
||||
stream s = *this;
|
||||
for (h = 0; s.more(); s.advance())
|
||||
h += s.current()->height();
|
||||
return h;
|
||||
}
|
||||
|
||||
int stream::rawht()
|
||||
{
|
||||
int h;
|
||||
stream s = *this;
|
||||
for (h = 0; s.more(); s.advance())
|
||||
h += s.current()->rawht();
|
||||
return h;
|
||||
}
|
||||
|
||||
int measure(stream *sp) // record high-water mark of stream
|
||||
{ // sets nested stream heights
|
||||
stream s = *sp;
|
||||
int curv, maxv;
|
||||
for (maxv = curv = 0; s.more(); s.advance())
|
||||
s.current()->reheight(&curv, &maxv);
|
||||
return maxv;
|
||||
}
|
||||
|
||||
int rawmeasure(stream *sp)
|
||||
{
|
||||
stream s = *sp;
|
||||
int curv, maxv;
|
||||
for (maxv = curv = 0; s.more(); s.advance())
|
||||
s.current()->rerawht(&curv, &maxv);
|
||||
return maxv;
|
||||
}
|
||||
|
||||
void nestrange::rdump()
|
||||
{
|
||||
dump();
|
||||
if (subrange)
|
||||
subrange->rdump();
|
||||
}
|
||||
|
||||
void nestrange::killkids()
|
||||
{
|
||||
subrange = new stream;
|
||||
}
|
||||
|
||||
int nestrange::print(int curv, int col)
|
||||
{
|
||||
int ocurv = curv;
|
||||
first->slugout(col);
|
||||
for (stream s = *subrange; s.more(); s.advance())
|
||||
curv = s.current()->print(curv, col);
|
||||
return ocurv + height();
|
||||
}
|
||||
|
||||
#define macroclone(rangetype) range *rangetype::clone() {\
|
||||
rangetype *t = new rangetype;\
|
||||
*t = *this;\
|
||||
return t; }
|
||||
|
||||
macroclone(usrange);
|
||||
macroclone(ufrange);
|
||||
macroclone(bfrange);
|
||||
|
||||
#undef macroclone
|
||||
|
||||
#define macropickgoal(rangetype) void rangetype::pickgoal(int acv, double scale) {\
|
||||
if (scale > 1) {\
|
||||
goalV = (int)(scale*goalV);\
|
||||
goal2 = (int)(scale*goal2);\
|
||||
}\
|
||||
if (abs(acv - goalV) > abs(acv-goal2))\
|
||||
goalV = goal2; }
|
||||
|
||||
macropickgoal(ufrange)
|
||||
macropickgoal(bfrange)
|
||||
|
||||
#undef macropickgoal
|
||||
|
||||
range *generator::next()
|
||||
{
|
||||
range *r;
|
||||
if (child) {
|
||||
if ((r = child->next()) != 0)
|
||||
return r;
|
||||
delete child;
|
||||
child = 0;
|
||||
}
|
||||
if (!s.more())
|
||||
return 0;
|
||||
r = s.current();
|
||||
if (r->isnested())
|
||||
child = new generator(r->children());
|
||||
s.advance();
|
||||
return r;
|
||||
}
|
||||
|
||||
range *queue::enqueue(range *r)
|
||||
{
|
||||
if (dbg & 8)
|
||||
printf("#entering queue::enqueue()\n");
|
||||
check("queue::enqueue");
|
||||
if (!last || last->rp->serialno() < r->serialno()) // common case
|
||||
return append(r);
|
||||
if (dbg & 8)
|
||||
printf("#queue::enqueue() pushing back\n");
|
||||
newguy = new strblk;
|
||||
newguy->rp = r;
|
||||
if (r->serialno() < first->rp->serialno()) {
|
||||
newguy->next = first;
|
||||
curr = first = newguy;
|
||||
return newguy->rp;
|
||||
}
|
||||
if (dbg & 8)
|
||||
printf("#queue::enqueue() searching down queue\n");
|
||||
for (curr = first;
|
||||
next() && next()->serialno() < r->serialno();
|
||||
curr = curr->next)
|
||||
;
|
||||
newguy->next = curr->next;
|
||||
curr->next = newguy;
|
||||
curr = first; // restore important queue condition
|
||||
return newguy->rp;
|
||||
}
|
||||
|
||||
range *queue::dequeue()
|
||||
{
|
||||
if (dbg & 8)
|
||||
printf("#entering queue::dequeue()\n");
|
||||
check("queue::dequeue");
|
||||
curr = first->next;
|
||||
range *retval = first->rp;
|
||||
delete first;
|
||||
first = curr;
|
||||
if (!curr)
|
||||
last = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
|
||||
// functions that munge the troff output stored in slugs[]
|
||||
|
||||
// ================================================================================
|
||||
|
||||
static void doprefix(FILE *fp) // copy 1st "x" commands to output
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = getc(fp)) != EOF) {
|
||||
if (c != 'x') {
|
||||
ungetc(c, fp);
|
||||
break;
|
||||
}
|
||||
putchar(c);
|
||||
do {
|
||||
putchar(c = getc(fp));
|
||||
} while (c != '\n');
|
||||
linenum++;
|
||||
}
|
||||
// printf("x font 1 R\n"); // horrible kludge: ensure a font for first f1 command
|
||||
}
|
||||
|
||||
#define DELTASLUGS 15000
|
||||
|
||||
static slug *slugs = 0;
|
||||
static int nslugs = 0; // slugs has nslugs slots
|
||||
static slug *slugp = 0; // next free slug in slugs
|
||||
|
||||
static void readslugs(FILE *fp)
|
||||
{
|
||||
if ((slugs = (slug *) malloc((nslugs = DELTASLUGS)*sizeof(slug))) == NULL)
|
||||
ERROR "no room for %d-slug array\n", nslugs FATAL;
|
||||
slugp = slugs;
|
||||
for (slugp = slugs; ; slugp++) {
|
||||
if (slugp >= slugs+nslugs-2) {
|
||||
int where = slugp - slugs;
|
||||
if ((slugs = (slug *) realloc((char *) slugs, (nslugs += DELTASLUGS)*sizeof(slug))) == NULL)
|
||||
ERROR "no room for %d slugs\n", nslugs FATAL;
|
||||
ERROR "now slug array can hold %d slugs\n", nslugs WARNING;
|
||||
slugp = slugs + where;
|
||||
}
|
||||
*slugp = getslug(fp);
|
||||
if (slugp->type == EOF)
|
||||
break;
|
||||
}
|
||||
*++slugp = eofslug();
|
||||
printf("# %d slugs\n", slugp-slugs);
|
||||
}
|
||||
|
||||
static slug *findend(slug *sp)
|
||||
{
|
||||
slug *p;
|
||||
for (p = sp; p->type == sp->type; p++) // skip runs
|
||||
; // espec UF UF UF
|
||||
for ( ; p < slugp; p++)
|
||||
switch (p->type) {
|
||||
case US:
|
||||
case UF:
|
||||
case BF:
|
||||
case PT:
|
||||
case BT:
|
||||
p = findend(p);
|
||||
break;
|
||||
case END:
|
||||
return p;
|
||||
}
|
||||
ERROR "walked past EOF in findend looking for %d (%s), line %d\n",
|
||||
sp->type, sp->typename(), sp->lineno() FATAL;
|
||||
return sp;
|
||||
}
|
||||
|
||||
static int markp(int i, int n, int parm)
|
||||
{ // should VBOX i of n be marked to brevent breaking after it?
|
||||
if (i >= n-1)
|
||||
return 0;
|
||||
return i <= parm-2 || i >= n-parm;
|
||||
}
|
||||
|
||||
static void markbreak(slug *p)
|
||||
{
|
||||
// Mark impermissible breakpoints in BS's.
|
||||
// The parm field of a VBOX is >0 if we shouldn't break after it.
|
||||
int parm; // how many lines must stay on page
|
||||
int goahead = 1; // true until we see the next BS
|
||||
int nowmark = 0; // true when we should be marking
|
||||
int n = 0;
|
||||
while (p->type == BS)
|
||||
parm = p++->parm; // latest BS parm applies
|
||||
slug *op = p;
|
||||
while (goahead) {
|
||||
switch (p->type) {
|
||||
case VBOX: // count VBOXes so second pass knows
|
||||
if (p->dv > 0) // knows how far to end of BS
|
||||
n++;
|
||||
break;
|
||||
case US: // mark around EQ/EN, etc.
|
||||
nowmark = 1;
|
||||
p = findend(p);
|
||||
break;
|
||||
case UF: // but not around floats, PTs, and BTs
|
||||
case BF:
|
||||
case PT:
|
||||
case BT:
|
||||
p = findend(p);
|
||||
break;
|
||||
case SP: // naked SP: probable macro botch
|
||||
nowmark = 1; // mark around it anyhow
|
||||
break;
|
||||
case BS: // beginning of next paragraph
|
||||
case END: // probable macro botch
|
||||
case EOF:
|
||||
goahead = 0; // stop work after marking
|
||||
nowmark = 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
if (nowmark) {
|
||||
int i = 0; // VBOX counter for second pass
|
||||
while (op < p) {
|
||||
switch (op->type) {
|
||||
case VBOX:
|
||||
if (op->dv > 0)
|
||||
op->parm = markp(i, n, parm);
|
||||
i++;
|
||||
break;
|
||||
case US: // caused second pass to begin
|
||||
case SP:
|
||||
case BS:
|
||||
case END:
|
||||
case EOF:
|
||||
op = p;
|
||||
break;
|
||||
case UF: // skip on this pass too
|
||||
case BF:
|
||||
case PT:
|
||||
case BT:
|
||||
op = findend(op);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
op++;
|
||||
}
|
||||
if (i != n)
|
||||
ERROR "markbreak failed : i %d n %d\n",
|
||||
i, n WARNING;
|
||||
op = p;
|
||||
nowmark = n = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fixslugs() // adjust bases and dv's, set parameters, etc.
|
||||
{
|
||||
slug *p, *prevV = 0;
|
||||
for (p = slugs; p < slugp; p++) {
|
||||
if (p->type == VBOX) {
|
||||
prevV = p;
|
||||
continue;
|
||||
}
|
||||
if (p->base != 0) {
|
||||
ERROR "%s slug (type %d) has base = %d, line %d\n",
|
||||
p->typename(), p->type, p->base, p->lineno() WARNING;
|
||||
}
|
||||
if ((p->type == SP) || (p->type == NE))
|
||||
continue;
|
||||
if (p->type == PAGE)
|
||||
prevV = 0;
|
||||
if (p->dv != 0)
|
||||
if (prevV) {
|
||||
prevV->base = max(prevV->base, p->dv);
|
||||
p->dv = 0;
|
||||
} else {
|
||||
ERROR "%s slug (type %d) has dv = %d, line %d\n",
|
||||
p->typename(), p->type, p->dv, p->lineno() WARNING;
|
||||
}
|
||||
}
|
||||
prevV = 0;
|
||||
int firstNP = 0, firstFO = 0, firstPL = 0;
|
||||
for (p = slugs; p < slugp; p++) {
|
||||
switch (p->type) {
|
||||
// adjust the dv in a sequence of VBOXes
|
||||
// by subtracting from each the base of the preceding VBOX
|
||||
case VBOX:
|
||||
if (prevV)
|
||||
p->dv -= prevV->base;
|
||||
prevV = p;
|
||||
break;
|
||||
case SP:
|
||||
p->dv = max(p->dv, 0);
|
||||
break;
|
||||
case PAGE:
|
||||
p->neutralize();
|
||||
prevV = 0;
|
||||
break;
|
||||
// record only first "declarations" of Page Top and bottom (FO);
|
||||
case PARM:
|
||||
switch (p->parm) {
|
||||
case NP:
|
||||
if (firstNP++ == 0)
|
||||
pagetop = p->parm2;
|
||||
p->neutralize();
|
||||
break;
|
||||
case FO:
|
||||
if (firstFO++ == 0)
|
||||
pagebot = p->parm2;
|
||||
p->neutralize();
|
||||
break;
|
||||
case PL:
|
||||
if (firstPL++ == 0)
|
||||
physbot = p->parm2;
|
||||
p->neutralize();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// things that begin groups; not US, which should nest properly
|
||||
case UF:
|
||||
case BF:
|
||||
while ((p+1)->type == p->type) {
|
||||
// join adjacent identical
|
||||
(p+1)->parm2 = p->parm; // parm is latest
|
||||
// parm2 is previous
|
||||
p->neutralize(); // so it's not seen later
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
// none of the above
|
||||
case US:
|
||||
case PT:
|
||||
case BT:
|
||||
case BS:
|
||||
case END:
|
||||
case TM:
|
||||
case COORD:
|
||||
case NE:
|
||||
case MC:
|
||||
case CMD:
|
||||
case EOF:
|
||||
break;
|
||||
default:
|
||||
ERROR "Unknown slug type %d in fixslugs, line %d\n",
|
||||
p->type, p->lineno() WARNING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int pagesize = pagebot - pagetop;
|
||||
if (pagesize == 0)
|
||||
ERROR "Page dimensions not declared\n" FATAL;
|
||||
if (physbot == 0)
|
||||
physbot = pagebot + pagetop;
|
||||
printf("# page top %d bot %d size %d physbot %d\n",
|
||||
pagetop, pagebot, pagesize, physbot);
|
||||
for (p = slugs; p < slugp; p++) {
|
||||
switch (p->type) {
|
||||
// normalize float parameters
|
||||
case BF:
|
||||
case UF:
|
||||
// primary goal
|
||||
p->parm = max(min(p->parm-pagetop, pagesize), 0);
|
||||
// secondary goal
|
||||
p->parm2 = max(min(p->parm2-pagetop, pagesize), 0);
|
||||
break;
|
||||
// normalize need parameters
|
||||
case NE:
|
||||
p->dv = max( min(p->dv, pagesize), 0);
|
||||
break;
|
||||
// mark permissible breaks
|
||||
case BS:
|
||||
markbreak(p);
|
||||
break;
|
||||
}
|
||||
if (dbg & 1)
|
||||
p->dump();
|
||||
}
|
||||
}
|
||||
|
||||
void checkout()
|
||||
{
|
||||
for (slug *p = slugs; p < slugp; p++)
|
||||
switch (p->type) {
|
||||
case PT:
|
||||
case BT:
|
||||
p = findend(p);
|
||||
break;
|
||||
case SP:
|
||||
case VBOX:
|
||||
if (p->seen != 1)
|
||||
ERROR "%s slug %d seen %d times\n",
|
||||
p->typename(), p->serialno(),
|
||||
p->seen WARNING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
eofrange *lastrange;
|
||||
stream ptlist, btlist;
|
||||
|
||||
static slug *makeranges(slug *p, stream *s, int level)
|
||||
{
|
||||
stream *t;
|
||||
|
||||
for ( ; p < slugp; p++)
|
||||
switch (p->type) {
|
||||
case VBOX:
|
||||
s->append(new vboxrange(p));
|
||||
break;
|
||||
case SP:
|
||||
s->append(new sprange(p));
|
||||
break;
|
||||
case BS:
|
||||
s->append(new bsrange(p));
|
||||
break;
|
||||
case US:
|
||||
s->append(new usrange(p, t = new stream));
|
||||
p = makeranges(p+1, t, level+1);
|
||||
break;
|
||||
case BF:
|
||||
s->append(new bfrange(p, t = new stream));
|
||||
p = makeranges(p+1, t, level+1);
|
||||
break;
|
||||
case UF:
|
||||
s->append(new ufrange(p, t = new stream));
|
||||
p = makeranges(p+1, t, level+1);
|
||||
break;
|
||||
case PT:
|
||||
ptlist.append(new ptrange(p, t = new stream));
|
||||
p = makeranges(p+1, t, level+1);
|
||||
break;
|
||||
case BT:
|
||||
btlist.append(new btrange(p, t = new stream));
|
||||
p = makeranges(p+1, t, level+1);
|
||||
break;
|
||||
case END:
|
||||
s->append(new endrange(p));
|
||||
return p;
|
||||
case TM:
|
||||
s->append(new tmrange(p));
|
||||
break;
|
||||
case COORD:
|
||||
s->append(new coordrange(p));
|
||||
break;
|
||||
case NE:
|
||||
if (level) {
|
||||
ERROR "Nested NE commands are ignored, line %d\n",
|
||||
p->lineno() WARNING;
|
||||
p->dv = 0;
|
||||
}
|
||||
s->append(new nerange(p));
|
||||
break;
|
||||
case MC:
|
||||
s->append(new mcrange(p));
|
||||
break;
|
||||
case CMD:
|
||||
if (level)
|
||||
ERROR "Nested command ignored, line %d\n",
|
||||
p->lineno() WARNING;
|
||||
s->append(new cmdrange(p));
|
||||
break;
|
||||
case PARM:
|
||||
if (level)
|
||||
ERROR "Nested parameter ignored, line %d\n",
|
||||
p->lineno() WARNING;
|
||||
s->append(new parmrange(p));
|
||||
break;
|
||||
case EOF:
|
||||
lastrange = new eofrange(p);
|
||||
return 0;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static queue text; // unexamined input ranges; the real data
|
||||
|
||||
void startup(FILE *fp)
|
||||
{
|
||||
doprefix(fp); // peel off 'x' commands
|
||||
readslugs(fp); // read everything into slugs[]
|
||||
fixslugs(); // measure parameters and clean up
|
||||
makeranges(slugs, &text, 0); // add range superstructure
|
||||
measure(&text); // heights of nested things
|
||||
rawmeasure(&text);
|
||||
while (text.more()) {
|
||||
range *r = text.dequeue();
|
||||
if (dbg & 2)
|
||||
r->dump();
|
||||
r->enqueue();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue