xref: /csrg-svn/old/dbx/object.c (revision 21615)
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.1 (Berkeley) 05/31/85";
9 #endif not lint
10 
11 static char rcsid[] = "$Header: object.c,v 1.6 84/12/26 10:40:51 linton 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     register Boolean afterlg;
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     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 	    enter_nl(name, np);
279 	} else if (name[0] == '-') {
280 	    afterlg = true;
281 	    foundglobals();
282 	} else if (afterlg) {
283 	    check_global(name, np);
284 	} else if ((np->n_type&N_EXT) == N_EXT) {
285 	    afterlg = true;
286 	    foundglobals();
287 	    check_global(name, np);
288 	} else if (name[0] == '_') {
289 	    check_local(&name[1], np);
290 	} else if ((np->n_type&N_TEXT) == N_TEXT) {
291 	    check_filename(name);
292 	}
293 	++curnp;
294 	np = curnp;
295     }
296     dispose(namelist);
297 }
298 
299 /*
300  * Get a continuation entry from the name list.
301  * Return the beginning of the name.
302  */
303 
304 public String getcont ()
305 {
306     register integer index;
307     register String name;
308 
309     ++curnp;
310     index = curnp->n_un.n_strx;
311     if (index == 0) {
312 	panic("continuation followed by empty stab");
313     }
314     name = &stringtab[index - 4];
315     return name;
316 }
317 
318 /*
319  * Initialize symbol information.
320  */
321 
322 private initsyms ()
323 {
324     curblock = nil;
325     curlevel = 0;
326     nesting = 0;
327     program = insert(identname("", true));
328     program->class = PROG;
329     program->symvalue.funcv.beginaddr = 0;
330     program->symvalue.funcv.inline = false;
331     newfunc(program, codeloc(program));
332     findbeginning(program);
333     enterblock(program);
334     curmodule = program;
335 }
336 
337 /*
338  * Free all the object file information that's being stored.
339  */
340 
341 public objfree ()
342 {
343     symbol_free();
344     /* keywords_free(); */
345     /* names_free(); */
346     /* dispose(stringtab); */
347     clrfunctab();
348 }
349 
350 /*
351  * Enter a namelist entry.
352  */
353 
354 private enter_nl (name, np)
355 String name;
356 register struct nlist *np;
357 {
358     register Symbol s;
359     register Name n;
360 
361     s = nil;
362     switch (np->n_type) {
363 	/*
364 	 * Build a symbol for the FORTRAN common area.  All GSYMS that follow
365 	 * will be chained in a list with the head kept in common.offset, and
366 	 * the tail in common.chain.
367 	 */
368 	case N_BCOMM:
369  	    if (curcomm) {
370 		curcomm->symvalue.common.chain = commchain;
371 	    }
372 	    n = identname(name, true);
373 	    curcomm = lookup(n);
374 	    if (curcomm == nil) {
375 		curcomm = insert(n);
376 		curcomm->class = COMMON;
377 		curcomm->block = curblock;
378 		curcomm->level = program->level;
379 		curcomm->symvalue.common.chain = nil;
380 	    }
381 	    commchain = curcomm->symvalue.common.chain;
382 	    break;
383 
384 	case N_ECOMM:
385 	    if (curcomm) {
386 		curcomm->symvalue.common.chain = commchain;
387 		curcomm = nil;
388 	    }
389 	    break;
390 
391 	case N_LBRAC:
392 	    ++nesting;
393 	    addrstk[nesting] = (linep - 1)->addr;
394 	    break;
395 
396 	case N_RBRAC:
397 	    --nesting;
398 	    if (addrstk[nesting] == NOADDR) {
399 		exitblock();
400 		newfunc(curblock, (linep - 1)->addr);
401 		addrstk[nesting] = (linep - 1)->addr;
402 	    }
403 	    break;
404 
405 	case N_SLINE:
406 	    enterline((Lineno) np->n_desc, (Address) np->n_value);
407 	    break;
408 
409 	/*
410 	 * Source files.
411 	 */
412 	case N_SO:
413 	    n = identname(name, true);
414 	    enterSourceModule(n, (Address) np->n_value);
415 	    break;
416 
417 	/*
418 	 * Textually included files.
419 	 */
420 	case N_SOL:
421 	    enterfile(name, (Address) np->n_value);
422 	    break;
423 
424 	/*
425 	 * These symbols are assumed to have non-nil names.
426 	 */
427 	case N_GSYM:
428 	case N_FUN:
429 	case N_STSYM:
430 	case N_LCSYM:
431 	case N_RSYM:
432 	case N_PSYM:
433 	case N_LSYM:
434 	case N_SSYM:
435 	case N_LENG:
436 	    if (index(name, ':') == nil) {
437 		if (not warned) {
438 		    warned = true;
439 		    warning("old style symbol information found in \"%s\"",
440 			curfilename());
441 		}
442 	    } else {
443 		entersym(name, np);
444 	    }
445 	    break;
446 
447 	case N_PC:
448 	case N_MOD2:
449 	    break;
450 
451 	default:
452 	    printf("warning:  stab entry unrecognized: ");
453 	    if (name != nil) {
454 		printf("name %s,", name);
455 	    }
456 	    printf("ntype %2x, desc %x, value %x'\n",
457 		np->n_type, np->n_desc, np->n_value);
458 	    break;
459     }
460 }
461 
462 /*
463  * Try to find the symbol that is referred to by the given name.  Since it's
464  * an external, we need to follow a level or two of indirection.
465  */
466 
467 private Symbol findsym (n, var_isextref)
468 Name n;
469 boolean *var_isextref;
470 {
471     register Symbol r, s;
472 
473     *var_isextref = false;
474     find(s, n) where
475 	(
476 	    s->level == program->level and (
477 		s->class == EXTREF or s->class == VAR or
478 		s->class == PROC or s->class == FUNC
479 	    )
480 	) or (
481 	    s->block == program and s->class == MODULE
482 	)
483     endfind(s);
484     if (s == nil) {
485 	r = nil;
486     } else if (s->class == EXTREF) {
487 	*var_isextref = true;
488 	r = s->symvalue.extref;
489 	delete(s);
490 
491 	/*
492 	 * Now check for another level of indirection that could come from
493 	 * a forward reference in procedure nesting information.  In this case
494 	 * the symbol has already been deleted.
495 	 */
496 	if (r != nil and r->class == EXTREF) {
497 	    r = r->symvalue.extref;
498 	}
499 /*
500     } else if (s->class == MODULE) {
501 	s->class = FUNC;
502 	s->level = program->level;
503 	r = s;
504  */
505     } else {
506 	r = s;
507     }
508     return r;
509 }
510 
511 /*
512  * Create a symbol for a text symbol with no source information.
513  * We treat it as an assembly language function.
514  */
515 
516 private Symbol deffunc (n)
517 Name n;
518 {
519     Symbol f;
520 
521     f = insert(n);
522     f->language = findlanguage(".s");
523     f->class = FUNC;
524     f->type = t_int;
525     f->block = curblock;
526     f->level = program->level;
527     f->symvalue.funcv.src = false;
528     f->symvalue.funcv.inline = false;
529     return f;
530 }
531 
532 /*
533  * Create a symbol for a data or bss symbol with no source information.
534  * We treat it as an assembly language variable.
535  */
536 
537 private Symbol defvar (n)
538 Name n;
539 {
540     Symbol v;
541 
542     v = insert(n);
543     v->language = findlanguage(".s");
544     v->class = VAR;
545     v->type = t_int;
546     v->level = program->level;
547     v->block = curblock;
548     return v;
549 }
550 
551 /*
552  * Update a symbol entry with a text address.
553  */
554 
555 private updateTextSym (s, name, addr)
556 Symbol s;
557 char *name;
558 Address addr;
559 {
560     if (s->class == VAR) {
561 	s->symvalue.offset = addr;
562     } else {
563 	s->symvalue.funcv.beginaddr = addr;
564 	if (name[0] == '_') {
565 	    newfunc(s, codeloc(s));
566 	    findbeginning(s);
567 	}
568     }
569 }
570 
571 /*
572  * Check to see if a global _name is already in the symbol table,
573  * if not then insert it.
574  */
575 
576 private check_global (name, np)
577 String name;
578 register struct nlist *np;
579 {
580     register Name n;
581     register Symbol t, u;
582     char buf[4096];
583     boolean isextref;
584     integer count;
585 
586     if (not streq(name, "_end")) {
587 	if (name[0] == '_') {
588 	    n = identname(&name[1], true);
589 	} else {
590 	    n = identname(name, true);
591 	    if (lookup(n) != nil) {
592 		sprintf(buf, "$%s", name);
593 		n = identname(buf, false);
594 	    }
595 	}
596 	if ((np->n_type&N_TYPE) == N_TEXT) {
597 	    count = 0;
598 	    t = findsym(n, &isextref);
599 	    while (isextref) {
600 		++count;
601 		updateTextSym(t, name, np->n_value);
602 		t = findsym(n, &isextref);
603 	    }
604 	    if (count == 0) {
605 		if (t == nil) {
606 		    t = deffunc(n);
607 		    updateTextSym(t, name, np->n_value);
608 		    if (tracesyms) {
609 			printdecl(t);
610 		    }
611 		} else {
612 		    if (t->class == MODULE) {
613 			u = t;
614 			t = deffunc(n);
615 			t->block = u;
616 			if (tracesyms) {
617 			    printdecl(t);
618 			}
619 		    }
620 		    updateTextSym(t, name, np->n_value);
621 		}
622 	    }
623 	} else if ((np->n_type&N_TYPE) == N_BSS) {
624 	    find(t, n) where
625 		t->class == COMMON
626 	    endfind(t);
627 	    if (t != nil) {
628 		u = (Symbol) t->symvalue.common.offset;
629 		while (u != nil) {
630 		    u->symvalue.offset = u->symvalue.common.offset+np->n_value;
631 		    u = u->symvalue.common.chain;
632 		}
633             } else {
634 		check_var(np, n);
635 	    }
636         } else {
637 	    check_var(np, n);
638 	}
639     }
640 }
641 
642 /*
643  * Check to see if a namelist entry refers to a variable.
644  * If not, create a variable for the entry.  In any case,
645  * set the offset of the variable according to the value field
646  * in the entry.
647  *
648  * If the external name has been referred to by several other symbols,
649  * we must update each of them.
650  */
651 
652 private check_var (np, n)
653 struct nlist *np;
654 register Name n;
655 {
656     register Symbol t, u, next;
657     Symbol conflict;
658 
659     t = lookup(n);
660     if (t == nil) {
661 	t = defvar(n);
662 	t->symvalue.offset = np->n_value;
663 	if (tracesyms) {
664 	    printdecl(t);
665 	}
666     } else {
667 	conflict = nil;
668 	do {
669 	    next = t->next_sym;
670 	    if (t->name == n) {
671 		if (t->class == MODULE and t->block == program) {
672 		    conflict = t;
673 		} else if (t->class == EXTREF and t->level == program->level) {
674 		    u = t->symvalue.extref;
675 		    while (u != nil and u->class == EXTREF) {
676 			u = u->symvalue.extref;
677 		    }
678 		    u->symvalue.offset = np->n_value;
679 		    delete(t);
680 		} else if (t->level == program->level and
681 		    (t->class == VAR or t->class == PROC or t->class == FUNC)
682 		) {
683 		    conflict = nil;
684 		    t->symvalue.offset = np->n_value;
685 		}
686 	    }
687 	    t = next;
688 	} while (t != nil);
689 	if (conflict != nil) {
690 	    u = defvar(n);
691 	    u->block = conflict;
692 	    u->symvalue.offset = np->n_value;
693 	}
694     }
695 }
696 
697 /*
698  * Check to see if a local _name is known in the current scope.
699  * If not then enter it.
700  */
701 
702 private check_local (name, np)
703 String name;
704 register struct nlist *np;
705 {
706     register Name n;
707     register Symbol t, cur;
708 
709     n = identname(name, true);
710     cur = ((np->n_type&N_TYPE) == N_TEXT) ? curmodule : curblock;
711     find(t, n) where t->block == cur endfind(t);
712     if (t == nil) {
713 	t = insert(n);
714 	t->language = findlanguage(".s");
715 	t->type = t_int;
716 	t->block = cur;
717 	t->level = cur->level;
718 	if ((np->n_type&N_TYPE) == N_TEXT) {
719 	    t->class = FUNC;
720 	    t->symvalue.funcv.src = false;
721 	    t->symvalue.funcv.inline = false;
722 	    t->symvalue.funcv.beginaddr = np->n_value;
723 	    newfunc(t, codeloc(t));
724 	    findbeginning(t);
725 	} else {
726 	    t->class = VAR;
727 	    t->symvalue.offset = np->n_value;
728 	}
729     }
730 }
731 
732 /*
733  * Check to see if a symbol corresponds to a object file name.
734  * For some reason these are listed as in the text segment.
735  */
736 
737 private check_filename (name)
738 String name;
739 {
740     register String mname;
741     register integer i;
742     Name n;
743     Symbol s;
744 
745     mname = strdup(name);
746     i = strlen(mname) - 2;
747     if (i >= 0 and mname[i] == '.' and mname[i+1] == 'o') {
748 	mname[i] = '\0';
749 	--i;
750 	while (mname[i] != '/' and i >= 0) {
751 	    --i;
752 	}
753 	n = identname(&mname[i+1], true);
754 	find(s, n) where s->block == program and s->class == MODULE endfind(s);
755 	if (s == nil) {
756 	    s = insert(n);
757 	    s->language = findlanguage(".s");
758 	    s->class = MODULE;
759 	    s->symvalue.funcv.beginaddr = 0;
760 	    findbeginning(s);
761 	}
762 	if (curblock->class != PROG) {
763 	    exitblock();
764 	    if (curblock->class != PROG) {
765 		exitblock();
766 	    }
767 	}
768 	enterblock(s);
769 	curmodule = s;
770     }
771 }
772 
773 /*
774  * Check to see if a symbol is about to be defined within an unnamed block.
775  * If this happens, we create a procedure for the unnamed block, make it
776  * "inline" so that tracebacks don't associate an activation record with it,
777  * and enter it into the function table so that it will be detected
778  * by "whatblock".
779  */
780 
781 public chkUnnamedBlock ()
782 {
783     register Symbol s;
784     static int bnum = 0;
785     char buf[100];
786     Address startaddr;
787 
788     if (nesting > 0 and addrstk[nesting] != NOADDR) {
789 	startaddr = (linep - 1)->addr;
790 	++bnum;
791 	sprintf(buf, "$b%d", bnum);
792 	s = insert(identname(buf, false));
793 	s->language = curlang;
794 	s->class = PROC;
795 	s->symvalue.funcv.src = false;
796 	s->symvalue.funcv.inline = true;
797 	s->symvalue.funcv.beginaddr = startaddr;
798 	enterblock(s);
799 	newfunc(s, startaddr);
800 	addrstk[nesting] = NOADDR;
801     }
802 }
803 
804 /*
805  * Compilation unit.  C associates scope with filenames
806  * so we treat them as "modules".  The filename without
807  * the suffix is used for the module name.
808  *
809  * Because there is no explicit "end-of-block" mark in
810  * the object file, we must exit blocks for the current
811  * procedure and module.
812  */
813 
814 private enterSourceModule (n, addr)
815 Name n;
816 Address addr;
817 {
818     register Symbol s;
819     Name nn;
820     String mname, suffix;
821 
822     mname = strdup(ident(n));
823     if (rindex(mname, '/') != nil) {
824 	mname = rindex(mname, '/') + 1;
825     }
826     suffix = rindex(mname, '.');
827     curlang = findlanguage(suffix);
828     if (curlang == findlanguage(".f")) {
829 	strip_ = true;
830     }
831     if (suffix != nil) {
832 	*suffix = '\0';
833     }
834     if (not (*language_op(curlang, L_HASMODULES))()) {
835 	if (curblock->class != PROG) {
836 	    exitblock();
837 	    if (curblock->class != PROG) {
838 		exitblock();
839 	    }
840 	}
841 	nn = identname(mname, true);
842 	if (curmodule == nil or curmodule->name != nn) {
843 	    s = insert(nn);
844 	    s->class = MODULE;
845 	    s->symvalue.funcv.beginaddr = 0;
846 	    findbeginning(s);
847 	} else {
848 	    s = curmodule;
849 	}
850 	s->language = curlang;
851 	enterblock(s);
852 	curmodule = s;
853     }
854     if (program->language == nil) {
855 	program->language = curlang;
856     }
857     warned = false;
858     enterfile(ident(n), addr);
859     initTypeTable();
860 }
861 
862 /*
863  * Allocate file and line tables and initialize indices.
864  */
865 
866 private allocmaps (nf, nl)
867 integer nf, nl;
868 {
869     if (filetab != nil) {
870 	dispose(filetab);
871     }
872     if (linetab != nil) {
873 	dispose(linetab);
874     }
875     filetab = newarr(Filetab, nf);
876     linetab = newarr(Linetab, nl);
877     filep = filetab;
878     linep = linetab;
879 }
880 
881 /*
882  * Add a file to the file table.
883  *
884  * If the new address is the same as the previous file address
885  * this routine used to not enter the file, but this caused some
886  * problems so it has been removed.  It's not clear that this in
887  * turn may not also cause a problem.
888  */
889 
890 private enterfile (filename, addr)
891 String filename;
892 Address addr;
893 {
894     filep->addr = addr;
895     filep->filename = filename;
896     filep->lineindex = linep - linetab;
897     ++filep;
898 }
899 
900 /*
901  * Since we only estimated the number of lines (and it was a poor
902  * estimation) and since we need to know the exact number of lines
903  * to do a binary search, we set it when we're done.
904  */
905 
906 private setnlines ()
907 {
908     nlhdr.nlines = linep - linetab;
909 }
910 
911 /*
912  * Similarly for nfiles ...
913  */
914 
915 private setnfiles ()
916 {
917     nlhdr.nfiles = filep - filetab;
918     setsource(filetab[0].filename);
919 }
920