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