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