xref: /csrg-svn/old/sdb/fio.c (revision 7775)
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