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