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