| #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(); |
| } |