diff options
Diffstat (limited to 'src/cmd/mpm/range.cc')
-rw-r--r-- | src/cmd/mpm/range.cc | 613 |
1 files changed, 613 insertions, 0 deletions
diff --git a/src/cmd/mpm/range.cc b/src/cmd/mpm/range.cc new file mode 100644 index 00000000..988dff2f --- /dev/null +++ b/src/cmd/mpm/range.cc @@ -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(); + } +} |