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