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