15521Slinton /* Copyright (c) 1982 Regents of the University of California */
25521Slinton 
3*5762Slinton static char sccsid[] = "@(#)source.c 1.2 02/11/82";
45521Slinton 
55521Slinton /*
6*5762Slinton  * Source file management.
75521Slinton  */
85521Slinton 
95521Slinton #include "defs.h"
105521Slinton #include "source.h"
115521Slinton 
125521Slinton /*
13*5762Slinton  * Seektab is the data structure used for indexing source
14*5762Slinton  * seek addresses by line number.
155521Slinton  *
165521Slinton  * The constraints are:
175521Slinton  *
18*5762Slinton  *  we want an array so indexing is fast and easy
19*5762Slinton  *  we don't want to waste space for small files
20*5762Slinton  *  we don't want an upper bound on # of lines in a file
21*5762Slinton  *  we don't know how many lines there are
225521Slinton  *
235521Slinton  * The solution is a "dirty" hash table.  We have NSLOTS pointers to
245521Slinton  * arrays of NLINESPERSLOT addresses.  To find the source address of
255521Slinton  * a particular line we find the slot, allocate space if necessary,
265521Slinton  * and then find its location within the pointed to array.
27*5762Slinton  *
28*5762Slinton  * As a result, there is a limit of NSLOTS*NLINESPERSLOT lines per file
29*5762Slinton  * but this is plenty high and still fairly inexpensive.
30*5762Slinton  *
31*5762Slinton  * This implementation maintains only one source file at any given
32*5762Slinton  * so as to avoid consuming too much memory.  In an environment where
33*5762Slinton  * memory is less constrained and one expects to be changing between
34*5762Slinton  * files often enough, it would be reasonable to have multiple seek tables.
355521Slinton  */
365521Slinton 
375521Slinton typedef int SEEKADDR;
385521Slinton 
395521Slinton #define NSLOTS 20
405521Slinton #define NLINESPERSLOT 500
415521Slinton 
42*5762Slinton #define slotno(line)    ((line)/NLINESPERSLOT)
43*5762Slinton #define index(line) ((line)%NLINESPERSLOT)
44*5762Slinton #define slot_alloc()    alloc(NLINESPERSLOT, SEEKADDR)
45*5762Slinton #define srcaddr(line)   seektab[(line)/NLINESPERSLOT][(line)%NLINESPERSLOT]
465521Slinton 
475521Slinton LOCAL SEEKADDR *seektab[NSLOTS];
485521Slinton 
495521Slinton LOCAL FILE *srcfp;
505521Slinton 
515521Slinton /*
525521Slinton  * check to make sure a source line number is valid
535521Slinton  */
545521Slinton 
555521Slinton chkline(linenum)
565521Slinton register LINENO linenum;
575521Slinton {
58*5762Slinton     if (linenum < 1) {
59*5762Slinton 	error("line number must be positive");
60*5762Slinton     }
61*5762Slinton     if (linenum > lastlinenum) {
62*5762Slinton 	error("not that many lines");
63*5762Slinton     }
645521Slinton }
655521Slinton 
665521Slinton /*
675521Slinton  * print out the given lines from the source
685521Slinton  */
695521Slinton 
705521Slinton printlines(l1, l2)
715521Slinton LINENO l1, l2;
725521Slinton {
73*5762Slinton     register int c;
74*5762Slinton     register LINENO i;
75*5762Slinton     register FILE *fp;
765521Slinton 
77*5762Slinton     chkline(l1);
78*5762Slinton     chkline(l2);
79*5762Slinton     if (l2 < l1) {
80*5762Slinton 	error("second line number less than first");
81*5762Slinton     }
82*5762Slinton     fp = srcfp;
83*5762Slinton     fseek(fp, (long) srcaddr(l1), 0);
84*5762Slinton     for (i = l1; i <= l2; i++) {
85*5762Slinton 	printf("%5d   ", i);
86*5762Slinton 	while ((c = getc(fp)) != '\n') {
87*5762Slinton 	    putchar(c);
885521Slinton 	}
89*5762Slinton 	putchar('\n');
90*5762Slinton     }
915521Slinton }
925521Slinton 
935521Slinton /*
945521Slinton  * read the source file getting seek pointers for each line
955521Slinton  */
965521Slinton 
975521Slinton skimsource(file)
985521Slinton char *file;
995521Slinton {
100*5762Slinton     register int c;
101*5762Slinton     register LINENO count;
102*5762Slinton     register FILE *fp;
103*5762Slinton     register LINENO linenum;
104*5762Slinton     register SEEKADDR lastaddr;
105*5762Slinton     register int slot;
1065521Slinton 
107*5762Slinton     if (file == NIL || file == cursource) {
108*5762Slinton 	return;
109*5762Slinton     }
110*5762Slinton     if ((fp = fopen(file, "r")) == NULL) {
111*5762Slinton 	panic("can't open \"%s\"", file);
112*5762Slinton     }
113*5762Slinton     if (cursource != NIL) {
114*5762Slinton 	free_seektab();
115*5762Slinton     }
116*5762Slinton     cursource = file;
117*5762Slinton     linenum = 0, count = 0, lastaddr = 0;
118*5762Slinton     while ((c = getc(fp)) != EOF) {
119*5762Slinton 	count++;
120*5762Slinton 	if (c == '\n') {
121*5762Slinton 	    slot = slotno(++linenum);
122*5762Slinton 	    if (slot >= NSLOTS) {
123*5762Slinton 		panic("skimsource: too many lines");
124*5762Slinton 	    }
125*5762Slinton 	    if (seektab[slot] == NIL) {
126*5762Slinton 		seektab[slot] = slot_alloc();
127*5762Slinton 	    }
128*5762Slinton 	    seektab[slot][index(linenum)] = lastaddr;
129*5762Slinton 	    lastaddr = count;
1305521Slinton 	}
131*5762Slinton     }
132*5762Slinton     lastlinenum = linenum;
133*5762Slinton     srcfp = fp;
1345521Slinton }
1355521Slinton 
1365521Slinton /*
1375521Slinton  * Erase information and release space in the current seektab.
1385521Slinton  * This is in preparation for reading in seek pointers for a
1395521Slinton  * new file.  It is possible that seek pointers for all files
1405521Slinton  * should be kept around, but the current concern is space.
1415521Slinton  */
1425521Slinton 
1435521Slinton LOCAL free_seektab()
1445521Slinton {
145*5762Slinton     register int slot;
1465521Slinton 
147*5762Slinton     for (slot = 0; slot < NSLOTS; slot++) {
148*5762Slinton 	if (seektab[slot] != NIL) {
149*5762Slinton 	    free(seektab[slot]);
150*5762Slinton 	    seektab[slot] = NIL;
1515521Slinton 	}
152*5762Slinton     }
1535521Slinton }
154