#include "misc.h" #include "slug.h" #include "range.h" #include "page.h" queue squeue; queue bfqueue; queue ufqueue; // We use the stream function current() to access a queue's head. // Thus, queue member curr should always point to its first range. void queue::check(char *whence) { if (dbg & 8) { char *p; if (this == &squeue) p = "squeue"; else if (this == &bfqueue) p = "bfqueue"; else if (this == &ufqueue) p = "ufqueue"; else p = "weird queue"; printf("#checking %s\n", p); } if (first != curr) ERROR "check(%s): first != curr, line %d\n", whence, curr->rp->lineno() FATAL; } // When ranges are told to enqueue themselves, they are being rejected from the // stage back onto their original queues. // They reset any parameters that may have been altered by staging or trial // composition. void range::enqueue(int block) { squeue.enqueue(this); if (block) squeue.block(); } void ufrange::enqueue(int block) { restore(); // both goal positions ufqueue.enqueue(this); if (block) ufqueue.block(); } void bfrange::enqueue(int block) { restore(); // both goal positions bfqueue.enqueue(this); if (block) bfqueue.block(); } int anymore() { return !(squeue.empty() && ufqueue.empty() && bfqueue.empty()); } void mergestream::unblock() { squeue.unblock(); bfqueue.unblock(); ufqueue.unblock(); } // Fill the staging area with a minimal chunk of input ranges. int mergestream::prime() { if (dbg & 4) printf("#entering mergestream::prime()\n"); if (!empty()) return 1; int brkok = 1; // is it OK to break after the last // VBOX that was added to the stage? int needheight = -1; // minimum acceptable height of the // chunk being constructed on stage // If the range at the head of any queue is breaking, // deal with it first. if (squeue.more() && squeue.current()->breaking()) enqueue(squeue.dequeue()); else if (bfqueue.more() && (bfqueue.current()->breaking() || (bfqueue.serialno() < squeue.serialno()))) enqueue(bfqueue.dequeue()); else if (ufqueue.more() && (ufqueue.current()->breaking() || (ufqueue.serialno() < squeue.serialno()))) enqueue(ufqueue.dequeue()); else while (squeue.more()) { // Fill the stage with enough ranges to be a valid chunk. range *r = squeue.dequeue(); if (r->isvbox()) { // VBOX if (dbg & 16) printf("#VBOX: !empty: %d; brkok: %d; vsince: %d\n", !empty(), brkok, currpage->vsince); if (!empty() // there's something there && brkok // it's OK to break here && currpage->vsince >= 2 // enough stream has gone onto this page && rawht() >= needheight // current need has been satisfied ) { // the stage already contains enough // ranges, so this one can wait r->enqueue(); break; } else { if (r->rawht() > 0) { ++currpage->vsince; brkok = r->brkafter(); } enqueue(r); } } else if (r->isnested() || r->issp()) { // US, SP if (!empty() && rawht() >= needheight) { // enough already, wait r->enqueue(); break; } currpage->vsince = 0; enqueue(r); if (height() >= needheight) break; } else if (r->isneed()) { // NE if (!empty() && rawht() >= needheight) { // not currently working on an unsatisfied NEed r->enqueue(); break; } // deal with overlapping NEeds needheight = rawht() + max(needheight - rawht(), r->needht()); enqueue(r); } else if (r->forceflush() == NO) { enqueue(r); } else if (r->forceflush() == YES) { currpage->vsince = 0; if (!empty()) { // ready or not, r must wait r->enqueue(); break; } enqueue(r); break; } else ERROR "unexpected %s[%s] in prime(), line %d\n", r->typename(), r->headstr(), r->lineno() FATAL; } return more(); // 0 if nothing was staged } void page::cmdproc() { if (stage->next()) ERROR "more than a single command on bsqueue\n" FATAL; switch (stage->current()->cmdtype()) { case FC: // freeze the current 2-column range and start a new one adddef(stage->dequeue()); twocol->compose(FINAL); adddef(twocol); twocol = new multicol(this); break; case BP: // force a page break adddef(stage->dequeue()); squeue.block(); break; case FL: // flush out all floatables that precede this range: // no more stream input allowed until they're past if (stage->serialno() > ufqueue.serialno() || stage->serialno() > bfqueue.serialno()) { range *r = stage->dequeue(); r->enqueue(ANDBLOCK); } else adddef(stage->dequeue()); break; default: stage->current()->dump(); ERROR "unknown command\n" FATAL; } } void page::parmproc() { if (stage->next()) ERROR "more than a single parameter on bsqueue\n" FATAL; switch (stage->current()->parmtype()) { case NP: // page top margin if (blank()) pagetop = stage->current()->parm(); pagesize = pagebot - pagetop; break; case FO: if (blank()) pagebot = stage->current()->parm(); pagesize = pagebot - pagetop; break; case PL: if (blank()) physbot = stage->current()->parm(); break; case MF: minfull = 0.01*stage->current()->parm(); break; case CT: coltol = 0.01*stage->current()->parm(); break; case WARN: wantwarn = stage->current()->parm(); break; case DBG: dbg = stage->current()->parm(); break; default: stage->current()->dump(); ERROR "unknown parameter\n" FATAL; } adddef(stage->dequeue()); } // Process the contents of the staging area; a relic that used to do more. void mergestream::pend() { if (dbg & 4) printf("#entering mergestream::pend()\n"); if (!more()) return; if (current()->iscmd()) currpage->cmdproc(); else if (current()->isparm()) currpage->parmproc(); else currpage->tryout(); }