1: /* 2: * Copyright (c) 1983 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: static char sccsid[] = "@(#)source.c 5.1 (Berkeley) 5/31/85"; 9: #endif not lint 10: 11: static char rcsid[] = "$Header: source.c,v 1.4 84/06/07 16:29:38 linton Exp $"; 12: 13: /* 14: * Source file management. 15: */ 16: 17: #include "defs.h" 18: #include "source.h" 19: #include "object.h" 20: #include "mappings.h" 21: #include "machine.h" 22: #include "keywords.h" 23: #include "tree.h" 24: #include "eval.h" 25: #include <sys/file.h> 26: 27: #ifndef public 28: typedef int Lineno; 29: 30: String cursource; 31: Lineno curline; 32: Lineno cursrcline; 33: 34: #define LASTLINE 0 /* recognized by printlines */ 35: 36: #include "lists.h" 37: 38: List sourcepath; 39: #endif 40: 41: extern char *re_comp(); 42: 43: private Lineno lastlinenum; 44: private String prevsource = nil; 45: 46: /* 47: * Data structure for indexing source seek addresses by line number. 48: * 49: * The constraints are: 50: * 51: * we want an array so indexing is fast and easy 52: * we don't want to waste space for small files 53: * we don't want an upper bound on # of lines in a file 54: * we don't know how many lines there are 55: * 56: * The solution is a "dirty" hash table. We have NSLOTS pointers to 57: * arrays of NLINESPERSLOT addresses. To find the source address of 58: * a particular line we find the slot, allocate space if necessary, 59: * and then find its location within the pointed to array. 60: */ 61: 62: typedef long Seekaddr; 63: 64: #define NSLOTS 40 65: #define NLINESPERSLOT 500 66: 67: #define slotno(line) ((line) div NLINESPERSLOT) 68: #define index(line) ((line) mod NLINESPERSLOT) 69: #define slot_alloc() newarr(Seekaddr, NLINESPERSLOT) 70: #define srcaddr(line) seektab[slotno(line)][index(line)] 71: 72: private File srcfp; 73: private Seekaddr *seektab[NSLOTS]; 74: 75: /* 76: * Determine if the current source file is available. 77: */ 78: 79: public boolean canReadSource () 80: { 81: boolean b; 82: 83: if (cursource == nil) { 84: b = false; 85: } else if (cursource != prevsource) { 86: skimsource(); 87: b = (boolean) (lastlinenum != 0); 88: } else { 89: b = true; 90: } 91: return b; 92: } 93: 94: /* 95: * Print out the given lines from the source. 96: */ 97: 98: public printlines(l1, l2) 99: Lineno l1, l2; 100: { 101: register int c; 102: register Lineno i, lb, ub; 103: register File f; 104: 105: if (cursource == nil) { 106: beginerrmsg(); 107: fprintf(stderr, "no source file\n"); 108: } else { 109: if (cursource != prevsource) { 110: skimsource(); 111: } 112: if (lastlinenum == 0) { 113: beginerrmsg(); 114: fprintf(stderr, "couldn't read \"%s\"\n", cursource); 115: } else { 116: lb = (l1 == LASTLINE) ? lastlinenum : l1; 117: ub = (l2 == LASTLINE) ? lastlinenum : l2; 118: if (lb < 1) { 119: beginerrmsg(); 120: fprintf(stderr, "line number must be positive\n"); 121: } else if (lb > lastlinenum) { 122: beginerrmsg(); 123: if (lastlinenum == 1) { 124: fprintf(stderr, "\"%s\" has only 1 line\n", cursource); 125: } else { 126: fprintf(stderr, "\"%s\" has only %d lines\n", 127: cursource, lastlinenum); 128: } 129: } else if (ub < lb) { 130: beginerrmsg(); 131: fprintf(stderr, "second number must be greater than first\n"); 132: } else { 133: if (ub > lastlinenum) { 134: ub = lastlinenum; 135: } 136: f = srcfp; 137: fseek(f, srcaddr(lb), 0); 138: for (i = lb; i <= ub; i++) { 139: printf("%5d ", i); 140: while ((c = getc(f)) != '\n') { 141: putchar(c); 142: } 143: putchar('\n'); 144: } 145: cursrcline = ub + 1; 146: } 147: } 148: } 149: } 150: 151: /* 152: * Search the sourcepath for a file. 153: */ 154: 155: static char fileNameBuf[1024]; 156: 157: public String findsource(filename) 158: String filename; 159: { 160: register String src, dir; 161: 162: if (filename[0] == '/') { 163: src = filename; 164: } else { 165: src = nil; 166: foreach (String, dir, sourcepath) 167: sprintf(fileNameBuf, "%s/%s", dir, filename); 168: if (access(fileNameBuf, R_OK) == 0) { 169: src = fileNameBuf; 170: break; 171: } 172: endfor 173: } 174: return src; 175: } 176: 177: /* 178: * Open a source file looking in the appropriate places. 179: */ 180: 181: public File opensource(filename) 182: String filename; 183: { 184: String s; 185: File f; 186: 187: s = findsource(filename); 188: if (s == nil) { 189: f = nil; 190: } else { 191: f = fopen(s, "r"); 192: } 193: return f; 194: } 195: 196: /* 197: * Set the current source file. 198: */ 199: 200: public setsource(filename) 201: String filename; 202: { 203: if (filename != nil and filename != cursource) { 204: prevsource = cursource; 205: cursource = filename; 206: cursrcline = 1; 207: } 208: } 209: 210: /* 211: * Read the source file getting seek pointers for each line. 212: */ 213: 214: private skimsource() 215: { 216: register int c; 217: register Seekaddr count; 218: register File f; 219: register Lineno linenum; 220: register Seekaddr lastaddr; 221: register int slot; 222: 223: f = opensource(cursource); 224: if (f == nil) { 225: lastlinenum = 0; 226: } else { 227: if (prevsource != nil) { 228: free_seektab(); 229: if (srcfp != nil) { 230: fclose(srcfp); 231: } 232: } 233: prevsource = cursource; 234: linenum = 0; 235: count = 0; 236: lastaddr = 0; 237: while ((c = getc(f)) != EOF) { 238: ++count; 239: if (c == '\n') { 240: slot = slotno(++linenum); 241: if (slot >= NSLOTS) { 242: panic("skimsource: too many lines"); 243: } 244: if (seektab[slot] == nil) { 245: seektab[slot] = slot_alloc(); 246: } 247: seektab[slot][index(linenum)] = lastaddr; 248: lastaddr = count; 249: } 250: } 251: lastlinenum = linenum; 252: srcfp = f; 253: } 254: } 255: 256: /* 257: * Erase information and release space in the current seektab. 258: * This is in preparation for reading in seek pointers for a 259: * new file. It is possible that seek pointers for all files 260: * should be kept around, but the current concern is space. 261: */ 262: 263: private free_seektab() 264: { 265: register int slot; 266: 267: for (slot = 0; slot < NSLOTS; slot++) { 268: if (seektab[slot] != nil) { 269: dispose(seektab[slot]); 270: } 271: } 272: } 273: 274: /* 275: * Figure out current source position. 276: */ 277: 278: public getsrcpos() 279: { 280: String filename; 281: 282: curline = srcline(pc); 283: filename = srcfilename(pc); 284: setsource(filename); 285: if (curline != 0) { 286: cursrcline = curline; 287: } 288: } 289: 290: /* 291: * Print out the current source position. 292: */ 293: 294: public printsrcpos() 295: { 296: printf("at line %d", curline); 297: if (nlhdr.nfiles > 1) { 298: printf(" in file \"%s\"", cursource); 299: } 300: } 301: 302: #define DEF_EDITOR "vi" 303: 304: /* 305: * Invoke an editor on the given file. Which editor to use might change 306: * installation to installation. For now, we use "vi". In any event, 307: * the environment variable "EDITOR" overrides any default. 308: */ 309: 310: public edit(filename) 311: String filename; 312: { 313: extern String getenv(); 314: String ed, src, s; 315: Symbol f; 316: Address addr; 317: char lineno[10]; 318: 319: ed = getenv("EDITOR"); 320: if (ed == nil) { 321: ed = DEF_EDITOR; 322: } 323: src = findsource((filename != nil) ? filename : cursource); 324: if (src == nil) { 325: f = which(identname(filename, true)); 326: if (not isblock(f)) { 327: error("can't read \"%s\"", filename); 328: } 329: addr = firstline(f); 330: if (addr == NOADDR) { 331: error("no source for \"%s\"", filename); 332: } 333: src = srcfilename(addr); 334: s = findsource(src); 335: if (s != nil) { 336: src = s; 337: } 338: sprintf(lineno, "+%d", srcline(addr)); 339: } else { 340: sprintf(lineno, "+1"); 341: } 342: if (streq(ed, "vi") or streq(ed, "ex")) { 343: call(ed, stdin, stdout, lineno, src, nil); 344: } else { 345: call(ed, stdin, stdout, src, nil); 346: } 347: } 348: 349: /* 350: * Strip away portions of a given pattern not part of the regular expression. 351: */ 352: 353: private String getpattern (pattern) 354: String pattern; 355: { 356: register char *p, *r; 357: 358: p = pattern; 359: while (*p == ' ' or *p == '\t') { 360: ++p; 361: } 362: r = p; 363: while (*p != '\0') { 364: ++p; 365: } 366: --p; 367: if (*p == '\n') { 368: *p = '\0'; 369: --p; 370: } 371: if (*p == *r) { 372: *p = '\0'; 373: --p; 374: } 375: return r + 1; 376: } 377: 378: /* 379: * Search the current file for a regular expression. 380: */ 381: 382: public search (direction, pattern) 383: char direction; 384: String pattern; 385: { 386: register String p; 387: register File f; 388: String re, err; 389: Lineno line; 390: boolean matched; 391: char buf[512]; 392: 393: if (cursource == nil) { 394: beginerrmsg(); 395: fprintf(stderr, "no source file\n"); 396: } else { 397: if (cursource != prevsource) { 398: skimsource(); 399: } 400: if (lastlinenum == 0) { 401: beginerrmsg(); 402: fprintf(stderr, "couldn't read \"%s\"\n", cursource); 403: } else { 404: re = getpattern(pattern); 405: /* circf = 0; */ 406: if (re != nil and *re != '\0') { 407: err = re_comp(re); 408: if (err != nil) { 409: error(err); 410: } 411: } 412: matched = false; 413: f = srcfp; 414: line = cursrcline; 415: do { 416: if (direction == '/') { 417: ++line; 418: if (line > lastlinenum) { 419: line = 1; 420: } 421: } else { 422: --line; 423: if (line < 1) { 424: line = lastlinenum; 425: } 426: } 427: fseek(f, srcaddr(line), L_SET); 428: p = buf; 429: *p = getc(f); 430: while ((*p != '\n') and (*p != EOF)) { 431: ++p; 432: *p = getc(f); 433: } 434: *p = '\0'; 435: matched = (boolean) re_exec(buf); 436: } while (not matched and line != cursrcline); 437: if (not matched) { 438: beginerrmsg(); 439: fprintf(stderr, "no match\n"); 440: } else { 441: printlines(line, line); 442: cursrcline = line; 443: } 444: } 445: } 446: } 447: 448: /* 449: * Compute a small window around the given line. 450: */ 451: 452: public getsrcwindow (line, l1, l2) 453: Lineno line, *l1, *l2; 454: { 455: Node s; 456: integer size; 457: 458: s = findvar(identname("$listwindow", true)); 459: if (s == nil) { 460: size = 10; 461: } else { 462: eval(s); 463: size = pop(integer); 464: } 465: *l1 = line - (size div 2); 466: if (*l1 < 1) { 467: *l1 = 1; 468: } 469: *l2 = *l1 + size; 470: if (lastlinenum != LASTLINE and *l2 > lastlinenum) { 471: *l2 = lastlinenum; 472: } 473: }