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