/* Copyright (c) 1979 Regents of the University of California */
* This program searches through the specified directory and then
* the directory usrpath(preserve) looking for an instance of the specified
* file from a crashed editor or a crashed system.
* If this file is found, it is unscrambled and written to
* If this program terminates without a "broken pipe" diagnostic
* (i.e. the editor doesn't die right away) then the buffer we are
* writing from is removed when we finish. This is potentially a mistake
* as there is not enough handshaking to guarantee that the file has actually
* been recovered, but should suffice for most cases.
* This directory definition also appears (obviously) in expreserve.c.
* Change both if you change either.
char mydir
[] = usrpath(preserve
);
* Limit on the number of printed entries
* when an, e.g. ``ex -r'' command is given.
int vercnt
; /* Count number of versions of file found */
* Initialize as though the editor had just started.
fendcore
= (line
*) sbrk(0);
dot
= zero
= dol
= fendcore
;
* If given only a -r argument, then list the saved files.
if (argc
== 2 && eq(argv
[1], "-r")) {
error(" Wrong number of arguments to exrecover", 0);
* Got (one of the versions of) it, write it back to the editor.
fprintf(stderr
, " [Dated: %s", cp
);
fprintf(stderr
, vercnt
> 1 ? ", newest of %d saved]" : "]", vercnt
);
* Allocate space for the line pointers from the temp file.
if ((int) sbrk((int) (H
.Flines
* sizeof (line
))) == -1)
error(" Not enough core for lines", 0);
fprintf(stderr
, "%d lines\n", H
.Flines
);
* Now go get the blocks of seek pointers which are scattered
* throughout the temp file, reconstructing the incore
* line pointers at point of crash.
ignorl(lseek(tfile
, (long) blocks
[b
] * BUFSIZ
, 0));
i
= H
.Flines
< BUFSIZ
/ sizeof (line
) ?
H
.Flines
* sizeof (line
) : BUFSIZ
;
if (read(tfile
, (char *) dot
, i
) != i
) {
dot
+= i
/ sizeof (line
);
H
.Flines
-= i
/ sizeof (line
);
* Sigh... due to sandbagging some lines may really not be there.
* Find and discard such. This shouldn't happen much.
* Now if there were any lines in the recovered file
* write them to the standard output.
addr1
= one
; addr2
= dol
; io
= 1;
* Trash the saved buffer.
* Hopefully the system won't crash before the editor
* syncs the new recovered buffer; i.e. for an instant here
* you may lose if the system crashes because this file
* is gone, but the editor hasn't completed reading the recovered
* file from the pipe from us to it.
* This doesn't work if we are coming from an non-absolute path
* name since we may have chdir'ed but what the hay, noone really
* ever edits with temporaries in "." anyways.
* Print an error message (notably not in error
* message file). If terminal is in RAW mode, then
* we should be writing output for "vi", so don't print
* a newline which would screw up the screen.
fprintf(stderr
, str
, inf
);
if ((tty
.sg_flags
& RAW
) == 0)
* Here we save the information about files, when
* you ask us what files we have saved for you.
* We buffer file name, number of lines, and the time
* at which the file was saved.
char sf_name
[FNSIZE
+ 1];
char sf_entry
[DIRSIZ
+ 1];
struct svfile
*fp
, svbuf
[NENTRY
];
* Open usrpath(preserve), and go there to make things quick.
dir
= fopen(dirname
, "r");
if (chdir(dirname
) < 0) {
* Look at the candidate files in usrpath(preserve).
while (fread((char *) &dirent
, sizeof dirent
, 1, dir
) == 1) {
if (dirent
.d_name
[0] != 'E')
fprintf(stderr
, "considering %s\n", dirent
.d_name
);
* Name begins with E; open it and
* make sure the uid in the header is our uid.
* If not, then don't bother with this file, it can't
f
= open(dirent
.d_name
, 0);
fprintf(stderr
, "open failed\n");
if (read(f
, (char *) &H
, sizeof H
) != sizeof H
) {
fprintf(stderr
, "culdnt read hedr\n");
fprintf(stderr
, "uid wrong\n");
enter(fp
++, dirent
.d_name
, ecount
);
fprintf(stderr
, "entered file %s\n", dirent
.d_name
);
* If any files were saved, then sort them and print
fprintf(stderr
, "No files saved.\n");
qsort(&svbuf
[0], ecount
, sizeof svbuf
[0], qucmp
);
for (fp
= &svbuf
[0]; fp
< &svbuf
[ecount
]; fp
++) {
cp
= ctime(&fp
->sf_time
);
fprintf(stderr
, "On %s at ", cp
);
fprintf(stderr
, &cp
[11]);
fprintf(stderr
, " saved %d lines of file \"%s\"\n",
fp
->sf_lines
, fp
->sf_name
);
* Enter a new file into the saved file information.
register struct svfile
*f
, *fl
;
* My god, a huge number of saved files.
* Would you work on a system that crashed this
* often? Hope not. So lets trash the oldest
* (I wonder if this code has ever run?)
fl
= fp
- count
+ NENTRY
- 1;
for (f
= fl
; --f
> fp
-count
; )
if (f
->sf_time
< curtime
)
for (f
= fl
; --f
> fp
-count
; )
if (f
->sf_time
== curtime
)
for (cp2
= fp
->sf_name
, cp
= savedfile
; *cp
;)
for (cp2
= fp
->sf_entry
, cp
= fname
; *cp
&& cp
-fname
< 14;)
* Do the qsort compare to sort the entries first by file name,
if (t
= strcmp(p1
->sf_name
, p2
->sf_name
))
if (p1
->sf_time
> p2
->sf_time
)
return(p1
->sf_time
< p2
->sf_time
);
char bestnb
[BUFSIZ
]; /* Name of the best one */
long besttime
; /* Time at which the best file was saved */
int bestfd
; /* Keep best file open so it dont vanish */
* Look for a file, both in the users directory option value
* (i.e. usually /tmp) and in usrpath(preserve).
* Want to find the newest so we search on and on.
* No name or file so far.
* Search usrpath(preserve) and, if we can get there, /tmp
* (actually the users "directory" option).
* Put the file (which is already open) in the file
* used by the temp file routines, and save its
* name for later unlinking.
ignorl(lseek(tfile
, 0l, 0));
* Gotta be able to read the header or fall through
if (read(tfile
, (char *) &H
, sizeof H
) == sizeof H
)
error(" File not found", 0);
* Search for the file in directory dirname.
* Don't chdir here, because the users directory
* may be ".", and we would move away before we searched it.
* Note that we actually chdir elsewhere (because it is too slow
* to look around in usrpath(preserve) without chdir'ing there) so we
* can't win, because we don't know the name of '.' and if the path
* name of the file we want to unlink is relative, rather than absolute
* we won't be able to find it again.
dir
= fopen(dirname
, "r");
while (fread((char *) &dirent
, sizeof dirent
, 1, dir
) == 1) {
if (dirent
.d_name
[0] != 'E' || dirent
.d_name
[DIRSIZ
- 1] != 0)
* Got a file in the directory starting with E...
* Save a consed up name for the file to unlink
* later, and check that this is really a file
ignore(strcat(strcat(strcpy(nb
, dirname
), "/"), dirent
.d_name
));
* Well, it is the file we are looking for.
* Is it more recent than any version we found before?
* Count versions so user can be told there are
* ``yet more pages to be turned''.
* Given a candidate file to be recovered, see
* if its really an editor temporary and of this
* user and the file specified.
if (read(tfile
, (char *) &H
, sizeof H
) != sizeof H
) {
if (!eq(savedfile
, file
))
* This is old and stupid code, which
* puts a word LOST in the header block, so that lost lines
* can be made to point at it.
ignorl(lseek(tfile
, (long)(BUFSIZ
*HBLKS
-8), 0));
ignore(write(tfile
, "LOST", 5));
* Find the true end of the scratch file, and ``LOSE''
* lines which point into thin air. This lossage occurs
* due to the sandbagging of i/o which can cause blocks to
* be written in a non-obvious order, different from the order
* in which the editor tried to write them.
* Lines which are lost are replaced with the text LOST so
* they are easy to find. We work hard at pretty formatting here
* as lines tend to be lost in blocks.
* This only seems to happen on very heavily loaded systems, and
ignore(fstat(tfile
, &stbuf
));
maxt
= (size
>> SHFT
) | (BNDRY
-1);
bno
= (maxt
>> OFFBTS
) & BLKMSK
;
fprintf(stderr
, "size %ld, maxt %o, bno %d\n", size
, maxt
, bno
);
* Look for a null separating two lines in the temp file;
* if last line was split across blocks, then it is lost
ignorl(lseek(tfile
, (long) BUFSIZ
* bno
, 0));
cnt
= read(tfile
, (char *) bk
, BUFSIZ
);
* Magically calculate the largest valid pointer in the temp file,
* consing it up from the block number and the count.
maxt
= ((bno
<< OFFBTS
) | (cnt
>> SHFT
)) & ~1;
fprintf(stderr
, "bno %d, cnt %d, maxt %o\n", bno
, cnt
, maxt
);
* Now cycle through the line pointers,
for (ip
= one
; ip
<= dol
; ip
++)
fprintf(stderr
, "%d bad, %o > %o\n", ip
- zero
, *ip
, maxt
);
*ip
= ((HBLKS
*BUFSIZ
)-8) >> SHFT
;
fprintf(stderr
, " [Lost line(s):");
fprintf(stderr
, " %d", was
);
if ((ip
- 1) - zero
> was
)
fprintf(stderr
, "-%d", (ip
- 1) - zero
);
fprintf(stderr
, " [Lost line(s):");
fprintf(stderr
, " %d", was
);
fprintf(stderr
, "-%d", dol
- zero
);
* Aw shucks, if we only had a (void) cast.
int cntch
, cntln
, cntodd
, cntnull
;
* Following routines stolen mercilessly from ex.
if (write(io
, genbuf
, nib
) != nib
)
if ((*fp
++ = *lp
++) == 0) {
if (write(io
, genbuf
, nib
) != nib
)
bp
= getblock(tl
+= INCRMT
, READ
);
bno
= (atl
>> OFFBTS
) & BLKMSK
;
off
= (atl
<< SHFT
) & LBTMSK
;
error(" Tmp file too large");
blkio(iblock
, ibuff
, write
);
blkio(oblock
, obuff
, write
);
lseek(tfile
, (long) (unsigned) b
* BUFSIZ
, 0);
if ((*iofcn
)(tfile
, buf
, BUFSIZ
) != BUFSIZ
)
extern char *sys_errlist
[];
if (errno
>= 0 && errno
<= sys_nerr
)
error(sys_errlist
[errno
]);
error("System error %d", errno
);