1 /************************************************************************
2 * This program is Copyright (C) 1986-1996 by Jonathan Payne. JOVE is *
3 * 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. It directs recover to
11 move all the jove tmp files from tmp_dir (/tmp) to RECDIR (/usr/preserve).
12 recover -syscrash must be invoked in /etc/rc BEFORE /tmp gets cleared out.
13 (about the same place as expreserve gets invoked to save ed/vi/ex files.
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 #include "jove.h"
23
24 #define SMALLSTRSIZE 30 /* used for small buffers */
25
26 #ifndef RECOVER
27
28 int
main(argc,argv)29 main(argc, argv)
30 int argc;
31 char *argv[];
32 {
33 printf("recovery is not implemented in this JOVE configuration.\n");
34 return 1;
35 }
36
37 #else /* RECOVER */ /* the body is the rest of this file */
38
39 #include "temp.h"
40 #include "sysprocs.h"
41 #include "rec.h"
42 #include "paths.h"
43
44 #include "recover.h"
45
46 #ifdef UNIX
47 # include <signal.h>
48 # include <sys/file.h>
49 # include <pwd.h>
50 # include <time.h>
51 #endif
52
53 #ifdef USE_UNAME
54 # include <sys/utsname.h>
55 #endif
56
57 /* Strictly speaking, popen is not available in stdio.h in POSIX.1 or ANSI-C.
58 * It is part of POSIX.2, and declared incorrectly in OSF/1, so we suppress
59 * it for OSF.
60 */
61 #ifndef _OSF_SOURCE
62 extern FILE *popen proto((const char *, const char *));
63 #endif
64
65 #ifndef FULL_UNISTD
66
67 /* The parameter of getpwuid is widened uid_t,
68 * but there no easy portable way to write this
69 */
70 extern struct passwd *getpwuid(/*widened uid_t*/);
71 extern char *ctime proto((const time_t *));
72 extern void perror proto((const char *));
73
74 # ifdef USE_UNAME
75 extern int uname proto((struct utsname *));
76 # endif
77
78 # ifdef USE_GETHOSTNAME
79 extern int gethostname proto((const char *, size_t));
80 # endif
81
82 #endif /* !FULL_UNISTD */
83
84 #ifndef L_SET
85 # define L_SET 0
86 # define L_INCR 1
87 #endif
88
89 private char blk_buf[JBUFSIZ];
90 private int nleft;
91 private FILE *ptrs_fp;
92 private int data_fd;
93 private struct rec_head Header;
94 private long Nchars,
95 Nlines;
96 private char tty[] = "/dev/tty";
97 private char *tmp_dir = TMPDIR;
98 private int UserID;
99 private bool Verbose = NO;
100 private char RecDir[] = RECDIR;
101
102 private struct file_pair {
103 char *file_data,
104 *file_rec;
105 #define INSPECTED 01
106 int file_flags;
107 struct file_pair *file_next;
108 } *First = NULL;
109
110 private struct rec_entry *buflist[100]; /* system initializes to 0 */
111
112 #ifndef F_COMPLETION
113 # define F_COMPLETION /* since scandir.c is surrounded by an ifdef */
114 #endif
115
116 /* simpler version of one in util.c, needed by scandir.c */
117 UnivPtr
emalloc(size)118 emalloc(size)
119 size_t size;
120 {
121 register UnivPtr ptr;
122
123 if ((ptr = malloc(size)) == NULL) {
124 fprintf(stderr, "couldn't malloc(%ld)\n", (long)size);
125 exit(1);
126 }
127 return ptr;
128 }
129
130 /* simpler version of one in util.c, needed by scandir.c */
131 UnivPtr
erealloc(ptr,size)132 erealloc(ptr, size)
133 UnivPtr ptr;
134 size_t size;
135 {
136 if ((ptr = realloc(ptr, size)) == NULL) {
137 fprintf(stderr, "couldn't realloc(%ld)\n", (long)size);
138 exit(1);
139 }
140 return ptr;
141 }
142
143 /* duplicated in util.c, needed by scandir.c */
144 void
null_ncpy(to,from,n)145 null_ncpy(to, from, n)
146 char *to;
147 const char *from;
148 size_t n;
149 {
150 (void) strncpy(to, from, n);
151 to[n] = '\0';
152 }
153
154 #define complain printf /* kludge! needed by scandir.c */
155 #include "scandir.c" /* to get dirent simulation and jscandir */
156
157 /* Get a line at `tl' in the tmp file into `buf' which should be LBSIZE
158 long. */
159
160 private char *getblock proto((daddr atl));
161
162 void
get_line(tl,buf)163 get_line(tl, buf)
164 daddr tl;
165 char *buf;
166 {
167 register char *bp,
168 *lp;
169 register int nl;
170
171 lp = buf;
172 bp = getblock(tl);
173 nl = nleft;
174
175 while ((*lp++ = *bp++) != '\0') {
176 if (--nl == 0) {
177 /* oops: line didn't end within block: fake it */
178 *lp++ = '\0';
179 break;
180 }
181 }
182 }
183
184 private char *
getblock(atl)185 getblock(atl)
186 daddr atl;
187 {
188 int bno,
189 off;
190 static int curblock = -1;
191
192 bno = da_to_bno(atl);
193 off = da_to_off(atl);
194 nleft = JBUFSIZ - off;
195
196 if (bno != curblock) {
197 lseek(data_fd, (long) bno * JBUFSIZ, L_SET);
198 read(data_fd, (UnivPtr)blk_buf, (size_t)JBUFSIZ);
199 curblock = bno;
200 }
201 return blk_buf + off;
202 }
203
204 char *
copystr(s)205 copystr(s)
206 const char *s;
207 {
208 char *str;
209
210 str = malloc((size_t) (strlen(s) + 1));
211 if (str == NULL) {
212 fprintf(stderr, "recover: cannot malloc for copystr.\n");
213 exit(-1);
214 }
215 strcpy(str, s);
216
217 return str;
218 }
219
220 private char *CurDir;
221
222 /* Scan the DIRNAME directory for jove tmp files, and make a linked list
223 out of them. */
224
225 private bool add_name proto((char *));
226
227 private void
get_files(dirname)228 get_files(dirname)
229 char *dirname;
230 {
231 char **nmptr;
232
233 CurDir = dirname;
234 First = NULL;
235 jscandir(dirname, &nmptr, add_name,
236 (int (*) ptrproto((UnivConstPtr, UnivConstPtr)))NULL);
237 }
238
239 private bool
add_name(fname)240 add_name(fname)
241 char *fname;
242 {
243 char dfile[FILESIZE],
244 rfile[FILESIZE];
245 struct file_pair *fp;
246 struct rec_head header;
247 int fd;
248 static const char jrecstr[] = "jrec";
249
250 if (strncmp(fname, jrecstr, sizeof(jrecstr)-1) != 0)
251 return NO;
252 /* If we get here, we found a "recover" tmp file, so now
253 we look for the corresponding "data" tmp file. First,
254 though, we check to see whether there is anything in
255 the "recover" file. If it's 0 length, there's no point
256 in saving its name. */
257 (void) sprintf(rfile, "%s/%s", CurDir, fname);
258 if ((fd = open(rfile, 0)) != -1) {
259 if (read(fd, (UnivPtr) &header, sizeof header) != sizeof header) {
260 close(fd);
261 fprintf(stderr, "recover: could not read complete header from %s, skipping\n", rfile);
262 return NO;
263 }
264 if (header.RecMagic != RECMAGIC) {
265 close(fd);
266 fprintf(stderr, "recover: %s is not from this version of JOVE, skipping\n", rfile);
267 return NO;
268 }
269 close(fd);
270 }
271 (void) sprintf(dfile, "%s/%s", CurDir, header.TmpFileName);
272 if (access(dfile, 0) != 0) {
273 fprintf(stderr, "recover: can't find the data file `%s' for %s\n", header.TmpFileName, rfile);
274 #ifdef NEVER
275 /*
276 * MM: I don't think it's a good idea to delete the files
277 * because access() failed. We should probably ask the user
278 * if it is ok to delete the file!
279 */
280 fprintf(stderr, "so deleting...\n");
281 (void) unlink(rfile);
282 #endif
283 return NO;
284 }
285 /* If we get here, we've found both files, so we put them
286 in the list. */
287 fp = (struct file_pair *) malloc (sizeof *fp);
288 if (fp == NULL) {
289 fprintf(stderr, "recover: cannot malloc for file_pair.\n");
290 exit(-1);
291 }
292 fp->file_data = copystr(dfile);
293 fp->file_rec = copystr(rfile);
294 fp->file_flags = 0;
295 fp->file_next = First;
296 First = fp;
297
298 return YES;
299 }
300
301 private void
options()302 options()
303 {
304 printf("Options are:\n");
305 printf(" ? list options.\n");
306 printf(" get get a buffer to a file.\n");
307 printf(" list list known buffers.\n");
308 printf(" print print a buffer to terminal.\n");
309 printf(" quit quit and delete jove tmp files.\n");
310 printf(" restore restore all buffers.\n");
311 }
312
313 /* Returns a legitimate buffer # */
314
315 private void tellme proto((char *, char *, size_t)),
316 list proto((void));
317
318 private struct rec_entry **
getsrc()319 getsrc()
320 {
321 char name[FILESIZE];
322 int number;
323
324 for (;;) {
325 tellme("Which buffer ('?' for list)? ", name, sizeof(name));
326 if (name[0] == '?')
327 list();
328 else if (name[0] == '\0')
329 return NULL;
330 else if ((number = atoi(name)) > 0 && number <= Header.Nbuffers)
331 return &buflist[number];
332 else {
333 int i;
334
335 for (i = 1; i <= Header.Nbuffers; i++)
336 if (strcmp(buflist[i]->r_bname, name) == 0)
337 return &buflist[i];
338 printf("%s: unknown buffer.\n", name);
339 }
340 }
341 }
342
343 /* Get a destination file name. */
344
345 private char *
getdest()346 getdest()
347 {
348 static char filebuf[FILESIZE];
349
350 tellme("Output file: ", filebuf, sizeof(filebuf));
351 if (filebuf[0] == '\0')
352 return NULL;
353 return filebuf;
354 }
355
356 #include "jctype.h"
357
358 private char *
readword(buf,buflen)359 readword(buf, buflen)
360 char *buf;
361 size_t buflen;
362 {
363 int c;
364 char *bp = buf,
365 *ep = buf + buflen - 1;
366
367 do ; while (strchr(" \t\n", c = getchar()) != NULL);
368
369 for (;;) {
370 if (c == EOF)
371 exit(0);
372 if (strchr(" \t\n", c) != NULL)
373 break;
374 if (bp == ep) {
375 *bp = '\0';
376 fprintf(stderr, "%lu byte buffer too small for word `%s'",
377 (unsigned long) buflen, buf);
378 exit(0);
379 }
380 *bp++ = c;
381 c = getchar();
382 }
383 *bp = '\0';
384
385 return buf;
386 }
387
388 private void
tellme(quest,answer,anslen)389 tellme(quest, answer, anslen)
390 char *quest,
391 *answer;
392 size_t anslen;
393 {
394 printf("%s", quest);
395 fflush(stdout);
396 readword(answer, anslen);
397 }
398
399 /* Print the specified file to standard output. */
400
401 private jmp_buf int_env;
402
403 private SIGRESTYPE
catch(junk)404 catch(junk)
405 int junk;
406 {
407 longjmp(int_env, 1);
408 /*NOTREACHED*/
409 }
410
411 private void get proto((struct rec_entry **src, char *dest));
412
413 private void
restore()414 restore()
415 {
416 register int i;
417 char tofile[FILESIZE],
418 answer[SMALLSTRSIZE];
419 int nrecovered = 0;
420
421 for (i = 1; i <= Header.Nbuffers; i++) {
422 (void) sprintf(tofile, "#%s", buflist[i]->r_bname);
423 tryagain:
424 printf("Restoring %s to %s, okay?", buflist[i]->r_bname,
425 tofile);
426 tellme(" ", answer, sizeof(answer));
427 switch (answer[0]) {
428 case 'y':
429 break;
430
431 case 'n':
432 continue;
433
434 default:
435 tellme("What file should I use instead? ", tofile,
436 sizeof(tofile));
437 goto tryagain;
438 }
439 get(&buflist[i], tofile);
440 nrecovered += 1;
441 }
442 printf("Recovered %d buffers.\n", nrecovered);
443 }
444
445 private void dump_file proto((int which, FILE *out));
446
447 private void
get(src,dest)448 get(src, dest)
449 struct rec_entry **src;
450 char *dest;
451 {
452 FILE *volatile outfile; /* "volatile" to preserve outfile across setjmp */
453
454 if (src == NULL || dest == NULL)
455 return;
456 if (dest == tty)
457 outfile = stdout;
458 else {
459 if ((outfile = fopen(dest, "w")) == NULL) {
460 printf("recover: cannot create %s.\n", dest);
461 (void) signal(SIGINT, SIG_DFL);
462 return;
463 }
464 printf("\"%s\"", dest);
465 }
466 if (setjmp(int_env) == 0) {
467 (void) signal(SIGINT, catch);
468 dump_file(src - buflist, outfile);
469 } else {
470 printf("\nAborted!\n");
471 }
472 (void) signal(SIGINT, SIG_DFL);
473 if (dest != tty) {
474 fclose(outfile);
475 printf(" %ld lines, %ld characters.\n", Nlines, Nchars);
476 }
477 }
478
479 private char **
scanvec(args,str)480 scanvec(args, str)
481 register char **args,
482 *str;
483 {
484 while (*args) {
485 if (strcmp(*args, str) == 0)
486 return args;
487 args += 1;
488 }
489 return NULL;
490 }
491
492 private void
read_rec(recptr)493 read_rec(recptr)
494 struct rec_entry *recptr;
495 {
496 if (fread((UnivPtr) recptr, sizeof *recptr, (size_t)1, ptrs_fp) != 1)
497 fprintf(stderr, "recover: cannot read record.\n");
498 }
499
500 private void
seekto(which)501 seekto(which)
502 int which;
503 {
504 long offset;
505 int i;
506
507 offset = sizeof (Header) + (Header.Nbuffers * sizeof (struct rec_entry));
508 for (i = 1; i < which; i++)
509 offset += buflist[i]->r_nlines * sizeof (daddr);
510 fseek(ptrs_fp, offset, L_SET);
511 }
512
513 private void
makblist()514 makblist()
515 {
516 int i;
517
518 fseek(ptrs_fp, (long) sizeof (Header), L_SET);
519 for (i = 1; i <= Header.Nbuffers; i++) {
520 if (buflist[i] == NULL) {
521 buflist[i] = (struct rec_entry *) malloc (sizeof (struct rec_entry));
522 if (buflist[i] == NULL) {
523 fprintf(stderr, "recover: cannot malloc for makblist.\n");
524 exit(-1);
525 }
526 }
527 read_rec(buflist[i]);
528 }
529 while (buflist[i]) {
530 free((UnivPtr) buflist[i]);
531 buflist[i] = NULL;
532 i += 1;
533 }
534 }
535
536 private daddr
getaddr(fp)537 getaddr(fp)
538 register FILE *fp;
539 {
540 register int nchars = sizeof (daddr);
541 daddr addr;
542 register char *cp = (char *) &addr;
543
544 while (--nchars >= 0)
545 *cp++ = getc(fp);
546
547 return addr;
548 }
549
550 private void
dump_file(which,out)551 dump_file(which, out)
552 int which;
553 FILE *out;
554 {
555 register int nlines;
556 register daddr addr;
557 char buf[JBUFSIZ];
558
559 seekto(which);
560 nlines = buflist[which]->r_nlines;
561 Nchars = Nlines = 0L;
562 while (--nlines >= 0) {
563 addr = getaddr(ptrs_fp);
564 get_line(addr, buf);
565 Nlines += 1;
566 Nchars += 1 + strlen(buf);
567 fputs(buf, out);
568 if (nlines > 0)
569 fputc('\n', out);
570 }
571 }
572
573 /* List all the buffers. */
574
575 private void
list()576 list()
577 {
578 int i;
579
580 for (i = 1; i <= Header.Nbuffers; i++)
581 printf("%d) buffer %s \"%s\" (%d lines)\n", i,
582 buflist[i]->r_bname,
583 buflist[i]->r_fname,
584 buflist[i]->r_nlines);
585 }
586
587 private void ask_del proto((char *prompt, struct file_pair *fp));
588
589 private int
doit(fp)590 doit(fp)
591 struct file_pair *fp;
592 {
593 char answer[SMALLSTRSIZE];
594 char *datafile = fp->file_data,
595 *pntrfile = fp->file_rec;
596
597 ptrs_fp = fopen(pntrfile, "r");
598 if (ptrs_fp == NULL) {
599 if (Verbose)
600 fprintf(stderr, "recover: cannot read rec file (%s).\n", pntrfile);
601 return 0;
602 }
603 fread((UnivPtr) &Header, sizeof Header, (size_t)1, ptrs_fp);
604 if (Header.Uid != UserID)
605 return 0;
606
607 /* Ask about JOVE's that are still running ... */
608 if (kill(Header.Pid, 0) == 0)
609 return 0;
610
611 if (Header.Nbuffers == 0) {
612 printf("There are no modified buffers in %s; should I delete the tmp file?", pntrfile);
613 ask_del(" ", fp);
614 return 1;
615 }
616
617 if (Header.Nbuffers < 0) {
618 fprintf(stderr, "recover: %s doesn't look like a jove file.\n", pntrfile);
619 ask_del("Should I delete it? ", fp);
620 return 1; /* We'll, we sort of found something. */
621 }
622 printf("Found %d buffer%s last updated: %s",
623 Header.Nbuffers,
624 Header.Nbuffers != 1 ? "s" : "",
625 ctime(&Header.UpdTime));
626 data_fd = open(datafile, 0);
627 if (data_fd == -1) {
628 fprintf(stderr, "recover: but I can't read the data file (%s).\n", datafile);
629 ask_del("Should I delete the tmp files? ", fp);
630 return 1;
631 }
632 makblist();
633 list();
634
635 for (;;) {
636 tellme("(Type '?' for options): ", answer, sizeof(answer));
637 switch (answer[0]) {
638 case '\0':
639 continue;
640
641 case '?':
642 options();
643 break;
644
645 case 'l':
646 list();
647 break;
648
649 case 'p':
650 get(getsrc(), tty);
651 break;
652
653 case 'q':
654 ask_del("Shall I delete the tmp files? ", fp);
655 return 1;
656
657 case 'g':
658 { /* So it asks for src first. */
659 char *dest;
660 struct rec_entry **src;
661
662 if ((src = getsrc()) == NULL)
663 break;
664 dest = getdest();
665 get(src, dest);
666 break;
667 }
668
669 case 'r':
670 restore();
671 break;
672
673 default:
674 printf("I don't know how to \"%s\"!\n", answer);
675 break;
676 }
677 }
678 }
679
680 private void del_files proto((struct file_pair *fp));
681
682 private void
ask_del(prompt,fp)683 ask_del(prompt, fp)
684 char *prompt;
685 struct file_pair *fp;
686 {
687 char yorn[SMALLSTRSIZE];
688
689 tellme(prompt, yorn, sizeof(yorn));
690 if (yorn[0] == 'y')
691 del_files(fp);
692 }
693
694 private void
del_files(fp)695 del_files(fp)
696 struct file_pair *fp;
697 {
698 (void) unlink(fp->file_data);
699 (void) unlink(fp->file_rec);
700 }
701
702
703 private char *
hname()704 hname()
705 {
706 char *p = "unknown";
707 #ifdef USE_UNAME
708 static struct utsname mach;
709
710 if (uname(&mach) >= 0)
711 p = mach.nodename;
712 #endif
713 #ifdef USE_GETHOSTNAME
714 static char mach[BUFSIZ];
715
716 if (gethostname(mach, sizeof(mach)) >= 0)
717 p = mach;
718 #endif
719 return p;
720 }
721
722 private void
MailUser(rec)723 MailUser(rec)
724 struct rec_head *rec;
725 {
726 char mail_cmd[BUFSIZ];
727 char *last_update;
728 char *buf_string;
729 FILE *mail_pipe;
730 struct passwd *pw;
731
732 if ((pw = getpwuid(rec->Uid))== NULL)
733 return;
734 last_update = ctime(&(rec->UpdTime));
735 /* Start up mail */
736 sprintf(mail_cmd, "/bin/mail %s", pw->pw_name);
737 setuid(getuid());
738 if ((mail_pipe = popen(mail_cmd, "w")) == NULL)
739 return;
740 setbuf(mail_pipe, mail_cmd);
741 /* Let's be grammatically correct! */
742 if (rec->Nbuffers == 1)
743 buf_string = "buffer";
744 else
745 buf_string = "buffers";
746 fprintf(mail_pipe, "Subject: System crash\n");
747 fprintf(mail_pipe, " \n");
748 fprintf(mail_pipe, "Jove saved %d %s when the system \"%s\"\n",
749 rec->Nbuffers, buf_string, hname());
750 fprintf(mail_pipe, "crashed on %s\n\n", last_update);
751 fprintf(mail_pipe, "You can retrieve the %s using Jove's -r\n",
752 buf_string);
753 fprintf(mail_pipe, "(recover option) i.e. give the command.\n");
754 fprintf(mail_pipe, "\tjove -r\n");
755 fprintf(mail_pipe, "See the Jove manual for more details\n");
756 pclose(mail_pipe);
757 }
758
759
760 private void
savetmps()761 savetmps()
762 {
763 struct file_pair *fp;
764 wait_status_t status;
765 pid_t pid;
766 int fd;
767 struct rec_head header;
768 char buf[BUFSIZ];
769 char *fname;
770 struct stat stbuf;
771
772 if (strcmp(tmp_dir, RecDir) == 0)
773 return; /* Files are moved to the same place. */
774 get_files(tmp_dir);
775 for (fp = First; fp != NULL; fp = fp->file_next) {
776 stat(fp->file_data, &stbuf);
777 switch (pid = fork()) {
778 case -1:
779 fprintf(stderr, "recover: can't fork\n!");
780 exit(-1);
781 /*NOTREACHED*/
782
783 case 0:
784 fprintf(stderr, "Recovering: %s, %s\n", fp->file_data,
785 fp->file_rec);
786 if ((fd = open(fp->file_rec, 0)) != -1) {
787 if ((read(fd, (UnivPtr) &header, sizeof header) != sizeof header)) {
788 close(fd);
789 return;
790 } else
791 close(fd);
792 }
793 MailUser(&header);
794 execl("/bin/mv", "mv", fp->file_data, fp->file_rec,
795 RecDir, (char *)NULL);
796 fprintf(stderr, "recover: cannot execl /bin/mv.\n");
797 exit(-1);
798 /*NOTREACHED*/
799
800 default:
801 do ; while (wait(&status) != pid);
802 if (WIFSIGNALED(status))
803 fprintf(stderr, "recover: copy terminated by signal %d\n.\n", WTERMSIG(status));
804 if (WIFEXITED(status))
805 fprintf(stderr, "recover: copy exited with %d.\n", WEXITSTATUS(status));
806 fname = fp->file_data + strlen(tmp_dir);
807 strcpy(buf, RecDir);
808 strcat(buf, fname);
809 if (chown(buf, (int) stbuf.st_uid, (int) stbuf.st_gid) != 0)
810 perror("recover: chown failed.");
811 fname = fp->file_rec + strlen(tmp_dir);
812 strcpy(buf, RecDir);
813 strcat(buf, fname);
814 if (chown(buf, (int) stbuf.st_uid, (int) stbuf.st_gid) != 0)
815 perror("recover: chown failed.");
816 }
817 }
818 }
819
820 private int
lookup(dir)821 lookup(dir)
822 char *dir;
823 {
824 struct file_pair *fp;
825 int nfound = 0;
826
827 printf("Checking %s ...\n", dir);
828 get_files(dir);
829 for (fp = First; fp != NULL; fp = fp->file_next) {
830 nfound += doit(fp);
831 if (ptrs_fp)
832 (void) fclose(ptrs_fp);
833 if (data_fd > 0)
834 (void) close(data_fd);
835 }
836 return nfound;
837 }
838
839 int
main(argc,argv)840 main(argc, argv)
841 int argc;
842 char *argv[];
843 {
844 int nfound;
845 char **argvp;
846
847 UserID = getuid();
848
849 /* override <TMPDIR> with $TMPDIR, if any */
850 {
851 char *cp = getenv("TMPDIR");
852
853 if (cp != NULL)
854 tmp_dir = cp;
855 }
856
857 if (scanvec(argv, "-help")) {
858 printf("recover: usage: recover [-d directory] [-syscrash]\n\n");
859 printf("Use \"jove -r\" after JOVE has died for some unknown reason.\n\n");
860 printf("Use \"%s/recover -syscrash\"\n", LIBDIR);
861 printf("\twhen the system is in the process of rebooting.\n");
862 printf("\tThis is done automatically at reboot time and\n");
863 printf("\tso most of you don't have to worry about that.\n\n");
864 printf("Use \"recover -d directory\"\n");
865 printf("\twhen the tmp files are stored in 'directory'\n");
866 printf("\tinstead of in the default one (%s).\n\n", tmp_dir);
867 exit(0);
868 }
869 if (scanvec(argv, "-v"))
870 Verbose = YES;
871 if ((argvp = scanvec(argv, "-d")) != NULL)
872 tmp_dir = argvp[1];
873 if (scanvec(argv, "-syscrash")) {
874 printf("Recovering jove files ... ");
875 savetmps();
876 printf("Done.\n");
877 exit(0);
878 }
879 if ((argvp = scanvec(argv, "-uid")) != NULL)
880 UserID = atoi(argvp[1]);
881 /* Check default directory */
882 nfound = lookup(tmp_dir);
883 /* Check whether anything was saved when system died? */
884 if (strcmp(tmp_dir, RecDir) != 0)
885 nfound += lookup(RecDir);
886 if (nfound == 0)
887 printf("There's nothing to recover.\n");
888 return 0;
889 }
890
891 #endif /* RECOVER */
892