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