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