121623Sdist /* 221623Sdist * Copyright (c) 1983 Regents of the University of California. 321623Sdist * All rights reserved. The Berkeley software License Agreement 421623Sdist * specifies the terms and conditions for redistribution. 521623Sdist */ 69680Slinton 721623Sdist #ifndef lint 8*33335Sdonn static char sccsid[] = "@(#)source.c 5.2 (Berkeley) 01/12/88"; 921623Sdist #endif not lint 109680Slinton 11*33335Sdonn static char rcsid[] = "$Header: source.c,v 1.3 87/08/19 15:19:40 mike Exp $"; 1218233Slinton 139680Slinton /* 149680Slinton * Source file management. 159680Slinton */ 169680Slinton 179680Slinton #include "defs.h" 189680Slinton #include "source.h" 199680Slinton #include "object.h" 209680Slinton #include "mappings.h" 219680Slinton #include "machine.h" 2218233Slinton #include "keywords.h" 2318233Slinton #include "tree.h" 2418233Slinton #include "eval.h" 25*33335Sdonn #ifdef IRIS 26*33335Sdonn # define R_OK 04 /* read access */ 27*33335Sdonn # define L_SET 01 /* absolute offset for seek */ 28*33335Sdonn #else 29*33335Sdonn # include <sys/file.h> 30*33335Sdonn #endif 319680Slinton 329680Slinton #ifndef public 339680Slinton typedef int Lineno; 349680Slinton 359680Slinton String cursource; 369680Slinton Lineno curline; 379680Slinton Lineno cursrcline; 389680Slinton 399680Slinton #define LASTLINE 0 /* recognized by printlines */ 409680Slinton 419680Slinton #include "lists.h" 429680Slinton 439680Slinton List sourcepath; 449680Slinton #endif 459680Slinton 46*33335Sdonn #ifdef IRIS 47*33335Sdonn # define re_comp regcmp 48*33335Sdonn # define re_exec(buf) (regex(buf) != NULL) 49*33335Sdonn #endif 50*33335Sdonn 5118233Slinton extern char *re_comp(); 5218233Slinton 539680Slinton private Lineno lastlinenum; 549680Slinton private String prevsource = nil; 559680Slinton 569680Slinton /* 579680Slinton * Data structure for indexing source seek addresses by line number. 589680Slinton * 599680Slinton * The constraints are: 609680Slinton * 619680Slinton * we want an array so indexing is fast and easy 629680Slinton * we don't want to waste space for small files 639680Slinton * we don't want an upper bound on # of lines in a file 649680Slinton * we don't know how many lines there are 659680Slinton * 669680Slinton * The solution is a "dirty" hash table. We have NSLOTS pointers to 679680Slinton * arrays of NLINESPERSLOT addresses. To find the source address of 689680Slinton * a particular line we find the slot, allocate space if necessary, 699680Slinton * and then find its location within the pointed to array. 709680Slinton */ 719680Slinton 7210617Slinton typedef long Seekaddr; 739680Slinton 7418233Slinton #define NSLOTS 40 759680Slinton #define NLINESPERSLOT 500 769680Slinton 779680Slinton #define slotno(line) ((line) div NLINESPERSLOT) 789680Slinton #define index(line) ((line) mod NLINESPERSLOT) 799680Slinton #define slot_alloc() newarr(Seekaddr, NLINESPERSLOT) 809680Slinton #define srcaddr(line) seektab[slotno(line)][index(line)] 819680Slinton 829680Slinton private File srcfp; 839680Slinton private Seekaddr *seektab[NSLOTS]; 849680Slinton 859680Slinton /* 8618233Slinton * Determine if the current source file is available. 8718233Slinton */ 8818233Slinton 8918233Slinton public boolean canReadSource () 9018233Slinton { 9118233Slinton boolean b; 9218233Slinton 9318233Slinton if (cursource == nil) { 9418233Slinton b = false; 9518233Slinton } else if (cursource != prevsource) { 9618233Slinton skimsource(); 9718233Slinton b = (boolean) (lastlinenum != 0); 9818233Slinton } else { 9918233Slinton b = true; 10018233Slinton } 10118233Slinton return b; 10218233Slinton } 10318233Slinton 10418233Slinton /* 1059680Slinton * Print out the given lines from the source. 1069680Slinton */ 1079680Slinton 1089680Slinton public printlines(l1, l2) 1099680Slinton Lineno l1, l2; 1109680Slinton { 1119680Slinton register int c; 1129680Slinton register Lineno i, lb, ub; 1139680Slinton register File f; 1149680Slinton 1159680Slinton if (cursource == nil) { 1169680Slinton beginerrmsg(); 1179680Slinton fprintf(stderr, "no source file\n"); 1189680Slinton } else { 1199680Slinton if (cursource != prevsource) { 1209680Slinton skimsource(); 1219680Slinton } 1229680Slinton if (lastlinenum == 0) { 1239680Slinton beginerrmsg(); 1249680Slinton fprintf(stderr, "couldn't read \"%s\"\n", cursource); 1259680Slinton } else { 12618233Slinton lb = (l1 == LASTLINE) ? lastlinenum : l1; 12718233Slinton ub = (l2 == LASTLINE) ? lastlinenum : l2; 1289680Slinton if (lb < 1) { 1299680Slinton beginerrmsg(); 1309680Slinton fprintf(stderr, "line number must be positive\n"); 1319680Slinton } else if (lb > lastlinenum) { 1329680Slinton beginerrmsg(); 1339680Slinton if (lastlinenum == 1) { 1349680Slinton fprintf(stderr, "\"%s\" has only 1 line\n", cursource); 1359680Slinton } else { 1369680Slinton fprintf(stderr, "\"%s\" has only %d lines\n", 1379680Slinton cursource, lastlinenum); 1389680Slinton } 1399680Slinton } else if (ub < lb) { 1409680Slinton beginerrmsg(); 1419680Slinton fprintf(stderr, "second number must be greater than first\n"); 1429680Slinton } else { 1439680Slinton if (ub > lastlinenum) { 1449680Slinton ub = lastlinenum; 1459680Slinton } 1469680Slinton f = srcfp; 14710617Slinton fseek(f, srcaddr(lb), 0); 1489680Slinton for (i = lb; i <= ub; i++) { 1499680Slinton printf("%5d ", i); 1509680Slinton while ((c = getc(f)) != '\n') { 1519680Slinton putchar(c); 1529680Slinton } 1539680Slinton putchar('\n'); 1549680Slinton } 1559680Slinton cursrcline = ub + 1; 1569680Slinton } 1579680Slinton } 1589680Slinton } 1599680Slinton } 1609680Slinton 1619680Slinton /* 16214337Slinton * Search the sourcepath for a file. 1639680Slinton */ 1649680Slinton 16514337Slinton static char fileNameBuf[1024]; 16614337Slinton 16714337Slinton public String findsource(filename) 1689680Slinton String filename; 1699680Slinton { 17014337Slinton register String src, dir; 1719680Slinton 17212608Slinton if (filename[0] == '/') { 17314337Slinton src = filename; 17412608Slinton } else { 17514337Slinton src = nil; 17612608Slinton foreach (String, dir, sourcepath) 17714337Slinton sprintf(fileNameBuf, "%s/%s", dir, filename); 17816637Ssam if (access(fileNameBuf, R_OK) == 0) { 17914337Slinton src = fileNameBuf; 18012608Slinton break; 18114337Slinton } 18212608Slinton endfor 18312608Slinton } 18414337Slinton return src; 18514337Slinton } 18614337Slinton 18714337Slinton /* 18814337Slinton * Open a source file looking in the appropriate places. 18914337Slinton */ 19014337Slinton 19114337Slinton public File opensource(filename) 19214337Slinton String filename; 19314337Slinton { 19414337Slinton String s; 19514337Slinton File f; 19614337Slinton 19714337Slinton s = findsource(filename); 19814337Slinton if (s == nil) { 19914337Slinton f = nil; 20014337Slinton } else { 20114337Slinton f = fopen(s, "r"); 20214337Slinton } 2039680Slinton return f; 2049680Slinton } 2059680Slinton 2069680Slinton /* 2079680Slinton * Set the current source file. 2089680Slinton */ 2099680Slinton 2109680Slinton public setsource(filename) 2119680Slinton String filename; 2129680Slinton { 2139680Slinton if (filename != nil and filename != cursource) { 2149680Slinton prevsource = cursource; 2159680Slinton cursource = filename; 2169680Slinton cursrcline = 1; 2179680Slinton } 2189680Slinton } 2199680Slinton 2209680Slinton /* 2219680Slinton * Read the source file getting seek pointers for each line. 2229680Slinton */ 2239680Slinton 2249680Slinton private skimsource() 2259680Slinton { 2269680Slinton register int c; 22710617Slinton register Seekaddr count; 2289680Slinton register File f; 2299680Slinton register Lineno linenum; 2309680Slinton register Seekaddr lastaddr; 2319680Slinton register int slot; 2329680Slinton 2339680Slinton f = opensource(cursource); 2349680Slinton if (f == nil) { 2359680Slinton lastlinenum = 0; 2369680Slinton } else { 2379680Slinton if (prevsource != nil) { 2389680Slinton free_seektab(); 2399680Slinton if (srcfp != nil) { 2409680Slinton fclose(srcfp); 2419680Slinton } 2429680Slinton } 2439680Slinton prevsource = cursource; 2449680Slinton linenum = 0; 2459680Slinton count = 0; 2469680Slinton lastaddr = 0; 2479680Slinton while ((c = getc(f)) != EOF) { 2489680Slinton ++count; 2499680Slinton if (c == '\n') { 2509680Slinton slot = slotno(++linenum); 2519680Slinton if (slot >= NSLOTS) { 2529680Slinton panic("skimsource: too many lines"); 2539680Slinton } 2549680Slinton if (seektab[slot] == nil) { 2559680Slinton seektab[slot] = slot_alloc(); 2569680Slinton } 2579680Slinton seektab[slot][index(linenum)] = lastaddr; 2589680Slinton lastaddr = count; 2599680Slinton } 2609680Slinton } 2619680Slinton lastlinenum = linenum; 2629680Slinton srcfp = f; 2639680Slinton } 2649680Slinton } 2659680Slinton 2669680Slinton /* 2679680Slinton * Erase information and release space in the current seektab. 2689680Slinton * This is in preparation for reading in seek pointers for a 2699680Slinton * new file. It is possible that seek pointers for all files 2709680Slinton * should be kept around, but the current concern is space. 2719680Slinton */ 2729680Slinton 2739680Slinton private free_seektab() 2749680Slinton { 2759680Slinton register int slot; 2769680Slinton 2779680Slinton for (slot = 0; slot < NSLOTS; slot++) { 2789680Slinton if (seektab[slot] != nil) { 2799680Slinton dispose(seektab[slot]); 2809680Slinton } 2819680Slinton } 2829680Slinton } 2839680Slinton 2849680Slinton /* 2859680Slinton * Figure out current source position. 2869680Slinton */ 2879680Slinton 2889680Slinton public getsrcpos() 2899680Slinton { 2909680Slinton String filename; 2919680Slinton 29211105Slinton curline = srcline(pc); 29311105Slinton filename = srcfilename(pc); 2949680Slinton setsource(filename); 29511837Slinton if (curline != 0) { 29611837Slinton cursrcline = curline; 29711837Slinton } 2989680Slinton } 2999680Slinton 3009680Slinton /* 3019680Slinton * Print out the current source position. 3029680Slinton */ 3039680Slinton 3049680Slinton public printsrcpos() 3059680Slinton { 3069680Slinton printf("at line %d", curline); 3079680Slinton if (nlhdr.nfiles > 1) { 3089680Slinton printf(" in file \"%s\"", cursource); 3099680Slinton } 3109680Slinton } 31114337Slinton 31214337Slinton #define DEF_EDITOR "vi" 31314337Slinton 31414337Slinton /* 31514337Slinton * Invoke an editor on the given file. Which editor to use might change 31614337Slinton * installation to installation. For now, we use "vi". In any event, 31714337Slinton * the environment variable "EDITOR" overrides any default. 31814337Slinton */ 31914337Slinton 32014337Slinton public edit(filename) 32114337Slinton String filename; 32214337Slinton { 32314337Slinton extern String getenv(); 32414337Slinton String ed, src, s; 32514337Slinton Symbol f; 32614337Slinton Address addr; 32714337Slinton char lineno[10]; 32814337Slinton 32914337Slinton ed = getenv("EDITOR"); 33014337Slinton if (ed == nil) { 33114337Slinton ed = DEF_EDITOR; 33214337Slinton } 33314337Slinton src = findsource((filename != nil) ? filename : cursource); 33414337Slinton if (src == nil) { 33514337Slinton f = which(identname(filename, true)); 33614337Slinton if (not isblock(f)) { 33714337Slinton error("can't read \"%s\"", filename); 33814337Slinton } 33914337Slinton addr = firstline(f); 34014337Slinton if (addr == NOADDR) { 34114337Slinton error("no source for \"%s\"", filename); 34214337Slinton } 34314337Slinton src = srcfilename(addr); 34414337Slinton s = findsource(src); 34514337Slinton if (s != nil) { 34614337Slinton src = s; 34714337Slinton } 34814337Slinton sprintf(lineno, "+%d", srcline(addr)); 34914337Slinton } else { 35014337Slinton sprintf(lineno, "+1"); 35114337Slinton } 35218233Slinton if (streq(ed, "vi") or streq(ed, "ex")) { 35318233Slinton call(ed, stdin, stdout, lineno, src, nil); 35418233Slinton } else { 35518233Slinton call(ed, stdin, stdout, src, nil); 35618233Slinton } 35714337Slinton } 35816637Ssam 35916637Ssam /* 36018233Slinton * Strip away portions of a given pattern not part of the regular expression. 36116637Ssam */ 36218233Slinton 36318233Slinton private String getpattern (pattern) 36418233Slinton String pattern; 36516637Ssam { 36618233Slinton register char *p, *r; 36718233Slinton 36818233Slinton p = pattern; 36918233Slinton while (*p == ' ' or *p == '\t') { 37018233Slinton ++p; 37118233Slinton } 37218233Slinton r = p; 37318233Slinton while (*p != '\0') { 37418233Slinton ++p; 37518233Slinton } 37618233Slinton --p; 37718233Slinton if (*p == '\n') { 37818233Slinton *p = '\0'; 37918233Slinton --p; 38018233Slinton } 38118233Slinton if (*p == *r) { 38218233Slinton *p = '\0'; 38318233Slinton --p; 38418233Slinton } 38518233Slinton return r + 1; 38618233Slinton } 38718233Slinton 38818233Slinton /* 38918233Slinton * Search the current file for a regular expression. 39018233Slinton */ 39118233Slinton 39218233Slinton public search (direction, pattern) 39318233Slinton char direction; 39418233Slinton String pattern; 39518233Slinton { 39618233Slinton register String p; 39718233Slinton register File f; 39818233Slinton String re, err; 39918233Slinton Lineno line; 40018233Slinton boolean matched; 40118233Slinton char buf[512]; 40218233Slinton 40318233Slinton if (cursource == nil) { 40418233Slinton beginerrmsg(); 40518233Slinton fprintf(stderr, "no source file\n"); 40618233Slinton } else { 40718233Slinton if (cursource != prevsource) { 40818233Slinton skimsource(); 40916637Ssam } 41016637Ssam if (lastlinenum == 0) { 41118233Slinton beginerrmsg(); 41218233Slinton fprintf(stderr, "couldn't read \"%s\"\n", cursource); 41318233Slinton } else { 41418233Slinton re = getpattern(pattern); 41518233Slinton /* circf = 0; */ 41618233Slinton if (re != nil and *re != '\0') { 41718233Slinton err = re_comp(re); 41818233Slinton if (err != nil) { 41918233Slinton error(err); 42018233Slinton } 42118233Slinton } 42218233Slinton matched = false; 42318233Slinton f = srcfp; 42418233Slinton line = cursrcline; 42518233Slinton do { 42618233Slinton if (direction == '/') { 42718233Slinton ++line; 42818233Slinton if (line > lastlinenum) { 42918233Slinton line = 1; 43018233Slinton } 43116637Ssam } else { 43218233Slinton --line; 43318233Slinton if (line < 1) { 43418233Slinton line = lastlinenum; 43518233Slinton } 43616637Ssam } 43716637Ssam fseek(f, srcaddr(line), L_SET); 43818233Slinton p = buf; 43918233Slinton *p = getc(f); 44018233Slinton while ((*p != '\n') and (*p != EOF)) { 44118233Slinton ++p; 44218233Slinton *p = getc(f); 44318233Slinton } 44416637Ssam *p = '\0'; 44518233Slinton matched = (boolean) re_exec(buf); 44618233Slinton } while (not matched and line != cursrcline); 44718233Slinton if (not matched) { 44818233Slinton beginerrmsg(); 44918233Slinton fprintf(stderr, "no match\n"); 45018233Slinton } else { 45118233Slinton printlines(line, line); 45218233Slinton cursrcline = line; 45318233Slinton } 45418233Slinton } 45518233Slinton } 45616637Ssam } 45718233Slinton 458*33335Sdonn public integer srcwindowlen () 459*33335Sdonn { 460*33335Sdonn Node s; 461*33335Sdonn 462*33335Sdonn s = findvar(identname("$listwindow", true)); 463*33335Sdonn if (s == nil) 464*33335Sdonn return 10; 465*33335Sdonn eval(s); 466*33335Sdonn return pop(integer); 467*33335Sdonn } 468*33335Sdonn 46918233Slinton /* 47018233Slinton * Compute a small window around the given line. 47118233Slinton */ 47218233Slinton 47318233Slinton public getsrcwindow (line, l1, l2) 47418233Slinton Lineno line, *l1, *l2; 47518233Slinton { 47618233Slinton integer size; 47718233Slinton 478*33335Sdonn size = srcwindowlen(); 47918233Slinton *l1 = line - (size div 2); 48018233Slinton if (*l1 < 1) { 48118233Slinton *l1 = 1; 48218233Slinton } 48318233Slinton *l2 = *l1 + size; 48418233Slinton if (lastlinenum != LASTLINE and *l2 > lastlinenum) { 48518233Slinton *l2 = lastlinenum; 48618233Slinton } 48718233Slinton } 488