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) 05/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 } 474