1 static char sccsid[] = "@(#)rdwr.c 4.2 08/17/82"; 2 /* 3 * sdb - a symbolic debugger for unix - source file access routines. 4 */ 5 #include "head.h" 6 #include <stdio.h> 7 8 /* 9 * These procedures manage the source files examined by sdb, 10 * providing access to lines by number and utilities for printing 11 * and scrolling. One file is kept open by these routines, and 12 * line index tables are maintained for all files which have been 13 * ``current'' at any time so far. This makes line access trivial, 14 * since the location of each line in the files is known, 15 * although we get ``burned'' if the file is changed. 16 * SHOULD WATCH THE MODTIME OF FILES AND REINDEX IF IT CHANGES. 17 */ 18 19 /* 20 * Structure for files which have been ``indexed''. 21 * Contains a pointer to the file name, a pointer to an 22 * array of seek pointers for the lines in the file, 23 * and a next link in a chain of these for all files we have indexed, 24 * The currently open file is cinfo->; the chain of active files is finfo. 25 */ 26 struct finfo { 27 char *name; /* name of this file w/o common pfx */ 28 off_t *lines; /* array of seek pointers */ 29 /* line i stretches from lines[i-1] to lines[i] - 1, if first line is 1 */ 30 int nlines; /* number of lines in file */ 31 /* lines array actually has nlines+1 elements, so last line is bracketed */ 32 struct finfo *next; /* link in chain of known files */ 33 } *finfo, *cfile; 34 FILE *FIO; /* current open file (only 1 now) */ 35 char fibuf[BUFSIZ]; 36 /* 37 * We use stdio when first reading the file, but thereafter 38 * use our own routines, because we want to be able 39 * to read backwards efficiently and avoid a tell() system 40 * call on each line. Fseekpt remebers where we are in the current 41 * file. 42 */ 43 off_t fseekpt; 44 45 /* 46 * Make ``name'' the current source file, if it isn't already. 47 * If we have never seen this file before, then we create a finfo 48 * structure for it indexing the lines (this requires reading the 49 * entire file and building an index, but is well worth it since 50 * we otherwise have to brute force search the files all the time.) 51 */ 52 finit(name) 53 char *name; 54 { 55 char buf[BUFSIZ]; 56 register off_t *lp; 57 58 if (cfile && !strcmp(cfile->name, name)) 59 return; /* its already current, do nothing */ 60 /* IT WOULD BE BETTER TO HAVE A COUPLE OF FILE DESCRIPTORS, LRU */ 61 if (FIO) { 62 fclose(FIO); 63 FIO = NULL; 64 } 65 /* 66 * Paste the given name onto the common prefix (directory path) 67 * to form the full name of the file to be opened. 68 */ 69 strcpy(fp, name); 70 if ((FIO = fopen(filework, "r")) == NULL) { 71 nolines = 1; 72 perror(filework); 73 return; 74 } 75 setbuf(FIO, fibuf); 76 fseekpt = -BUFSIZ; /* putatively illegal */ 77 strcpy(curfile, name); 78 /* 79 * See if we have alread indexed this file. 80 * If so, nothing much to do. 81 */ 82 for (cfile = finfo; cfile; cfile = cfile->next) 83 if (!strcmp(cfile->name, name)) 84 return; 85 /* 86 * Create a structure for this (new) file. 87 * Lines array grows 100 lines at a time. 88 * 1 extra so last line is bracketed. 89 */ 90 cfile = (struct finfo *)sbrk(sizeof (struct finfo)); 91 lp = cfile->lines = (off_t *)sbrk(101 * sizeof (off_t)); 92 *lp++ = 0; /* line 1 starts at 0 ... */ 93 cfile->nlines = 0; 94 /* IT WOULD PROBABLY BE FASTER TO JUST USE GETC AND LOOK FOR \n */ 95 while (fgets(buf, sizeof buf, FIO)) { 96 if ((++cfile->nlines % 100) == 0) 97 sbrk(100 * sizeof (off_t)); 98 /* 99 * Mark end of the cfile->nlines'th line 100 */ 101 lp[0] = lp[-1] + strlen(buf); 102 lp++; 103 } 104 if (cfile->nlines == 0) { 105 printf("%s: no lines in file\n", filework); 106 cfile = 0; 107 return; 108 } 109 /* 110 * Allocate space for the name, making sure to leave the 111 * break on a word boundary. 112 * IT WOULD BE MUCH BETTER TO USE MALLOC AND REALLOC IN SDB. 113 */ 114 sbrk(lp + ((strlen(name)+sizeof(off_t)-1)&~(sizeof(off_t)-1))); 115 strcpy(cfile->name = (char *)lp, name); 116 cfile->next = finfo; 117 finfo = cfile; 118 } 119 120 /* 121 * Get the current line (fline) into fbuf 122 */ 123 fgetline() 124 { 125 register off_t *op = &cfile->lines[fline-1]; 126 int o, n; 127 128 n = op[1] - op[0]; 129 fbuf[n] = 0; 130 /* 131 * Case 1. Line begins in current buffer. 132 * 133 * Compute the number of characters into the buffer where 134 * the line starts. If this offset plus its length is greater 135 * than BUFSIZ, then this line splits across a buffer boundary 136 * so take the rest of this buffer and the first part of the next. 137 * Otherwise just take a chunk of this buffer. 138 */ 139 if (*op >= fseekpt && *op < fseekpt + BUFSIZ) { 140 case1: 141 o = op[0] - fseekpt; 142 if (o + n > BUFSIZ) { 143 strncpy(fbuf, fibuf+o, BUFSIZ-o); 144 fseekpt += BUFSIZ; 145 read(fileno(FIO), fibuf, BUFSIZ); 146 strncpy(fbuf+BUFSIZ-o, fibuf, n-(BUFSIZ-o)); 147 } else 148 strncpy(fbuf, fibuf+o, n); 149 return; 150 } 151 /* 152 * Case 2. Line ends in current buffer. 153 * 154 * If the line ends in this buffer (but doesn't begin in 155 * it or else we would have had case 1) take the beginning 156 * part of the buffer (end of the line) and then back up and 157 * get the rest of the line from the end of the previous block. 158 */ 159 if (op[1]-1 >= fseekpt && op[1] <= fseekpt+BUFSIZ) { 160 o = op[1] - fseekpt; 161 strncpy(fbuf+n-o, fibuf, o); 162 fseekpt -= BUFSIZ; 163 lseek(fileno(FIO), fseekpt, 0); 164 read(fileno(FIO), fibuf, BUFSIZ); 165 strncpy(fbuf, fibuf+op[0]-fseekpt, n-o); 166 return; 167 } 168 /* 169 * Case 3. Line not in current buffer at all. 170 * 171 * Read in the buffer where the line starts and then go 172 * back and handle as case 1. 173 */ 174 fseekpt = (op[0] / BUFSIZ) * BUFSIZ; 175 lseek(fileno(FIO), fseekpt, 0); 176 read(fileno(FIO), fibuf, BUFSIZ); 177 goto case1; 178 } 179 180 /* 181 * Advance current line, end-around (like for / search). 182 */ 183 fnext() 184 { 185 186 if (cfile == 0) 187 return; 188 if (fline == cfile->nlines) { 189 fline = 1; 190 } else 191 fline++; 192 fgetline(); 193 } 194 195 /* 196 * Retreat the current line, end around. 197 */ 198 fprev() 199 { 200 201 if (cfile == 0) 202 return; 203 if (fline == 1) 204 fline = cfile->nlines; 205 else 206 fline--; 207 fgetline(); 208 } 209 210 /* 211 * Print the current line. 212 */ 213 fprint() 214 { 215 register char *p; 216 217 if (cfile == 0) { 218 error("No lines in file"); 219 return; 220 } 221 printf("%d: %s", fline, fbuf); 222 } 223 224 /* 225 * Make line `num' current. 226 */ 227 ffind(num) 228 register int num; 229 { 230 231 if (cfile == 0) 232 return; 233 if (num > cfile->nlines) 234 error("Not that many lines in file"); 235 else if (num <= 0) 236 error("Zero or negative line?"); 237 else { 238 fline = num; 239 fgetline(); 240 } 241 } 242 243 /* 244 * Go back n lines. 245 */ 246 fback(n) 247 { 248 int i; 249 250 if (cfile == 0) 251 return (0); 252 if (n > fline - 1) 253 n = fline - 1; 254 fline -= n; 255 fgetline(); 256 return (n); 257 } 258 259 /* 260 * Go forwards n lines. 261 */ 262 fforward(n) 263 int n; 264 { 265 register int fnext; 266 267 if (cfile == 0) 268 return(0); 269 if (fline + n > cfile->nlines) 270 n = cfile->nlines - fline; 271 fline += n; 272 fgetline(); 273 return (n); 274 } 275 276 /* 277 * Print (upto) n lines, returning number printed. 278 */ 279 fprintn(n) 280 int n; 281 { 282 register int i; 283 284 if (cfile == 0) { 285 error("No lines in file"); 286 return (0); 287 } 288 for (i = 1; i <= n; i++) { 289 fprint(); 290 if (fline == cfile->nlines || i == n) 291 return(i); 292 fnext(); 293 } 294 return (n); 295 } 296