blob: d065ac827c81c8738e28ac73add1e1316985a12f [file] [log] [blame]
#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();
}