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