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
canReadSource()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
printlines(l1,l2)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
findsource(filename)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
opensource(filename)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
setsource(filename)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
skimsource()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
free_seektab()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
getsrcpos()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
printsrcpos()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
edit(filename)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
getpattern(pattern)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
search(direction,pattern)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
srcwindowlen()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
getsrcwindow(line,l1,l2)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