static char sccsid[] = "@(#)fio.c 4.2 8/17/82"; /* * sdb - a symbolic debugger for unix - source file access routines. */ #include "head.h" #include /* * These procedures manage the source files examined by sdb, * providing access to lines by number and utilities for printing * and scrolling. One file is kept open by these routines, and * line index tables are maintained for all files which have been * ``current'' at any time so far. This makes line access trivial, * since the location of each line in the files is known, * although we get ``burned'' if the file is changed. * SHOULD WATCH THE MODTIME OF FILES AND REINDEX IF IT CHANGES. */ /* * Structure for files which have been ``indexed''. * Contains a pointer to the file name, a pointer to an * array of seek pointers for the lines in the file, * and a next link in a chain of these for all files we have indexed, * The currently open file is cinfo->; the chain of active files is finfo. */ struct finfo { char *name; /* name of this file w/o common pfx */ off_t *lines; /* array of seek pointers */ /* line i stretches from lines[i-1] to lines[i] - 1, if first line is 1 */ int nlines; /* number of lines in file */ /* lines array actually has nlines+1 elements, so last line is bracketed */ struct finfo *next; /* link in chain of known files */ } *finfo, *cfile; FILE *FIO; /* current open file (only 1 now) */ char fibuf[BUFSIZ]; /* * We use stdio when first reading the file, but thereafter * use our own routines, because we want to be able * to read backwards efficiently and avoid a tell() system * call on each line. Fseekpt remebers where we are in the current * file. */ off_t fseekpt; /* * Make ``name'' the current source file, if it isn't already. * If we have never seen this file before, then we create a finfo * structure for it indexing the lines (this requires reading the * entire file and building an index, but is well worth it since * we otherwise have to brute force search the files all the time.) */ finit(name) char *name; { char buf[BUFSIZ]; register off_t *lp; if (cfile && !strcmp(cfile->name, name)) return; /* its already current, do nothing */ /* IT WOULD BE BETTER TO HAVE A COUPLE OF FILE DESCRIPTORS, LRU */ if (FIO) { fclose(FIO); FIO = NULL; } /* * Paste the given name onto the common prefix (directory path) * to form the full name of the file to be opened. */ strcpy(fp, name); if ((FIO = fopen(filework, "r")) == NULL) { nolines = 1; perror(filework); return; } setbuf(FIO, fibuf); fseekpt = -BUFSIZ; /* putatively illegal */ strcpy(curfile, name); /* * See if we have alread indexed this file. * If so, nothing much to do. */ for (cfile = finfo; cfile; cfile = cfile->next) if (!strcmp(cfile->name, name)) return; /* * Create a structure for this (new) file. * Lines array grows 100 lines at a time. * 1 extra so last line is bracketed. */ cfile = (struct finfo *)sbrk(sizeof (struct finfo)); lp = cfile->lines = (off_t *)sbrk(101 * sizeof (off_t)); *lp++ = 0; /* line 1 starts at 0 ... */ cfile->nlines = 0; /* IT WOULD PROBABLY BE FASTER TO JUST USE GETC AND LOOK FOR \n */ while (fgets(buf, sizeof buf, FIO)) { if ((++cfile->nlines % 100) == 0) sbrk(100 * sizeof (off_t)); /* * Mark end of the cfile->nlines'th line */ lp[0] = lp[-1] + strlen(buf); lp++; } if (cfile->nlines == 0) { printf("%s: no lines in file\n", filework); cfile = 0; return; } /* * Allocate space for the name, making sure to leave the * break on a word boundary. * IT WOULD BE MUCH BETTER TO USE MALLOC AND REALLOC IN SDB. */ sbrk(lp + ((strlen(name)+sizeof(off_t)-1)&~(sizeof(off_t)-1))); strcpy(cfile->name = (char *)lp, name); cfile->next = finfo; finfo = cfile; } /* * Get the current line (fline) into fbuf */ fgetline() { register off_t *op = &cfile->lines[fline-1]; int o, n; n = op[1] - op[0]; fbuf[n] = 0; /* * Case 1. Line begins in current buffer. * * Compute the number of characters into the buffer where * the line starts. If this offset plus its length is greater * than BUFSIZ, then this line splits across a buffer boundary * so take the rest of this buffer and the first part of the next. * Otherwise just take a chunk of this buffer. */ if (*op >= fseekpt && *op < fseekpt + BUFSIZ) { case1: o = op[0] - fseekpt; if (o + n > BUFSIZ) { strncpy(fbuf, fibuf+o, BUFSIZ-o); fseekpt += BUFSIZ; read(fileno(FIO), fibuf, BUFSIZ); strncpy(fbuf+BUFSIZ-o, fibuf, n-(BUFSIZ-o)); } else strncpy(fbuf, fibuf+o, n); return; } /* * Case 2. Line ends in current buffer. * * If the line ends in this buffer (but doesn't begin in * it or else we would have had case 1) take the beginning * part of the buffer (end of the line) and then back up and * get the rest of the line from the end of the previous block. */ if (op[1]-1 >= fseekpt && op[1] <= fseekpt+BUFSIZ) { o = op[1] - fseekpt; strncpy(fbuf+n-o, fibuf, o); fseekpt -= BUFSIZ; lseek(fileno(FIO), fseekpt, 0); read(fileno(FIO), fibuf, BUFSIZ); strncpy(fbuf, fibuf+op[0]-fseekpt, n-o); return; } /* * Case 3. Line not in current buffer at all. * * Read in the buffer where the line starts and then go * back and handle as case 1. */ fseekpt = (op[0] / BUFSIZ) * BUFSIZ; lseek(fileno(FIO), fseekpt, 0); read(fileno(FIO), fibuf, BUFSIZ); goto case1; } /* * Advance current line, end-around (like for / search). */ fnext() { if (cfile == 0) return; if (fline == cfile->nlines) { fline = 1; } else fline++; fgetline(); } /* * Retreat the current line, end around. */ fprev() { if (cfile == 0) return; if (fline == 1) fline = cfile->nlines; else fline--; fgetline(); } /* * Print the current line. */ fprint() { register char *p; if (cfile == 0) { error("No lines in file"); return; } printf("%d: %s", fline, fbuf); } /* * Make line `num' current. */ ffind(num) register int num; { if (cfile == 0) return; if (num > cfile->nlines) error("Not that many lines in file"); else if (num <= 0) error("Zero or negative line?"); else { fline = num; fgetline(); } } /* * Go back n lines. */ fback(n) { int i; if (cfile == 0) return (0); if (n > fline - 1) n = fline - 1; fline -= n; fgetline(); return (n); } /* * Go forwards n lines. */ fforward(n) int n; { register int fnext; if (cfile == 0) return(0); if (fline + n > cfile->nlines) n = cfile->nlines - fline; fline += n; fgetline(); return (n); } /* * Print (upto) n lines, returning number printed. */ fprintn(n) int n; { register int i; if (cfile == 0) { error("No lines in file"); return (0); } for (i = 1; i <= n; i++) { fprint(); if (fline == cfile->nlines || i == n) return(i); fnext(); } return (n); }