xref: /titanic_44/usr/src/cmd/troff/n3.c (revision bde3d612a7c090234c60e6e4578821237a5db135)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*
43  * troff3.c
44  *
45  * macro and string routines, storage allocation
46  */
47 
48 
49 #include "tdef.h"
50 #ifdef NROFF
51 #include "tw.h"
52 #endif
53 #include "ext.h"
54 
55 #define	MHASH(x)	((x>>6)^x)&0177
56 struct	contab *mhash[128];	/* 128 == the 0177 on line above */
57 #define	blisti(i)	(((i)-ENV_BLK*BLK) / BLK)
58 filep	blist[NBLIST];
59 tchar	*argtop;
60 int	pagech = '%';
61 int	strflg;
62 
63 #ifdef	INCORE
64 	tchar *wbuf;
65 	tchar corebuf[(ENV_BLK + NBLIST + 1) * BLK];
66 #else
67 	tchar wbuf[BLK];
68 	tchar rbuf[BLK];
69 #endif
70 
71 int
72 caseig()
73 {
74 	int	i;
75 	filep oldoff;
76 
77 	oldoff = offset;
78 	offset = 0;
79 	i = copyb();
80 	offset = oldoff;
81 	if (i != '.')
82 		control(i, 1);
83 
84 	return (0);
85 }
86 
87 int
88 casern()
89 {
90 	int	i, j;
91 
92 	lgf++;
93 	skip();
94 	if ((i = getrq()) == 0 || (oldmn = findmn(i)) < 0)
95 		return (0);
96 	skip();
97 	clrmn(findmn(j = getrq()));
98 	if (j) {
99 		munhash(&contab[oldmn]);
100 		contab[oldmn].rq = j;
101 		maddhash(&contab[oldmn]);
102 	}
103 
104 	return (0);
105 }
106 
107 int
108 maddhash(rp)
109 struct contab *rp;
110 {
111 	struct contab **hp;
112 
113 	if (rp->rq == 0)
114 		return (0);
115 	hp = &mhash[MHASH(rp->rq)];
116 	rp->link = *hp;
117 	*hp = rp;
118 
119 	return (0);
120 }
121 
122 int
123 munhash(mp)
124 struct contab *mp;
125 {
126 	struct contab *p;
127 	struct contab **lp;
128 
129 	if (mp->rq == 0)
130 		return (0);
131 	lp = &mhash[MHASH(mp->rq)];
132 	p = *lp;
133 	while (p) {
134 		if (p == mp) {
135 			*lp = p->link;
136 			p->link = 0;
137 			return (0);
138 		}
139 		lp = &p->link;
140 		p = p->link;
141 	}
142 
143 	return (0);
144 }
145 
146 int
147 mrehash()
148 {
149 	struct contab *p;
150 	int	i;
151 
152 	for (i=0; i<128; i++)
153 		mhash[i] = 0;
154 	for (p=contab; p < &contab[NM]; p++)
155 		p->link = 0;
156 	for (p=contab; p < &contab[NM]; p++) {
157 		if (p->rq == 0)
158 			continue;
159 		i = MHASH(p->rq);
160 		p->link = mhash[i];
161 		mhash[i] = p;
162 	}
163 
164 	return (0);
165 }
166 
167 int
168 caserm()
169 {
170 	int j;
171 
172 	lgf++;
173 	while (!skip() && (j = getrq()) != 0)
174 		clrmn(findmn(j));
175 	lgf--;
176 
177 	return (0);
178 }
179 
180 
181 int
182 caseas()
183 {
184 	app++;
185 	caseds();
186 
187 	return (0);
188 }
189 
190 
191 int
192 caseds()
193 {
194 	ds++;
195 	casede();
196 
197 	return (0);
198 }
199 
200 
201 int
202 caseam()
203 {
204 	app++;
205 	casede();
206 
207 	return (0);
208 }
209 
210 
211 int
212 casede()
213 {
214 	int	i, req;
215 	filep savoff;
216 	extern filep finds();
217 
218 	if (dip != d)
219 		wbfl();
220 	req = '.';
221 	lgf++;
222 	skip();
223 	if ((i = getrq()) == 0)
224 		goto de1;
225 	if ((offset = finds(i)) == 0)
226 		goto de1;
227 	if (ds)
228 		copys();
229 	else
230 		req = copyb();
231 	wbfl();
232 	clrmn(oldmn);
233 	if (newmn) {
234 		if (contab[newmn].rq)
235 			munhash(&contab[newmn]);
236 		contab[newmn].rq = i;
237 		maddhash(&contab[newmn]);
238 	}
239 	if (apptr) {
240 		savoff = offset;
241 		offset = apptr;
242 		wbt((tchar) IMP);
243 		offset = savoff;
244 	}
245 	offset = dip->op;
246 	if (req != '.')
247 		control(req, 1);
248 de1:
249 	ds = app = 0;
250 	return (0);
251 }
252 
253 
254 int
255 findmn(i)
256 int	i;
257 {
258 	struct contab *p;
259 
260 	for (p = mhash[MHASH(i)]; p; p = p->link)
261 		if (i == p->rq)
262 			return(p - contab);
263 	return(-1);
264 }
265 
266 
267 int
268 clrmn(i)
269 int	i;
270 {
271 	if (i >= 0) {
272 		if (contab[i].mx)
273 			ffree((filep)contab[i].mx);
274 		munhash(&contab[i]);
275 		contab[i].rq = 0;
276 		contab[i].mx = 0;
277 		contab[i].f = 0;
278 	}
279 
280 	return (0);
281 }
282 
283 
284 filep finds(mn)
285 int	mn;
286 {
287 	int	i;
288 	filep savip;
289 	extern filep alloc();
290 	extern filep incoff();
291 
292 	oldmn = findmn(mn);
293 	newmn = 0;
294 	apptr = (filep)0;
295 	if (app && oldmn >= 0 && contab[oldmn].mx) {
296 		savip = ip;
297 		ip = (filep)contab[oldmn].mx;
298 		oldmn = -1;
299 		while ((i = rbf()) != 0)
300 			;
301 		apptr = ip;
302 		if (!diflg)
303 			ip = incoff(ip);
304 		nextb = ip;
305 		ip = savip;
306 	} else {
307 		for (i = 0; i < NM; i++) {
308 			if (contab[i].rq == 0)
309 				break;
310 		}
311 		if (i == NM || (nextb = alloc()) == 0) {
312 			app = 0;
313 			if (macerr++ > 1)
314 				done2(02);
315 			errprint(gettext("Too many (%d) string/macro names"),
316 					 NM);
317 			edone(04);
318 			return(offset = 0);
319 		}
320 		contab[i].mx = (unsigned) nextb;
321 		if (!diflg) {
322 			newmn = i;
323 			if (oldmn == -1)
324 				contab[i].rq = -1;
325 		} else {
326 			contab[i].rq = mn;
327 			maddhash(&contab[i]);
328 		}
329 	}
330 	app = 0;
331 	return(offset = nextb);
332 }
333 
334 
335 int
336 skip()			/*skip over blanks; return nlflg*/
337 {
338 	tchar i;
339 
340 	while (cbits(i = getch()) == ' ')
341 		;
342 	ch = i;
343 	return(nlflg);
344 }
345 
346 
347 int
348 copyb()
349 {
350 	int	i, j, state;
351 	tchar ii;
352 	int	req, k;
353 	filep savoff;
354 
355 	if (skip() || !(j = getrq()))
356 		j = '.';
357 	req = j;
358 	k = j >> BYTE;
359 	j &= BYTEMASK;
360 	copyf++;
361 	flushi();
362 	nlflg = 0;
363 	state = 1;
364 
365 /* state 0	eat up
366  * state 1	look for .
367  * state 2	look for first char of end macro
368  * state 3	look for second char of end macro
369  */
370 
371 	while (1) {
372 		i = cbits(ii = getch());
373 		if (state == 3) {
374 			if (i == k)
375 				break;
376 			if (!k) {
377 				ch = ii;
378 				i = getach();
379 				ch = ii;
380 				if (!i)
381 					break;
382 			}
383 			state = 0;
384 			goto c0;
385 		}
386 		if (i == '\n') {
387 			state = 1;
388 			nlflg = 0;
389 			goto c0;
390 		}
391 		if (state == 1 && i == '.') {
392 			state++;
393 			savoff = offset;
394 			goto c0;
395 		}
396 		if ((state == 2) && (i == j)) {
397 			state++;
398 			goto c0;
399 		}
400 		state = 0;
401 c0:
402 		if (offset)
403 			wbf(ii);
404 	}
405 	if (offset) {
406 		wbfl();
407 		offset = savoff;
408 		wbt((tchar)0);
409 	}
410 	copyf--;
411 	return(req);
412 }
413 
414 
415 int
416 copys()
417 {
418 	tchar i;
419 
420 	copyf++;
421 	if (skip())
422 		goto c0;
423 	if (cbits(i = getch()) != '"')
424 		wbf(i);
425 	while (cbits(i = getch()) != '\n')
426 		wbf(i);
427 c0:
428 	wbt((tchar)0);
429 	copyf--;
430 
431 	return (0);
432 }
433 
434 
435 filep alloc()		/*return free blist[] block in nextb*/
436 {
437 	int	i;
438 	filep j;
439 
440 	for (i = 0; i < NBLIST; i++) {
441 		if (blist[i] == 0)
442 			break;
443 	}
444 	if (i == NBLIST) {
445 		j = 0;
446 	} else {
447 		blist[i] = -1;
448 		j = (filep)i * BLK + ENV_BLK * BLK;
449 	}
450 #ifdef	DEBUG
451 	if (debug & DB_ALLC) {
452 		char cc1, cc2;
453 		fdprintf(stderr, "alloc: ");
454 		if (oldmn >= 0 && oldmn < NM) {
455 			cc1 = contab[oldmn].rq & 0177;
456 			if ((cc2 = (contab[oldmn].rq >> BYTE) & 0177) == 0)
457 				cc2 = ' ';
458 			fdprintf(stderr, "oldmn %d %c%c, ", oldmn, cc1, cc2);
459 		}
460 		fdprintf(stderr, "newmn %d; nextb was %x, will be %x\n",
461 			newmn, nextb, j);
462 	}
463 #endif	/* DEBUG */
464 	return(nextb = j);
465 }
466 
467 
468 int
469 ffree(i)		/*free blist[i] and blocks pointed to*/
470 filep i;
471 {
472 	int	j;
473 
474 	while (blist[j = blisti(i)] != (unsigned) ~0) {
475 		i = (filep) blist[j];
476 		blist[j] = 0;
477 	}
478 	blist[j] = 0;
479 
480 	return (0);
481 }
482 
483 int
484 wbt(i)
485 tchar i;
486 {
487 	wbf(i);
488 	wbfl();
489 
490 	return (0);
491 }
492 
493 
494 int
495 wbf(i)			/*store i into blist[offset] (?) */
496 tchar i;
497 {
498 	int	j;
499 
500 	if (!offset)
501 		return (0);
502 	if (!woff) {
503 		woff = offset;
504 #ifdef INCORE
505 		wbuf = &corebuf[woff];	/* INCORE only */
506 #endif
507 		wbfi = 0;
508 	}
509 	wbuf[wbfi++] = i;
510 	if (!((++offset) & (BLK - 1))) {
511 		wbfl();
512 		j = blisti(--offset);
513 		if (j < 0 || j >= NBLIST) {
514 			errprint(gettext("Out of temp file space"));
515 			done2(01);
516 		}
517 		if (blist[j] == (unsigned) ~0) {
518 			if (alloc() == 0) {
519 				errprint(gettext("Out of temp file space"));
520 				done2(01);
521 			}
522 			blist[j] = (unsigned)(nextb);
523 		}
524 		offset = ((filep)blist[j]);
525 	}
526 	if (wbfi >= BLK)
527 		wbfl();
528 
529 	return (0);
530 }
531 
532 
533 int
534 wbfl()			/*flush current blist[] block*/
535 {
536 	if (woff == 0)
537 		return (0);
538 #ifndef INCORE
539 	lseek(ibf, ((long)woff) * sizeof(tchar), 0);
540 	write(ibf, (char *)wbuf, wbfi * sizeof(tchar));
541 #endif
542 	if ((woff & (~(BLK - 1))) == (roff & (~(BLK - 1))))
543 		roff = -1;
544 	woff = 0;
545 
546 	return (0);
547 }
548 
549 
550 tchar rbf()		/*return next char from blist[] block*/
551 {
552 	tchar i;
553 	filep j, p;
554 	extern filep incoff();
555 
556 	if (ip == NBLIST*BLK) {		/* for rdtty */
557 		if (j = rdtty())
558 			return(j);
559 		else
560 			return(popi());
561 	}
562 	/* this is an inline expansion of rbf0: dirty! */
563 #ifndef INCORE
564 	j = ip & ~(BLK - 1);
565 	if (j != roff) {
566 		roff = j;
567 		lseek(ibf, (long)j * sizeof(tchar), 0);
568 		if (read(ibf, (char *)rbuf, BLK * sizeof(tchar)) <= 0)
569 			i = 0;
570 		else
571 			i = rbuf[ip & (BLK-1)];
572 	} else
573 		i = rbuf[ip & (BLK-1)];
574 #else
575 	i = corebuf[ip];
576 #endif
577 	/* end of rbf0 */
578 	if (i == 0) {
579 		if (!app)
580 			i = popi();
581 		return(i);
582 	}
583 	/* this is an inline expansion of incoff: also dirty */
584 	p = ++ip;
585 	if ((p & (BLK - 1)) == 0) {
586 		if ((ip = blist[blisti(p-1)]) == (unsigned) ~0) {
587 			errprint(gettext("Bad storage allocation"));
588 			ip = 0;
589 			done2(-5);
590 		}
591 		/* this was meant to protect against people removing
592 		 * the macro they were standing on, but it's too
593 		 * sensitive to block boundaries.
594 		 * if (ip == 0) {
595 		 *	errprint(gettext("Block removed while in use"));
596 		 *	done2(-6);
597 		 * }
598 		 */
599 	}
600 	return(i);
601 }
602 
603 
604 tchar rbf0(p)
605 filep p;
606 {
607 #ifndef INCORE
608 	filep i;
609 
610 	if ((i = p & ~(BLK - 1)) != roff) {
611 		roff = i;
612 		lseek(ibf, (long)roff * sizeof(tchar), 0);
613 		if (read(ibf, (char *)rbuf, BLK * sizeof(tchar)) == 0)
614 			return(0);
615 	}
616 	return(rbuf[p & (BLK-1)]);
617 #else
618 	return(corebuf[p]);
619 #endif
620 }
621 
622 
623 filep incoff(p)		/*get next blist[] block*/
624 filep p;
625 {
626 	p++;
627 	if ((p & (BLK - 1)) == 0) {
628 		if ((p = blist[blisti(p-1)]) == (unsigned) ~0) {
629 			errprint(gettext("Bad storage allocation"));
630 			done2(-5);
631 		}
632 	}
633 	return(p);
634 }
635 
636 
637 tchar popi()
638 {
639 	struct s *p;
640 
641 	if (frame == stk)
642 		return(0);
643 	if (strflg)
644 		strflg--;
645 	p = nxf = frame;
646 	p->nargs = 0;
647 	frame = p->pframe;
648 	ip = p->pip;
649 	pendt = p->ppendt;
650 	lastpbp = p->lastpbp;
651 	return(p->pch);
652 }
653 
654 /*
655  *	test that the end of the allocation is above a certain location
656  *	in memory
657  */
658 #define SPACETEST(base, size) while ((enda - (size)) <= (char *)(base)){setbrk(DELTA);}
659 
660 int
661 pushi(newip, mname)
662 filep newip;
663 int mname;
664 {
665 	struct s *p;
666 	extern char *setbrk();
667 
668 	SPACETEST(nxf, sizeof(struct s));
669 	p = nxf;
670 	p->pframe = frame;
671 	p->pip = ip;
672 	p->ppendt = pendt;
673 	p->pch = ch;
674 	p->lastpbp = lastpbp;
675 	p->mname = mname;
676 	lastpbp = pbp;
677 	pendt = ch = 0;
678 	frame = nxf;
679 	if (nxf->nargs == 0)
680 		nxf += 1;
681 	else
682 		nxf = (struct s *)argtop;
683 	return(ip = newip);
684 }
685 
686 
687 char	*setbrk(x)
688 int	x;
689 {
690 	char	*i, *k;
691 	int	j;
692 	char	*sbrk();
693 
694 	if ((i = sbrk(x)) == (char *) -1) {
695 		errprint(gettext("Core limit reached"));
696 		edone(0100);
697 	}
698 	if (j = (unsigned)i % sizeof(int)) {	/*check alignment for 3B*/
699 		j = sizeof(int) - j;		/*only init calls should need this*/
700 		if ((k = sbrk(j)) == (char *) -1) {
701 			errprint("Core limit reached");
702 			edone(0100);
703 		}
704 		if (k != i + x) {	/*there must have been an intervening sbrk*/
705 			errprint ("internal error in setbrk: i=%x, j=%d, k=%x",
706 				i, j, k);
707 			edone(0100);
708 		}
709 		i += j;
710 	}
711 	enda = i + x;
712 	return(i);
713 }
714 
715 
716 int
717 getsn()
718 {
719 	int	i;
720 
721 	if ((i = getach()) == 0)
722 		return(0);
723 	if (i == '(')
724 		return(getrq());
725 	else
726 		return(i);
727 }
728 
729 
730 int
731 setstr()
732 {
733 	int	i, j;
734 
735 	lgf++;
736 	if ((i = getsn()) == 0 || (j = findmn(i)) == -1 || !contab[j].mx) {
737 		lgf--;
738 		return(0);
739 	} else {
740 		SPACETEST(nxf, sizeof(struct s));
741 		nxf->nargs = 0;
742 		strflg++;
743 		lgf--;
744 		return pushi((filep)contab[j].mx, i);
745 	}
746 }
747 
748 
749 int
750 collect()
751 {
752 	int	j;
753 	tchar i;
754 	tchar *strp;
755 	tchar * lim;
756 	tchar * *argpp, **argppend;
757 	int	quote;
758 	struct s *savnxf;
759 
760 	copyf++;
761 	nxf->nargs = 0;
762 	savnxf = nxf;
763 	if (skip())
764 		goto rtn;
765 
766 	{
767 		char *memp;
768 		memp = (char *)savnxf;
769 		/*
770 		 *	1 s structure for the macro descriptor
771 		 *	APERMAC tchar *'s for pointers into the strings
772 		 *	space for the tchar's themselves
773 		 */
774 		memp += sizeof(struct s);
775 		/*
776 		 *	CPERMAC (the total # of characters for ALL arguments)
777 		 *	to a macros, has been carefully chosen
778 		 *	so that the distance between stack frames is < DELTA
779 		 */
780 #define	CPERMAC	200
781 #define	APERMAC	9
782 		memp += APERMAC * sizeof(tchar *);
783 		memp += CPERMAC * sizeof(tchar);
784 		nxf = (struct s*)memp;
785 	}
786 	lim = (tchar *)nxf;
787 	argpp = (tchar **)(savnxf + 1);
788 	argppend = &argpp[APERMAC];
789 	SPACETEST(argppend, sizeof(tchar *));
790 	strp = (tchar *)argppend;
791 	/*
792 	 *	Zero out all the string pointers before filling them in.
793 	 */
794 	for (j = 0; j < APERMAC; j++){
795 		argpp[j] = (tchar *)0;
796 	}
797 #if 0
798 	errprint("savnxf=0x%x,nxf=0x%x,argpp=0x%x,strp=argppend=0x%x,lim=0x%x,enda=0x%x",
799 		savnxf, nxf, argpp, strp, lim, enda);
800 #endif
801 	strflg = 0;
802 	while ((argpp != argppend) && (!skip())) {
803 		*argpp++ = strp;
804 		quote = 0;
805 		if (cbits(i = getch()) == '"')
806 			quote++;
807 		else
808 			ch = i;
809 		while (1) {
810 			i = getch();
811 			if (nlflg || (!quote && cbits(i) == ' '))
812 				break;
813 			if (   quote
814 			    && (cbits(i) == '"')
815 			    && (cbits(i = getch()) != '"')) {
816 				ch = i;
817 				break;
818 			}
819 			*strp++ = i;
820 			if (strflg && strp >= lim) {
821 #if 0
822 				errprint("strp=0x%x, lim = 0x%x",
823 					strp, lim);
824 #endif
825 				errprint(gettext("Macro argument too long"));
826 				copyf--;
827 				edone(004);
828 			}
829 			SPACETEST(strp, 3 * sizeof(tchar));
830 		}
831 		*strp++ = 0;
832 	}
833 	nxf = savnxf;
834 	nxf->nargs = argpp - (tchar **)(savnxf + 1);
835 	argtop = strp;
836 rtn:
837 	copyf--;
838 
839 	return (0);
840 }
841 
842 
843 int
844 seta()
845 {
846 	int	i;
847 
848 	i = cbits(getch()) - '0';
849 	if (i > 0 && i <= APERMAC && i <= frame->nargs)
850 		pushback(*(((tchar **)(frame + 1)) + i - 1));
851 
852 	return (0);
853 }
854 
855 
856 int
857 caseda()
858 {
859 	app++;
860 	casedi();
861 
862 	return (0);
863 }
864 
865 
866 int
867 casedi()
868 {
869 	int	i, j;
870 	int	*k;
871 
872 	lgf++;
873 	if (skip() || (i = getrq()) == 0) {
874 		if (dip != d)
875 			wbt((tchar)0);
876 		if (dilev > 0) {
877 			numtab[DN].val = dip->dnl;
878 			numtab[DL].val = dip->maxl;
879 			dip = &d[--dilev];
880 			offset = dip->op;
881 		}
882 		goto rtn;
883 	}
884 	if (++dilev == NDI) {
885 		--dilev;
886 		errprint(gettext("Diversions nested too deep"));
887 		edone(02);
888 	}
889 	if (dip != d)
890 		wbt((tchar)0);
891 	diflg++;
892 	dip = &d[dilev];
893 	dip->op = finds(i);
894 	dip->curd = i;
895 	clrmn(oldmn);
896 	k = (int *) & dip->dnl;
897 	for (j = 0; j < 10; j++)
898 		k[j] = 0;	/*not op and curd*/
899 rtn:
900 	app = 0;
901 	diflg = 0;
902 
903 	return (0);
904 }
905 
906 
907 int
908 casedt()
909 {
910 	lgf++;
911 	dip->dimac = dip->ditrap = dip->ditf = 0;
912 	skip();
913 	dip->ditrap = vnumb((int *)0);
914 	if (nonumb)
915 		return (0);
916 	skip();
917 	dip->dimac = getrq();
918 
919 	return (0);
920 }
921 
922 
923 int
924 casetl()
925 {
926 	int	j;
927 	int w[3];
928 	tchar buf[LNSIZE];
929 	tchar *tp;
930 	tchar i, delim;
931 
932 	dip->nls = 0;
933 	skip();
934 	if (ismot(delim = getch())) {
935 		ch = delim;
936 		delim = '\'';
937 	} else
938 		delim = cbits(delim);
939 	tp = buf;
940 	numtab[HP].val = 0;
941 	w[0] = w[1] = w[2] = 0;
942 	j = 0;
943 	while (cbits(i = getch()) != '\n') {
944 		if (cbits(i) == cbits(delim)) {
945 			if (j < 3)
946 				w[j] = numtab[HP].val;
947 			numtab[HP].val = 0;
948 			j++;
949 			*tp++ = 0;
950 		} else {
951 			if (cbits(i) == pagech) {
952 				setn1(numtab[PN].val, numtab[findr('%')].fmt,
953 				      i&SFMASK);
954 				continue;
955 			}
956 			numtab[HP].val += width(i);
957 			if (tp < &buf[LNSIZE-10])
958 				*tp++ = i;
959 		}
960 	}
961 	if (j<3)
962 		w[j] = numtab[HP].val;
963 	*tp++ = 0;
964 	*tp++ = 0;
965 	*tp++ = 0;
966 	tp = buf;
967 #ifdef NROFF
968 	horiz(po);
969 #endif
970 	while (i = *tp++)
971 		pchar(i);
972 	if (w[1] || w[2])
973 		horiz(j = quant((lt - w[1]) / 2 - w[0], HOR));
974 	while (i = *tp++)
975 		pchar(i);
976 	if (w[2]) {
977 		horiz(lt - w[0] - w[1] - w[2] - j);
978 		while (i = *tp++)
979 			pchar(i);
980 	}
981 	newline(0);
982 	if (dip != d) {
983 		if (dip->dnl > dip->hnl)
984 			dip->hnl = dip->dnl;
985 	} else {
986 		if (numtab[NL].val > dip->hnl)
987 			dip->hnl = numtab[NL].val;
988 	}
989 
990 	return (0);
991 }
992 
993 
994 int
995 casepc()
996 {
997 	pagech = chget(IMP);
998 
999 	return (0);
1000 }
1001 
1002 
1003 int
1004 casepm()
1005 {
1006 	int	i, k;
1007 	char	*p;
1008 	int	xx, cnt, tcnt, kk, tot;
1009 	filep j;
1010 	char	pmline[10];
1011 
1012 	kk = cnt = tcnt = 0;
1013 	tot = !skip();
1014 	for (i = 0; i < NM; i++) {
1015 		if ((xx = contab[i].rq) == 0 || contab[i].mx == 0)
1016 			continue;
1017 		tcnt++;
1018 		p = pmline;
1019 		j = (filep) contab[i].mx;
1020 		k = 1;
1021 		while ((j = blist[blisti(j)]) != (unsigned) ~0) {
1022 			k++;
1023 		}
1024 		cnt++;
1025 		kk += k;
1026 		if (!tot) {
1027 			*p++ = xx & 0177;
1028 			if (!(*p++ = (xx >> BYTE) & 0177))
1029 				*(p - 1) = ' ';
1030 			*p++ = 0;
1031 			fdprintf(stderr, "%s %d\n", pmline, k);
1032 		}
1033 	}
1034 	fdprintf(stderr, "pm: total %d, macros %d, space %d\n", tcnt, cnt, kk);
1035 
1036 	return (0);
1037 }
1038 
1039 int
1040 stackdump()	/* dumps stack of macros in process */
1041 {
1042 	struct s *p;
1043 
1044 	if (frame != stk) {
1045 		for (p = frame; p != stk; p = p->pframe)
1046 			fdprintf(stderr, "%c%c ", p->mname&0177, (p->mname>>BYTE)&0177);
1047 		fdprintf(stderr, "\n");
1048 	}
1049 
1050 	return (0);
1051 }
1052