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