date and time created 90/06/05 10:57:54 by bostic
[unix-history] / usr / src / usr.bin / ex / ex3.7recover / ex3.7recover.c
CommitLineData
f9679f70
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
70190965 8char *copyright =
f9679f70
DF
9"@(#) Copyright (c) 1980 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
13#ifndef lint
7af1139d 14static char *sccsid = "@(#)ex3.7recover.c 7.12 (Berkeley) %G%";
f9679f70
DF
15#endif not lint
16
299f2784
MH
17#include <stdio.h> /* mjm: BUFSIZ: stdio = 512, VMUNIX = 1024 */
18#undef BUFSIZ /* mjm: BUFSIZ different */
19#undef EOF /* mjm: EOF and NULL effectively the same */
20#undef NULL
121208f7 21
f3270895
MH
22#include "ex.h"
23#include "ex_temp.h"
24#include "ex_tty.h"
7d3f9b2b 25#include <sys/dir.h>
f3270895 26
df6bc3aa
MH
27char xstr[1]; /* make loader happy */
28short tfile = -1; /* ditto */
f3270895 29
121208f7 30/*
f3270895
MH
31 *
32 * This program searches through the specified directory and then
435e8dff 33 * the directory _PATH_USRPRESERVE looking for an instance of the specified
f3270895
MH
34 * file from a crashed editor or a crashed system.
35 * If this file is found, it is unscrambled and written to
36 * the standard output.
37 *
38 * If this program terminates without a "broken pipe" diagnostic
39 * (i.e. the editor doesn't die right away) then the buffer we are
40 * writing from is removed when we finish. This is potentially a mistake
41 * as there is not enough handshaking to guarantee that the file has actually
42 * been recovered, but should suffice for most cases.
43 */
44
45/*
46 * For lint's sake...
47 */
48#ifndef lint
49#define ignorl(a) a
50#endif
51
f3270895
MH
52/*
53 * Limit on the number of printed entries
54 * when an, e.g. ``ex -r'' command is given.
55 */
56#define NENTRY 50
57
58char *ctime();
59char nb[BUFSIZ];
60int vercnt; /* Count number of versions of file found */
61
62main(argc, argv)
63 int argc;
64 char *argv[];
65{
66 register char *cp;
67 register int b, i;
68
69 /*
70 * Initialize as though the editor had just started.
71 */
72 fendcore = (line *) sbrk(0);
73 dot = zero = dol = fendcore;
74 one = zero + 1;
75 endcore = fendcore - 2;
76 iblock = oblock = -1;
77
78 /*
79 * If given only a -r argument, then list the saved files.
80 */
81 if (argc == 2 && eq(argv[1], "-r")) {
435e8dff 82 listfiles(_PATH_PRESERVE);
f3270895
MH
83 exit(0);
84 }
85 if (argc != 3)
86 error(" Wrong number of arguments to exrecover", 0);
87
88 CP(file, argv[2]);
89
90 /*
91 * Search for this file.
92 */
93 findtmp(argv[1]);
94
95 /*
96 * Got (one of the versions of) it, write it back to the editor.
97 */
98 cp = ctime(&H.Time);
99 cp[19] = 0;
100 fprintf(stderr, " [Dated: %s", cp);
101 fprintf(stderr, vercnt > 1 ? ", newest of %d saved]" : "]", vercnt);
102 H.Flines++;
103
104 /*
105 * Allocate space for the line pointers from the temp file.
106 */
107 if ((int) sbrk((int) (H.Flines * sizeof (line))) == -1)
108 /*
109 * Good grief.
110 */
111 error(" Not enough core for lines", 0);
112#ifdef DEBUG
113 fprintf(stderr, "%d lines\n", H.Flines);
114#endif
115
116 /*
117 * Now go get the blocks of seek pointers which are scattered
118 * throughout the temp file, reconstructing the incore
119 * line pointers at point of crash.
120 */
121 b = 0;
122 while (H.Flines > 0) {
123 ignorl(lseek(tfile, (long) blocks[b] * BUFSIZ, 0));
124 i = H.Flines < BUFSIZ / sizeof (line) ?
125 H.Flines * sizeof (line) : BUFSIZ;
126 if (read(tfile, (char *) dot, i) != i) {
127 perror(nb);
128 exit(1);
129 }
130 dot += i / sizeof (line);
131 H.Flines -= i / sizeof (line);
132 b++;
133 }
134 dot--; dol = dot;
135
136 /*
137 * Sigh... due to sandbagging some lines may really not be there.
138 * Find and discard such. This shouldn't happen much.
139 */
140 scrapbad();
141
142 /*
143 * Now if there were any lines in the recovered file
144 * write them to the standard output.
145 */
146 if (dol > zero) {
147 addr1 = one; addr2 = dol; io = 1;
299f2784 148 putfile(0);
f3270895
MH
149 }
150
151 /*
152 * Trash the saved buffer.
153 * Hopefully the system won't crash before the editor
154 * syncs the new recovered buffer; i.e. for an instant here
155 * you may lose if the system crashes because this file
156 * is gone, but the editor hasn't completed reading the recovered
157 * file from the pipe from us to it.
158 *
159 * This doesn't work if we are coming from an non-absolute path
160 * name since we may have chdir'ed but what the hay, noone really
161 * ever edits with temporaries in "." anyways.
162 */
163 if (nb[0] == '/')
164 ignore(unlink(nb));
165
166 /*
167 * Adieu.
168 */
169 exit(0);
170}
171
172/*
173 * Print an error message (notably not in error
174 * message file). If terminal is in RAW mode, then
175 * we should be writing output for "vi", so don't print
176 * a newline which would screw up the screen.
177 */
178/*VARARGS2*/
179error(str, inf)
180 char *str;
181 int inf;
182{
183
184 fprintf(stderr, str, inf);
88ff8fc7 185 (void)ioctl(2, TIOCGETP, &tty);
f3270895
MH
186 if ((tty.sg_flags & RAW) == 0)
187 fprintf(stderr, "\n");
188 exit(1);
189}
190
191/*
192 * Here we save the information about files, when
193 * you ask us what files we have saved for you.
194 * We buffer file name, number of lines, and the time
195 * at which the file was saved.
196 */
197struct svfile {
198 char sf_name[FNSIZE + 1];
199 int sf_lines;
7d3f9b2b 200 char sf_entry[MAXNAMLEN + 1];
f3270895
MH
201 time_t sf_time;
202};
203
204listfiles(dirname)
205 char *dirname;
206{
7d3f9b2b
RC
207 register DIR *dir;
208 struct direct *dirent;
f3270895
MH
209 int ecount, qucmp();
210 register int f;
211 char *cp;
212 struct svfile *fp, svbuf[NENTRY];
213
214 /*
435e8dff 215 * Open _PATH_PRESERVE, and go there to make things quick.
f3270895 216 */
7d3f9b2b 217 dir = opendir(dirname);
f3270895
MH
218 if (dir == NULL) {
219 perror(dirname);
220 return;
221 }
222 if (chdir(dirname) < 0) {
223 perror(dirname);
224 return;
225 }
226
227 /*
435e8dff 228 * Look at the candidate files in _PATH_PRESERVE.
f3270895
MH
229 */
230 fp = &svbuf[0];
231 ecount = 0;
7d3f9b2b
RC
232 while ((dirent = readdir(dir)) != NULL) {
233 if (dirent->d_name[0] != 'E')
f3270895
MH
234 continue;
235#ifdef DEBUG
7d3f9b2b 236 fprintf(stderr, "considering %s\n", dirent->d_name);
f3270895
MH
237#endif
238 /*
239 * Name begins with E; open it and
240 * make sure the uid in the header is our uid.
241 * If not, then don't bother with this file, it can't
242 * be ours.
243 */
7d3f9b2b 244 f = open(dirent->d_name, 0);
f3270895
MH
245 if (f < 0) {
246#ifdef DEBUG
247 fprintf(stderr, "open failed\n");
248#endif
249 continue;
250 }
251 if (read(f, (char *) &H, sizeof H) != sizeof H) {
252#ifdef DEBUG
253 fprintf(stderr, "culdnt read hedr\n");
254#endif
255 ignore(close(f));
256 continue;
257 }
258 ignore(close(f));
259 if (getuid() != H.Uid) {
260#ifdef DEBUG
261 fprintf(stderr, "uid wrong\n");
262#endif
263 continue;
264 }
265
266 /*
267 * Saved the day!
268 */
7d3f9b2b 269 enter(fp++, dirent->d_name, ecount);
f3270895
MH
270 ecount++;
271#ifdef DEBUG
7d3f9b2b 272 fprintf(stderr, "entered file %s\n", dirent->d_name);
f3270895
MH
273#endif
274 }
7d3f9b2b 275 ignore(closedir(dir));
f3270895
MH
276
277 /*
278 * If any files were saved, then sort them and print
279 * them out.
280 */
281 if (ecount == 0) {
282 fprintf(stderr, "No files saved.\n");
283 return;
284 }
285 qsort(&svbuf[0], ecount, sizeof svbuf[0], qucmp);
286 for (fp = &svbuf[0]; fp < &svbuf[ecount]; fp++) {
287 cp = ctime(&fp->sf_time);
288 cp[10] = 0;
289 fprintf(stderr, "On %s at ", cp);
290 cp[16] = 0;
291 fprintf(stderr, &cp[11]);
292 fprintf(stderr, " saved %d lines of file \"%s\"\n",
293 fp->sf_lines, fp->sf_name);
294 }
295}
296
297/*
298 * Enter a new file into the saved file information.
299 */
300enter(fp, fname, count)
301 struct svfile *fp;
302 char *fname;
303{
304 register char *cp, *cp2;
305 register struct svfile *f, *fl;
306 time_t curtime, itol();
307
308 f = 0;
309 if (count >= NENTRY) {
310 /*
311 * My god, a huge number of saved files.
312 * Would you work on a system that crashed this
313 * often? Hope not. So lets trash the oldest
314 * as the most useless.
315 *
316 * (I wonder if this code has ever run?)
317 */
318 fl = fp - count + NENTRY - 1;
319 curtime = fl->sf_time;
320 for (f = fl; --f > fp-count; )
321 if (f->sf_time < curtime)
322 curtime = f->sf_time;
323 for (f = fl; --f > fp-count; )
324 if (f->sf_time == curtime)
325 break;
326 fp = f;
327 }
328
329 /*
330 * Gotcha.
331 */
332 fp->sf_time = H.Time;
333 fp->sf_lines = H.Flines;
334 for (cp2 = fp->sf_name, cp = savedfile; *cp;)
335 *cp2++ = *cp++;
336 for (cp2 = fp->sf_entry, cp = fname; *cp && cp-fname < 14;)
337 *cp2++ = *cp++;
338 *cp2++ = 0;
339}
340
341/*
342 * Do the qsort compare to sort the entries first by file name,
343 * then by modify time.
344 */
345qucmp(p1, p2)
346 struct svfile *p1, *p2;
347{
348 register int t;
349
350 if (t = strcmp(p1->sf_name, p2->sf_name))
351 return(t);
352 if (p1->sf_time > p2->sf_time)
353 return(-1);
354 return(p1->sf_time < p2->sf_time);
355}
356
357/*
358 * Scratch for search.
359 */
360char bestnb[BUFSIZ]; /* Name of the best one */
361long besttime; /* Time at which the best file was saved */
362int bestfd; /* Keep best file open so it dont vanish */
363
364/*
365 * Look for a file, both in the users directory option value
435e8dff 366 * (i.e. usually /tmp) and in _PATH_PRESERVE.
f3270895
MH
367 * Want to find the newest so we search on and on.
368 */
369findtmp(dir)
370 char *dir;
371{
372
373 /*
374 * No name or file so far.
375 */
376 bestnb[0] = 0;
377 bestfd = -1;
378
379 /*
435e8dff 380 * Search _PATH_PRESERVE and, if we can get there, /tmp
f3270895
MH
381 * (actually the users "directory" option).
382 */
383 searchdir(dir);
435e8dff
KB
384 if (chdir(_PATH_PRESERVE) == 0)
385 searchdir(_PATH_PRESERVE);
f3270895
MH
386 if (bestfd != -1) {
387 /*
388 * Gotcha.
389 * Put the file (which is already open) in the file
390 * used by the temp file routines, and save its
391 * name for later unlinking.
392 */
393 tfile = bestfd;
394 CP(nb, bestnb);
395 ignorl(lseek(tfile, 0l, 0));
396
397 /*
398 * Gotta be able to read the header or fall through
399 * to lossage.
400 */
401 if (read(tfile, (char *) &H, sizeof H) == sizeof H)
402 return;
403 }
404
405 /*
406 * Extreme lossage...
407 */
408 error(" File not found", 0);
409}
410
411/*
412 * Search for the file in directory dirname.
413 *
414 * Don't chdir here, because the users directory
415 * may be ".", and we would move away before we searched it.
416 * Note that we actually chdir elsewhere (because it is too slow
435e8dff 417 * to look around in _PATH_PRESERVE without chdir'ing there) so we
f3270895
MH
418 * can't win, because we don't know the name of '.' and if the path
419 * name of the file we want to unlink is relative, rather than absolute
420 * we won't be able to find it again.
421 */
422searchdir(dirname)
423 char *dirname;
424{
7d3f9b2b
RC
425 struct direct *dirent;
426 register DIR *dir;
f3270895
MH
427 char dbuf[BUFSIZ];
428
7d3f9b2b 429 dir = opendir(dirname);
f3270895
MH
430 if (dir == NULL)
431 return;
7d3f9b2b
RC
432 while ((dirent = readdir(dir)) != NULL) {
433 if (dirent->d_name[0] != 'E')
f3270895
MH
434 continue;
435 /*
436 * Got a file in the directory starting with E...
437 * Save a consed up name for the file to unlink
438 * later, and check that this is really a file
439 * we are looking for.
440 */
7d3f9b2b 441 ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent->d_name));
f3270895
MH
442 if (yeah(nb)) {
443 /*
444 * Well, it is the file we are looking for.
445 * Is it more recent than any version we found before?
446 */
447 if (H.Time > besttime) {
448 /*
449 * A winner.
450 */
451 ignore(close(bestfd));
452 bestfd = dup(tfile);
453 besttime = H.Time;
454 CP(bestnb, nb);
455 }
456 /*
457 * Count versions so user can be told there are
458 * ``yet more pages to be turned''.
459 */
460 vercnt++;
461 }
462 ignore(close(tfile));
463 }
7d3f9b2b 464 ignore(closedir(dir));
f3270895
MH
465}
466
467/*
468 * Given a candidate file to be recovered, see
469 * if its really an editor temporary and of this
470 * user and the file specified.
471 */
472yeah(name)
473 char *name;
474{
475
476 tfile = open(name, 2);
477 if (tfile < 0)
478 return (0);
479 if (read(tfile, (char *) &H, sizeof H) != sizeof H) {
480nope:
481 ignore(close(tfile));
482 return (0);
483 }
484 if (!eq(savedfile, file))
485 goto nope;
486 if (getuid() != H.Uid)
487 goto nope;
488 /*
489 * This is old and stupid code, which
490 * puts a word LOST in the header block, so that lost lines
491 * can be made to point at it.
492 */
44232d5b 493 ignorl(lseek(tfile, (long)(BUFSIZ*HBLKS-8), 0));
f3270895
MH
494 ignore(write(tfile, "LOST", 5));
495 return (1);
496}
497
498preserve()
499{
500
501}
502
503/*
504 * Find the true end of the scratch file, and ``LOSE''
505 * lines which point into thin air. This lossage occurs
506 * due to the sandbagging of i/o which can cause blocks to
507 * be written in a non-obvious order, different from the order
508 * in which the editor tried to write them.
509 *
510 * Lines which are lost are replaced with the text LOST so
511 * they are easy to find. We work hard at pretty formatting here
512 * as lines tend to be lost in blocks.
513 *
514 * This only seems to happen on very heavily loaded systems, and
515 * not very often.
516 */
517scrapbad()
518{
519 register line *ip;
520 struct stat stbuf;
521 off_t size, maxt;
522 int bno, cnt, bad, was;
523 char bk[BUFSIZ];
524
525 ignore(fstat(tfile, &stbuf));
526 size = stbuf.st_size;
44232d5b 527 maxt = (size >> SHFT) | (BNDRY-1);
f3270895
MH
528 bno = (maxt >> OFFBTS) & BLKMSK;
529#ifdef DEBUG
530 fprintf(stderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno);
531#endif
532
533 /*
534 * Look for a null separating two lines in the temp file;
535 * if last line was split across blocks, then it is lost
536 * if the last block is.
537 */
538 while (bno > 0) {
539 ignorl(lseek(tfile, (long) BUFSIZ * bno, 0));
540 cnt = read(tfile, (char *) bk, BUFSIZ);
541 while (cnt > 0)
542 if (bk[--cnt] == 0)
543 goto null;
544 bno--;
545 }
546null:
547
548 /*
549 * Magically calculate the largest valid pointer in the temp file,
550 * consing it up from the block number and the count.
551 */
552 maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1;
553#ifdef DEBUG
554 fprintf(stderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt);
555#endif
556
557 /*
558 * Now cycle through the line pointers,
559 * trashing the Lusers.
560 */
561 was = bad = 0;
562 for (ip = one; ip <= dol; ip++)
563 if (*ip > maxt) {
564#ifdef DEBUG
565 fprintf(stderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt);
566#endif
567 if (was == 0)
568 was = ip - zero;
44232d5b 569 *ip = ((HBLKS*BUFSIZ)-8) >> SHFT;
f3270895
MH
570 } else if (was) {
571 if (bad == 0)
572 fprintf(stderr, " [Lost line(s):");
573 fprintf(stderr, " %d", was);
574 if ((ip - 1) - zero > was)
575 fprintf(stderr, "-%d", (ip - 1) - zero);
576 bad++;
577 was = 0;
578 }
579 if (was != 0) {
580 if (bad == 0)
581 fprintf(stderr, " [Lost line(s):");
582 fprintf(stderr, " %d", was);
583 if (dol - zero != was)
584 fprintf(stderr, "-%d", dol - zero);
585 bad++;
586 }
587 if (bad)
588 fprintf(stderr, "]");
589}
590
591/*
592 * Aw shucks, if we only had a (void) cast.
593 */
594#ifdef lint
595Ignorl(a)
596 long a;
597{
598
599 a = a;
600}
601
602Ignore(a)
603 char *a;
604{
605
606 a = a;
607}
608
609Ignorf(a)
610 int (*a)();
611{
612
613 a = a;
614}
615
616ignorl(a)
617 long a;
618{
619
620 a = a;
621}
622#endif
623
624int cntch, cntln, cntodd, cntnull;
625/*
626 * Following routines stolen mercilessly from ex.
627 */
628putfile()
629{
630 line *a1;
631 register char *fp, *lp;
632 register int nib;
633
634 a1 = addr1;
635 clrstats();
636 cntln = addr2 - a1 + 1;
637 if (cntln == 0)
638 return;
639 nib = BUFSIZ;
640 fp = genbuf;
641 do {
642 getline(*a1++);
643 lp = linebuf;
644 for (;;) {
645 if (--nib < 0) {
646 nib = fp - genbuf;
647 if (write(io, genbuf, nib) != nib)
648 wrerror();
649 cntch += nib;
650 nib = 511;
651 fp = genbuf;
652 }
653 if ((*fp++ = *lp++) == 0) {
654 fp[-1] = '\n';
655 break;
656 }
657 }
658 } while (a1 <= addr2);
659 nib = fp - genbuf;
660 if (write(io, genbuf, nib) != nib)
661 wrerror();
662 cntch += nib;
663}
664
665wrerror()
666{
667
668 syserror();
669}
670
671clrstats()
672{
673
674 ninbuf = 0;
675 cntch = 0;
676 cntln = 0;
677 cntnull = 0;
678 cntodd = 0;
679}
680
681#define READ 0
682#define WRITE 1
683
684getline(tl)
685 line tl;
686{
687 register char *bp, *lp;
688 register int nl;
689
690 lp = linebuf;
691 bp = getblock(tl, READ);
692 nl = nleft;
693 tl &= ~OFFMSK;
694 while (*lp++ = *bp++)
695 if (--nl == 0) {
696 bp = getblock(tl += INCRMT, READ);
697 nl = nleft;
698 }
699}
700
701int read();
702int write();
703
704char *
705getblock(atl, iof)
706 line atl;
707 int iof;
708{
709 register int bno, off;
710
711 bno = (atl >> OFFBTS) & BLKMSK;
712 off = (atl << SHFT) & LBTMSK;
713 if (bno >= NMBLKS)
714 error(" Tmp file too large");
715 nleft = BUFSIZ - off;
716 if (bno == iblock) {
717 ichanged |= iof;
718 return (ibuff + off);
719 }
720 if (bno == oblock)
721 return (obuff + off);
722 if (iof == READ) {
723 if (ichanged)
724 blkio(iblock, ibuff, write);
725 ichanged = 0;
726 iblock = bno;
727 blkio(bno, ibuff, read);
728 return (ibuff + off);
729 }
730 if (oblock >= 0)
731 blkio(oblock, obuff, write);
732 oblock = bno;
733 return (obuff + off);
734}
735
736blkio(b, buf, iofcn)
737 short b;
738 char *buf;
739 int (*iofcn)();
740{
741
742 lseek(tfile, (long) (unsigned) b * BUFSIZ, 0);
44232d5b 743 if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ)
f3270895
MH
744 syserror();
745}
746
747syserror()
748{
7af1139d 749 char *strerror();
f3270895
MH
750
751 dirtcnt = 0;
752 write(2, " ", 1);
7af1139d 753 error(strerror(errno));
f3270895
MH
754 exit(1);
755}
0b7a03e9
KM
756
757/*
758 * Must avoid stdio because expreserve uses sbrk to do memory
759 * allocation and stdio uses malloc.
760 */
761fprintf(fp, fmt, a1, a2, a3, a4, a5)
762 FILE *fp;
763 char *fmt;
764 char *a1, *a2, *a3, *a4, *a5;
765{
766 char buf[BUFSIZ];
767
768 if (fp != stderr)
769 return;
770 sprintf(buf, fmt, a1, a2, a3, a4, a5);
771 write(2, buf, strlen(buf));
772}