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