xref: /plan9/sys/src/cmd/troff/n3.c (revision 14f51593fd82e19ba95969a8c07ff71131015979)
1 /*
2  * troff3.c
3  *
4  * macro and string routines, storage allocation
5  */
6 
7 #include "tdef.h"
8 #include "fns.h"
9 #include "ext.h"
10 
11 Tchar	*argtop;
12 int	pagech = '%';
13 int	strflg;
14 
15 #define	MHASHSIZE	128	/* must be 2**n */
16 #define	MHASH(x)	((x>>6)^x) & (MHASHSIZE-1)
17 Contab	*mhash[MHASHSIZE];
18 
19 
20 Blockp	*blist;		/* allocated blocks for macros and strings */
21 int	nblist;		/* how many there are */
22 int	bfree = -1;	/* first (possible) free block in the list */
23 
24 Contab *contabp = NULL;
25 #define MDELTA 500
26 int	nm = 0;
27 
28 int savname;		/* name of macro/string being defined */
29 int savslot;		/* place in Contab of savname */
30 int freeslot = -1;	/* first (possible) free slot in contab */
31 
prcontab(Contab * p)32 void prcontab(Contab *p)
33 {
34 	int i;
35 	for (i = 0; i < nm; i++)
36 		if (p)
37 			if (p[i].rq != 0)
38 				fprintf(stderr, "slot %d, %-2.2s\n", i, unpair(p[i].rq));
39 			else
40 				fprintf(stderr, "slot %d empty\n", i);
41 		else
42 			fprintf(stderr, "slot %d empty\n", i);
43 }
44 
45 
blockinit(void)46 void blockinit(void)
47 {
48 	blist = (Blockp *) calloc(NBLIST, sizeof(Blockp));
49 	if (blist == NULL) {
50 		ERROR "not enough room for %d blocks", NBLIST WARN;
51 		done2(1);
52 	}
53 	nblist = NBLIST;
54 	blist[0].nextoff = blist[1].nextoff = -1;
55 	blist[0].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
56 	blist[1].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
57 		/* -1 prevents blist[0] from being used; temporary fix */
58 		/* for a design botch: offset==0 is overloaded. */
59 		/* blist[1] reserved for .rd indicator -- also unused. */
60 		/* but someone unwittingly looks at these, so allocate something */
61 	bfree = 2;
62 }
63 
64 
grow(char * ptr,int num,int size)65 char *grow(char *ptr, int num, int size)	/* make array bigger */
66 {
67 	char *p;
68 
69 	if (ptr == NULL)
70 		p = (char *) calloc(num, size);
71 	else
72 		p = (char *) realloc(ptr, num * size);
73 	return p;
74 }
75 
mnspace(void)76 void mnspace(void)
77 {
78 	nm = sizeof(contab)/sizeof(Contab) + MDELTA;
79 	freeslot = sizeof(contab)/sizeof(Contab) + 1;
80 	contabp = (Contab *) grow((char *) contabp, nm, sizeof(Contab));
81 	if (contabp == NULL) {
82 		ERROR "not enough memory for namespace of %d marcos", nm WARN;
83 		exit(1);
84 	}
85 	contabp = (Contab *) memcpy((char *) contabp, (char *)contab,
86 							sizeof(contab));
87 	if (contabp == NULL) {
88 		ERROR "Cannot reinitialize macro/request name list" WARN;
89 		exit(1);
90 	}
91 
92 }
93 
caseig(void)94 void caseig(void)
95 {
96 	int i;
97 	Offset oldoff = offset;
98 
99 	offset = 0;
100 	i = copyb();
101 	offset = oldoff;
102 	if (i != '.')
103 		control(i, 1);
104 }
105 
106 
casern(void)107 void casern(void)
108 {
109 	int i, j, k;
110 
111 	lgf++;
112 	skip();
113 	if ((i = getrq()) == 0 || (oldmn = findmn(i)) < 0)
114 		return;
115 	skip();
116 	clrmn(findmn(j = getrq()));
117 	if (j) {
118 		munhash(&contabp[oldmn]);
119 		contabp[oldmn].rq = j;
120 		maddhash(&contabp[oldmn]);
121 		if (dip != d )
122 			for (k = dilev; k; k--)
123 				if (d[k].curd == i)
124 					d[k].curd = j;
125 	}
126 }
127 
maddhash(Contab * rp)128 void maddhash(Contab *rp)
129 {
130 	Contab **hp;
131 
132 	if (rp->rq == 0)
133 		return;
134 	hp = &mhash[MHASH(rp->rq)];
135 	rp->link = *hp;
136 	*hp = rp;
137 }
138 
munhash(Contab * mp)139 void munhash(Contab *mp)
140 {
141 	Contab *p;
142 	Contab **lp;
143 
144 	if (mp->rq == 0)
145 		return;
146 	lp = &mhash[MHASH(mp->rq)];
147 	p = *lp;
148 	while (p) {
149 		if (p == mp) {
150 			*lp = p->link;
151 			p->link = 0;
152 			return;
153 		}
154 		lp = &p->link;
155 		p = p->link;
156 	}
157 }
158 
mrehash(void)159 void mrehash(void)
160 {
161 	Contab *p;
162 	int i;
163 
164 	for (i=0; i < MHASHSIZE; i++)
165 		mhash[i] = 0;
166 	for (p=contabp; p < &contabp[nm]; p++)
167 		p->link = 0;
168 	for (p=contabp; p < &contabp[nm]; p++) {
169 		if (p->rq == 0)
170 			continue;
171 		i = MHASH(p->rq);
172 		p->link = mhash[i];
173 		mhash[i] = p;
174 	}
175 }
176 
caserm(void)177 void caserm(void)
178 {
179 	int j, k;
180 
181 	lgf++;
182 g0:
183 	while (!skip() && (j = getrq()) != 0) {
184 		if (dip != d)
185 			for (k = dilev; k; k--)
186 				if (d[k].curd == j) {
187 					ERROR "cannot remove diversion %s during definition",
188 								unpair(j) WARN;
189 					goto g0;
190 				}
191 		clrmn(findmn(j));
192 	}
193 	lgf--;
194 }
195 
196 
caseas(void)197 void caseas(void)
198 {
199 	app++;
200 	caseds();
201 }
202 
203 
caseds(void)204 void caseds(void)
205 {
206 	ds++;
207 	casede();
208 }
209 
210 
caseam(void)211 void caseam(void)
212 {
213 	app++;
214 	casede();
215 }
216 
217 
casede(void)218 void casede(void)
219 {
220 	int i, req;
221 	Offset savoff;
222 
223 	req = '.';
224 	lgf++;
225 	skip();
226 	if ((i = getrq()) == 0)
227 		goto de1;
228 	if ((offset = finds(i)) == 0)
229 		goto de1;
230 	if (newmn)
231 		savslot = newmn;
232 	else
233 		savslot = findmn(i);
234 	savname = i;
235 	if (ds)
236 		copys();
237 	else
238 		req = copyb();
239 	clrmn(oldmn);
240 	if (newmn) {
241 		if (contabp[newmn].rq)
242 			munhash(&contabp[newmn]);
243 		contabp[newmn].rq = i;
244 		maddhash(&contabp[newmn]);
245 
246 	}
247 	if (apptr) {
248 		savoff = offset;
249 		offset = apptr;
250 		wbf((Tchar) IMP);
251 		offset = savoff;	/* pointless */
252 	}
253 	offset = dip->op;
254 	if (req != '.')
255 		control(req, 1);
256 de1:
257 	ds = app = 0;
258 }
259 
260 
findmn(int i)261 int findmn(int i)
262 {
263 	Contab *p;
264 
265 	for (p = mhash[MHASH(i)]; p; p = p->link)
266 		if (i == p->rq)
267 			return(p - contabp);
268 	return(-1);
269 }
270 
271 
clrmn(int i)272 void clrmn(int i)
273 {
274 	if (i >= 0) {
275 		if (contabp[i].mx)
276 			ffree(contabp[i].mx);
277 		munhash(&contabp[i]);
278 		contabp[i].rq = 0;
279 		contabp[i].mx = 0;
280 		contabp[i].emx = 0;
281 		contabp[i].f = 0;
282 		if (contabp[i].divsiz != NULL) {
283 			free(contabp[i].divsiz);
284 			contabp[i].divsiz = NULL;
285 		}
286 		if (freeslot > i)
287 			freeslot = i;
288 	}
289 }
290 
growcontab(void)291 void growcontab(void)
292 {
293 	nm += MDELTA;
294 	contabp = (Contab *) grow((char *) contabp , nm, sizeof(Contab));
295 	if (contabp == NULL) {
296 		ERROR "Too many (%d) string/macro names", nm WARN;
297 		done2(02);
298 	} else {
299 		memset((char *)(contabp) + (nm - MDELTA) * sizeof(Contab),
300 						0, MDELTA * sizeof(Contab));
301 		mrehash();
302 	}
303 }
304 
305 
finds(int mn)306 Offset finds(int mn)
307 {
308 	int i;
309 	Offset savip;
310 
311 	oldmn = findmn(mn);
312 	newmn = 0;
313 	apptr = 0;
314 	if (app && oldmn >= 0 && contabp[oldmn].mx) {
315 		savip = ip;
316 		ip = contabp[oldmn].emx;
317 		oldmn = -1;
318 		apptr = ip;
319 		if (!diflg)
320 			ip = incoff(ip);
321 		nextb = ip;
322 		ip = savip;
323 	} else {
324 		for (i = freeslot; i < nm; i++) {
325 			if (contabp[i].rq == 0)
326 				break;
327 		}
328 		if (i == nm)
329 			growcontab();
330 		freeslot = i + 1;
331 		if ((nextb = alloc()) == -1) {
332 			app = 0;
333 			if (macerr++ > 1)
334 				done2(02);
335 			if (nextb == 0)
336 				ERROR "Not enough space for string/macro names" WARN;
337 			edone(04);
338 			return(offset = 0);
339 		}
340 		contabp[i].mx = nextb;
341 		if (!diflg) {
342 			newmn = i;
343 			if (oldmn == -1)
344 				contabp[i].rq = -1;
345 		} else {
346 			contabp[i].rq = mn;
347 			maddhash(&contabp[i]);
348 		}
349 	}
350 	app = 0;
351 	return(offset = nextb);
352 }
353 
skip(void)354 int skip(void)
355 {
356 	Tchar i;
357 
358 	while (cbits(i = getch()) == ' ' || ismot(i))
359 		;
360 	ch = i;
361 	return(nlflg);
362 }
363 
364 
copyb(void)365 int copyb(void)
366 {
367 	int i, j, state;
368 	Tchar ii;
369 	int req, k;
370 	Offset savoff;
371 	Uchar *p;
372 
373 	if (skip() || !(j = getrq()))
374 		j = '.';
375 	req = j;
376 	p = unpair(j);
377 	/* was: k = j >> BYTE; j &= BYTEMASK; */
378 	j = p[0];
379 	k = p[1];
380 	copyf++;
381 	flushi();
382 	nlflg = 0;
383 	state = 1;
384 	savoff = 0;
385 
386 /* state 0	eat up
387  * state 1	look for .
388  * state 2	look for first char of end macro
389  * state 3	look for second char of end macro
390  */
391 
392 	while (1) {
393 		i = cbits(ii = getch());
394 		if (state == 3) {
395 			if (i == k)
396 				break;
397 			if (!k) {
398 				ch = ii;
399 				i = getach();
400 				ch = ii;
401 				if (!i)
402 					break;
403 			}
404 			state = 0;
405 			goto c0;
406 		}
407 		if (i == '\n') {
408 			state = 1;
409 			nlflg = 0;
410 			goto c0;
411 		}
412 		if (state == 1 && i == '.') {
413 			state++;
414 			savoff = offset;
415 			goto c0;
416 		}
417 		if (state == 2 && i == j) {
418 			state++;
419 			goto c0;
420 		}
421 		state = 0;
422 c0:
423 		if (offset)
424 			wbf(ii);
425 	}
426 	if (offset) {
427 		offset = savoff;
428 		wbf((Tchar)0);
429 	}
430 	copyf--;
431 	return(req);
432 }
433 
434 
copys(void)435 void copys(void)
436 {
437 	Tchar i;
438 
439 	copyf++;
440 	if (skip())
441 		goto c0;
442 	if (cbits(i = getch()) != '"')
443 		wbf(i);
444 	while (cbits(i = getch()) != '\n')
445 		wbf(i);
446 c0:
447 	wbf((Tchar)0);
448 	copyf--;
449 }
450 
451 
alloc(void)452 Offset alloc(void)	/* return free Offset in nextb */
453 {
454 	int i, j;
455 
456 	for (i = bfree; i < nblist; i++)
457 		if (blist[i].nextoff == 0)
458 			break;
459 	if (i == nblist) {
460 		blist = (Blockp *) realloc((char *) blist, 2 * nblist * sizeof(Blockp));
461 		if (blist == NULL) {
462 			ERROR "can't grow blist for string/macro defns" WARN;
463 			done2(2);
464 		}
465 		nblist *= 2;
466 		for (j = i; j < nblist; j++) {
467 			blist[j].nextoff = 0;
468 			blist[j].bp = 0;
469 		}
470 	}
471 	blist[i].nextoff = -1;	/* this block is the end */
472 	bfree = i + 1;
473 	if (blist[i].bp == 0)
474 		blist[i].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
475 	if (blist[i].bp == NULL) {
476 		ERROR "can't allocate memory for string/macro definitions" WARN;
477 		done2(2);
478 	}
479 	nextb = (Offset) i * BLK;
480 	return nextb;
481 }
482 
483 
ffree(Offset i)484 void ffree(Offset i)	/* free list of blocks starting at blist(o) */
485 {			/* (doesn't actually free the blocks, just the pointers) */
486 	int j;
487 
488 	for ( ; blist[j = bindex(i)].nextoff != -1; ) {
489 		if (bfree > j)
490 			bfree = j;
491 		i = blist[j].nextoff;
492 		blist[j].nextoff = 0;
493 	}
494 	blist[j].nextoff = 0;
495 }
496 
497 
wbf(Tchar i)498 void wbf(Tchar i)	/* store i into offset, get ready for next one */
499 {
500 	int j, off;
501 
502 	if (!offset)
503 		return;
504 	j = bindex(offset);
505 	if (i == 0)
506 		contabp[savslot].emx = offset;
507 	off = boffset(offset);
508 	blist[j].bp[off] = i;
509 	offset++;
510 	if (pastend(offset)) {	/* off the end of this block */
511 		if (blist[j].nextoff == -1) {
512 			if ((nextb = alloc()) == -1) {
513 				ERROR "Out of temp file space" WARN;
514 				done2(01);
515 			}
516 			blist[j].nextoff = nextb;
517 		}
518 		offset = blist[j].nextoff;
519 	}
520 }
521 
522 
rbf(void)523 Tchar rbf(void)	/* return next char from blist[] block */
524 {
525 	Tchar i, j;
526 
527 	if (ip == RD_OFFSET) {		/* for rdtty */
528 		if (j = rdtty())
529 			return(j);
530 		else
531 			return(popi());
532 	}
533 
534 	i = rbf0(ip);
535 	if (i == 0) {
536 		if (!app)
537 			i = popi();
538 		return(i);
539 	}
540 	ip = incoff(ip);
541 	return(i);
542 }
543 
544 
xxxincoff(Offset p)545 Offset xxxincoff(Offset p)		/* get next blist[] block */
546 {
547 	p++;
548 	if (pastend(p)) {		/* off the end of this block */
549 		if ((p = blist[bindex(p-1)].nextoff) == -1) {	/* and nothing was allocated after it */
550 			ERROR "Bad storage allocation" WARN;
551 			done2(-5);
552 		}
553 	}
554 	return(p);
555 }
556 
557 
popi(void)558 Tchar popi(void)
559 {
560 	Stack *p;
561 
562 	if (frame == stk)
563 		return(0);
564 	if (strflg)
565 		strflg--;
566 	p = nxf = frame;
567 	p->nargs = 0;
568 	frame = p->pframe;
569 	ip = p->pip;
570 	pendt = p->ppendt;
571 	lastpbp = p->lastpbp;
572 	return(p->pch);
573 }
574 
575 /*
576  *	test that the end of the allocation is above a certain location
577  *	in memory
578  */
579 #define SPACETEST(base, size) \
580 	if ((char*)base + size >= (char*)stk+STACKSIZE) \
581 		ERROR "Stacksize overflow in n3" WARN
582 
pushi(Offset newip,int mname)583 Offset pushi(Offset newip, int  mname)
584 {
585 	Stack *p;
586 
587 	SPACETEST(nxf, sizeof(Stack));
588 	p = nxf;
589 	p->pframe = frame;
590 	p->pip = ip;
591 	p->ppendt = pendt;
592 	p->pch = ch;
593 	p->lastpbp = lastpbp;
594 	p->mname = mname;
595 	lastpbp = pbp;
596 	pendt = ch = 0;
597 	frame = nxf;
598 	if (nxf->nargs == 0)
599 		nxf += 1;
600 	else
601 		nxf = (Stack *)argtop;
602 	return(ip = newip);
603 }
604 
605 
setbrk(int x)606 void *setbrk(int x)
607 {
608 	char *i;
609 
610 	if ((i = (char *) calloc(x, 1)) == 0) {
611 		ERROR "Core limit reached" WARN;
612 		edone(0100);
613 	}
614 	return(i);
615 }
616 
617 
getsn(void)618 int getsn(void)
619 {
620 	int i;
621 
622 	if ((i = getach()) == 0)
623 		return(0);
624 	if (i == '(')
625 		return(getrq());
626 	else
627 		return(i);
628 }
629 
630 
setstr(void)631 Offset setstr(void)
632 {
633 	int i, j;
634 
635 	lgf++;
636 	if ((i = getsn()) == 0 || (j = findmn(i)) == -1 || !contabp[j].mx) {
637 		lgf--;
638 		return(0);
639 	} else {
640 		SPACETEST(nxf, sizeof(Stack));
641 		nxf->nargs = 0;
642 		strflg++;
643 		lgf--;
644 		return pushi(contabp[j].mx, i);
645 	}
646 }
647 
648 
649 
collect(void)650 void collect(void)
651 {
652 	int j;
653 	Tchar i, *strp, *lim, **argpp, **argppend;
654 	int quote;
655 	Stack *savnxf;
656 
657 	copyf++;
658 	nxf->nargs = 0;
659 	savnxf = nxf;
660 	if (skip())
661 		goto rtn;
662 
663 	{
664 		char *memp;
665 		memp = (char *)savnxf;
666 		/*
667 		 *	1 s structure for the macro descriptor
668 		 *	APERMAC Tchar *'s for pointers into the strings
669 		 *	space for the Tchar's themselves
670 		 */
671 		memp += sizeof(Stack);
672 		/*
673 		 *	CPERMAC = the total # of characters for ALL arguments
674 		 */
675 #define	CPERMAC	200
676 #define	APERMAC	9
677 		memp += APERMAC * sizeof(Tchar *);
678 		memp += CPERMAC * sizeof(Tchar);
679 		nxf = (Stack *)memp;
680 	}
681 	lim = (Tchar *)nxf;
682 	argpp = (Tchar **)(savnxf + 1);
683 	argppend = &argpp[APERMAC];
684 	SPACETEST(argppend, sizeof(Tchar *));
685 	strp = (Tchar *)argppend;
686 	/*
687 	 *	Zero out all the string pointers before filling them in.
688 	 */
689 	for (j = 0; j < APERMAC; j++)
690 		argpp[j] = 0;
691 	/* ERROR "savnxf=0x%x,nxf=0x%x,argpp=0x%x,strp=argppend=0x%x, lim=0x%x",
692 	 * 	savnxf, nxf, argpp, strp, lim WARN;
693 	 */
694 	strflg = 0;
695 	while (argpp != argppend && !skip()) {
696 		*argpp++ = strp;
697 		quote = 0;
698 		if (cbits(i = getch()) == '"')
699 			quote++;
700 		else
701 			ch = i;
702 		while (1) {
703 			i = getch();
704 /* fprintf(stderr, "collect %c %d\n", cbits(i), cbits(i)); */
705 			if (nlflg || (!quote && argpp != argppend && cbits(i) == ' '))
706 				break;	/* collects rest into $9 */
707 			if (   quote
708 			    && cbits(i) == '"'
709 			    && cbits(i = getch()) != '"') {
710 				ch = i;
711 				break;
712 			}
713 			*strp++ = i;
714 			if (strflg && strp >= lim) {
715 				/* ERROR "strp=0x%x, lim = 0x%x", strp, lim WARN; */
716 				ERROR "Macro argument too long" WARN;
717 				copyf--;
718 				edone(004);
719 			}
720 			SPACETEST(strp, 3 * sizeof(Tchar));
721 		}
722 		*strp++ = 0;
723 	}
724 	nxf = savnxf;
725 	nxf->nargs = argpp - (Tchar **)(savnxf + 1);
726 	argtop = strp;
727 rtn:
728 	copyf--;
729 }
730 
731 
seta(void)732 void seta(void)
733 {
734 	int i;
735 
736 	i = cbits(getch()) - '0';
737 	if (i > 0 && i <= APERMAC && i <= frame->nargs)
738 		pushback(*(((Tchar **)(frame + 1)) + i - 1));
739 }
740 
741 
caseda(void)742 void caseda(void)
743 {
744 	app++;
745 	casedi();
746 }
747 
casegd(void)748 void casegd(void)
749 {
750 	int i, j;
751 
752 	skip();
753 	if ((i = getrq()) == 0)
754 		return;
755 	if ((j = findmn(i)) >= 0) {
756 		if (contabp[j].divsiz != NULL) {
757 			numtabp[DN].val = contabp[j].divsiz->dix;
758 			numtabp[DL].val = contabp[j].divsiz->diy;
759 		}
760 	}
761 }
762 
763 #define FINDDIV(o) if ((o =  findmn(dip->curd)) < 0) \
764 			ERROR "lost diversion %s", unpair(dip->curd) WARN
765 
casedi(void)766 void casedi(void)
767 {
768 	int i, j, *k;
769 
770 	lgf++;
771 	if (skip() || (i = getrq()) == 0) {
772 		if (dip != d) {
773 			FINDDIV(savslot);
774 			wbf((Tchar)0);
775 		}
776 		if (dilev > 0) {
777 			numtabp[DN].val = dip->dnl;
778 			numtabp[DL].val = dip->maxl;
779 			FINDDIV(j);
780 			if ((contabp[j].divsiz = (Divsiz *) malloc(sizeof(Divsiz))) == NULL) {
781 				ERROR "Cannot alloc diversion size" WARN;
782 				done2(1);
783 			} else {
784 				contabp[j].divsiz->dix = numtabp[DN].val;
785 				contabp[j].divsiz->diy = numtabp[DL].val;
786 			}
787 			dip = &d[--dilev];
788 			offset = dip->op;
789 		}
790 		goto rtn;
791 	}
792 	if (++dilev == NDI) {
793 		--dilev;
794 		ERROR "Diversions nested too deep" WARN;
795 		edone(02);
796 	}
797 	if (dip != d) {
798 		FINDDIV(j);
799 		savslot = j;
800 		wbf((Tchar)0);
801 	}
802 	diflg++;
803 	dip = &d[dilev];
804 	dip->op = finds(i);
805 	dip->curd = i;
806 	clrmn(oldmn);
807 	k = (int *) & dip->dnl;
808 	for (j = 0; j < 10; j++)
809 		k[j] = 0;	/*not op and curd*/
810 rtn:
811 	app = 0;
812 	diflg = 0;
813 }
814 
815 
casedt(void)816 void casedt(void)
817 {
818 	lgf++;
819 	dip->dimac = dip->ditrap = dip->ditf = 0;
820 	skip();
821 	dip->ditrap = vnumb((int *)0);
822 	if (nonumb)
823 		return;
824 	skip();
825 	dip->dimac = getrq();
826 }
827 
828 #define LNSIZE 4000
casetl(void)829 void casetl(void)
830 {
831 	int j;
832 	int w[3];
833 	Tchar buf[LNSIZE];
834 	Tchar *tp;
835 	Tchar i, delim;
836 
837  	/*
838  	 * bug fix
839  	 *
840  	 * if .tl is the first thing in the file, the p1
841  	 * doesn't come out, also the pagenumber will be 0
842  	 *
843  	 * tends too confuse the device filter (and the user as well)
844  	 */
845  	if (dip == d && numtabp[NL].val == -1)
846  		newline(1);
847 	dip->nls = 0;
848 	skip();
849 	if (ismot(delim = getch())) {
850 		ch = delim;
851 		delim = '\'';
852 	} else
853 		delim = cbits(delim);
854 	tp = buf;
855 	numtabp[HP].val = 0;
856 	w[0] = w[1] = w[2] = 0;
857 	j = 0;
858 	while (cbits(i = getch()) != '\n') {
859 		if (cbits(i) == cbits(delim)) {
860 			if (j < 3)
861 				w[j] = numtabp[HP].val;
862 			numtabp[HP].val = 0;
863 			if (w[j] != 0)
864 				*tp++ = WORDSP;
865 			j++;
866 			*tp++ = 0;
867 		} else {
868 			if (cbits(i) == pagech) {
869 				setn1(numtabp[PN].val, numtabp[findr('%')].fmt,
870 				      i&SFMASK);
871 				continue;
872 			}
873 			numtabp[HP].val += width(i);
874 			if (tp < &buf[LNSIZE-10]) {
875 				if (cbits(i) == ' ' && *tp != WORDSP)
876 					*tp++ = WORDSP;
877 				*tp++ = i;
878 			} else {
879 				ERROR "Overflow in casetl" WARN;
880 			}
881 		}
882 	}
883 	if (j<3)
884 		w[j] = numtabp[HP].val;
885 	*tp++ = 0;
886 	*tp++ = 0;
887 	*tp = 0;
888 	tp = buf;
889 	if (NROFF)
890 		horiz(po);
891 	while (i = *tp++)
892 		pchar(i);
893 	if (w[1] || w[2])
894 		horiz(j = quant((lt - w[1]) / 2 - w[0], HOR));
895 	while (i = *tp++)
896 		pchar(i);
897 	if (w[2]) {
898 		horiz(lt - w[0] - w[1] - w[2] - j);
899 		while (i = *tp++)
900 			pchar(i);
901 	}
902 	newline(0);
903 	if (dip != d) {
904 		if (dip->dnl > dip->hnl)
905 			dip->hnl = dip->dnl;
906 	} else {
907 		if (numtabp[NL].val > dip->hnl)
908 			dip->hnl = numtabp[NL].val;
909 	}
910 }
911 
912 
casepc(void)913 void casepc(void)
914 {
915 	pagech = chget(IMP);
916 }
917 
918 
casepm(void)919 void casepm(void)
920 {
921 	int i, k;
922 	int xx, cnt, tcnt, kk, tot;
923 	Offset j;
924 
925 	kk = cnt = tcnt = 0;
926 	tot = !skip();
927 	stackdump();
928 	for (i = 0; i < nm; i++) {
929 		if ((xx = contabp[i].rq) == 0 || contabp[i].mx == 0)
930 			continue;
931 		tcnt++;
932 		j = contabp[i].mx;
933 		for (k = 1; (j = blist[bindex(j)].nextoff) != -1; )
934 			k++;
935 		cnt++;
936 		kk += k;
937 		if (!tot)
938 			fprintf(stderr, "%-2.2s %d\n", unpair(xx), k);
939 	}
940 	fprintf(stderr, "pm: total %d, macros %d, space %d\n", tcnt, cnt, kk);
941 }
942 
stackdump(void)943 void stackdump(void)	/* dumps stack of macros in process */
944 {
945 	Stack *p;
946 
947 	if (frame != stk) {
948 		fprintf(stderr, "stack: ");
949 		for (p = frame; p != stk; p = p->pframe)
950 			fprintf(stderr, "%s ", unpair(p->mname));
951 		fprintf(stderr, "\n");
952 	}
953 }
954