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