static char sccsid
[] = "@(#)rdwr.c 4.2 %G%";
* sdb - a symbolic debugger for unix - source file access routines.
* 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.
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 */
FILE *FIO
; /* current open file (only 1 now) */
* 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
* 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.)
if (cfile
&& !strcmp(cfile
->name
, name
))
return; /* its already current, do nothing */
/* IT WOULD BE BETTER TO HAVE A COUPLE OF FILE DESCRIPTORS, LRU */
* Paste the given name onto the common prefix (directory path)
* to form the full name of the file to be opened.
if ((FIO
= fopen(filework
, "r")) == NULL
) {
fseekpt
= -BUFSIZ
; /* putatively illegal */
* 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
))
* 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 ... */
/* 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
);
if (cfile
->nlines
== 0) {
printf("%s: no lines in file\n", filework
);
* 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
);
* Get the current line (fline) into fbuf
register off_t
*op
= &cfile
->lines
[fline
-1];
* 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
) {
strncpy(fbuf
, fibuf
+o
, BUFSIZ
-o
);
read(fileno(FIO
), fibuf
, BUFSIZ
);
strncpy(fbuf
+BUFSIZ
-o
, fibuf
, n
-(BUFSIZ
-o
));
strncpy(fbuf
, fibuf
+o
, n
);
* 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
) {
strncpy(fbuf
+n
-o
, fibuf
, o
);
lseek(fileno(FIO
), fseekpt
, 0);
read(fileno(FIO
), fibuf
, BUFSIZ
);
strncpy(fbuf
, fibuf
+op
[0]-fseekpt
, n
-o
);
* 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
);
* Advance current line, end-around (like for / search).
if (fline
== cfile
->nlines
) {
* Retreat the current line, end around.
* Print the current line.
error("No lines in file");
printf("%d: %s", fline
, fbuf
);
* Make line `num' current.
error("Not that many lines in file");
error("Zero or negative line?");
if (fline
+ n
> cfile
->nlines
)
n
= cfile
->nlines
- fline
;
* Print (upto) n lines, returning number printed.
error("No lines in file");
for (i
= 1; i
<= n
; i
++) {
if (fline
== cfile
->nlines
|| i
== n
)