1 /* 2 * Copyright (c) 1983 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)object.c 5.4 (Berkeley) 06/01/90"; 10 #endif /* not lint */ 11 12 /* 13 * Object code interface, mainly for extraction of symbolic information. 14 */ 15 16 #include "defs.h" 17 #include "object.h" 18 #include "stabstring.h" 19 #include "main.h" 20 #include "symbols.h" 21 #include "names.h" 22 #include "languages.h" 23 #include "mappings.h" 24 #include "lists.h" 25 #include <a.out.h> 26 #include <stab.h> 27 #include <ctype.h> 28 29 #ifndef public 30 31 struct { 32 unsigned int stringsize; /* size of the dumped string table */ 33 unsigned int nsyms; /* number of symbols */ 34 unsigned int nfiles; /* number of files */ 35 unsigned int nlines; /* number of lines */ 36 } nlhdr; 37 38 #include "languages.h" 39 #include "symbols.h" 40 41 #endif 42 43 #ifndef N_MOD2 44 # define N_MOD2 0x50 45 #endif 46 47 public String objname = "a.out"; 48 public integer objsize; 49 50 public Language curlang; 51 public Symbol curmodule; 52 public Symbol curparam; 53 public Symbol curcomm; 54 public Symbol commchain; 55 56 private char *stringtab; 57 private struct nlist *curnp; 58 private Boolean warned; 59 private Boolean strip_ = false; 60 61 private Filetab *filep; 62 private Linetab *linep, *prevlinep; 63 64 public String curfilename () 65 { 66 return ((filep-1)->filename); 67 } 68 69 /* 70 * Blocks are figured out on the fly while reading the symbol table. 71 */ 72 73 #define MAXBLKDEPTH 25 74 75 public Symbol curblock; 76 77 private Symbol blkstack[MAXBLKDEPTH]; 78 private integer curlevel; 79 private integer bnum, nesting; 80 private Address addrstk[MAXBLKDEPTH]; 81 82 public pushBlock (b) 83 Symbol b; 84 { 85 if (curlevel >= MAXBLKDEPTH) { 86 fatal("nesting depth too large (%d)", curlevel); 87 } 88 blkstack[curlevel] = curblock; 89 ++curlevel; 90 curblock = b; 91 if (traceblocks) { 92 printf("entering block %s\n", symname(b)); 93 } 94 } 95 96 /* 97 * Change the current block with saving the previous one, 98 * since it is assumed that the symbol for the current one is to be deleted. 99 */ 100 101 public changeBlock (b) 102 Symbol b; 103 { 104 curblock = b; 105 } 106 107 public enterblock (b) 108 Symbol b; 109 { 110 if (curblock == nil) { 111 b->level = 1; 112 } else { 113 b->level = curblock->level + 1; 114 } 115 b->block = curblock; 116 pushBlock(b); 117 } 118 119 public exitblock () 120 { 121 if (curblock->class == FUNC or curblock->class == PROC) { 122 if (prevlinep != linep) { 123 curblock->symvalue.funcv.src = true; 124 } 125 } 126 if (curlevel <= 0) { 127 panic("nesting depth underflow (%d)", curlevel); 128 } 129 --curlevel; 130 if (traceblocks) { 131 printf("exiting block %s\n", symname(curblock)); 132 } 133 curblock = blkstack[curlevel]; 134 } 135 136 /* 137 * Enter a source line or file name reference into the appropriate table. 138 * Expanded inline to reduce procedure calls. 139 * 140 * private enterline (linenumber, address) 141 * Lineno linenumber; 142 * Address address; 143 * ... 144 */ 145 146 #define enterline(linenumber, address) \ 147 { \ 148 register Linetab *lp; \ 149 \ 150 lp = linep - 1; \ 151 if (linenumber != lp->line) { \ 152 if (address != lp->addr) { \ 153 ++lp; \ 154 } \ 155 lp->line = linenumber; \ 156 lp->addr = address; \ 157 linep = lp + 1; \ 158 } \ 159 } 160 161 /* 162 * Read in the namelist from the obj file. 163 * 164 * Reads and seeks are used instead of fread's and fseek's 165 * for efficiency sake; there's a lot of data being read here. 166 */ 167 168 public readobj (file) 169 String file; 170 { 171 Fileid f; 172 struct exec hdr; 173 struct nlist nlist; 174 175 f = open(file, 0); 176 if (f < 0) { 177 fatal("can't open %s", file); 178 } 179 read(f, &hdr, sizeof(hdr)); 180 if (N_BADMAG(hdr)) { 181 objsize = 0; 182 nlhdr.nsyms = 0; 183 nlhdr.nfiles = 0; 184 nlhdr.nlines = 0; 185 } else { 186 objsize = hdr.a_text; 187 nlhdr.nsyms = hdr.a_syms / sizeof(nlist); 188 nlhdr.nfiles = nlhdr.nsyms; 189 nlhdr.nlines = nlhdr.nsyms; 190 } 191 if (nlhdr.nsyms > 0) { 192 lseek(f, (long) N_STROFF(hdr), 0); 193 read(f, &(nlhdr.stringsize), sizeof(nlhdr.stringsize)); 194 nlhdr.stringsize -= 4; 195 stringtab = newarr(char, nlhdr.stringsize); 196 read(f, stringtab, nlhdr.stringsize); 197 allocmaps(nlhdr.nfiles, nlhdr.nlines); 198 lseek(f, (long) N_SYMOFF(hdr), 0); 199 readsyms(f); 200 ordfunctab(); 201 setnlines(); 202 setnfiles(); 203 } else { 204 initsyms(); 205 } 206 close(f); 207 } 208 209 /* 210 * Found the beginning of the externals in the object file 211 * (signified by the "-lg" or find an external), close the 212 * block for the last procedure. 213 */ 214 215 private foundglobals () 216 { 217 if (curblock->class != PROG) { 218 exitblock(); 219 if (curblock->class != PROG) { 220 exitblock(); 221 } 222 } 223 enterline(0, (linep-1)->addr + 1); 224 } 225 226 /* 227 * Read in symbols from object file. 228 */ 229 230 private readsyms (f) 231 Fileid f; 232 { 233 struct nlist *namelist; 234 register struct nlist *np, *ub; 235 register String name; 236 boolean afterlg, foundstab; 237 integer index; 238 char *lastchar; 239 240 initsyms(); 241 namelist = newarr(struct nlist, nlhdr.nsyms); 242 read(f, namelist, nlhdr.nsyms * sizeof(struct nlist)); 243 afterlg = false; 244 foundstab = false; 245 ub = &namelist[nlhdr.nsyms]; 246 curnp = &namelist[0]; 247 np = curnp; 248 while (np < ub) { 249 index = np->n_un.n_strx; 250 if (index != 0) { 251 name = &stringtab[index - 4]; 252 /* 253 * If the program contains any .f files a trailing _ is stripped 254 * from the name on the assumption it was added by the compiler. 255 * This only affects names that follow the sdb N_SO entry with 256 * the .f name. 257 */ 258 if (strip_ and name[0] != '\0' ) { 259 lastchar = &name[strlen(name) - 1]; 260 if (*lastchar == '_') { 261 *lastchar = '\0'; 262 } 263 } 264 } else { 265 name = nil; 266 } 267 268 /* 269 * Assumptions: 270 * not an N_STAB ==> name != nil 271 * name[0] == '-' ==> name == "-lg" 272 * name[0] != '_' ==> filename or invisible 273 * 274 * The "-lg" signals the beginning of global loader symbols. 275 * 276 */ 277 if ((np->n_type&N_STAB) != 0) { 278 foundstab = true; 279 enter_nl(name, np); 280 } else if (name[0] == '-') { 281 afterlg = true; 282 foundglobals(); 283 } else if (afterlg) { 284 check_global(name, np); 285 } else if ((np->n_type&N_EXT) == N_EXT) { 286 afterlg = true; 287 foundglobals(); 288 check_global(name, np); 289 } else if (name[0] == '_') { 290 check_local(&name[1], np); 291 } else if ((np->n_type&N_TEXT) == N_TEXT) { 292 check_filename(name); 293 } 294 ++curnp; 295 np = curnp; 296 } 297 if (not foundstab) { 298 warning("no source compiled with -g"); 299 } 300 dispose(namelist); 301 } 302 303 /* 304 * Get a continuation entry from the name list. 305 * Return the beginning of the name. 306 */ 307 308 public String getcont () 309 { 310 register integer index; 311 register String name; 312 313 ++curnp; 314 index = curnp->n_un.n_strx; 315 if (index == 0) { 316 name = ""; 317 } else { 318 name = &stringtab[index - 4]; 319 } 320 return name; 321 } 322 323 /* 324 * Initialize symbol information. 325 */ 326 327 private initsyms () 328 { 329 curblock = nil; 330 curlevel = 0; 331 nesting = 0; 332 program = insert(identname("", true)); 333 program->class = PROG; 334 program->language = primlang; 335 program->symvalue.funcv.beginaddr = CODESTART; 336 program->symvalue.funcv.inline = false; 337 newfunc(program, codeloc(program)); 338 findbeginning(program); 339 enterblock(program); 340 curmodule = program; 341 } 342 343 /* 344 * Free all the object file information that's being stored. 345 */ 346 347 public objfree () 348 { 349 symbol_free(); 350 /* keywords_free(); */ 351 /* names_free(); */ 352 /* dispose(stringtab); */ 353 clrfunctab(); 354 } 355 356 /* 357 * Enter a namelist entry. 358 */ 359 360 private enter_nl (name, np) 361 String name; 362 register struct nlist *np; 363 { 364 register Symbol s; 365 register Name n; 366 367 s = nil; 368 switch (np->n_type) { 369 /* 370 * Build a symbol for the FORTRAN common area. All GSYMS that follow 371 * will be chained in a list with the head kept in common.offset, and 372 * the tail in common.chain. 373 */ 374 case N_BCOMM: 375 if (curcomm) { 376 curcomm->symvalue.common.chain = commchain; 377 } 378 n = identname(name, true); 379 curcomm = lookup(n); 380 if (curcomm == nil) { 381 curcomm = insert(n); 382 curcomm->class = COMMON; 383 curcomm->block = curblock; 384 curcomm->level = program->level; 385 curcomm->symvalue.common.chain = nil; 386 } 387 commchain = curcomm->symvalue.common.chain; 388 break; 389 390 case N_ECOMM: 391 if (curcomm) { 392 curcomm->symvalue.common.chain = commchain; 393 curcomm = nil; 394 } 395 break; 396 397 case N_LBRAC: 398 ++nesting; 399 addrstk[nesting] = (linep - 1)->addr; 400 break; 401 402 case N_RBRAC: 403 --nesting; 404 if (addrstk[nesting] == NOADDR) { 405 exitblock(); 406 newfunc(curblock, (linep - 1)->addr); 407 addrstk[nesting] = (linep - 1)->addr; 408 } 409 break; 410 411 case N_SLINE: 412 enterline((Lineno) np->n_desc, (Address) np->n_value); 413 break; 414 415 /* 416 * Source files. 417 */ 418 case N_SO: 419 n = identname(name, true); 420 enterSourceModule(n, (Address) np->n_value); 421 break; 422 423 /* 424 * Textually included files. 425 */ 426 case N_SOL: 427 enterfile(name, (Address) np->n_value); 428 break; 429 430 /* 431 * These symbols are assumed to have non-nil names. 432 */ 433 case N_GSYM: 434 case N_FUN: 435 case N_STSYM: 436 case N_LCSYM: 437 case N_RSYM: 438 case N_PSYM: 439 case N_LSYM: 440 case N_SSYM: 441 case N_LENG: 442 if (index(name, ':') == nil) { 443 if (not warned) { 444 warned = true; 445 printf("warning: old style symbol information "); 446 printf("found in \"%s\"\n", curfilename()); 447 } 448 } else { 449 entersym(name, np); 450 } 451 break; 452 453 case N_PC: 454 case N_MOD2: 455 break; 456 457 default: 458 printf("warning: stab entry unrecognized: "); 459 if (name != nil) { 460 printf("name %s,", name); 461 } 462 printf("ntype %2x, desc %x, value %x'\n", 463 np->n_type, np->n_desc, np->n_value); 464 break; 465 } 466 } 467 468 /* 469 * Try to find the symbol that is referred to by the given name. Since it's 470 * an external, we need to follow a level or two of indirection. 471 */ 472 473 private Symbol findsym (n, var_isextref) 474 Name n; 475 boolean *var_isextref; 476 { 477 register Symbol r, s; 478 479 *var_isextref = false; 480 find(s, n) where 481 ( 482 s->level == program->level and ( 483 s->class == EXTREF or s->class == VAR or 484 s->class == PROC or s->class == FUNC 485 ) 486 ) or ( 487 s->block == program and s->class == MODULE 488 ) 489 endfind(s); 490 if (s == nil) { 491 r = nil; 492 } else if (s->class == EXTREF) { 493 *var_isextref = true; 494 r = s->symvalue.extref; 495 delete(s); 496 497 /* 498 * Now check for another level of indirection that could come from 499 * a forward reference in procedure nesting information. In this case 500 * the symbol has already been deleted. 501 */ 502 if (r != nil and r->class == EXTREF) { 503 r = r->symvalue.extref; 504 } 505 /* 506 } else if (s->class == MODULE) { 507 s->class = FUNC; 508 s->level = program->level; 509 r = s; 510 */ 511 } else { 512 r = s; 513 } 514 return r; 515 } 516 517 /* 518 * Create a symbol for a text symbol with no source information. 519 * We treat it as an assembly language function. 520 */ 521 522 private Symbol deffunc (n) 523 Name n; 524 { 525 Symbol f; 526 527 f = insert(n); 528 f->language = findlanguage(".s"); 529 f->class = FUNC; 530 f->type = t_int; 531 f->block = curblock; 532 f->level = program->level; 533 f->symvalue.funcv.src = false; 534 f->symvalue.funcv.inline = false; 535 if (f->chain != nil) { 536 panic("chain not nil in deffunc"); 537 } 538 return f; 539 } 540 541 /* 542 * Create a symbol for a data or bss symbol with no source information. 543 * We treat it as an assembly language variable. 544 */ 545 546 private Symbol defvar (n) 547 Name n; 548 { 549 Symbol v; 550 551 v = insert(n); 552 v->language = findlanguage(".s"); 553 v->storage = EXT; 554 v->class = VAR; 555 v->type = t_int; 556 v->level = program->level; 557 v->block = curblock; 558 return v; 559 } 560 561 /* 562 * Update a symbol entry with a text address. 563 */ 564 565 private updateTextSym (s, name, addr) 566 Symbol s; 567 char *name; 568 Address addr; 569 { 570 if (s->class == VAR) { 571 s->symvalue.offset = addr; 572 } else { 573 s->symvalue.funcv.beginaddr = addr; 574 if (name[0] == '_') { 575 newfunc(s, codeloc(s)); 576 findbeginning(s); 577 } 578 } 579 } 580 581 /* 582 * Avoid seeing Pascal labels as text symbols. 583 */ 584 585 private boolean PascalLabel (n) 586 Name n; 587 { 588 boolean b; 589 register char *p; 590 591 b = false; 592 if (curlang == findlanguage(".p")) { 593 p = ident(n); 594 while (*p != '\0') { 595 if (*p == '_' and *(p+1) == '$') { 596 b = true; 597 break; 598 } 599 ++p; 600 } 601 } 602 return b; 603 } 604 605 /* 606 * Check to see if a global _name is already in the symbol table, 607 * if not then insert it. 608 */ 609 610 private check_global (name, np) 611 String name; 612 register struct nlist *np; 613 { 614 register Name n; 615 register Symbol t, u; 616 char buf[4096]; 617 boolean isextref; 618 integer count; 619 620 if (not streq(name, "_end")) { 621 if (name[0] == '_') { 622 n = identname(&name[1], true); 623 } else { 624 n = identname(name, true); 625 if (lookup(n) != nil) { 626 sprintf(buf, "$%s", name); 627 n = identname(buf, false); 628 } 629 } 630 if ((np->n_type&N_TYPE) == N_TEXT) { 631 count = 0; 632 t = findsym(n, &isextref); 633 while (isextref) { 634 ++count; 635 updateTextSym(t, name, np->n_value); 636 t = findsym(n, &isextref); 637 } 638 if (count == 0) { 639 if (t == nil) { 640 if (not PascalLabel(n)) { 641 t = deffunc(n); 642 updateTextSym(t, name, np->n_value); 643 if (tracesyms) { 644 printdecl(t); 645 } 646 } 647 } else { 648 if (t->class == MODULE) { 649 u = t; 650 t = deffunc(n); 651 t->block = u; 652 if (tracesyms) { 653 printdecl(t); 654 } 655 } 656 updateTextSym(t, name, np->n_value); 657 } 658 } 659 } else if ((np->n_type&N_TYPE) == N_BSS or (np->n_type&N_TYPE) == N_DATA) { 660 find(t, n) where 661 t->class == COMMON 662 endfind(t); 663 if (t != nil) { 664 u = (Symbol) t->symvalue.common.offset; 665 while (u != nil) { 666 u->symvalue.offset = u->symvalue.common.offset+np->n_value; 667 u = u->symvalue.common.chain; 668 } 669 } else { 670 check_var(np, n); 671 } 672 } else { 673 check_var(np, n); 674 } 675 } 676 } 677 678 /* 679 * Check to see if a namelist entry refers to a variable. 680 * If not, create a variable for the entry. In any case, 681 * set the offset of the variable according to the value field 682 * in the entry. 683 * 684 * If the external name has been referred to by several other symbols, 685 * we must update each of them. 686 */ 687 688 private check_var (np, n) 689 struct nlist *np; 690 register Name n; 691 { 692 register Symbol t, u, next; 693 Symbol conflict; 694 695 t = lookup(n); 696 if (t == nil) { 697 t = defvar(n); 698 t->symvalue.offset = np->n_value; 699 if (tracesyms) { 700 printdecl(t); 701 } 702 } else { 703 conflict = nil; 704 do { 705 next = t->next_sym; 706 if (t->name == n) { 707 if (t->class == MODULE and t->block == program) { 708 conflict = t; 709 } else if (t->class == EXTREF and t->level == program->level) { 710 u = t->symvalue.extref; 711 while (u != nil and u->class == EXTREF) { 712 u = u->symvalue.extref; 713 } 714 u->symvalue.offset = np->n_value; 715 delete(t); 716 } else if (t->level == program->level and 717 (t->class == VAR or t->class == PROC or t->class == FUNC) 718 ) { 719 conflict = nil; 720 t->symvalue.offset = np->n_value; 721 } 722 } 723 t = next; 724 } while (t != nil); 725 if (conflict != nil) { 726 u = defvar(n); 727 u->block = conflict; 728 u->symvalue.offset = np->n_value; 729 } 730 } 731 } 732 733 /* 734 * Check to see if a local _name is known in the current scope. 735 * If not then enter it. 736 */ 737 738 private check_local (name, np) 739 String name; 740 register struct nlist *np; 741 { 742 register Name n; 743 register Symbol t, cur; 744 745 n = identname(name, true); 746 cur = ((np->n_type&N_TYPE) == N_TEXT) ? curmodule : curblock; 747 find(t, n) where t->block == cur endfind(t); 748 if (t == nil) { 749 t = insert(n); 750 t->language = findlanguage(".s"); 751 t->type = t_int; 752 t->block = cur; 753 t->storage = EXT; 754 t->level = cur->level; 755 if ((np->n_type&N_TYPE) == N_TEXT) { 756 t->class = FUNC; 757 t->symvalue.funcv.src = false; 758 t->symvalue.funcv.inline = false; 759 t->symvalue.funcv.beginaddr = np->n_value; 760 newfunc(t, codeloc(t)); 761 findbeginning(t); 762 } else { 763 t->class = VAR; 764 t->symvalue.offset = np->n_value; 765 } 766 } 767 } 768 769 /* 770 * Check to see if a symbol corresponds to a object file name. 771 * For some reason these are listed as in the text segment. 772 */ 773 774 private check_filename (name) 775 String name; 776 { 777 register String mname; 778 register integer i; 779 Name n; 780 Symbol s; 781 782 mname = strdup(name); 783 i = strlen(mname) - 2; 784 if (i >= 0 and mname[i] == '.' and mname[i+1] == 'o') { 785 mname[i] = '\0'; 786 --i; 787 while (mname[i] != '/' and i >= 0) { 788 --i; 789 } 790 n = identname(&mname[i+1], true); 791 find(s, n) where s->block == program and s->class == MODULE endfind(s); 792 if (s == nil) { 793 s = insert(n); 794 s->language = findlanguage(".s"); 795 s->class = MODULE; 796 s->symvalue.funcv.beginaddr = 0; 797 findbeginning(s); 798 } 799 if (curblock->class != PROG) { 800 exitblock(); 801 if (curblock->class != PROG) { 802 exitblock(); 803 } 804 } 805 enterblock(s); 806 curmodule = s; 807 } 808 } 809 810 /* 811 * Check to see if a symbol is about to be defined within an unnamed block. 812 * If this happens, we create a procedure for the unnamed block, make it 813 * "inline" so that tracebacks don't associate an activation record with it, 814 * and enter it into the function table so that it will be detected 815 * by "whatblock". 816 */ 817 818 public chkUnnamedBlock () 819 { 820 register Symbol s; 821 static int bnum = 0; 822 char buf[100]; 823 Address startaddr; 824 825 if (nesting > 0 and addrstk[nesting] != NOADDR) { 826 startaddr = (linep - 1)->addr; 827 ++bnum; 828 sprintf(buf, "$b%d", bnum); 829 s = insert(identname(buf, false)); 830 s->language = curlang; 831 s->class = PROC; 832 s->symvalue.funcv.src = false; 833 s->symvalue.funcv.inline = true; 834 s->symvalue.funcv.beginaddr = startaddr; 835 enterblock(s); 836 newfunc(s, startaddr); 837 addrstk[nesting] = NOADDR; 838 } 839 } 840 841 /* 842 * Compilation unit. C associates scope with filenames 843 * so we treat them as "modules". The filename without 844 * the suffix is used for the module name. 845 * 846 * Because there is no explicit "end-of-block" mark in 847 * the object file, we must exit blocks for the current 848 * procedure and module. 849 */ 850 851 private enterSourceModule (n, addr) 852 Name n; 853 Address addr; 854 { 855 register Symbol s; 856 Name nn; 857 String mname, suffix; 858 859 mname = strdup(ident(n)); 860 if (rindex(mname, '/') != nil) { 861 mname = rindex(mname, '/') + 1; 862 } 863 suffix = rindex(mname, '.'); 864 if (suffix > mname && *(suffix-1) == '.') { 865 /* special hack for C++ */ 866 --suffix; 867 } 868 curlang = findlanguage(suffix); 869 if (curlang == findlanguage(".f")) { 870 strip_ = true; 871 } 872 if (suffix != nil) { 873 *suffix = '\0'; 874 } 875 if (not (*language_op(curlang, L_HASMODULES))()) { 876 if (curblock->class != PROG) { 877 exitblock(); 878 if (curblock->class != PROG) { 879 exitblock(); 880 } 881 } 882 nn = identname(mname, true); 883 if (curmodule == nil or curmodule->name != nn) { 884 s = insert(nn); 885 s->class = MODULE; 886 s->symvalue.funcv.beginaddr = 0; 887 findbeginning(s); 888 } else { 889 s = curmodule; 890 } 891 s->language = curlang; 892 enterblock(s); 893 curmodule = s; 894 } 895 if (program->language == nil) { 896 program->language = curlang; 897 } 898 warned = false; 899 enterfile(ident(n), addr); 900 initTypeTable(); 901 } 902 903 /* 904 * Allocate file and line tables and initialize indices. 905 */ 906 907 private allocmaps (nf, nl) 908 integer nf, nl; 909 { 910 if (filetab != nil) { 911 dispose(filetab); 912 } 913 if (linetab != nil) { 914 dispose(linetab); 915 } 916 filetab = newarr(Filetab, nf); 917 linetab = newarr(Linetab, nl); 918 filep = filetab; 919 linep = linetab; 920 } 921 922 /* 923 * Add a file to the file table. 924 * 925 * If the new address is the same as the previous file address 926 * this routine used to not enter the file, but this caused some 927 * problems so it has been removed. It's not clear that this in 928 * turn may not also cause a problem. 929 */ 930 931 private enterfile (filename, addr) 932 String filename; 933 Address addr; 934 { 935 filep->addr = addr; 936 filep->filename = filename; 937 filep->lineindex = linep - linetab; 938 ++filep; 939 } 940 941 /* 942 * Since we only estimated the number of lines (and it was a poor 943 * estimation) and since we need to know the exact number of lines 944 * to do a binary search, we set it when we're done. 945 */ 946 947 private setnlines () 948 { 949 nlhdr.nlines = linep - linetab; 950 } 951 952 /* 953 * Similarly for nfiles ... 954 */ 955 956 private setnfiles () 957 { 958 nlhdr.nfiles = filep - filetab; 959 setsource(filetab[0].filename); 960 } 961