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