1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)source.c 1.9 08/05/83"; 4 5 /* 6 * Source file management. 7 */ 8 9 #include "defs.h" 10 #include "source.h" 11 #include "object.h" 12 #include "mappings.h" 13 #include "machine.h" 14 15 #ifndef public 16 typedef int Lineno; 17 18 String cursource; 19 Lineno curline; 20 Lineno cursrcline; 21 22 #define LASTLINE 0 /* recognized by printlines */ 23 24 #include "lists.h" 25 26 List sourcepath; 27 #endif 28 29 private Lineno lastlinenum; 30 private String prevsource = nil; 31 32 /* 33 * Data structure for indexing source seek addresses by line number. 34 * 35 * The constraints are: 36 * 37 * we want an array so indexing is fast and easy 38 * we don't want to waste space for small files 39 * we don't want an upper bound on # of lines in a file 40 * we don't know how many lines there are 41 * 42 * The solution is a "dirty" hash table. We have NSLOTS pointers to 43 * arrays of NLINESPERSLOT addresses. To find the source address of 44 * a particular line we find the slot, allocate space if necessary, 45 * and then find its location within the pointed to array. 46 */ 47 48 typedef long Seekaddr; 49 50 #define NSLOTS 20 51 #define NLINESPERSLOT 500 52 53 #define slotno(line) ((line) div NLINESPERSLOT) 54 #define index(line) ((line) mod NLINESPERSLOT) 55 #define slot_alloc() newarr(Seekaddr, NLINESPERSLOT) 56 #define srcaddr(line) seektab[slotno(line)][index(line)] 57 58 private File srcfp; 59 private Seekaddr *seektab[NSLOTS]; 60 61 /* 62 * Print out the given lines from the source. 63 */ 64 65 public printlines(l1, l2) 66 Lineno l1, l2; 67 { 68 register int c; 69 register Lineno i, lb, ub; 70 register File f; 71 72 if (cursource == nil) { 73 beginerrmsg(); 74 fprintf(stderr, "no source file\n"); 75 } else { 76 if (cursource != prevsource) { 77 skimsource(); 78 } 79 if (lastlinenum == 0) { 80 beginerrmsg(); 81 fprintf(stderr, "couldn't read \"%s\"\n", cursource); 82 } else { 83 lb = (l1 == 0) ? lastlinenum : l1; 84 ub = (l2 == 0) ? lastlinenum : l2; 85 if (lb < 1) { 86 beginerrmsg(); 87 fprintf(stderr, "line number must be positive\n"); 88 } else if (lb > lastlinenum) { 89 beginerrmsg(); 90 if (lastlinenum == 1) { 91 fprintf(stderr, "\"%s\" has only 1 line\n", cursource); 92 } else { 93 fprintf(stderr, "\"%s\" has only %d lines\n", 94 cursource, lastlinenum); 95 } 96 } else if (ub < lb) { 97 beginerrmsg(); 98 fprintf(stderr, "second number must be greater than first\n"); 99 } else { 100 if (ub > lastlinenum) { 101 ub = lastlinenum; 102 } 103 f = srcfp; 104 fseek(f, srcaddr(lb), 0); 105 for (i = lb; i <= ub; i++) { 106 printf("%5d ", i); 107 while ((c = getc(f)) != '\n') { 108 putchar(c); 109 } 110 putchar('\n'); 111 } 112 cursrcline = ub + 1; 113 } 114 } 115 } 116 } 117 118 /* 119 * Search the sourcepath for a file. 120 */ 121 122 static char fileNameBuf[1024]; 123 124 public String findsource(filename) 125 String filename; 126 { 127 register File f; 128 register String src, dir; 129 130 if (filename[0] == '/') { 131 src = filename; 132 } else { 133 src = nil; 134 foreach (String, dir, sourcepath) 135 sprintf(fileNameBuf, "%s/%s", dir, filename); 136 f = fopen(fileNameBuf, "r"); 137 if (f != nil) { 138 fclose(f); 139 src = fileNameBuf; 140 break; 141 } 142 endfor 143 } 144 return src; 145 } 146 147 /* 148 * Open a source file looking in the appropriate places. 149 */ 150 151 public File opensource(filename) 152 String filename; 153 { 154 String s; 155 File f; 156 157 s = findsource(filename); 158 if (s == nil) { 159 f = nil; 160 } else { 161 f = fopen(s, "r"); 162 } 163 return f; 164 } 165 166 /* 167 * Set the current source file. 168 */ 169 170 public setsource(filename) 171 String filename; 172 { 173 if (filename != nil and filename != cursource) { 174 prevsource = cursource; 175 cursource = filename; 176 cursrcline = 1; 177 } 178 } 179 180 /* 181 * Read the source file getting seek pointers for each line. 182 */ 183 184 private skimsource() 185 { 186 register int c; 187 register Seekaddr count; 188 register File f; 189 register Lineno linenum; 190 register Seekaddr lastaddr; 191 register int slot; 192 193 f = opensource(cursource); 194 if (f == nil) { 195 lastlinenum = 0; 196 } else { 197 if (prevsource != nil) { 198 free_seektab(); 199 if (srcfp != nil) { 200 fclose(srcfp); 201 } 202 } 203 prevsource = cursource; 204 linenum = 0; 205 count = 0; 206 lastaddr = 0; 207 while ((c = getc(f)) != EOF) { 208 ++count; 209 if (c == '\n') { 210 slot = slotno(++linenum); 211 if (slot >= NSLOTS) { 212 panic("skimsource: too many lines"); 213 } 214 if (seektab[slot] == nil) { 215 seektab[slot] = slot_alloc(); 216 } 217 seektab[slot][index(linenum)] = lastaddr; 218 lastaddr = count; 219 } 220 } 221 lastlinenum = linenum; 222 srcfp = f; 223 } 224 } 225 226 /* 227 * Erase information and release space in the current seektab. 228 * This is in preparation for reading in seek pointers for a 229 * new file. It is possible that seek pointers for all files 230 * should be kept around, but the current concern is space. 231 */ 232 233 private free_seektab() 234 { 235 register int slot; 236 237 for (slot = 0; slot < NSLOTS; slot++) { 238 if (seektab[slot] != nil) { 239 dispose(seektab[slot]); 240 } 241 } 242 } 243 244 /* 245 * Figure out current source position. 246 */ 247 248 public getsrcpos() 249 { 250 String filename; 251 252 curline = srcline(pc); 253 filename = srcfilename(pc); 254 setsource(filename); 255 if (curline != 0) { 256 cursrcline = curline; 257 } 258 } 259 260 /* 261 * Print out the current source position. 262 */ 263 264 public printsrcpos() 265 { 266 printf("at line %d", curline); 267 if (nlhdr.nfiles > 1) { 268 printf(" in file \"%s\"", cursource); 269 } 270 } 271 272 #define DEF_EDITOR "vi" 273 274 /* 275 * Invoke an editor on the given file. Which editor to use might change 276 * installation to installation. For now, we use "vi". In any event, 277 * the environment variable "EDITOR" overrides any default. 278 */ 279 280 public edit(filename) 281 String filename; 282 { 283 extern String getenv(); 284 String ed, src, s; 285 Symbol f; 286 Address addr; 287 char lineno[10]; 288 289 ed = getenv("EDITOR"); 290 if (ed == nil) { 291 ed = DEF_EDITOR; 292 } 293 src = findsource((filename != nil) ? filename : cursource); 294 if (src == nil) { 295 f = which(identname(filename, true)); 296 if (not isblock(f)) { 297 error("can't read \"%s\"", filename); 298 } 299 addr = firstline(f); 300 if (addr == NOADDR) { 301 error("no source for \"%s\"", filename); 302 } 303 src = srcfilename(addr); 304 s = findsource(src); 305 if (s != nil) { 306 src = s; 307 } 308 sprintf(lineno, "+%d", srcline(addr)); 309 } else { 310 sprintf(lineno, "+1"); 311 } 312 call(ed, stdin, stdout, lineno, src, nil); 313 } 314