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