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