19680Slinton /* Copyright (c) 1982 Regents of the University of California */ 29680Slinton 3*18233Slinton static char sccsid[] = "@(#)source.c 1.12 (Berkeley) 03/01/85"; 49680Slinton 5*18233Slinton static char rcsid[] = "$Header: source.c,v 1.4 84/06/07 16:29:38 linton Exp $"; 6*18233Slinton 79680Slinton /* 89680Slinton * Source file management. 99680Slinton */ 109680Slinton 119680Slinton #include "defs.h" 129680Slinton #include "source.h" 139680Slinton #include "object.h" 149680Slinton #include "mappings.h" 159680Slinton #include "machine.h" 16*18233Slinton #include "keywords.h" 17*18233Slinton #include "tree.h" 18*18233Slinton #include "eval.h" 1916637Ssam #include <sys/file.h> 209680Slinton 219680Slinton #ifndef public 229680Slinton typedef int Lineno; 239680Slinton 249680Slinton String cursource; 259680Slinton Lineno curline; 269680Slinton Lineno cursrcline; 279680Slinton 289680Slinton #define LASTLINE 0 /* recognized by printlines */ 299680Slinton 309680Slinton #include "lists.h" 319680Slinton 329680Slinton List sourcepath; 339680Slinton #endif 349680Slinton 35*18233Slinton extern char *re_comp(); 36*18233Slinton 379680Slinton private Lineno lastlinenum; 389680Slinton private String prevsource = nil; 399680Slinton 409680Slinton /* 419680Slinton * Data structure for indexing source seek addresses by line number. 429680Slinton * 439680Slinton * The constraints are: 449680Slinton * 459680Slinton * we want an array so indexing is fast and easy 469680Slinton * we don't want to waste space for small files 479680Slinton * we don't want an upper bound on # of lines in a file 489680Slinton * we don't know how many lines there are 499680Slinton * 509680Slinton * The solution is a "dirty" hash table. We have NSLOTS pointers to 519680Slinton * arrays of NLINESPERSLOT addresses. To find the source address of 529680Slinton * a particular line we find the slot, allocate space if necessary, 539680Slinton * and then find its location within the pointed to array. 549680Slinton */ 559680Slinton 5610617Slinton typedef long Seekaddr; 579680Slinton 58*18233Slinton #define NSLOTS 40 599680Slinton #define NLINESPERSLOT 500 609680Slinton 619680Slinton #define slotno(line) ((line) div NLINESPERSLOT) 629680Slinton #define index(line) ((line) mod NLINESPERSLOT) 639680Slinton #define slot_alloc() newarr(Seekaddr, NLINESPERSLOT) 649680Slinton #define srcaddr(line) seektab[slotno(line)][index(line)] 659680Slinton 669680Slinton private File srcfp; 679680Slinton private Seekaddr *seektab[NSLOTS]; 689680Slinton 699680Slinton /* 70*18233Slinton * Determine if the current source file is available. 71*18233Slinton */ 72*18233Slinton 73*18233Slinton public boolean canReadSource () 74*18233Slinton { 75*18233Slinton boolean b; 76*18233Slinton 77*18233Slinton if (cursource == nil) { 78*18233Slinton b = false; 79*18233Slinton } else if (cursource != prevsource) { 80*18233Slinton skimsource(); 81*18233Slinton b = (boolean) (lastlinenum != 0); 82*18233Slinton } else { 83*18233Slinton b = true; 84*18233Slinton } 85*18233Slinton return b; 86*18233Slinton } 87*18233Slinton 88*18233Slinton /* 899680Slinton * Print out the given lines from the source. 909680Slinton */ 919680Slinton 929680Slinton public printlines(l1, l2) 939680Slinton Lineno l1, l2; 949680Slinton { 959680Slinton register int c; 969680Slinton register Lineno i, lb, ub; 979680Slinton register File f; 989680Slinton 999680Slinton if (cursource == nil) { 1009680Slinton beginerrmsg(); 1019680Slinton fprintf(stderr, "no source file\n"); 1029680Slinton } else { 1039680Slinton if (cursource != prevsource) { 1049680Slinton skimsource(); 1059680Slinton } 1069680Slinton if (lastlinenum == 0) { 1079680Slinton beginerrmsg(); 1089680Slinton fprintf(stderr, "couldn't read \"%s\"\n", cursource); 1099680Slinton } else { 110*18233Slinton lb = (l1 == LASTLINE) ? lastlinenum : l1; 111*18233Slinton ub = (l2 == LASTLINE) ? lastlinenum : l2; 1129680Slinton if (lb < 1) { 1139680Slinton beginerrmsg(); 1149680Slinton fprintf(stderr, "line number must be positive\n"); 1159680Slinton } else if (lb > lastlinenum) { 1169680Slinton beginerrmsg(); 1179680Slinton if (lastlinenum == 1) { 1189680Slinton fprintf(stderr, "\"%s\" has only 1 line\n", cursource); 1199680Slinton } else { 1209680Slinton fprintf(stderr, "\"%s\" has only %d lines\n", 1219680Slinton cursource, lastlinenum); 1229680Slinton } 1239680Slinton } else if (ub < lb) { 1249680Slinton beginerrmsg(); 1259680Slinton fprintf(stderr, "second number must be greater than first\n"); 1269680Slinton } else { 1279680Slinton if (ub > lastlinenum) { 1289680Slinton ub = lastlinenum; 1299680Slinton } 1309680Slinton f = srcfp; 13110617Slinton fseek(f, srcaddr(lb), 0); 1329680Slinton for (i = lb; i <= ub; i++) { 1339680Slinton printf("%5d ", i); 1349680Slinton while ((c = getc(f)) != '\n') { 1359680Slinton putchar(c); 1369680Slinton } 1379680Slinton putchar('\n'); 1389680Slinton } 1399680Slinton cursrcline = ub + 1; 1409680Slinton } 1419680Slinton } 1429680Slinton } 1439680Slinton } 1449680Slinton 1459680Slinton /* 14614337Slinton * Search the sourcepath for a file. 1479680Slinton */ 1489680Slinton 14914337Slinton static char fileNameBuf[1024]; 15014337Slinton 15114337Slinton public String findsource(filename) 1529680Slinton String filename; 1539680Slinton { 15414337Slinton register String src, dir; 1559680Slinton 15612608Slinton if (filename[0] == '/') { 15714337Slinton src = filename; 15812608Slinton } else { 15914337Slinton src = nil; 16012608Slinton foreach (String, dir, sourcepath) 16114337Slinton sprintf(fileNameBuf, "%s/%s", dir, filename); 16216637Ssam if (access(fileNameBuf, R_OK) == 0) { 16314337Slinton src = fileNameBuf; 16412608Slinton break; 16514337Slinton } 16612608Slinton endfor 16712608Slinton } 16814337Slinton return src; 16914337Slinton } 17014337Slinton 17114337Slinton /* 17214337Slinton * Open a source file looking in the appropriate places. 17314337Slinton */ 17414337Slinton 17514337Slinton public File opensource(filename) 17614337Slinton String filename; 17714337Slinton { 17814337Slinton String s; 17914337Slinton File f; 18014337Slinton 18114337Slinton s = findsource(filename); 18214337Slinton if (s == nil) { 18314337Slinton f = nil; 18414337Slinton } else { 18514337Slinton f = fopen(s, "r"); 18614337Slinton } 1879680Slinton return f; 1889680Slinton } 1899680Slinton 1909680Slinton /* 1919680Slinton * Set the current source file. 1929680Slinton */ 1939680Slinton 1949680Slinton public setsource(filename) 1959680Slinton String filename; 1969680Slinton { 1979680Slinton if (filename != nil and filename != cursource) { 1989680Slinton prevsource = cursource; 1999680Slinton cursource = filename; 2009680Slinton cursrcline = 1; 2019680Slinton } 2029680Slinton } 2039680Slinton 2049680Slinton /* 2059680Slinton * Read the source file getting seek pointers for each line. 2069680Slinton */ 2079680Slinton 2089680Slinton private skimsource() 2099680Slinton { 2109680Slinton register int c; 21110617Slinton register Seekaddr count; 2129680Slinton register File f; 2139680Slinton register Lineno linenum; 2149680Slinton register Seekaddr lastaddr; 2159680Slinton register int slot; 2169680Slinton 2179680Slinton f = opensource(cursource); 2189680Slinton if (f == nil) { 2199680Slinton lastlinenum = 0; 2209680Slinton } else { 2219680Slinton if (prevsource != nil) { 2229680Slinton free_seektab(); 2239680Slinton if (srcfp != nil) { 2249680Slinton fclose(srcfp); 2259680Slinton } 2269680Slinton } 2279680Slinton prevsource = cursource; 2289680Slinton linenum = 0; 2299680Slinton count = 0; 2309680Slinton lastaddr = 0; 2319680Slinton while ((c = getc(f)) != EOF) { 2329680Slinton ++count; 2339680Slinton if (c == '\n') { 2349680Slinton slot = slotno(++linenum); 2359680Slinton if (slot >= NSLOTS) { 2369680Slinton panic("skimsource: too many lines"); 2379680Slinton } 2389680Slinton if (seektab[slot] == nil) { 2399680Slinton seektab[slot] = slot_alloc(); 2409680Slinton } 2419680Slinton seektab[slot][index(linenum)] = lastaddr; 2429680Slinton lastaddr = count; 2439680Slinton } 2449680Slinton } 2459680Slinton lastlinenum = linenum; 2469680Slinton srcfp = f; 2479680Slinton } 2489680Slinton } 2499680Slinton 2509680Slinton /* 2519680Slinton * Erase information and release space in the current seektab. 2529680Slinton * This is in preparation for reading in seek pointers for a 2539680Slinton * new file. It is possible that seek pointers for all files 2549680Slinton * should be kept around, but the current concern is space. 2559680Slinton */ 2569680Slinton 2579680Slinton private free_seektab() 2589680Slinton { 2599680Slinton register int slot; 2609680Slinton 2619680Slinton for (slot = 0; slot < NSLOTS; slot++) { 2629680Slinton if (seektab[slot] != nil) { 2639680Slinton dispose(seektab[slot]); 2649680Slinton } 2659680Slinton } 2669680Slinton } 2679680Slinton 2689680Slinton /* 2699680Slinton * Figure out current source position. 2709680Slinton */ 2719680Slinton 2729680Slinton public getsrcpos() 2739680Slinton { 2749680Slinton String filename; 2759680Slinton 27611105Slinton curline = srcline(pc); 27711105Slinton filename = srcfilename(pc); 2789680Slinton setsource(filename); 27911837Slinton if (curline != 0) { 28011837Slinton cursrcline = curline; 28111837Slinton } 2829680Slinton } 2839680Slinton 2849680Slinton /* 2859680Slinton * Print out the current source position. 2869680Slinton */ 2879680Slinton 2889680Slinton public printsrcpos() 2899680Slinton { 2909680Slinton printf("at line %d", curline); 2919680Slinton if (nlhdr.nfiles > 1) { 2929680Slinton printf(" in file \"%s\"", cursource); 2939680Slinton } 2949680Slinton } 29514337Slinton 29614337Slinton #define DEF_EDITOR "vi" 29714337Slinton 29814337Slinton /* 29914337Slinton * Invoke an editor on the given file. Which editor to use might change 30014337Slinton * installation to installation. For now, we use "vi". In any event, 30114337Slinton * the environment variable "EDITOR" overrides any default. 30214337Slinton */ 30314337Slinton 30414337Slinton public edit(filename) 30514337Slinton String filename; 30614337Slinton { 30714337Slinton extern String getenv(); 30814337Slinton String ed, src, s; 30914337Slinton Symbol f; 31014337Slinton Address addr; 31114337Slinton char lineno[10]; 31214337Slinton 31314337Slinton ed = getenv("EDITOR"); 31414337Slinton if (ed == nil) { 31514337Slinton ed = DEF_EDITOR; 31614337Slinton } 31714337Slinton src = findsource((filename != nil) ? filename : cursource); 31814337Slinton if (src == nil) { 31914337Slinton f = which(identname(filename, true)); 32014337Slinton if (not isblock(f)) { 32114337Slinton error("can't read \"%s\"", filename); 32214337Slinton } 32314337Slinton addr = firstline(f); 32414337Slinton if (addr == NOADDR) { 32514337Slinton error("no source for \"%s\"", filename); 32614337Slinton } 32714337Slinton src = srcfilename(addr); 32814337Slinton s = findsource(src); 32914337Slinton if (s != nil) { 33014337Slinton src = s; 33114337Slinton } 33214337Slinton sprintf(lineno, "+%d", srcline(addr)); 33314337Slinton } else { 33414337Slinton sprintf(lineno, "+1"); 33514337Slinton } 336*18233Slinton if (streq(ed, "vi") or streq(ed, "ex")) { 337*18233Slinton call(ed, stdin, stdout, lineno, src, nil); 338*18233Slinton } else { 339*18233Slinton call(ed, stdin, stdout, src, nil); 340*18233Slinton } 34114337Slinton } 34216637Ssam 34316637Ssam /* 344*18233Slinton * Strip away portions of a given pattern not part of the regular expression. 34516637Ssam */ 346*18233Slinton 347*18233Slinton private String getpattern (pattern) 348*18233Slinton String pattern; 34916637Ssam { 350*18233Slinton register char *p, *r; 351*18233Slinton 352*18233Slinton p = pattern; 353*18233Slinton while (*p == ' ' or *p == '\t') { 354*18233Slinton ++p; 355*18233Slinton } 356*18233Slinton r = p; 357*18233Slinton while (*p != '\0') { 358*18233Slinton ++p; 359*18233Slinton } 360*18233Slinton --p; 361*18233Slinton if (*p == '\n') { 362*18233Slinton *p = '\0'; 363*18233Slinton --p; 364*18233Slinton } 365*18233Slinton if (*p == *r) { 366*18233Slinton *p = '\0'; 367*18233Slinton --p; 368*18233Slinton } 369*18233Slinton return r + 1; 370*18233Slinton } 371*18233Slinton 372*18233Slinton /* 373*18233Slinton * Search the current file for a regular expression. 374*18233Slinton */ 375*18233Slinton 376*18233Slinton public search (direction, pattern) 377*18233Slinton char direction; 378*18233Slinton String pattern; 379*18233Slinton { 380*18233Slinton register String p; 381*18233Slinton register File f; 382*18233Slinton String re, err; 383*18233Slinton Lineno line; 384*18233Slinton boolean matched; 385*18233Slinton char buf[512]; 386*18233Slinton 387*18233Slinton if (cursource == nil) { 388*18233Slinton beginerrmsg(); 389*18233Slinton fprintf(stderr, "no source file\n"); 390*18233Slinton } else { 391*18233Slinton if (cursource != prevsource) { 392*18233Slinton skimsource(); 39316637Ssam } 39416637Ssam if (lastlinenum == 0) { 395*18233Slinton beginerrmsg(); 396*18233Slinton fprintf(stderr, "couldn't read \"%s\"\n", cursource); 397*18233Slinton } else { 398*18233Slinton re = getpattern(pattern); 399*18233Slinton /* circf = 0; */ 400*18233Slinton if (re != nil and *re != '\0') { 401*18233Slinton err = re_comp(re); 402*18233Slinton if (err != nil) { 403*18233Slinton error(err); 404*18233Slinton } 405*18233Slinton } 406*18233Slinton matched = false; 407*18233Slinton f = srcfp; 408*18233Slinton line = cursrcline; 409*18233Slinton do { 410*18233Slinton if (direction == '/') { 411*18233Slinton ++line; 412*18233Slinton if (line > lastlinenum) { 413*18233Slinton line = 1; 414*18233Slinton } 41516637Ssam } else { 416*18233Slinton --line; 417*18233Slinton if (line < 1) { 418*18233Slinton line = lastlinenum; 419*18233Slinton } 42016637Ssam } 42116637Ssam fseek(f, srcaddr(line), L_SET); 422*18233Slinton p = buf; 423*18233Slinton *p = getc(f); 424*18233Slinton while ((*p != '\n') and (*p != EOF)) { 425*18233Slinton ++p; 426*18233Slinton *p = getc(f); 427*18233Slinton } 42816637Ssam *p = '\0'; 429*18233Slinton matched = (boolean) re_exec(buf); 430*18233Slinton } while (not matched and line != cursrcline); 431*18233Slinton if (not matched) { 432*18233Slinton beginerrmsg(); 433*18233Slinton fprintf(stderr, "no match\n"); 434*18233Slinton } else { 435*18233Slinton printlines(line, line); 436*18233Slinton cursrcline = line; 437*18233Slinton } 438*18233Slinton } 439*18233Slinton } 44016637Ssam } 441*18233Slinton 442*18233Slinton /* 443*18233Slinton * Compute a small window around the given line. 444*18233Slinton */ 445*18233Slinton 446*18233Slinton public getsrcwindow (line, l1, l2) 447*18233Slinton Lineno line, *l1, *l2; 448*18233Slinton { 449*18233Slinton Node s; 450*18233Slinton integer size; 451*18233Slinton 452*18233Slinton s = findvar(identname("$listwindow", true)); 453*18233Slinton if (s == nil) { 454*18233Slinton size = 10; 455*18233Slinton } else { 456*18233Slinton eval(s); 457*18233Slinton size = pop(integer); 458*18233Slinton } 459*18233Slinton *l1 = line - (size div 2); 460*18233Slinton if (*l1 < 1) { 461*18233Slinton *l1 = 1; 462*18233Slinton } 463*18233Slinton *l2 = *l1 + size; 464*18233Slinton if (lastlinenum != LASTLINE and *l2 > lastlinenum) { 465*18233Slinton *l2 = lastlinenum; 466*18233Slinton } 467*18233Slinton } 468