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