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