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