1*7775Srrh static char sccsid[] = "@(#)fio.c 4.2 08/17/82";
21333Sbill /*
31333Sbill * sdb - a symbolic debugger for unix - source file access routines.
41333Sbill */
51333Sbill #include "head.h"
61333Sbill #include <stdio.h>
71333Sbill
81333Sbill /*
91333Sbill * These procedures manage the source files examined by sdb,
101333Sbill * providing access to lines by number and utilities for printing
111333Sbill * and scrolling. One file is kept open by these routines, and
121333Sbill * line index tables are maintained for all files which have been
131333Sbill * ``current'' at any time so far. This makes line access trivial,
141333Sbill * since the location of each line in the files is known,
151333Sbill * although we get ``burned'' if the file is changed.
161333Sbill * SHOULD WATCH THE MODTIME OF FILES AND REINDEX IF IT CHANGES.
171333Sbill */
181333Sbill
191333Sbill /*
201333Sbill * Structure for files which have been ``indexed''.
211333Sbill * Contains a pointer to the file name, a pointer to an
221333Sbill * array of seek pointers for the lines in the file,
231333Sbill * and a next link in a chain of these for all files we have indexed,
241333Sbill * The currently open file is cinfo->; the chain of active files is finfo.
251333Sbill */
261333Sbill struct finfo {
271333Sbill char *name; /* name of this file w/o common pfx */
281333Sbill off_t *lines; /* array of seek pointers */
291333Sbill /* line i stretches from lines[i-1] to lines[i] - 1, if first line is 1 */
301333Sbill int nlines; /* number of lines in file */
311333Sbill /* lines array actually has nlines+1 elements, so last line is bracketed */
321333Sbill struct finfo *next; /* link in chain of known files */
331333Sbill } *finfo, *cfile;
341333Sbill FILE *FIO; /* current open file (only 1 now) */
351333Sbill char fibuf[BUFSIZ];
361333Sbill /*
371333Sbill * We use stdio when first reading the file, but thereafter
381333Sbill * use our own routines, because we want to be able
391333Sbill * to read backwards efficiently and avoid a tell() system
401333Sbill * call on each line. Fseekpt remebers where we are in the current
411333Sbill * file.
421333Sbill */
431333Sbill off_t fseekpt;
441333Sbill
451333Sbill /*
461333Sbill * Make ``name'' the current source file, if it isn't already.
471333Sbill * If we have never seen this file before, then we create a finfo
481333Sbill * structure for it indexing the lines (this requires reading the
491333Sbill * entire file and building an index, but is well worth it since
501333Sbill * we otherwise have to brute force search the files all the time.)
511333Sbill */
finit(name)521333Sbill finit(name)
531333Sbill char *name;
541333Sbill {
551333Sbill char buf[BUFSIZ];
561333Sbill register off_t *lp;
571333Sbill
581333Sbill if (cfile && !strcmp(cfile->name, name))
591333Sbill return; /* its already current, do nothing */
601333Sbill /* IT WOULD BE BETTER TO HAVE A COUPLE OF FILE DESCRIPTORS, LRU */
611333Sbill if (FIO) {
621333Sbill fclose(FIO);
631333Sbill FIO = NULL;
641333Sbill }
651333Sbill /*
661333Sbill * Paste the given name onto the common prefix (directory path)
671333Sbill * to form the full name of the file to be opened.
681333Sbill */
691333Sbill strcpy(fp, name);
701333Sbill if ((FIO = fopen(filework, "r")) == NULL) {
711333Sbill nolines = 1;
721333Sbill perror(filework);
731333Sbill return;
741333Sbill }
751333Sbill setbuf(FIO, fibuf);
761333Sbill fseekpt = -BUFSIZ; /* putatively illegal */
771333Sbill strcpy(curfile, name);
781333Sbill /*
791333Sbill * See if we have alread indexed this file.
801333Sbill * If so, nothing much to do.
811333Sbill */
821333Sbill for (cfile = finfo; cfile; cfile = cfile->next)
831333Sbill if (!strcmp(cfile->name, name))
841333Sbill return;
851333Sbill /*
861333Sbill * Create a structure for this (new) file.
871333Sbill * Lines array grows 100 lines at a time.
881333Sbill * 1 extra so last line is bracketed.
891333Sbill */
901333Sbill cfile = (struct finfo *)sbrk(sizeof (struct finfo));
911333Sbill lp = cfile->lines = (off_t *)sbrk(101 * sizeof (off_t));
921333Sbill *lp++ = 0; /* line 1 starts at 0 ... */
931333Sbill cfile->nlines = 0;
941333Sbill /* IT WOULD PROBABLY BE FASTER TO JUST USE GETC AND LOOK FOR \n */
951333Sbill while (fgets(buf, sizeof buf, FIO)) {
961333Sbill if ((++cfile->nlines % 100) == 0)
971333Sbill sbrk(100 * sizeof (off_t));
981333Sbill /*
991333Sbill * Mark end of the cfile->nlines'th line
1001333Sbill */
1011333Sbill lp[0] = lp[-1] + strlen(buf);
1021333Sbill lp++;
1031333Sbill }
1041333Sbill if (cfile->nlines == 0) {
1051333Sbill printf("%s: no lines in file\n", filework);
1061333Sbill cfile = 0;
1071333Sbill return;
1081333Sbill }
1091333Sbill /*
1101333Sbill * Allocate space for the name, making sure to leave the
1111333Sbill * break on a word boundary.
1121333Sbill * IT WOULD BE MUCH BETTER TO USE MALLOC AND REALLOC IN SDB.
1131333Sbill */
1141333Sbill sbrk(lp + ((strlen(name)+sizeof(off_t)-1)&~(sizeof(off_t)-1)));
1151333Sbill strcpy(cfile->name = (char *)lp, name);
1161333Sbill cfile->next = finfo;
1171333Sbill finfo = cfile;
1181333Sbill }
1191333Sbill
1201333Sbill /*
1211333Sbill * Get the current line (fline) into fbuf
1221333Sbill */
fgetline()1231333Sbill fgetline()
1241333Sbill {
1251333Sbill register off_t *op = &cfile->lines[fline-1];
1261333Sbill int o, n;
1271333Sbill
1281333Sbill n = op[1] - op[0];
1291333Sbill fbuf[n] = 0;
1301333Sbill /*
1311333Sbill * Case 1. Line begins in current buffer.
1321333Sbill *
1331333Sbill * Compute the number of characters into the buffer where
1341333Sbill * the line starts. If this offset plus its length is greater
1351333Sbill * than BUFSIZ, then this line splits across a buffer boundary
1361333Sbill * so take the rest of this buffer and the first part of the next.
1371333Sbill * Otherwise just take a chunk of this buffer.
1381333Sbill */
1391333Sbill if (*op >= fseekpt && *op < fseekpt + BUFSIZ) {
1401333Sbill case1:
1411333Sbill o = op[0] - fseekpt;
1421333Sbill if (o + n > BUFSIZ) {
1431333Sbill strncpy(fbuf, fibuf+o, BUFSIZ-o);
1441333Sbill fseekpt += BUFSIZ;
1451333Sbill read(fileno(FIO), fibuf, BUFSIZ);
1461333Sbill strncpy(fbuf+BUFSIZ-o, fibuf, n-(BUFSIZ-o));
1471333Sbill } else
1481333Sbill strncpy(fbuf, fibuf+o, n);
1491333Sbill return;
1501333Sbill }
1511333Sbill /*
1521333Sbill * Case 2. Line ends in current buffer.
1531333Sbill *
1541333Sbill * If the line ends in this buffer (but doesn't begin in
1551333Sbill * it or else we would have had case 1) take the beginning
1561333Sbill * part of the buffer (end of the line) and then back up and
1571333Sbill * get the rest of the line from the end of the previous block.
1581333Sbill */
1591333Sbill if (op[1]-1 >= fseekpt && op[1] <= fseekpt+BUFSIZ) {
1601333Sbill o = op[1] - fseekpt;
1611333Sbill strncpy(fbuf+n-o, fibuf, o);
1621333Sbill fseekpt -= BUFSIZ;
1631333Sbill lseek(fileno(FIO), fseekpt, 0);
1641333Sbill read(fileno(FIO), fibuf, BUFSIZ);
1651333Sbill strncpy(fbuf, fibuf+op[0]-fseekpt, n-o);
1661333Sbill return;
1671333Sbill }
1681333Sbill /*
1691333Sbill * Case 3. Line not in current buffer at all.
1701333Sbill *
1711333Sbill * Read in the buffer where the line starts and then go
1721333Sbill * back and handle as case 1.
1731333Sbill */
1741333Sbill fseekpt = (op[0] / BUFSIZ) * BUFSIZ;
1751333Sbill lseek(fileno(FIO), fseekpt, 0);
1761333Sbill read(fileno(FIO), fibuf, BUFSIZ);
1771333Sbill goto case1;
1781333Sbill }
1791333Sbill
1801333Sbill /*
1811333Sbill * Advance current line, end-around (like for / search).
1821333Sbill */
fnext()1831333Sbill fnext()
1841333Sbill {
1851333Sbill
1861333Sbill if (cfile == 0)
1871333Sbill return;
1881333Sbill if (fline == cfile->nlines) {
1891333Sbill fline = 1;
1901333Sbill } else
1911333Sbill fline++;
1921333Sbill fgetline();
1931333Sbill }
1941333Sbill
1951333Sbill /*
1961333Sbill * Retreat the current line, end around.
1971333Sbill */
fprev()1981333Sbill fprev()
1991333Sbill {
2001333Sbill
2011333Sbill if (cfile == 0)
2021333Sbill return;
2031333Sbill if (fline == 1)
2041333Sbill fline = cfile->nlines;
2051333Sbill else
2061333Sbill fline--;
2071333Sbill fgetline();
2081333Sbill }
2091333Sbill
2101333Sbill /*
2111333Sbill * Print the current line.
2121333Sbill */
fprint()2131333Sbill fprint()
2141333Sbill {
2151333Sbill register char *p;
2161333Sbill
2171333Sbill if (cfile == 0) {
2181333Sbill error("No lines in file");
2191333Sbill return;
2201333Sbill }
2211333Sbill printf("%d: %s", fline, fbuf);
2221333Sbill }
2231333Sbill
2241333Sbill /*
2251333Sbill * Make line `num' current.
2261333Sbill */
ffind(num)2271333Sbill ffind(num)
2281333Sbill register int num;
2291333Sbill {
2301333Sbill
2311333Sbill if (cfile == 0)
2321333Sbill return;
2331333Sbill if (num > cfile->nlines)
2341333Sbill error("Not that many lines in file");
2351333Sbill else if (num <= 0)
2361333Sbill error("Zero or negative line?");
2371333Sbill else {
2381333Sbill fline = num;
2391333Sbill fgetline();
2401333Sbill }
2411333Sbill }
2421333Sbill
2431333Sbill /*
2441333Sbill * Go back n lines.
2451333Sbill */
fback(n)2461333Sbill fback(n)
2471333Sbill {
2481333Sbill int i;
2491333Sbill
2501333Sbill if (cfile == 0)
2511333Sbill return (0);
2521333Sbill if (n > fline - 1)
2531333Sbill n = fline - 1;
2541333Sbill fline -= n;
2551333Sbill fgetline();
2561333Sbill return (n);
2571333Sbill }
2581333Sbill
2591333Sbill /*
2601333Sbill * Go forwards n lines.
2611333Sbill */
fforward(n)2621333Sbill fforward(n)
2631333Sbill int n;
2641333Sbill {
2651333Sbill register int fnext;
2661333Sbill
2671333Sbill if (cfile == 0)
2681333Sbill return(0);
2691333Sbill if (fline + n > cfile->nlines)
2701333Sbill n = cfile->nlines - fline;
2711333Sbill fline += n;
2721333Sbill fgetline();
2731333Sbill return (n);
2741333Sbill }
2751333Sbill
2761333Sbill /*
2771333Sbill * Print (upto) n lines, returning number printed.
2781333Sbill */
fprintn(n)2791333Sbill fprintn(n)
2801333Sbill int n;
2811333Sbill {
2821333Sbill register int i;
2831333Sbill
2841333Sbill if (cfile == 0) {
2851333Sbill error("No lines in file");
2861333Sbill return (0);
2871333Sbill }
2881333Sbill for (i = 1; i <= n; i++) {
2891333Sbill fprint();
2901333Sbill if (fline == cfile->nlines || i == n)
2911333Sbill return(i);
2921333Sbill fnext();
2931333Sbill }
2941333Sbill return (n);
2951333Sbill }
296