1: static char sccsid[] = "@(#)fio.c 4.2 8/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: }