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