xref: /csrg-svn/old/dbx/source.c (revision 38105)
121623Sdist /*
2*38105Sbostic  * Copyright (c) 1983 The Regents of the University of California.
3*38105Sbostic  * All rights reserved.
4*38105Sbostic  *
5*38105Sbostic  * Redistribution and use in source and binary forms are permitted
6*38105Sbostic  * provided that the above copyright notice and this paragraph are
7*38105Sbostic  * duplicated in all such forms and that any documentation,
8*38105Sbostic  * advertising materials, and other materials related to such
9*38105Sbostic  * distribution and use acknowledge that the software was developed
10*38105Sbostic  * by the University of California, Berkeley.  The name of the
11*38105Sbostic  * University may not be used to endorse or promote products derived
12*38105Sbostic  * from this software without specific prior written permission.
13*38105Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*38105Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*38105Sbostic  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621623Sdist  */
179680Slinton 
1821623Sdist #ifndef lint
19*38105Sbostic static char sccsid[] = "@(#)source.c	5.3 (Berkeley) 05/23/89";
20*38105Sbostic #endif /* not lint */
219680Slinton 
229680Slinton /*
239680Slinton  * Source file management.
249680Slinton  */
259680Slinton 
269680Slinton #include "defs.h"
279680Slinton #include "source.h"
289680Slinton #include "object.h"
299680Slinton #include "mappings.h"
309680Slinton #include "machine.h"
3118233Slinton #include "keywords.h"
3218233Slinton #include "tree.h"
3318233Slinton #include "eval.h"
3433335Sdonn #ifdef IRIS
3533335Sdonn #   define R_OK 04	/* read access */
3633335Sdonn #   define L_SET 01	/* absolute offset for seek */
3733335Sdonn #else
3833335Sdonn #   include <sys/file.h>
3933335Sdonn #endif
409680Slinton 
419680Slinton #ifndef public
429680Slinton typedef int Lineno;
439680Slinton 
449680Slinton String cursource;
459680Slinton Lineno curline;
469680Slinton Lineno cursrcline;
479680Slinton 
489680Slinton #define LASTLINE 0		/* recognized by printlines */
499680Slinton 
509680Slinton #include "lists.h"
519680Slinton 
529680Slinton List sourcepath;
539680Slinton #endif
549680Slinton 
5533335Sdonn #ifdef IRIS
5633335Sdonn #   define re_comp regcmp
5733335Sdonn #   define re_exec(buf) (regex(buf) != NULL)
5833335Sdonn #endif
5933335Sdonn 
6018233Slinton extern char *re_comp();
6118233Slinton 
629680Slinton private Lineno lastlinenum;
639680Slinton private String prevsource = nil;
649680Slinton 
659680Slinton /*
669680Slinton  * Data structure for indexing source seek addresses by line number.
679680Slinton  *
689680Slinton  * The constraints are:
699680Slinton  *
709680Slinton  *  we want an array so indexing is fast and easy
719680Slinton  *  we don't want to waste space for small files
729680Slinton  *  we don't want an upper bound on # of lines in a file
739680Slinton  *  we don't know how many lines there are
749680Slinton  *
759680Slinton  * The solution is a "dirty" hash table.  We have NSLOTS pointers to
769680Slinton  * arrays of NLINESPERSLOT addresses.  To find the source address of
779680Slinton  * a particular line we find the slot, allocate space if necessary,
789680Slinton  * and then find its location within the pointed to array.
799680Slinton  */
809680Slinton 
8110617Slinton typedef long Seekaddr;
829680Slinton 
8318233Slinton #define NSLOTS 40
849680Slinton #define NLINESPERSLOT 500
859680Slinton 
869680Slinton #define slotno(line)    ((line) div NLINESPERSLOT)
879680Slinton #define index(line)	((line) mod NLINESPERSLOT)
889680Slinton #define slot_alloc()    newarr(Seekaddr, NLINESPERSLOT)
899680Slinton #define srcaddr(line)	seektab[slotno(line)][index(line)]
909680Slinton 
919680Slinton private File srcfp;
929680Slinton private Seekaddr *seektab[NSLOTS];
939680Slinton 
949680Slinton /*
9518233Slinton  * Determine if the current source file is available.
9618233Slinton  */
9718233Slinton 
9818233Slinton public boolean canReadSource ()
9918233Slinton {
10018233Slinton     boolean b;
10118233Slinton 
10218233Slinton     if (cursource == nil) {
10318233Slinton 	b = false;
10418233Slinton     } else if (cursource != prevsource) {
10518233Slinton 	skimsource();
10618233Slinton 	b = (boolean) (lastlinenum != 0);
10718233Slinton     } else {
10818233Slinton 	b = true;
10918233Slinton     }
11018233Slinton     return b;
11118233Slinton }
11218233Slinton 
11318233Slinton /*
1149680Slinton  * Print out the given lines from the source.
1159680Slinton  */
1169680Slinton 
1179680Slinton public printlines(l1, l2)
1189680Slinton Lineno l1, l2;
1199680Slinton {
1209680Slinton     register int c;
1219680Slinton     register Lineno i, lb, ub;
1229680Slinton     register File f;
1239680Slinton 
1249680Slinton     if (cursource == nil) {
1259680Slinton 	beginerrmsg();
1269680Slinton 	fprintf(stderr, "no source file\n");
1279680Slinton     } else {
1289680Slinton 	if (cursource != prevsource) {
1299680Slinton 	    skimsource();
1309680Slinton 	}
1319680Slinton 	if (lastlinenum == 0) {
1329680Slinton 	    beginerrmsg();
1339680Slinton 	    fprintf(stderr, "couldn't read \"%s\"\n", cursource);
1349680Slinton 	} else {
13518233Slinton 	    lb = (l1 == LASTLINE) ? lastlinenum : l1;
13618233Slinton 	    ub = (l2 == LASTLINE) ? lastlinenum : l2;
1379680Slinton 	    if (lb < 1) {
1389680Slinton 		beginerrmsg();
1399680Slinton 		fprintf(stderr, "line number must be positive\n");
1409680Slinton 	    } else if (lb > lastlinenum) {
1419680Slinton 		beginerrmsg();
1429680Slinton 		if (lastlinenum == 1) {
1439680Slinton 		    fprintf(stderr, "\"%s\" has only 1 line\n", cursource);
1449680Slinton 		} else {
1459680Slinton 		    fprintf(stderr, "\"%s\" has only %d lines\n",
1469680Slinton 			cursource, lastlinenum);
1479680Slinton 		}
1489680Slinton 	    } else if (ub < lb) {
1499680Slinton 		beginerrmsg();
1509680Slinton 		fprintf(stderr, "second number must be greater than first\n");
1519680Slinton 	    } else {
1529680Slinton 		if (ub > lastlinenum) {
1539680Slinton 		    ub = lastlinenum;
1549680Slinton 		}
1559680Slinton 		f = srcfp;
15610617Slinton 		fseek(f, srcaddr(lb), 0);
1579680Slinton 		for (i = lb; i <= ub; i++) {
1589680Slinton 		    printf("%5d   ", i);
1599680Slinton 		    while ((c = getc(f)) != '\n') {
1609680Slinton 			putchar(c);
1619680Slinton 		    }
1629680Slinton 		    putchar('\n');
1639680Slinton 		}
1649680Slinton 		cursrcline = ub + 1;
1659680Slinton 	    }
1669680Slinton 	}
1679680Slinton     }
1689680Slinton }
1699680Slinton 
1709680Slinton /*
17114337Slinton  * Search the sourcepath for a file.
1729680Slinton  */
1739680Slinton 
17414337Slinton static char fileNameBuf[1024];
17514337Slinton 
17614337Slinton public String findsource(filename)
1779680Slinton String filename;
1789680Slinton {
17914337Slinton     register String src, dir;
1809680Slinton 
18112608Slinton     if (filename[0] == '/') {
18214337Slinton 	src = filename;
18312608Slinton     } else {
18414337Slinton 	src = nil;
18512608Slinton 	foreach (String, dir, sourcepath)
18614337Slinton 	    sprintf(fileNameBuf, "%s/%s", dir, filename);
18716637Ssam 	    if (access(fileNameBuf, R_OK) == 0) {
18814337Slinton 		src = fileNameBuf;
18912608Slinton 		break;
19014337Slinton 	    }
19112608Slinton 	endfor
19212608Slinton     }
19314337Slinton     return src;
19414337Slinton }
19514337Slinton 
19614337Slinton /*
19714337Slinton  * Open a source file looking in the appropriate places.
19814337Slinton  */
19914337Slinton 
20014337Slinton public File opensource(filename)
20114337Slinton String filename;
20214337Slinton {
20314337Slinton     String s;
20414337Slinton     File f;
20514337Slinton 
20614337Slinton     s = findsource(filename);
20714337Slinton     if (s == nil) {
20814337Slinton 	f = nil;
20914337Slinton     } else {
21014337Slinton 	f = fopen(s, "r");
21114337Slinton     }
2129680Slinton     return f;
2139680Slinton }
2149680Slinton 
2159680Slinton /*
2169680Slinton  * Set the current source file.
2179680Slinton  */
2189680Slinton 
2199680Slinton public setsource(filename)
2209680Slinton String filename;
2219680Slinton {
2229680Slinton     if (filename != nil and filename != cursource) {
2239680Slinton 	prevsource = cursource;
2249680Slinton 	cursource = filename;
2259680Slinton 	cursrcline = 1;
2269680Slinton     }
2279680Slinton }
2289680Slinton 
2299680Slinton /*
2309680Slinton  * Read the source file getting seek pointers for each line.
2319680Slinton  */
2329680Slinton 
2339680Slinton private skimsource()
2349680Slinton {
2359680Slinton     register int c;
23610617Slinton     register Seekaddr count;
2379680Slinton     register File f;
2389680Slinton     register Lineno linenum;
2399680Slinton     register Seekaddr lastaddr;
2409680Slinton     register int slot;
2419680Slinton 
2429680Slinton     f = opensource(cursource);
2439680Slinton     if (f == nil) {
2449680Slinton 	lastlinenum = 0;
2459680Slinton     } else {
2469680Slinton 	if (prevsource != nil) {
2479680Slinton 	    free_seektab();
2489680Slinton 	    if (srcfp != nil) {
2499680Slinton 		fclose(srcfp);
2509680Slinton 	    }
2519680Slinton 	}
2529680Slinton 	prevsource = cursource;
2539680Slinton 	linenum = 0;
2549680Slinton 	count = 0;
2559680Slinton 	lastaddr = 0;
2569680Slinton 	while ((c = getc(f)) != EOF) {
2579680Slinton 	    ++count;
2589680Slinton 	    if (c == '\n') {
2599680Slinton 		slot = slotno(++linenum);
2609680Slinton 		if (slot >= NSLOTS) {
2619680Slinton 		    panic("skimsource: too many lines");
2629680Slinton 		}
2639680Slinton 		if (seektab[slot] == nil) {
2649680Slinton 		    seektab[slot] = slot_alloc();
2659680Slinton 		}
2669680Slinton 		seektab[slot][index(linenum)] = lastaddr;
2679680Slinton 		lastaddr = count;
2689680Slinton 	    }
2699680Slinton 	}
2709680Slinton 	lastlinenum = linenum;
2719680Slinton 	srcfp = f;
2729680Slinton     }
2739680Slinton }
2749680Slinton 
2759680Slinton /*
2769680Slinton  * Erase information and release space in the current seektab.
2779680Slinton  * This is in preparation for reading in seek pointers for a
2789680Slinton  * new file.  It is possible that seek pointers for all files
2799680Slinton  * should be kept around, but the current concern is space.
2809680Slinton  */
2819680Slinton 
2829680Slinton private free_seektab()
2839680Slinton {
2849680Slinton     register int slot;
2859680Slinton 
2869680Slinton     for (slot = 0; slot < NSLOTS; slot++) {
2879680Slinton 	if (seektab[slot] != nil) {
2889680Slinton 	    dispose(seektab[slot]);
2899680Slinton 	}
2909680Slinton     }
2919680Slinton }
2929680Slinton 
2939680Slinton /*
2949680Slinton  * Figure out current source position.
2959680Slinton  */
2969680Slinton 
2979680Slinton public getsrcpos()
2989680Slinton {
2999680Slinton     String filename;
3009680Slinton 
30111105Slinton     curline = srcline(pc);
30211105Slinton     filename = srcfilename(pc);
3039680Slinton     setsource(filename);
30411837Slinton     if (curline != 0) {
30511837Slinton 	cursrcline = curline;
30611837Slinton     }
3079680Slinton }
3089680Slinton 
3099680Slinton /*
3109680Slinton  * Print out the current source position.
3119680Slinton  */
3129680Slinton 
3139680Slinton public printsrcpos()
3149680Slinton {
3159680Slinton     printf("at line %d", curline);
3169680Slinton     if (nlhdr.nfiles > 1) {
3179680Slinton 	printf(" in file \"%s\"", cursource);
3189680Slinton     }
3199680Slinton }
32014337Slinton 
32114337Slinton #define DEF_EDITOR  "vi"
32214337Slinton 
32314337Slinton /*
32414337Slinton  * Invoke an editor on the given file.  Which editor to use might change
32514337Slinton  * installation to installation.  For now, we use "vi".  In any event,
32614337Slinton  * the environment variable "EDITOR" overrides any default.
32714337Slinton  */
32814337Slinton 
32914337Slinton public edit(filename)
33014337Slinton String filename;
33114337Slinton {
33214337Slinton     extern String getenv();
33314337Slinton     String ed, src, s;
33414337Slinton     Symbol f;
33514337Slinton     Address addr;
33614337Slinton     char lineno[10];
33714337Slinton 
33814337Slinton     ed = getenv("EDITOR");
33914337Slinton     if (ed == nil) {
34014337Slinton 	ed = DEF_EDITOR;
34114337Slinton     }
34214337Slinton     src = findsource((filename != nil) ? filename : cursource);
34314337Slinton     if (src == nil) {
34414337Slinton 	f = which(identname(filename, true));
34514337Slinton 	if (not isblock(f)) {
34614337Slinton 	    error("can't read \"%s\"", filename);
34714337Slinton 	}
34814337Slinton 	addr = firstline(f);
34914337Slinton 	if (addr == NOADDR) {
35014337Slinton 	    error("no source for \"%s\"", filename);
35114337Slinton 	}
35214337Slinton 	src = srcfilename(addr);
35314337Slinton 	s = findsource(src);
35414337Slinton 	if (s != nil) {
35514337Slinton 	    src = s;
35614337Slinton 	}
35714337Slinton 	sprintf(lineno, "+%d", srcline(addr));
35814337Slinton     } else {
35914337Slinton 	sprintf(lineno, "+1");
36014337Slinton     }
36118233Slinton     if (streq(ed, "vi") or streq(ed, "ex")) {
36218233Slinton 	call(ed, stdin, stdout, lineno, src, nil);
36318233Slinton     } else {
36418233Slinton 	call(ed, stdin, stdout, src, nil);
36518233Slinton     }
36614337Slinton }
36716637Ssam 
36816637Ssam /*
36918233Slinton  * Strip away portions of a given pattern not part of the regular expression.
37016637Ssam  */
37118233Slinton 
37218233Slinton private String getpattern (pattern)
37318233Slinton String pattern;
37416637Ssam {
37518233Slinton     register char *p, *r;
37618233Slinton 
37718233Slinton     p = pattern;
37818233Slinton     while (*p == ' ' or *p == '\t') {
37918233Slinton 	++p;
38018233Slinton     }
38118233Slinton     r = p;
38218233Slinton     while (*p != '\0') {
38318233Slinton 	++p;
38418233Slinton     }
38518233Slinton     --p;
38618233Slinton     if (*p == '\n') {
38718233Slinton 	*p = '\0';
38818233Slinton 	--p;
38918233Slinton     }
39018233Slinton     if (*p == *r) {
39118233Slinton 	*p = '\0';
39218233Slinton 	--p;
39318233Slinton     }
39418233Slinton     return r + 1;
39518233Slinton }
39618233Slinton 
39718233Slinton /*
39818233Slinton  * Search the current file for a regular expression.
39918233Slinton  */
40018233Slinton 
40118233Slinton public search (direction, pattern)
40218233Slinton char direction;
40318233Slinton String pattern;
40418233Slinton {
40518233Slinton     register String p;
40618233Slinton     register File f;
40718233Slinton     String re, err;
40818233Slinton     Lineno line;
40918233Slinton     boolean matched;
41018233Slinton     char buf[512];
41118233Slinton 
41218233Slinton     if (cursource == nil) {
41318233Slinton 	beginerrmsg();
41418233Slinton 	fprintf(stderr, "no source file\n");
41518233Slinton     } else {
41618233Slinton 	if (cursource != prevsource) {
41718233Slinton 	    skimsource();
41816637Ssam 	}
41916637Ssam 	if (lastlinenum == 0) {
42018233Slinton 	    beginerrmsg();
42118233Slinton 	    fprintf(stderr, "couldn't read \"%s\"\n", cursource);
42218233Slinton 	} else {
42318233Slinton 	    re = getpattern(pattern);
42418233Slinton 	    /* circf = 0; */
42518233Slinton 	    if (re != nil and *re != '\0') {
42618233Slinton 		err = re_comp(re);
42718233Slinton 		if (err != nil) {
42818233Slinton 		    error(err);
42918233Slinton 		}
43018233Slinton 	    }
43118233Slinton 	    matched = false;
43218233Slinton 	    f = srcfp;
43318233Slinton 	    line = cursrcline;
43418233Slinton 	    do {
43518233Slinton 		if (direction == '/') {
43618233Slinton 		    ++line;
43718233Slinton 		    if (line > lastlinenum) {
43818233Slinton 			line = 1;
43918233Slinton 		    }
44016637Ssam 		} else {
44118233Slinton 		    --line;
44218233Slinton 		    if (line < 1) {
44318233Slinton 			line = lastlinenum;
44418233Slinton 		    }
44516637Ssam 		}
44616637Ssam 		fseek(f, srcaddr(line), L_SET);
44718233Slinton 		p = buf;
44818233Slinton 		*p = getc(f);
44918233Slinton 		while ((*p != '\n') and (*p != EOF)) {
45018233Slinton 		    ++p;
45118233Slinton 		    *p = getc(f);
45218233Slinton 		}
45316637Ssam 		*p = '\0';
45418233Slinton 		matched = (boolean) re_exec(buf);
45518233Slinton 	    } while (not matched and line != cursrcline);
45618233Slinton 	    if (not matched) {
45718233Slinton 		beginerrmsg();
45818233Slinton 		fprintf(stderr, "no match\n");
45918233Slinton 	    } else {
46018233Slinton 		printlines(line, line);
46118233Slinton 		cursrcline = line;
46218233Slinton 	    }
46318233Slinton 	}
46418233Slinton     }
46516637Ssam }
46618233Slinton 
46733335Sdonn public integer srcwindowlen ()
46833335Sdonn {
46933335Sdonn     Node s;
47033335Sdonn 
47133335Sdonn     s = findvar(identname("$listwindow", true));
47233335Sdonn     if (s == nil)
47333335Sdonn 	return 10;
47433335Sdonn     eval(s);
47533335Sdonn     return pop(integer);
47633335Sdonn }
47733335Sdonn 
47818233Slinton /*
47918233Slinton  * Compute a small window around the given line.
48018233Slinton  */
48118233Slinton 
48218233Slinton public getsrcwindow (line, l1, l2)
48318233Slinton Lineno line, *l1, *l2;
48418233Slinton {
48518233Slinton     integer size;
48618233Slinton 
48733335Sdonn     size = srcwindowlen();
48818233Slinton     *l1 = line - (size div 2);
48918233Slinton     if (*l1 < 1) {
49018233Slinton 	*l1 = 1;
49118233Slinton     }
49218233Slinton     *l2 = *l1 + size;
49318233Slinton     if (lastlinenum != LASTLINE and *l2 > lastlinenum) {
49418233Slinton 	*l2 = lastlinenum;
49518233Slinton     }
49618233Slinton }
497