1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static char sccsid[] = "@(#)source.c 1.4 03/24/83";
4 
5 /*
6  * Source file management.
7  */
8 
9 #include "defs.h"
10 #include "source.h"
11 
12 /*
13  * Seektab is the data structure used for indexing source
14  * seek addresses by line number.
15  *
16  * The constraints are:
17  *
18  *  we want an array so indexing is fast and easy
19  *  we don't want to waste space for small files
20  *  we don't want an upper bound on # of lines in a file
21  *  we don't know how many lines there are
22  *
23  * The solution is a sparse array. We have NSLOTS pointers to
24  * arrays of NLINESPERSLOT addresses.  To find the source address of
25  * a particular line we find the slot, allocate space if necessary,
26  * and then find its location within the pointed to array.
27  *
28  * As a result, there is a limit of NSLOTS*NLINESPERSLOT lines per file
29  * but this is plenty high and still fairly inexpensive.
30  *
31  * This implementation maintains only one source file at any given
32  * so as to avoid consuming too much memory.  In an environment where
33  * memory is less constrained and one expects to be changing between
34  * files often enough, it would be reasonable to have multiple seek tables.
35  */
36 
37 typedef int SEEKADDR;
38 
39 #define NSLOTS 40
40 #define NLINESPERSLOT 500
41 
42 #define slotno(line)	((line)/NLINESPERSLOT)
43 #define index(line)	((line)%NLINESPERSLOT)
44 #define slot_alloc()	alloc(NLINESPERSLOT, SEEKADDR)
45 #define srcaddr(line)	seektab[(line)/NLINESPERSLOT][(line)%NLINESPERSLOT]
46 
47 LOCAL SEEKADDR *seektab[NSLOTS];
48 
49 LOCAL FILE *srcfp;
50 
51 /*
52  * check to make sure a source line number is valid
53  */
54 
55 chkline(linenum)
56 register LINENO linenum;
57 {
58     if (linenum < 1) {
59 	error("line number must be positive");
60     }
61     if (linenum > lastlinenum) {
62 	error("not that many lines");
63     }
64 }
65 
66 /*
67  * print out the given lines from the source
68  */
69 
70 printlines(l1, l2)
71 LINENO l1, l2;
72 {
73     register int c;
74     register LINENO i;
75     register FILE *fp;
76 
77     chkline(l1);
78     chkline(l2);
79     if (l2 < l1) {
80 	error("second line number less than first");
81     }
82     fp = srcfp;
83     fseek(fp, (long) srcaddr(l1), 0);
84     for (i = l1; i <= l2; i++) {
85 	printf("%5d   ", i);
86 	while ((c = getc(fp)) != '\n') {
87 	    putchar(c);
88 	}
89 	putchar('\n');
90     }
91 }
92 
93 /*
94  * read the source file getting seek pointers for each line
95  */
96 
97 skimsource(file)
98 char *file;
99 {
100     register int c;
101     register SEEKADDR count;
102     register FILE *fp;
103     register LINENO linenum;
104     register SEEKADDR lastaddr;
105     register int slot;
106 
107     if (file == NIL || file == cursource) {
108 	return;
109     }
110     if ((fp = fopen(file, "r")) == NULL) {
111 	panic("can't open \"%s\"", file);
112     }
113     if (cursource != NIL) {
114 	free_seektab();
115     }
116     cursource = file;
117     linenum = 0, count = 0, lastaddr = 0;
118     while ((c = getc(fp)) != EOF) {
119 	count++;
120 	if (c == '\n') {
121 	    slot = slotno(++linenum);
122 	    if (slot >= NSLOTS) {
123 		panic("skimsource: too many lines");
124 	    }
125 	    if (seektab[slot] == NIL) {
126 		seektab[slot] = slot_alloc();
127 	    }
128 	    seektab[slot][index(linenum)] = lastaddr;
129 	    lastaddr = count;
130 	}
131     }
132     lastlinenum = linenum;
133     srcfp = fp;
134 }
135 
136 /*
137  * Erase information and release space in the current seektab.
138  * This is in preparation for reading in seek pointers for a
139  * new file.  It is possible that seek pointers for all files
140  * should be kept around, but the current concern is space.
141  */
142 
143 LOCAL free_seektab()
144 {
145     register int slot;
146 
147     for (slot = 0; slot < NSLOTS; slot++) {
148 	if (seektab[slot] != NIL) {
149 	    free(seektab[slot]);
150 	    seektab[slot] = NIL;
151 	}
152     }
153 }
154