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