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