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