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