/************************************************************************ * This program is Copyright (C) 1986 by Jonathan Payne. JOVE is * * provided to you without charge, and with no warranty. You may give * * away copies of JOVE, including sources, provided that this notice is * * included in all the files. * ************************************************************************/ #include "jove.h" RCS("$Id: rec.c,v 14.31.0.12 1994/06/23 02:55:32 tom Exp tom $") #include "io.h" #define Extern public #include "rec.h" private int rec_fd ZERO; private File *rec_out; private struct rec_head Header; void recclose() { register File *fp; if ((fp = rec_out) == NULL) return; unlink(fp->f_name); f_close(fp); #if 0 rec_out = NULL; rec_fd = 0; #endif } /* Goes through all the buffers and syncs them to the disk. */ DEF_INT( "sync-frequency", SyncFreq, V_BASE10 ) = 50; void SyncRec() { register Buffer *b; register File *fp; if ((fp = rec_out) == NULL) { /* Init recover file. */ char buf[FILESIZE]; register char *fname; if (rec_fd != 0) /* we tried it already, to no avail */ return; #ifdef DOS /* for slow systems, show what's going on */ { extern int ILI; i_set(ILI, 0); swrite("(creating tmp file)", NO); flusho(); updmesg(); /* to restore original mesg */ } #endif fname = mktemp(make_filename(buf, TmpFilePath, p_tempfile)); if ((rec_fd = creat(fname, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)) < 0 || (rec_out = fp = fd_open(fname, F_WRITE|F_LOCKED|F_BINARY, rec_fd, iobuff, BLKSIZ)) == NULL) complain("Cannot create \"%s\"; recovery disabled.", fname); /* Initialize the record header. */ Header.Uid = getuid(); Header.Pid = getpid(); } f_rewind(fp); time(&Header.UpdTime); Header.Nbuffers = 0; if (b = world) do { /* count buffers, */ if (b->b_type == B_SCRATCH || !IsModified(b)) continue; Header.Nbuffers++; } while (b = b->b_next); fputnchar((char *) &Header, sizeof Header, fp); if (Header.Nbuffers) { struct rec_entry record; register Line *lp; lsave(); /* !!! */ SyncTmp(); b = world; do { if (b->b_type == B_SCRATCH || !IsModified(b)) continue; /* dump the buffer info and then the actual line pointers. */ bzero((char *) &record, sizeof record); if (b->b_fname) strcpy(record.r_fname, b->b_fname); strcpy(record.r_bname, b->b_name); record.r_nlines = lineno(b->b_last); #ifdef FULLRECOVER record.r_dot = lineno(b->b_dot); record.r_char = b->b_char; ino_cpy(record.r_ino, b->b_ino); record.r_dev = b->b_dev; record.r_mtime = b->b_mtime; record.r_statsize = b->b_statsize; record.r_type = b->b_type; if (b == curbuf) record.r_curbuf++; record.r_major = b->b_major; record.r_minor = b->b_minor; #endif /* FULLRECOVER */ fputnchar((char *) &record, sizeof record, fp); if (lp = b->b_first) do { fputnchar((char *) &lp->l_dline, sizeof lp->l_dline, fp); } while (lp = lp->l_next); } while (b = b->b_next); } flush(fp); } #ifdef FULLRECOVER /* the rest of this file */ #include "ctype.h" #include "process.h" #include "readdir.h" #include "temp.h" extern char *ctime __(( const time_t *_(time) )); private int do_fullrecover __(( File *_(rec_inf), int _(data_fd), int _(allp) )); private int do_fullrecover(rec_inf, data_fd, allp) register File *rec_inf; int data_fd; { struct rec_head header; char iobuff[BLKSIZ]; int curblock = -1; #if !unix int lastblock = lseek(data_fd, 0L, SEEK_END) >> BNO_SHIFT; #endif int nbuffers = 0; f_rewind(rec_inf); if (fgetnchar(&header, sizeof header, rec_inf) != NIL) { message("[Cannot read header]"); return EOF; } while (--header.Nbuffers >= 0) { struct rec_entry record; disk_line bad_line; register Buffer *b; if (fgetnchar(&record, sizeof record, rec_inf) != NIL) { message("[Cannot read buffer record]"); return EOF; } if (record.r_nlines == 0) continue; { /* make sure we get a fresh buffer */ register char *bname = record.r_bname; char bnamebuf[BNAMESIZE]; register int try = 0; if (!allp && !yes_or_no_p('Y', "Recover %s \"%s\"? ", bname, record.r_fname[0] ? record.r_fname : "[No file]")) { /* skip to next buffer record */ do { disk_line d; if (fgetnchar(&d, sizeof d, rec_inf) != NIL) { message("[Cannot skip to next record]"); return EOF; } } while (--record.r_nlines); continue; } while ((b = buf_exists(bname)) && /* b_empty(b) can't be used since assumes b == curbuf */ !(lastp(b, b->b_first) && lcontents(b->b_dot)[0] == '\0' && !b->b_ntbf)) sprintf(bname = bnamebuf, "%s'%d", record.r_bname, ++try); b = do_select((Window *)0, bname); } if (record.r_fname[0]) setfname(b, record.r_fname); f_mess("Recovering %b \"%s\"", b, filename(b)); b->b_modified++; /* now, read the lines */ do { disk_line addr; register int bno; if (fgetnchar(&addr, sizeof addr, rec_inf) != NIL) { b->b_modified = NO; SavLine(b->b_last, "[Unexpected EOF]"); SetLine(b->b_last); message("[Unexpected EOF]"); return EOF; } addr >>= 1; if ((bno = daddr2bno(addr)) != curblock) { register long offset = (long) bno << BNO_SHIFT; #if !unix if ((unsigned) bno >= lastblock) addr = 0; else #endif if (lseek(data_fd, offset, SEEK_SET) != offset) addr = 0; else if (read(data_fd, iobuff, BLKSIZ) != BLKSIZ) { curblock = -1; addr = 0; } else curblock = bno; } if (addr == 0) { if (b->b_modified) { b->b_modified = NO; bad_line = putline("~~[Bad line]~~"); } b->b_last->l_dline = bad_line; } else { SavLine(b->b_last, iobuff + daddr2off(addr)); } if (--record.r_dot == 0) { b->b_dot = b->b_last; b->b_char = record.r_char; } } while (--record.r_nlines && (b->b_last = listput(b, b->b_last), YES)); nbuffers++; if (!IsModified(b)) { b->b_last = listput(b, b->b_last); SavLine(b->b_last, "~~[Buffer is corrupted]~~"); SetLine(b->b_last); continue; } ino_cpy(b->b_ino, record.r_ino); b->b_dev = record.r_dev; b->b_mtime = record.r_mtime; SETBUFTYPE(b, record.r_type); b->b_major = record.r_major; b->b_minor = record.r_minor; if (record.r_curbuf) { SetBuf(b); tiewind(curwind, b); } } s_mess("Recovered %d buffer%n.", nbuffers); return NIL; } private int rec_list __(( File *_(rec_inf) )); private int rec_list(rec_inf) register File *rec_inf; { struct rec_head header; register int i = 0; register char *timestr; f_rewind(rec_inf); if (fgetnchar(&header, sizeof header, rec_inf) != NIL) { Typeout("[Cannot read header]"); return EOF; } timestr = ctime(&header.UpdTime); timestr[strlen(timestr) - 1] = '\0'; Typeout("Found %d buffer%n last updated: %s", header.Nbuffers, timestr); Typeout((char *) 0); Typeout("NO Lines %-14s File", "Name"); Typeout("-- ----- %-14s ----", "----"); while (++i <= header.Nbuffers) { struct rec_entry record; if (fgetnchar(&record, sizeof record, rec_inf) != NIL) { Typeout("[cannot read buffer record]"); return EOF; } Typeout("%-2d %-5d %-14s %s", i, record.r_nlines, record.r_bname, record.r_fname[0] ? record.r_fname : "[No file]"); if (TOabort) break; /* skip to next record. (lseek should be faster) */ while (--record.r_nlines >= 0) { disk_line dummy; if (fgetnchar(&dummy, sizeof dummy, rec_inf) != NIL) { Typeout("[Cannot skip to next record]"); return EOF; } } } return NIL; } private void cleanup __(( const char *_(prompt), const char *_(file1), const char *_(file2) )); private void cleanup(prompt, file1, file2) const char *prompt, *file1, *file2; { if (yes_or_no_p(NIL, "%s \"%s\", delete? ", prompt, file1)) { unlink(file1); if (file2) unlink(file2); } } #ifdef REC_DIR private const char *RecDir; #else # define RecDir TmpFilePath #endif private int rec_match __(( const char *_(name) )); private int rec_match(name) const char *name; { extern time_t time0; struct rec_head header; char rname[FILESIZE], dname[FILESIZE]; register int i; #define build_other(fnamebuf, template) do { \ register const char *s = template; \ register char *d = basename(fnamebuf); \ while (--i >= 0) \ *d++ = *s++; \ } while (0) strcpy(dname, make_filename(rname, RecDir, name)); if (d_tempfile[i = numcomp(d_tempfile, name)] == 'X') { /* found a datafile: check that we can read the corresponding record file; if not, ask to remove the file if it is ours and it is more than a day old. (this way we minimize the risk of deleting a datafile from under a still-active JOVE) */ build_other(rname, p_tempfile); if (access(rname, R_OK) != 0) { struct stat stbuf; if (stat(dname, &stbuf) == 0 && stbuf.st_uid == getuid() && stbuf.st_atime < time0 - 24*60*60L) { cleanup("Can't find the record file for", dname, (char *)0); } } return NO; } if (p_tempfile[i = numcomp(p_tempfile, name)] == 'X') { /* if we found a record file, check that we can read its header; if not, ask to remove it. Then check that it is ours, and that it does not belong to an active JOVE (for instance, ourselves), and that the corresponding datafile exists. */ register int fd; build_other(dname, d_tempfile); if ((fd = open(rname, O_RDONLY)) < 0) return NO; i = read(fd, &header, sizeof header); close(fd); if (i != sizeof header || header.Nbuffers < 0) { cleanup("Malformed record file", rname, dname); return NO; } if (header.Uid != getuid()) return NO; /* Don't accept if this is our own record file. {check time to avoid stray pid coincidences; only 1 in 30000 chance on most UNIX systems, but mandatory for some others like VMS} */ if (header.Pid == getpid() && header.UpdTime >= time0) return NO; #if unix # ifdef LSRHS if (pexist(header.Pid)) return NO; # else # ifdef KILL0 if (kill(header.Pid, 0) == 0) return NO; # else if (kill(header.Pid, SIGALRM) == 0) return NO; # endif # endif #endif if (access(dname, R_OK) != 0) { cleanup("Can't find the datafile for", rname, (char *)0); return NO; } if (header.Nbuffers == 0) { cleanup("No modified buffers in", rname, dname); return NO; } return YES; } return NO; } private int do_recdir __(( const char *_(rec_dir) )); private int do_recdir(rec_dir) const char *rec_dir; { char **dir_vec; int nentries; int ent; #ifdef REC_DIR RecDir = rec_dir; #endif #ifdef CHDIR # ifndef TINY ask_dir_only--; # endif #endif if ((nentries = scandir(rec_dir, &dir_vec, rec_match, (int (*)()) 0)) < 0) { message(IOerr("scan directory", rec_dir)); return 0; } for (ent = 0; ent < nentries; ent++) { char rname[FILESIZE], dname[FILESIZE]; File *rec_inf; int data_fd; int i = numcomp(p_tempfile, dir_vec[ent]); strcpy(dname, make_filename(rname, rec_dir, dir_vec[ent])); build_other(dname, d_tempfile); if (rec_inf = open_file(rname, iobuff, F_READ|F_BINARY)) { if ((data_fd = open(dname, O_RDONLY)) < 0) { message(IOerr("read datafile", dname)); } else { TOstart("*Recover*", TRUE); rec_list(rec_inf); Typeout((char *)0); Typeout("[recover N)one, S)ome, A)ll]"); TOstop(); message("[recover N)one, S)ome, A)ll]"); i = toupper(getch()); if (i != 'N' && i != AbortChar) do_fullrecover(rec_inf, data_fd, i-'S'); close(data_fd); } f_close(rec_inf); } cleanup(strcpy(genbuf, mesgbuf), rname, dname); } freedir(&dir_vec, nentries); return nentries; } DEF_CMD( "recover", FullRecover, NO ) _IF(def FULLRECOVER) { char filebuf[FILESIZE]; const char *tmp_dir = TmpFilePath; int nfound; if (exp_p) { #ifdef CHDIR # ifndef TINY ask_dir_only = YES; # endif #endif tmp_dir = ask_file((char *) 0, tmp_dir, filebuf); } f_mess(ProcArgFmt, tmp_dir); nfound = do_recdir(tmp_dir); #ifdef REC_DIR if (strcmp(tmp_dir, REC_DIR) != 0) { f_mess("%N: %f %s, %s", tmp_dir, REC_DIR); nfound += do_recdir(REC_DIR); } #endif if (nfound == 0) add_mess(" (Nothing to recover.)"); } #endif /* FULLRECOVER */ /*====================================================================== * $Log: rec.c,v $ * Revision 14.31.0.12 1994/06/23 02:55:32 tom * (do_recdir): fix name of scratch buffer; * (do_fullrecover): fix uniquized buffer names. * * Revision 14.31 1993/02/17 01:25:25 tom * remove (void) casts; lotsa random optimizations. * * Revision 14.30 1993/02/06 00:48:32 tom * cleanup whitespace; some random optimizations; some more Posix-ation. * * Revision 14.27 1992/09/22 02:03:53 tom * add statsize field to header record. * * Revision 14.26 1992/08/26 23:56:58 tom * add RCS directives. * */