xref: /csrg-svn/old/dbx/source.c (revision 14337)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static char sccsid[] = "@(#)source.c 1.9 08/05/83";
4 
5 /*
6  * Source file management.
7  */
8 
9 #include "defs.h"
10 #include "source.h"
11 #include "object.h"
12 #include "mappings.h"
13 #include "machine.h"
14 
15 #ifndef public
16 typedef int Lineno;
17 
18 String cursource;
19 Lineno curline;
20 Lineno cursrcline;
21 
22 #define LASTLINE 0		/* recognized by printlines */
23 
24 #include "lists.h"
25 
26 List sourcepath;
27 #endif
28 
29 private Lineno lastlinenum;
30 private String prevsource = nil;
31 
32 /*
33  * Data structure for indexing source seek addresses by line number.
34  *
35  * The constraints are:
36  *
37  *  we want an array so indexing is fast and easy
38  *  we don't want to waste space for small files
39  *  we don't want an upper bound on # of lines in a file
40  *  we don't know how many lines there are
41  *
42  * The solution is a "dirty" hash table.  We have NSLOTS pointers to
43  * arrays of NLINESPERSLOT addresses.  To find the source address of
44  * a particular line we find the slot, allocate space if necessary,
45  * and then find its location within the pointed to array.
46  */
47 
48 typedef long Seekaddr;
49 
50 #define NSLOTS 20
51 #define NLINESPERSLOT 500
52 
53 #define slotno(line)    ((line) div NLINESPERSLOT)
54 #define index(line)	((line) mod NLINESPERSLOT)
55 #define slot_alloc()    newarr(Seekaddr, NLINESPERSLOT)
56 #define srcaddr(line)	seektab[slotno(line)][index(line)]
57 
58 private File srcfp;
59 private Seekaddr *seektab[NSLOTS];
60 
61 /*
62  * Print out the given lines from the source.
63  */
64 
65 public printlines(l1, l2)
66 Lineno l1, l2;
67 {
68     register int c;
69     register Lineno i, lb, ub;
70     register File f;
71 
72     if (cursource == nil) {
73 	beginerrmsg();
74 	fprintf(stderr, "no source file\n");
75     } else {
76 	if (cursource != prevsource) {
77 	    skimsource();
78 	}
79 	if (lastlinenum == 0) {
80 	    beginerrmsg();
81 	    fprintf(stderr, "couldn't read \"%s\"\n", cursource);
82 	} else {
83 	    lb = (l1 == 0) ? lastlinenum : l1;
84 	    ub = (l2 == 0) ? lastlinenum : l2;
85 	    if (lb < 1) {
86 		beginerrmsg();
87 		fprintf(stderr, "line number must be positive\n");
88 	    } else if (lb > lastlinenum) {
89 		beginerrmsg();
90 		if (lastlinenum == 1) {
91 		    fprintf(stderr, "\"%s\" has only 1 line\n", cursource);
92 		} else {
93 		    fprintf(stderr, "\"%s\" has only %d lines\n",
94 			cursource, lastlinenum);
95 		}
96 	    } else if (ub < lb) {
97 		beginerrmsg();
98 		fprintf(stderr, "second number must be greater than first\n");
99 	    } else {
100 		if (ub > lastlinenum) {
101 		    ub = lastlinenum;
102 		}
103 		f = srcfp;
104 		fseek(f, srcaddr(lb), 0);
105 		for (i = lb; i <= ub; i++) {
106 		    printf("%5d   ", i);
107 		    while ((c = getc(f)) != '\n') {
108 			putchar(c);
109 		    }
110 		    putchar('\n');
111 		}
112 		cursrcline = ub + 1;
113 	    }
114 	}
115     }
116 }
117 
118 /*
119  * Search the sourcepath for a file.
120  */
121 
122 static char fileNameBuf[1024];
123 
124 public String findsource(filename)
125 String filename;
126 {
127     register File f;
128     register String src, dir;
129 
130     if (filename[0] == '/') {
131 	src = filename;
132     } else {
133 	src = nil;
134 	foreach (String, dir, sourcepath)
135 	    sprintf(fileNameBuf, "%s/%s", dir, filename);
136 	    f = fopen(fileNameBuf, "r");
137 	    if (f != nil) {
138 		fclose(f);
139 		src = fileNameBuf;
140 		break;
141 	    }
142 	endfor
143     }
144     return src;
145 }
146 
147 /*
148  * Open a source file looking in the appropriate places.
149  */
150 
151 public File opensource(filename)
152 String filename;
153 {
154     String s;
155     File f;
156 
157     s = findsource(filename);
158     if (s == nil) {
159 	f = nil;
160     } else {
161 	f = fopen(s, "r");
162     }
163     return f;
164 }
165 
166 /*
167  * Set the current source file.
168  */
169 
170 public setsource(filename)
171 String filename;
172 {
173     if (filename != nil and filename != cursource) {
174 	prevsource = cursource;
175 	cursource = filename;
176 	cursrcline = 1;
177     }
178 }
179 
180 /*
181  * Read the source file getting seek pointers for each line.
182  */
183 
184 private skimsource()
185 {
186     register int c;
187     register Seekaddr count;
188     register File f;
189     register Lineno linenum;
190     register Seekaddr lastaddr;
191     register int slot;
192 
193     f = opensource(cursource);
194     if (f == nil) {
195 	lastlinenum = 0;
196     } else {
197 	if (prevsource != nil) {
198 	    free_seektab();
199 	    if (srcfp != nil) {
200 		fclose(srcfp);
201 	    }
202 	}
203 	prevsource = cursource;
204 	linenum = 0;
205 	count = 0;
206 	lastaddr = 0;
207 	while ((c = getc(f)) != EOF) {
208 	    ++count;
209 	    if (c == '\n') {
210 		slot = slotno(++linenum);
211 		if (slot >= NSLOTS) {
212 		    panic("skimsource: too many lines");
213 		}
214 		if (seektab[slot] == nil) {
215 		    seektab[slot] = slot_alloc();
216 		}
217 		seektab[slot][index(linenum)] = lastaddr;
218 		lastaddr = count;
219 	    }
220 	}
221 	lastlinenum = linenum;
222 	srcfp = f;
223     }
224 }
225 
226 /*
227  * Erase information and release space in the current seektab.
228  * This is in preparation for reading in seek pointers for a
229  * new file.  It is possible that seek pointers for all files
230  * should be kept around, but the current concern is space.
231  */
232 
233 private free_seektab()
234 {
235     register int slot;
236 
237     for (slot = 0; slot < NSLOTS; slot++) {
238 	if (seektab[slot] != nil) {
239 	    dispose(seektab[slot]);
240 	}
241     }
242 }
243 
244 /*
245  * Figure out current source position.
246  */
247 
248 public getsrcpos()
249 {
250     String filename;
251 
252     curline = srcline(pc);
253     filename = srcfilename(pc);
254     setsource(filename);
255     if (curline != 0) {
256 	cursrcline = curline;
257     }
258 }
259 
260 /*
261  * Print out the current source position.
262  */
263 
264 public printsrcpos()
265 {
266     printf("at line %d", curline);
267     if (nlhdr.nfiles > 1) {
268 	printf(" in file \"%s\"", cursource);
269     }
270 }
271 
272 #define DEF_EDITOR  "vi"
273 
274 /*
275  * Invoke an editor on the given file.  Which editor to use might change
276  * installation to installation.  For now, we use "vi".  In any event,
277  * the environment variable "EDITOR" overrides any default.
278  */
279 
280 public edit(filename)
281 String filename;
282 {
283     extern String getenv();
284     String ed, src, s;
285     Symbol f;
286     Address addr;
287     char lineno[10];
288 
289     ed = getenv("EDITOR");
290     if (ed == nil) {
291 	ed = DEF_EDITOR;
292     }
293     src = findsource((filename != nil) ? filename : cursource);
294     if (src == nil) {
295 	f = which(identname(filename, true));
296 	if (not isblock(f)) {
297 	    error("can't read \"%s\"", filename);
298 	}
299 	addr = firstline(f);
300 	if (addr == NOADDR) {
301 	    error("no source for \"%s\"", filename);
302 	}
303 	src = srcfilename(addr);
304 	s = findsource(src);
305 	if (s != nil) {
306 	    src = s;
307 	}
308 	sprintf(lineno, "+%d", srcline(addr));
309     } else {
310 	sprintf(lineno, "+1");
311     }
312     call(ed, stdin, stdout, lineno, src, nil);
313 }
314