1: /*************************************************************************
2: * This program is copyright (C) 1985, 1986 by Jonathan Payne. It is *
3: * provided to you without charge for use only on a licensed Unix *
4: * system. You may copy JOVE provided that this notice is included with *
5: * the copy. You may not sell copies of this program or versions *
6: * modified for use on microcomputer systems, unless the copies are *
7: * included with a Unix system distribution and the source is provided. *
8: *************************************************************************/
9:
10: /* Recovers JOVE files after a system/editor crash.
11: Usage: recover [-d directory] [-syscrash]
12: The -syscrash option is specified in /etc/rc and what it does it
13: move all the jove tmp files from TMP_DIR to REC_DIR.
14:
15: The -d option lets you specify the directory to search for tmp files when
16: the default isn't the right one.
17:
18: Look in Makefile to change the default directories. */
19:
20: #include <stdio.h> /* Do stdio first so it doesn't override OUR
21: definitions. */
22: #undef EOF
23: #undef BUFSIZ
24: #undef putchar
25:
26: #include "jove.h"
27: #include "temp.h"
28: #include "rec.h"
29: #include <signal.h>
30: #include <sys/file.h>
31: #include <sys/stat.h>
32: #include <sys/dir.h>
33:
34: #ifndef L_SET
35: # define L_SET 0
36: # define L_INCR 1
37: #endif
38:
39: char blk_buf[BUFSIZ];
40: int nleft;
41: FILE *ptrs_fp;
42: int data_fd;
43: struct rec_head ;
44: char datafile[40],
45: pntrfile[40];
46: long Nchars,
47: Nlines;
48: char tty[] = "/dev/tty";
49: int UserID,
50: Verbose = 0;
51:
52: struct file_pair {
53: char *file_data,
54: *file_rec;
55: #define INSPECTED 01
56: int file_flags;
57: struct file_pair *file_next;
58: } *First = 0,
59: *Last = 0;
60:
61: struct rec_entry *buflist[100] = {0};
62:
63: #ifndef BSD4_2
64:
65: typedef struct {
66: int d_fd; /* File descriptor for this directory */
67: } DIR;
68:
69: DIR *
70: opendir(dir)
71: char *dir;
72: {
73: DIR *dp = (DIR *) malloc(sizeof *dp);
74:
75: if ((dp->d_fd = open(dir, 0)) == -1)
76: return NULL;
77: return dp;
78: }
79:
80: closedir(dp)
81: DIR *dp;
82: {
83: (void) close(dp->d_fd);
84: free(dp);
85: }
86:
87: struct direct *
88: readdir(dp)
89: DIR *dp;
90: {
91: static struct direct dir;
92:
93: do
94: if (read(dp->d_fd, &dir, sizeof dir) != sizeof dir)
95: return NULL;
96: while (dir.d_ino == 0);
97:
98: return &dir;
99: }
100:
101: #endif BSD4_2
102:
103: /* Get a line at `tl' in the tmp file into `buf' which should be LBSIZE
104: long. */
105:
106: char *
107: getline(tl, buf)
108: disk_line tl;
109: char *buf;
110: {
111: register char *bp,
112: *lp;
113: register int nl;
114:
115: lp = buf;
116: bp = getblock(tl);
117: nl = nleft;
118: tl &= ~OFFMSK;
119:
120: while (*lp++ = *bp++) {
121: if (--nl == 0) {
122: /* += INCRMT moves tl to the next block in
123: the tmp file. */
124: bp = getblock(tl += INCRMT);
125: nl = nleft;
126: }
127: }
128: }
129:
130: char *
131: getblock(atl)
132: disk_line atl;
133: {
134: int bno,
135: off;
136: static int curblock = -1;
137:
138: bno = (atl >> OFFBTS) & BLKMSK;
139: off = (atl << SHFT) & LBTMSK;
140: nleft = BUFSIZ - off;
141:
142: if (bno != curblock) {
143: lseek(data_fd, (long) bno * BUFSIZ, L_SET);
144: read(data_fd, blk_buf, BUFSIZ);
145: curblock = bno;
146: }
147: return blk_buf + off;
148: }
149:
150: char *
151: copystr(s)
152: char *s;
153: {
154: char *str;
155:
156: str = malloc(strlen(s) + 1);
157: strcpy(str, s);
158:
159: return str;
160: }
161:
162: /* Scandir returns the number of entries or -1 if the directory cannoot
163: be opened or malloc fails. */
164:
165: scandir(dir, nmptr, qualify, sorter)
166: char *dir;
167: struct direct ***nmptr;
168: int (*qualify)();
169: struct direct *(*sorter)();
170: {
171: DIR *dirp;
172: struct direct *entry,
173: **ourarray;
174: int nalloc = 10,
175: nentries = 0;
176:
177: if ((dirp = opendir(dir)) == NULL)
178: return -1;
179: ourarray = (struct direct **) malloc(nalloc * sizeof (struct direct *));
180: while ((entry = readdir(dirp)) != NULL) {
181: if (qualify != 0 && (*qualify)(entry) == 0)
182: continue;
183: if (nentries == nalloc) {
184: ourarray = (struct direct **) realloc(ourarray, (nalloc += 10) * sizeof (struct direct));
185: if (ourarray == NULL)
186: return -1;
187: }
188: ourarray[nentries] = (struct direct *) malloc(sizeof *entry);
189: *ourarray[nentries] = *entry;
190: nentries++;
191: }
192: closedir(dirp);
193: if (nentries != nalloc)
194: ourarray = (struct direct **) realloc(ourarray,
195: (nentries * sizeof (struct direct)));
196: if (sorter != 0)
197: qsort(ourarray, nentries, sizeof (struct direct **), sorter);
198: *nmptr = ourarray;
199:
200: return nentries;
201: }
202:
203: alphacomp(a, b)
204: struct direct **a,
205: **b;
206: {
207: return strcmp((*a)->d_name, (*b)->d_name);
208: }
209:
210: char *CurDir;
211:
212: /* Scan the DIRNAME directory for jove tmp files, and make a linked list
213: out of them. */
214:
215: get_files(dirname)
216: char *dirname;
217: {
218: int add_name();
219: struct direct **nmptr;
220:
221: CurDir = dirname;
222: scandir(dirname, &nmptr, add_name, (int (*)())0);
223: }
224:
225: add_name(dp)
226: struct direct *dp;
227: {
228: char dfile[128],
229: rfile[128];
230: struct file_pair *fp;
231: struct rec_head header;
232: int fd;
233:
234: if (strncmp(dp->d_name, REC_BASE, strlen(REC_BASE)) != 0)
235: return 0;
236: /* If we get here, we found a "recover" tmp file, so now
237: we look for the corresponding "data" tmp file. First,
238: though, we check to see whether there is anything in
239: the "recover" file. If it's 0 length, there's no point
240: in saving its name. */
241: (void) sprintf(rfile, "%s/%s", CurDir, dp->d_name);
242: (void) sprintf(dfile, "%s/jove%s", CurDir, dp->d_name + strlen(REC_BASE));
243: if ((fd = open(rfile, 0)) != -1) {
244: if ((read(fd, (char *) &header, sizeof header) != sizeof header)) {
245: close(fd);
246: return 0;
247: } else
248: close(fd);
249: }
250: if (access(dfile, 0) != 0) {
251: fprintf(stderr, "recover: can't find the data file for %s/%s\n", TMP_DIR, dp->d_name);
252: fprintf(stderr, "so deleting...\n");
253: (void) unlink(rfile);
254: (void) unlink(dfile);
255: return 0;
256: }
257: /* If we get here, we've found both files, so we put them
258: in the list. */
259: fp = (struct file_pair *) malloc (sizeof *fp);
260: if ((char *) fp == 0) {
261: fprintf(stderr, "recover: cannot malloc for file_pair.\n");
262: exit(-1);
263: }
264: fp->file_data = copystr(dfile);
265: fp->file_rec = copystr(rfile);
266: fp->file_flags = 0;
267: fp->file_next = First;
268: First = fp;
269:
270: return 1;
271: }
272:
273: options()
274: {
275: printf("Options are:\n");
276: printf(" ? list options.\n");
277: printf(" get get a buffer to a file.\n");
278: printf(" list list known buffers.\n");
279: printf(" print print a buffer to terminal.\n");
280: printf(" quit quit and delete jove tmp files.\n");
281: printf(" restore restore all buffers.\n");
282: }
283:
284: /* Returns a legitimate buffer # */
285:
286: struct rec_entry **
287: getsrc()
288: {
289: char name[128];
290: int number;
291:
292: for (;;) {
293: tellme("Which buffer ('?' for list)? ", name);
294: if (name[0] == '?')
295: list();
296: else if (name[0] == '\0')
297: return 0;
298: else if ((number = atoi(name)) > 0 && number <= Header.Nbuffers)
299: return &buflist[number];
300: else {
301: int i;
302:
303: for (i = 1; i <= Header.Nbuffers; i++)
304: if (strcmp(buflist[i]->r_bname, name) == 0)
305: return &buflist[i];
306: printf("%s: unknown buffer.\n", name);
307: }
308: }
309: }
310:
311: /* Get a destination file name. */
312:
313: static char *
314: getdest()
315: {
316: static char filebuf[256];
317:
318: tellme("Output file: ", filebuf);
319: if (filebuf[0] == '\0')
320: return 0;
321: return filebuf;
322: }
323:
324: #include "ctype.h"
325:
326: char *
327: readword(buf)
328: char *buf;
329: {
330: int c;
331: char *bp = buf;
332:
333: while (index(" \t\n", c = getchar()))
334: ;
335:
336: do {
337: if (index(" \t\n", c))
338: break;
339: *bp++ = c;
340: } while ((c = getchar()) != EOF);
341: *bp = 0;
342:
343: return buf;
344: }
345:
346: tellme(quest, answer)
347: char *quest,
348: *answer;
349: {
350: if (stdin->_cnt <= 0) {
351: printf("%s", quest);
352: fflush(stdout);
353: }
354: readword(answer);
355: }
356:
357: /* Print the specified file to strandard output. */
358:
359: jmp_buf int_env;
360:
361: catch()
362: {
363: longjmp(int_env, 1);
364: }
365:
366: restore()
367: {
368: register int i;
369: char tofile[100],
370: answer[30];
371: int nrecovered = 0;
372:
373: for (i = 1; i <= Header.Nbuffers; i++) {
374: (void) sprintf(tofile, "#%s", buflist[i]->r_bname);
375: tryagain:
376: printf("Restoring %s to %s, okay?", buflist[i]->r_bname,
377: tofile);
378: tellme(" ", answer);
379: switch (answer[0]) {
380: case 'y':
381: break;
382:
383: case 'n':
384: continue;
385:
386: default:
387: tellme("What file should I use instead? ", tofile);
388: goto tryagain;
389: }
390: get(&buflist[i], tofile);
391: nrecovered++;
392: }
393: printf("Recovered %d buffers.\n", nrecovered);
394: }
395:
396: get(src, dest)
397: struct rec_entry **src;
398: char *dest;
399: {
400: FILE *outfile;
401:
402: if (src == 0 || dest == 0)
403: return;
404: (void) signal(SIGINT, catch);
405: if (setjmp(int_env) == 0) {
406: if ((outfile = fopen(dest, "w")) == NULL) {
407: printf("recover: cannot create %s.\n", dest);
408: return;
409: }
410: seekto(src - buflist);
411: if (dest != tty)
412: printf("\"%s\"", dest);
413: dump_file(outfile);
414: } else
415: printf("\nAborted!\n");
416: fclose(outfile);
417: if (dest != tty)
418: printf(" %ld lines, %ld characters.\n", Nlines, Nchars);
419: (void) signal(SIGINT, SIG_DFL);
420: }
421:
422: char **
423: scanvec(args, str)
424: register char **args,
425: *str;
426: {
427: while (*args) {
428: if (strcmp(*args, str) == 0)
429: return args;
430: args++;
431: }
432: return 0;
433: }
434:
435: read_rec(recptr)
436: struct rec_entry *recptr;
437: {
438: if (fread((char *) recptr, sizeof *recptr, 1, ptrs_fp) != 1)
439: fprintf(stderr, "recover: cannot read record.\n");
440: }
441:
442: seekto(which)
443: {
444: struct rec_entry rec;
445:
446: fseek(ptrs_fp, (long) (sizeof Header), L_SET);
447:
448: while (which-- > 1) {
449: read_rec(&rec);
450: if (fseek(ptrs_fp, (long) rec.r_nlines * sizeof (disk_line),
451: L_INCR) == -1)
452: printf("recover: improper fseek!\n");
453: }
454: }
455:
456: makblist()
457: {
458: int i;
459:
460: for (i = 1; i <= Header.Nbuffers; i++) {
461: seekto(i);
462: if (buflist[i] == 0)
463: buflist[i] = (struct rec_entry *) malloc (sizeof (struct rec_entry));
464: read_rec(buflist[i]);
465: }
466: if (buflist[i]) {
467: free((char *) buflist[i]);
468: buflist[i] = 0;
469: }
470: }
471:
472: disk_line
473: getaddr(fp)
474: register FILE *fp;
475: {
476: register int nchars = sizeof (disk_line);
477: disk_line addr;
478: register char *cp = (char *) &addr;
479:
480: while (--nchars >= 0)
481: *cp++ = getc(fp);
482:
483: return addr;
484: }
485:
486: dump_file(out)
487: FILE *out;
488: {
489: struct rec_entry record;
490: register int nlines;
491: register disk_line daddr;
492: char buf[BUFSIZ];
493:
494: read_rec(&record);
495: nlines = record.r_nlines;
496: Nchars = Nlines = 0L;
497: while (--nlines >= 0) {
498: daddr = getaddr(ptrs_fp);
499: getline(daddr, buf);
500: Nlines++;
501: Nchars += 1 + strlen(buf);
502: fputs(buf, out);
503: if (nlines > 0)
504: fputc('\n', out);
505: }
506: if (out != stdout)
507: fclose(out);
508: }
509:
510: /* List all the buffers. */
511:
512: list()
513: {
514: int i;
515:
516: for (i = 1; i <= Header.Nbuffers; i++)
517: printf("%d) buffer %s \"%s\" (%d lines)\n", i,
518: buflist[i]->r_bname,
519: buflist[i]->r_fname,
520: buflist[i]->r_nlines);
521: }
522:
523: doit(fp)
524: struct file_pair *fp;
525: {
526: char answer[30];
527: char *datafile = fp->file_data,
528: *pntrfile = fp->file_rec;
529:
530: ptrs_fp = fopen(pntrfile, "r");
531: if (ptrs_fp == NULL) {
532: if (Verbose)
533: fprintf(stderr, "recover: cannot read rec file (%s).\n", pntrfile);
534: return 0;
535: }
536: fread((char *) &Header, sizeof Header, 1, ptrs_fp);
537: if (Header.Uid != UserID)
538: return 0;
539:
540: /* Don't ask about JOVE's that are still running ... */
541: #ifdef KILL0
542: if (kill(Header.Pid, 0) == 0)
543: return 0;
544: #else
545: #ifdef LSRHS
546: if (pexist(Header.Pid))
547: return 0;
548: #endif LSRHS
549: #endif KILL0
550:
551: if (Header.Nbuffers == 0) {
552: printf("There are no modified buffers in %s; should I delete the tmp file?", pntrfile);
553: ask_del(" ", fp);
554: return 1;
555: }
556:
557: if (Header.Nbuffers < 0) {
558: fprintf(stderr, "recover: %s doesn't look like a jove file.\n", pntrfile);
559: ask_del("Should I delete it? ", fp);
560: return 1; /* We'll, we sort of found something. */
561: }
562: printf("Found %d buffer%s last updated: %s",
563: Header.Nbuffers,
564: Header.Nbuffers != 1 ? "s" : "",
565: ctime(&Header.UpdTime));
566: data_fd = open(datafile, 0);
567: if (data_fd == -1) {
568: fprintf(stderr, "recover: but I can't read the data file (%s).\n", datafile);
569: ask_del("Should I delete the tmp files? ", fp);
570: return 1;
571: }
572: makblist();
573:
574: for (;;) {
575: tellme("(Type '?' for options): ", answer);
576: switch (answer[0]) {
577: case '\0':
578: continue;
579:
580: case '?':
581: options();
582: break;
583:
584: case 'l':
585: list();
586: break;
587:
588: case 'p':
589: get(getsrc(), tty);
590: break;
591:
592: case 'q':
593: ask_del("Shall I delete the tmp files? ", fp);
594: return 1;
595:
596: case 'g':
597: { /* So it asks for src first. */
598: char *dest;
599: struct rec_entry **src;
600:
601: if ((src = getsrc()) == 0)
602: break;
603: dest = getdest();
604: get(src, dest);
605: break;
606: }
607:
608: case 'r':
609: restore();
610: break;
611:
612: default:
613: printf("I don't know how to \"%s\"!\n", answer);
614: break;
615: }
616: }
617: }
618:
619: ask_del(prompt, fp)
620: char *prompt;
621: struct file_pair *fp;
622: {
623: char yorn[20];
624:
625: tellme(prompt, yorn);
626: if (yorn[0] == 'y')
627: del_files(fp);
628: }
629:
630: del_files(fp)
631: struct file_pair *fp;
632: {
633: (void) unlink(fp->file_data);
634: (void) unlink(fp->file_rec);
635: }
636:
637: savetmps()
638: {
639: struct file_pair *fp;
640: int status,
641: pid;
642:
643: if (strcmp(TMP_DIR, REC_DIR) == 0)
644: return; /* Files are moved to the same place. */
645: get_files(TMP_DIR);
646: for (fp = First; fp != 0; fp = fp->file_next) {
647: switch (pid = fork()) {
648: case -1:
649: fprintf(stderr, "recover: can't fork\n!");
650: exit(-1);
651:
652: case 0:
653: execl("/bin/cp", "cp", fp->file_data, fp->file_rec, REC_DIR, 0);
654: fprintf(stderr, "recover: cannot execl /bin/cp.\n");
655: exit(-1);
656:
657: default:
658: while (wait(&status) != pid)
659: ;
660: if (status != 0)
661: fprintf(stderr, "recover: non-zero status (%d) returned from copy.\n", status);
662: }
663: }
664: }
665:
666: lookup(dir)
667: char *dir;
668: {
669: struct file_pair *fp;
670: struct rec_head header;
671: char yorn[20];
672: int nfound = 0,
673: this_one;
674:
675: get_files(dir);
676: for (fp = First; fp != 0; fp = fp->file_next) {
677: nfound += doit(fp);
678: if (ptrs_fp)
679: (void) fclose(ptrs_fp);
680: if (data_fd > 0)
681: (void) close(data_fd);
682: }
683: return nfound;
684: }
685:
686: main(argc, argv)
687: int argc;
688: char *argv[];
689: {
690: int nfound;
691: char **argvp;
692:
693: UserID = getuid();
694:
695: if (scanvec(argv, "-help")) {
696: printf("recover: usage: recover [-d directory] [-syscrash]\n");
697: printf("Use \"recover\" after JOVE has died for some\n");
698: printf("unknown reason.\n\n");
699: printf("Use \"recover -syscrash\" when the system is in the process\n");
700: printf("of rebooting. This is done automatically at reboot time\n");
701: printf("and so most of you don't have to worry about that.\n\n");
702: printf("Use \"recover -d directory\" when the tmp files are store\n");
703: printf("in DIRECTORY instead of the default one (%s).\n", TMP_DIR);
704: exit(0);
705: }
706: if (scanvec(argv, "-v"))
707: Verbose++;
708: if (scanvec(argv, "-syscrash")) {
709: printf("Recovering jove files ... ");
710: savetmps();
711: printf("Done.\n");
712: exit(0);
713: }
714: if (argvp = scanvec(argv, "-uid"))
715: UserID = atoi(argvp[1]);
716: if (argvp = scanvec(argv, "-d"))
717: nfound = lookup(argvp[1]);
718: else {
719: if ((nfound = lookup(REC_DIR)) ||
720: (nfound = lookup(TMP_DIR)))
721: ;
722: }
723: if (nfound == 0)
724: printf("There's nothing to recover.\n");
725: }