121623Sdist /*
238105Sbostic * Copyright (c) 1983 The Regents of the University of California.
338105Sbostic * All rights reserved.
438105Sbostic *
5*42685Sbostic * %sccs.include.redist.c%
621623Sdist */
79680Slinton
821623Sdist #ifndef lint
9*42685Sbostic static char sccsid[] = "@(#)source.c 5.4 (Berkeley) 06/01/90";
1038105Sbostic #endif /* not lint */
119680Slinton
129680Slinton /*
139680Slinton * Source file management.
149680Slinton */
159680Slinton
169680Slinton #include "defs.h"
179680Slinton #include "source.h"
189680Slinton #include "object.h"
199680Slinton #include "mappings.h"
209680Slinton #include "machine.h"
2118233Slinton #include "keywords.h"
2218233Slinton #include "tree.h"
2318233Slinton #include "eval.h"
2433335Sdonn #ifdef IRIS
2533335Sdonn # define R_OK 04 /* read access */
2633335Sdonn # define L_SET 01 /* absolute offset for seek */
2733335Sdonn #else
2833335Sdonn # include <sys/file.h>
2933335Sdonn #endif
309680Slinton
319680Slinton #ifndef public
329680Slinton typedef int Lineno;
339680Slinton
349680Slinton String cursource;
359680Slinton Lineno curline;
369680Slinton Lineno cursrcline;
379680Slinton
389680Slinton #define LASTLINE 0 /* recognized by printlines */
399680Slinton
409680Slinton #include "lists.h"
419680Slinton
429680Slinton List sourcepath;
439680Slinton #endif
449680Slinton
4533335Sdonn #ifdef IRIS
4633335Sdonn # define re_comp regcmp
4733335Sdonn # define re_exec(buf) (regex(buf) != NULL)
4833335Sdonn #endif
4933335Sdonn
5018233Slinton extern char *re_comp();
5118233Slinton
529680Slinton private Lineno lastlinenum;
539680Slinton private String prevsource = nil;
549680Slinton
559680Slinton /*
569680Slinton * Data structure for indexing source seek addresses by line number.
579680Slinton *
589680Slinton * The constraints are:
599680Slinton *
609680Slinton * we want an array so indexing is fast and easy
619680Slinton * we don't want to waste space for small files
629680Slinton * we don't want an upper bound on # of lines in a file
639680Slinton * we don't know how many lines there are
649680Slinton *
659680Slinton * The solution is a "dirty" hash table. We have NSLOTS pointers to
669680Slinton * arrays of NLINESPERSLOT addresses. To find the source address of
679680Slinton * a particular line we find the slot, allocate space if necessary,
689680Slinton * and then find its location within the pointed to array.
699680Slinton */
709680Slinton
7110617Slinton typedef long Seekaddr;
729680Slinton
7318233Slinton #define NSLOTS 40
749680Slinton #define NLINESPERSLOT 500
759680Slinton
769680Slinton #define slotno(line) ((line) div NLINESPERSLOT)
779680Slinton #define index(line) ((line) mod NLINESPERSLOT)
789680Slinton #define slot_alloc() newarr(Seekaddr, NLINESPERSLOT)
799680Slinton #define srcaddr(line) seektab[slotno(line)][index(line)]
809680Slinton
819680Slinton private File srcfp;
829680Slinton private Seekaddr *seektab[NSLOTS];
839680Slinton
849680Slinton /*
8518233Slinton * Determine if the current source file is available.
8618233Slinton */
8718233Slinton
canReadSource()8818233Slinton public boolean canReadSource ()
8918233Slinton {
9018233Slinton boolean b;
9118233Slinton
9218233Slinton if (cursource == nil) {
9318233Slinton b = false;
9418233Slinton } else if (cursource != prevsource) {
9518233Slinton skimsource();
9618233Slinton b = (boolean) (lastlinenum != 0);
9718233Slinton } else {
9818233Slinton b = true;
9918233Slinton }
10018233Slinton return b;
10118233Slinton }
10218233Slinton
10318233Slinton /*
1049680Slinton * Print out the given lines from the source.
1059680Slinton */
1069680Slinton
printlines(l1,l2)1079680Slinton public printlines(l1, l2)
1089680Slinton Lineno l1, l2;
1099680Slinton {
1109680Slinton register int c;
1119680Slinton register Lineno i, lb, ub;
1129680Slinton register File f;
1139680Slinton
1149680Slinton if (cursource == nil) {
1159680Slinton beginerrmsg();
1169680Slinton fprintf(stderr, "no source file\n");
1179680Slinton } else {
1189680Slinton if (cursource != prevsource) {
1199680Slinton skimsource();
1209680Slinton }
1219680Slinton if (lastlinenum == 0) {
1229680Slinton beginerrmsg();
1239680Slinton fprintf(stderr, "couldn't read \"%s\"\n", cursource);
1249680Slinton } else {
12518233Slinton lb = (l1 == LASTLINE) ? lastlinenum : l1;
12618233Slinton ub = (l2 == LASTLINE) ? lastlinenum : l2;
1279680Slinton if (lb < 1) {
1289680Slinton beginerrmsg();
1299680Slinton fprintf(stderr, "line number must be positive\n");
1309680Slinton } else if (lb > lastlinenum) {
1319680Slinton beginerrmsg();
1329680Slinton if (lastlinenum == 1) {
1339680Slinton fprintf(stderr, "\"%s\" has only 1 line\n", cursource);
1349680Slinton } else {
1359680Slinton fprintf(stderr, "\"%s\" has only %d lines\n",
1369680Slinton cursource, lastlinenum);
1379680Slinton }
1389680Slinton } else if (ub < lb) {
1399680Slinton beginerrmsg();
1409680Slinton fprintf(stderr, "second number must be greater than first\n");
1419680Slinton } else {
1429680Slinton if (ub > lastlinenum) {
1439680Slinton ub = lastlinenum;
1449680Slinton }
1459680Slinton f = srcfp;
14610617Slinton fseek(f, srcaddr(lb), 0);
1479680Slinton for (i = lb; i <= ub; i++) {
1489680Slinton printf("%5d ", i);
1499680Slinton while ((c = getc(f)) != '\n') {
1509680Slinton putchar(c);
1519680Slinton }
1529680Slinton putchar('\n');
1539680Slinton }
1549680Slinton cursrcline = ub + 1;
1559680Slinton }
1569680Slinton }
1579680Slinton }
1589680Slinton }
1599680Slinton
1609680Slinton /*
16114337Slinton * Search the sourcepath for a file.
1629680Slinton */
1639680Slinton
16414337Slinton static char fileNameBuf[1024];
16514337Slinton
findsource(filename)16614337Slinton public String findsource(filename)
1679680Slinton String filename;
1689680Slinton {
16914337Slinton register String src, dir;
1709680Slinton
17112608Slinton if (filename[0] == '/') {
17214337Slinton src = filename;
17312608Slinton } else {
17414337Slinton src = nil;
17512608Slinton foreach (String, dir, sourcepath)
17614337Slinton sprintf(fileNameBuf, "%s/%s", dir, filename);
17716637Ssam if (access(fileNameBuf, R_OK) == 0) {
17814337Slinton src = fileNameBuf;
17912608Slinton break;
18014337Slinton }
18112608Slinton endfor
18212608Slinton }
18314337Slinton return src;
18414337Slinton }
18514337Slinton
18614337Slinton /*
18714337Slinton * Open a source file looking in the appropriate places.
18814337Slinton */
18914337Slinton
opensource(filename)19014337Slinton public File opensource(filename)
19114337Slinton String filename;
19214337Slinton {
19314337Slinton String s;
19414337Slinton File f;
19514337Slinton
19614337Slinton s = findsource(filename);
19714337Slinton if (s == nil) {
19814337Slinton f = nil;
19914337Slinton } else {
20014337Slinton f = fopen(s, "r");
20114337Slinton }
2029680Slinton return f;
2039680Slinton }
2049680Slinton
2059680Slinton /*
2069680Slinton * Set the current source file.
2079680Slinton */
2089680Slinton
setsource(filename)2099680Slinton public setsource(filename)
2109680Slinton String filename;
2119680Slinton {
2129680Slinton if (filename != nil and filename != cursource) {
2139680Slinton prevsource = cursource;
2149680Slinton cursource = filename;
2159680Slinton cursrcline = 1;
2169680Slinton }
2179680Slinton }
2189680Slinton
2199680Slinton /*
2209680Slinton * Read the source file getting seek pointers for each line.
2219680Slinton */
2229680Slinton
skimsource()2239680Slinton private skimsource()
2249680Slinton {
2259680Slinton register int c;
22610617Slinton register Seekaddr count;
2279680Slinton register File f;
2289680Slinton register Lineno linenum;
2299680Slinton register Seekaddr lastaddr;
2309680Slinton register int slot;
2319680Slinton
2329680Slinton f = opensource(cursource);
2339680Slinton if (f == nil) {
2349680Slinton lastlinenum = 0;
2359680Slinton } else {
2369680Slinton if (prevsource != nil) {
2379680Slinton free_seektab();
2389680Slinton if (srcfp != nil) {
2399680Slinton fclose(srcfp);
2409680Slinton }
2419680Slinton }
2429680Slinton prevsource = cursource;
2439680Slinton linenum = 0;
2449680Slinton count = 0;
2459680Slinton lastaddr = 0;
2469680Slinton while ((c = getc(f)) != EOF) {
2479680Slinton ++count;
2489680Slinton if (c == '\n') {
2499680Slinton slot = slotno(++linenum);
2509680Slinton if (slot >= NSLOTS) {
2519680Slinton panic("skimsource: too many lines");
2529680Slinton }
2539680Slinton if (seektab[slot] == nil) {
2549680Slinton seektab[slot] = slot_alloc();
2559680Slinton }
2569680Slinton seektab[slot][index(linenum)] = lastaddr;
2579680Slinton lastaddr = count;
2589680Slinton }
2599680Slinton }
2609680Slinton lastlinenum = linenum;
2619680Slinton srcfp = f;
2629680Slinton }
2639680Slinton }
2649680Slinton
2659680Slinton /*
2669680Slinton * Erase information and release space in the current seektab.
2679680Slinton * This is in preparation for reading in seek pointers for a
2689680Slinton * new file. It is possible that seek pointers for all files
2699680Slinton * should be kept around, but the current concern is space.
2709680Slinton */
2719680Slinton
free_seektab()2729680Slinton private free_seektab()
2739680Slinton {
2749680Slinton register int slot;
2759680Slinton
2769680Slinton for (slot = 0; slot < NSLOTS; slot++) {
2779680Slinton if (seektab[slot] != nil) {
2789680Slinton dispose(seektab[slot]);
2799680Slinton }
2809680Slinton }
2819680Slinton }
2829680Slinton
2839680Slinton /*
2849680Slinton * Figure out current source position.
2859680Slinton */
2869680Slinton
getsrcpos()2879680Slinton public getsrcpos()
2889680Slinton {
2899680Slinton String filename;
2909680Slinton
29111105Slinton curline = srcline(pc);
29211105Slinton filename = srcfilename(pc);
2939680Slinton setsource(filename);
29411837Slinton if (curline != 0) {
29511837Slinton cursrcline = curline;
29611837Slinton }
2979680Slinton }
2989680Slinton
2999680Slinton /*
3009680Slinton * Print out the current source position.
3019680Slinton */
3029680Slinton
printsrcpos()3039680Slinton public printsrcpos()
3049680Slinton {
3059680Slinton printf("at line %d", curline);
3069680Slinton if (nlhdr.nfiles > 1) {
3079680Slinton printf(" in file \"%s\"", cursource);
3089680Slinton }
3099680Slinton }
31014337Slinton
31114337Slinton #define DEF_EDITOR "vi"
31214337Slinton
31314337Slinton /*
31414337Slinton * Invoke an editor on the given file. Which editor to use might change
31514337Slinton * installation to installation. For now, we use "vi". In any event,
31614337Slinton * the environment variable "EDITOR" overrides any default.
31714337Slinton */
31814337Slinton
edit(filename)31914337Slinton public edit(filename)
32014337Slinton String filename;
32114337Slinton {
32214337Slinton extern String getenv();
32314337Slinton String ed, src, s;
32414337Slinton Symbol f;
32514337Slinton Address addr;
32614337Slinton char lineno[10];
32714337Slinton
32814337Slinton ed = getenv("EDITOR");
32914337Slinton if (ed == nil) {
33014337Slinton ed = DEF_EDITOR;
33114337Slinton }
33214337Slinton src = findsource((filename != nil) ? filename : cursource);
33314337Slinton if (src == nil) {
33414337Slinton f = which(identname(filename, true));
33514337Slinton if (not isblock(f)) {
33614337Slinton error("can't read \"%s\"", filename);
33714337Slinton }
33814337Slinton addr = firstline(f);
33914337Slinton if (addr == NOADDR) {
34014337Slinton error("no source for \"%s\"", filename);
34114337Slinton }
34214337Slinton src = srcfilename(addr);
34314337Slinton s = findsource(src);
34414337Slinton if (s != nil) {
34514337Slinton src = s;
34614337Slinton }
34714337Slinton sprintf(lineno, "+%d", srcline(addr));
34814337Slinton } else {
34914337Slinton sprintf(lineno, "+1");
35014337Slinton }
35118233Slinton if (streq(ed, "vi") or streq(ed, "ex")) {
35218233Slinton call(ed, stdin, stdout, lineno, src, nil);
35318233Slinton } else {
35418233Slinton call(ed, stdin, stdout, src, nil);
35518233Slinton }
35614337Slinton }
35716637Ssam
35816637Ssam /*
35918233Slinton * Strip away portions of a given pattern not part of the regular expression.
36016637Ssam */
36118233Slinton
getpattern(pattern)36218233Slinton private String getpattern (pattern)
36318233Slinton String pattern;
36416637Ssam {
36518233Slinton register char *p, *r;
36618233Slinton
36718233Slinton p = pattern;
36818233Slinton while (*p == ' ' or *p == '\t') {
36918233Slinton ++p;
37018233Slinton }
37118233Slinton r = p;
37218233Slinton while (*p != '\0') {
37318233Slinton ++p;
37418233Slinton }
37518233Slinton --p;
37618233Slinton if (*p == '\n') {
37718233Slinton *p = '\0';
37818233Slinton --p;
37918233Slinton }
38018233Slinton if (*p == *r) {
38118233Slinton *p = '\0';
38218233Slinton --p;
38318233Slinton }
38418233Slinton return r + 1;
38518233Slinton }
38618233Slinton
38718233Slinton /*
38818233Slinton * Search the current file for a regular expression.
38918233Slinton */
39018233Slinton
search(direction,pattern)39118233Slinton public search (direction, pattern)
39218233Slinton char direction;
39318233Slinton String pattern;
39418233Slinton {
39518233Slinton register String p;
39618233Slinton register File f;
39718233Slinton String re, err;
39818233Slinton Lineno line;
39918233Slinton boolean matched;
40018233Slinton char buf[512];
40118233Slinton
40218233Slinton if (cursource == nil) {
40318233Slinton beginerrmsg();
40418233Slinton fprintf(stderr, "no source file\n");
40518233Slinton } else {
40618233Slinton if (cursource != prevsource) {
40718233Slinton skimsource();
40816637Ssam }
40916637Ssam if (lastlinenum == 0) {
41018233Slinton beginerrmsg();
41118233Slinton fprintf(stderr, "couldn't read \"%s\"\n", cursource);
41218233Slinton } else {
41318233Slinton re = getpattern(pattern);
41418233Slinton /* circf = 0; */
41518233Slinton if (re != nil and *re != '\0') {
41618233Slinton err = re_comp(re);
41718233Slinton if (err != nil) {
41818233Slinton error(err);
41918233Slinton }
42018233Slinton }
42118233Slinton matched = false;
42218233Slinton f = srcfp;
42318233Slinton line = cursrcline;
42418233Slinton do {
42518233Slinton if (direction == '/') {
42618233Slinton ++line;
42718233Slinton if (line > lastlinenum) {
42818233Slinton line = 1;
42918233Slinton }
43016637Ssam } else {
43118233Slinton --line;
43218233Slinton if (line < 1) {
43318233Slinton line = lastlinenum;
43418233Slinton }
43516637Ssam }
43616637Ssam fseek(f, srcaddr(line), L_SET);
43718233Slinton p = buf;
43818233Slinton *p = getc(f);
43918233Slinton while ((*p != '\n') and (*p != EOF)) {
44018233Slinton ++p;
44118233Slinton *p = getc(f);
44218233Slinton }
44316637Ssam *p = '\0';
44418233Slinton matched = (boolean) re_exec(buf);
44518233Slinton } while (not matched and line != cursrcline);
44618233Slinton if (not matched) {
44718233Slinton beginerrmsg();
44818233Slinton fprintf(stderr, "no match\n");
44918233Slinton } else {
45018233Slinton printlines(line, line);
45118233Slinton cursrcline = line;
45218233Slinton }
45318233Slinton }
45418233Slinton }
45516637Ssam }
45618233Slinton
srcwindowlen()45733335Sdonn public integer srcwindowlen ()
45833335Sdonn {
45933335Sdonn Node s;
46033335Sdonn
46133335Sdonn s = findvar(identname("$listwindow", true));
46233335Sdonn if (s == nil)
46333335Sdonn return 10;
46433335Sdonn eval(s);
46533335Sdonn return pop(integer);
46633335Sdonn }
46733335Sdonn
46818233Slinton /*
46918233Slinton * Compute a small window around the given line.
47018233Slinton */
47118233Slinton
getsrcwindow(line,l1,l2)47218233Slinton public getsrcwindow (line, l1, l2)
47318233Slinton Lineno line, *l1, *l2;
47418233Slinton {
47518233Slinton integer size;
47618233Slinton
47733335Sdonn size = srcwindowlen();
47818233Slinton *l1 = line - (size div 2);
47918233Slinton if (*l1 < 1) {
48018233Slinton *l1 = 1;
48118233Slinton }
48218233Slinton *l2 = *l1 + size;
48318233Slinton if (lastlinenum != LASTLINE and *l2 > lastlinenum) {
48418233Slinton *l2 = lastlinenum;
48518233Slinton }
48618233Slinton }
487