aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/mpm/range.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/mpm/range.cc')
-rw-r--r--src/cmd/mpm/range.cc613
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();
+ }
+}