xref: /csrg-svn/old/dbx/source.c (revision 18233)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static	char sccsid[] = "@(#)source.c	1.12 (Berkeley) 03/01/85";
4 
5 static char rcsid[] = "$Header: source.c,v 1.4 84/06/07 16:29:38 linton Exp $";
6 
7 /*
8  * Source file management.
9  */
10 
11 #include "defs.h"
12 #include "source.h"
13 #include "object.h"
14 #include "mappings.h"
15 #include "machine.h"
16 #include "keywords.h"
17 #include "tree.h"
18 #include "eval.h"
19 #include <sys/file.h>
20 
21 #ifndef public
22 typedef int Lineno;
23 
24 String cursource;
25 Lineno curline;
26 Lineno cursrcline;
27 
28 #define LASTLINE 0		/* recognized by printlines */
29 
30 #include "lists.h"
31 
32 List sourcepath;
33 #endif
34 
35 extern char *re_comp();
36 
37 private Lineno lastlinenum;
38 private String prevsource = nil;
39 
40 /*
41  * Data structure for indexing source seek addresses by line number.
42  *
43  * The constraints are:
44  *
45  *  we want an array so indexing is fast and easy
46  *  we don't want to waste space for small files
47  *  we don't want an upper bound on # of lines in a file
48  *  we don't know how many lines there are
49  *
50  * The solution is a "dirty" hash table.  We have NSLOTS pointers to
51  * arrays of NLINESPERSLOT addresses.  To find the source address of
52  * a particular line we find the slot, allocate space if necessary,
53  * and then find its location within the pointed to array.
54  */
55 
56 typedef long Seekaddr;
57 
58 #define NSLOTS 40
59 #define NLINESPERSLOT 500
60 
61 #define slotno(line)    ((line) div NLINESPERSLOT)
62 #define index(line)	((line) mod NLINESPERSLOT)
63 #define slot_alloc()    newarr(Seekaddr, NLINESPERSLOT)
64 #define srcaddr(line)	seektab[slotno(line)][index(line)]
65 
66 private File srcfp;
67 private Seekaddr *seektab[NSLOTS];
68 
69 /*
70  * Determine if the current source file is available.
71  */
72 
73 public boolean canReadSource ()
74 {
75     boolean b;
76 
77     if (cursource == nil) {
78 	b = false;
79     } else if (cursource != prevsource) {
80 	skimsource();
81 	b = (boolean) (lastlinenum != 0);
82     } else {
83 	b = true;
84     }
85     return b;
86 }
87 
88 /*
89  * Print out the given lines from the source.
90  */
91 
92 public printlines(l1, l2)
93 Lineno l1, l2;
94 {
95     register int c;
96     register Lineno i, lb, ub;
97     register File f;
98 
99     if (cursource == nil) {
100 	beginerrmsg();
101 	fprintf(stderr, "no source file\n");
102     } else {
103 	if (cursource != prevsource) {
104 	    skimsource();
105 	}
106 	if (lastlinenum == 0) {
107 	    beginerrmsg();
108 	    fprintf(stderr, "couldn't read \"%s\"\n", cursource);
109 	} else {
110 	    lb = (l1 == LASTLINE) ? lastlinenum : l1;
111 	    ub = (l2 == LASTLINE) ? lastlinenum : l2;
112 	    if (lb < 1) {
113 		beginerrmsg();
114 		fprintf(stderr, "line number must be positive\n");
115 	    } else if (lb > lastlinenum) {
116 		beginerrmsg();
117 		if (lastlinenum == 1) {
118 		    fprintf(stderr, "\"%s\" has only 1 line\n", cursource);
119 		} else {
120 		    fprintf(stderr, "\"%s\" has only %d lines\n",
121 			cursource, lastlinenum);
122 		}
123 	    } else if (ub < lb) {
124 		beginerrmsg();
125 		fprintf(stderr, "second number must be greater than first\n");
126 	    } else {
127 		if (ub > lastlinenum) {
128 		    ub = lastlinenum;
129 		}
130 		f = srcfp;
131 		fseek(f, srcaddr(lb), 0);
132 		for (i = lb; i <= ub; i++) {
133 		    printf("%5d   ", i);
134 		    while ((c = getc(f)) != '\n') {
135 			putchar(c);
136 		    }
137 		    putchar('\n');
138 		}
139 		cursrcline = ub + 1;
140 	    }
141 	}
142     }
143 }
144 
145 /*
146  * Search the sourcepath for a file.
147  */
148 
149 static char fileNameBuf[1024];
150 
151 public String findsource(filename)
152 String filename;
153 {
154     register String src, dir;
155 
156     if (filename[0] == '/') {
157 	src = filename;
158     } else {
159 	src = nil;
160 	foreach (String, dir, sourcepath)
161 	    sprintf(fileNameBuf, "%s/%s", dir, filename);
162 	    if (access(fileNameBuf, R_OK) == 0) {
163 		src = fileNameBuf;
164 		break;
165 	    }
166 	endfor
167     }
168     return src;
169 }
170 
171 /*
172  * Open a source file looking in the appropriate places.
173  */
174 
175 public File opensource(filename)
176 String filename;
177 {
178     String s;
179     File f;
180 
181     s = findsource(filename);
182     if (s == nil) {
183 	f = nil;
184     } else {
185 	f = fopen(s, "r");
186     }
187     return f;
188 }
189 
190 /*
191  * Set the current source file.
192  */
193 
194 public setsource(filename)
195 String filename;
196 {
197     if (filename != nil and filename != cursource) {
198 	prevsource = cursource;
199 	cursource = filename;
200 	cursrcline = 1;
201     }
202 }
203 
204 /*
205  * Read the source file getting seek pointers for each line.
206  */
207 
208 private skimsource()
209 {
210     register int c;
211     register Seekaddr count;
212     register File f;
213     register Lineno linenum;
214     register Seekaddr lastaddr;
215     register int slot;
216 
217     f = opensource(cursource);
218     if (f == nil) {
219 	lastlinenum = 0;
220     } else {
221 	if (prevsource != nil) {
222 	    free_seektab();
223 	    if (srcfp != nil) {
224 		fclose(srcfp);
225 	    }
226 	}
227 	prevsource = cursource;
228 	linenum = 0;
229 	count = 0;
230 	lastaddr = 0;
231 	while ((c = getc(f)) != EOF) {
232 	    ++count;
233 	    if (c == '\n') {
234 		slot = slotno(++linenum);
235 		if (slot >= NSLOTS) {
236 		    panic("skimsource: too many lines");
237 		}
238 		if (seektab[slot] == nil) {
239 		    seektab[slot] = slot_alloc();
240 		}
241 		seektab[slot][index(linenum)] = lastaddr;
242 		lastaddr = count;
243 	    }
244 	}
245 	lastlinenum = linenum;
246 	srcfp = f;
247     }
248 }
249 
250 /*
251  * Erase information and release space in the current seektab.
252  * This is in preparation for reading in seek pointers for a
253  * new file.  It is possible that seek pointers for all files
254  * should be kept around, but the current concern is space.
255  */
256 
257 private free_seektab()
258 {
259     register int slot;
260 
261     for (slot = 0; slot < NSLOTS; slot++) {
262 	if (seektab[slot] != nil) {
263 	    dispose(seektab[slot]);
264 	}
265     }
266 }
267 
268 /*
269  * Figure out current source position.
270  */
271 
272 public getsrcpos()
273 {
274     String filename;
275 
276     curline = srcline(pc);
277     filename = srcfilename(pc);
278     setsource(filename);
279     if (curline != 0) {
280 	cursrcline = curline;
281     }
282 }
283 
284 /*
285  * Print out the current source position.
286  */
287 
288 public printsrcpos()
289 {
290     printf("at line %d", curline);
291     if (nlhdr.nfiles > 1) {
292 	printf(" in file \"%s\"", cursource);
293     }
294 }
295 
296 #define DEF_EDITOR  "vi"
297 
298 /*
299  * Invoke an editor on the given file.  Which editor to use might change
300  * installation to installation.  For now, we use "vi".  In any event,
301  * the environment variable "EDITOR" overrides any default.
302  */
303 
304 public edit(filename)
305 String filename;
306 {
307     extern String getenv();
308     String ed, src, s;
309     Symbol f;
310     Address addr;
311     char lineno[10];
312 
313     ed = getenv("EDITOR");
314     if (ed == nil) {
315 	ed = DEF_EDITOR;
316     }
317     src = findsource((filename != nil) ? filename : cursource);
318     if (src == nil) {
319 	f = which(identname(filename, true));
320 	if (not isblock(f)) {
321 	    error("can't read \"%s\"", filename);
322 	}
323 	addr = firstline(f);
324 	if (addr == NOADDR) {
325 	    error("no source for \"%s\"", filename);
326 	}
327 	src = srcfilename(addr);
328 	s = findsource(src);
329 	if (s != nil) {
330 	    src = s;
331 	}
332 	sprintf(lineno, "+%d", srcline(addr));
333     } else {
334 	sprintf(lineno, "+1");
335     }
336     if (streq(ed, "vi") or streq(ed, "ex")) {
337 	call(ed, stdin, stdout, lineno, src, nil);
338     } else {
339 	call(ed, stdin, stdout, src, nil);
340     }
341 }
342 
343 /*
344  * Strip away portions of a given pattern not part of the regular expression.
345  */
346 
347 private String getpattern (pattern)
348 String pattern;
349 {
350     register char *p, *r;
351 
352     p = pattern;
353     while (*p == ' ' or *p == '\t') {
354 	++p;
355     }
356     r = p;
357     while (*p != '\0') {
358 	++p;
359     }
360     --p;
361     if (*p == '\n') {
362 	*p = '\0';
363 	--p;
364     }
365     if (*p == *r) {
366 	*p = '\0';
367 	--p;
368     }
369     return r + 1;
370 }
371 
372 /*
373  * Search the current file for a regular expression.
374  */
375 
376 public search (direction, pattern)
377 char direction;
378 String pattern;
379 {
380     register String p;
381     register File f;
382     String re, err;
383     Lineno line;
384     boolean matched;
385     char buf[512];
386 
387     if (cursource == nil) {
388 	beginerrmsg();
389 	fprintf(stderr, "no source file\n");
390     } else {
391 	if (cursource != prevsource) {
392 	    skimsource();
393 	}
394 	if (lastlinenum == 0) {
395 	    beginerrmsg();
396 	    fprintf(stderr, "couldn't read \"%s\"\n", cursource);
397 	} else {
398 	    re = getpattern(pattern);
399 	    /* circf = 0; */
400 	    if (re != nil and *re != '\0') {
401 		err = re_comp(re);
402 		if (err != nil) {
403 		    error(err);
404 		}
405 	    }
406 	    matched = false;
407 	    f = srcfp;
408 	    line = cursrcline;
409 	    do {
410 		if (direction == '/') {
411 		    ++line;
412 		    if (line > lastlinenum) {
413 			line = 1;
414 		    }
415 		} else {
416 		    --line;
417 		    if (line < 1) {
418 			line = lastlinenum;
419 		    }
420 		}
421 		fseek(f, srcaddr(line), L_SET);
422 		p = buf;
423 		*p = getc(f);
424 		while ((*p != '\n') and (*p != EOF)) {
425 		    ++p;
426 		    *p = getc(f);
427 		}
428 		*p = '\0';
429 		matched = (boolean) re_exec(buf);
430 	    } while (not matched and line != cursrcline);
431 	    if (not matched) {
432 		beginerrmsg();
433 		fprintf(stderr, "no match\n");
434 	    } else {
435 		printlines(line, line);
436 		cursrcline = line;
437 	    }
438 	}
439     }
440 }
441 
442 /*
443  * Compute a small window around the given line.
444  */
445 
446 public getsrcwindow (line, l1, l2)
447 Lineno line, *l1, *l2;
448 {
449     Node s;
450     integer size;
451 
452     s = findvar(identname("$listwindow", true));
453     if (s == nil) {
454 	size = 10;
455     } else {
456 	eval(s);
457 	size = pop(integer);
458     }
459     *l1 = line - (size div 2);
460     if (*l1 < 1) {
461 	*l1 = 1;
462     }
463     *l2 = *l1 + size;
464     if (lastlinenum != LASTLINE and *l2 > lastlinenum) {
465 	*l2 = lastlinenum;
466     }
467 }
468