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