added PDX constant
[unix-history] / usr / src / usr.bin / ex / ex3.7recover / ex3.7recover.c
CommitLineData
299f2784 1/* Copyright (c) 1981 Regents of the University of California */
427286eb 2static char *sccsid = "@(#)ex3.7recover.c 7.4 %G%";
299f2784
MH
3#include <stdio.h> /* mjm: BUFSIZ: stdio = 512, VMUNIX = 1024 */
4#undef BUFSIZ /* mjm: BUFSIZ different */
5#undef EOF /* mjm: EOF and NULL effectively the same */
6#undef NULL
121208f7 7
f3270895
MH
8#include "ex.h"
9#include "ex_temp.h"
10#include "ex_tty.h"
11#include "local/uparm.h"
121208f7 12#include "sys/dir.h"
f3270895 13
df6bc3aa
MH
14char xstr[1]; /* make loader happy */
15short tfile = -1; /* ditto */
f3270895 16
121208f7 17/*
f3270895
MH
18 *
19 * This program searches through the specified directory and then
20 * the directory usrpath(preserve) looking for an instance of the specified
21 * file from a crashed editor or a crashed system.
22 * If this file is found, it is unscrambled and written to
23 * the standard output.
24 *
25 * If this program terminates without a "broken pipe" diagnostic
26 * (i.e. the editor doesn't die right away) then the buffer we are
27 * writing from is removed when we finish. This is potentially a mistake
28 * as there is not enough handshaking to guarantee that the file has actually
29 * been recovered, but should suffice for most cases.
30 */
31
32/*
33 * For lint's sake...
34 */
35#ifndef lint
36#define ignorl(a) a
37#endif
38
39/*
40 * This directory definition also appears (obviously) in expreserve.c.
41 * Change both if you change either.
42 */
43char mydir[] = usrpath(preserve);
44
45/*
46 * Limit on the number of printed entries
47 * when an, e.g. ``ex -r'' command is given.
48 */
49#define NENTRY 50
50
51char *ctime();
52char nb[BUFSIZ];
53int vercnt; /* Count number of versions of file found */
54
55main(argc, argv)
56 int argc;
57 char *argv[];
58{
59 register char *cp;
60 register int b, i;
61
62 /*
63 * Initialize as though the editor had just started.
64 */
65 fendcore = (line *) sbrk(0);
66 dot = zero = dol = fendcore;
67 one = zero + 1;
68 endcore = fendcore - 2;
69 iblock = oblock = -1;
70
71 /*
72 * If given only a -r argument, then list the saved files.
73 */
74 if (argc == 2 && eq(argv[1], "-r")) {
75 listfiles(mydir);
76 exit(0);
77 }
78 if (argc != 3)
79 error(" Wrong number of arguments to exrecover", 0);
80
81 CP(file, argv[2]);
82
83 /*
84 * Search for this file.
85 */
86 findtmp(argv[1]);
87
88 /*
89 * Got (one of the versions of) it, write it back to the editor.
90 */
91 cp = ctime(&H.Time);
92 cp[19] = 0;
93 fprintf(stderr, " [Dated: %s", cp);
94 fprintf(stderr, vercnt > 1 ? ", newest of %d saved]" : "]", vercnt);
95 H.Flines++;
96
97 /*
98 * Allocate space for the line pointers from the temp file.
99 */
100 if ((int) sbrk((int) (H.Flines * sizeof (line))) == -1)
101 /*
102 * Good grief.
103 */
104 error(" Not enough core for lines", 0);
105#ifdef DEBUG
106 fprintf(stderr, "%d lines\n", H.Flines);
107#endif
108
109 /*
110 * Now go get the blocks of seek pointers which are scattered
111 * throughout the temp file, reconstructing the incore
112 * line pointers at point of crash.
113 */
114 b = 0;
115 while (H.Flines > 0) {
116 ignorl(lseek(tfile, (long) blocks[b] * BUFSIZ, 0));
117 i = H.Flines < BUFSIZ / sizeof (line) ?
118 H.Flines * sizeof (line) : BUFSIZ;
119 if (read(tfile, (char *) dot, i) != i) {
120 perror(nb);
121 exit(1);
122 }
123 dot += i / sizeof (line);
124 H.Flines -= i / sizeof (line);
125 b++;
126 }
127 dot--; dol = dot;
128
129 /*
130 * Sigh... due to sandbagging some lines may really not be there.
131 * Find and discard such. This shouldn't happen much.
132 */
133 scrapbad();
134
135 /*
136 * Now if there were any lines in the recovered file
137 * write them to the standard output.
138 */
139 if (dol > zero) {
140 addr1 = one; addr2 = dol; io = 1;
299f2784 141 putfile(0);
f3270895
MH
142 }
143
144 /*
145 * Trash the saved buffer.
146 * Hopefully the system won't crash before the editor
147 * syncs the new recovered buffer; i.e. for an instant here
148 * you may lose if the system crashes because this file
149 * is gone, but the editor hasn't completed reading the recovered
150 * file from the pipe from us to it.
151 *
152 * This doesn't work if we are coming from an non-absolute path
153 * name since we may have chdir'ed but what the hay, noone really
154 * ever edits with temporaries in "." anyways.
155 */
156 if (nb[0] == '/')
157 ignore(unlink(nb));
158
159 /*
160 * Adieu.
161 */
162 exit(0);
163}
164
165/*
166 * Print an error message (notably not in error
167 * message file). If terminal is in RAW mode, then
168 * we should be writing output for "vi", so don't print
169 * a newline which would screw up the screen.
170 */
171/*VARARGS2*/
172error(str, inf)
173 char *str;
174 int inf;
175{
176
177 fprintf(stderr, str, inf);
d266c416 178#ifndef USG3TTY
f3270895
MH
179 gtty(2, &tty);
180 if ((tty.sg_flags & RAW) == 0)
d266c416
MH
181#else
182 ioctl(2, TCGETA, &tty);
183 if (tty.c_lflag & ICANON)
184#endif
f3270895
MH
185 fprintf(stderr, "\n");
186 exit(1);
187}
188
189/*
190 * Here we save the information about files, when
191 * you ask us what files we have saved for you.
192 * We buffer file name, number of lines, and the time
193 * at which the file was saved.
194 */
195struct svfile {
196 char sf_name[FNSIZE + 1];
197 int sf_lines;
198 char sf_entry[DIRSIZ + 1];
199 time_t sf_time;
200};
201
202listfiles(dirname)
203 char *dirname;
204{
205 register FILE *dir;
206 struct direct dirent;
207 int ecount, qucmp();
208 register int f;
209 char *cp;
210 struct svfile *fp, svbuf[NENTRY];
211
212 /*
213 * Open usrpath(preserve), and go there to make things quick.
214 */
215 dir = fopen(dirname, "r");
216 if (dir == NULL) {
217 perror(dirname);
218 return;
219 }
220 if (chdir(dirname) < 0) {
221 perror(dirname);
222 return;
223 }
224
225 /*
226 * Look at the candidate files in usrpath(preserve).
227 */
228 fp = &svbuf[0];
229 ecount = 0;
230 while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) {
231 if (dirent.d_ino == 0)
232 continue;
233 if (dirent.d_name[0] != 'E')
234 continue;
235#ifdef DEBUG
236 fprintf(stderr, "considering %s\n", dirent.d_name);
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 */
244 f = open(dirent.d_name, 0);
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 */
269 enter(fp++, dirent.d_name, ecount);
270 ecount++;
271#ifdef DEBUG
272 fprintf(stderr, "entered file %s\n", dirent.d_name);
273#endif
274 }
275 ignore(fclose(dir));
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
366 * (i.e. usually /tmp) and in usrpath(preserve).
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 /*
380 * Search usrpath(preserve) and, if we can get there, /tmp
381 * (actually the users "directory" option).
382 */
383 searchdir(dir);
384 if (chdir(mydir) == 0)
385 searchdir(mydir);
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
417 * to look around in usrpath(preserve) without chdir'ing there) so we
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{
425 struct direct dirent;
426 register FILE *dir;
427 char dbuf[BUFSIZ];
428
429 dir = fopen(dirname, "r");
430 if (dir == NULL)
431 return;
427286eb 432 /* setbuf(dir, dbuf); this breaks UNIX/370. */
f3270895
MH
433 while (fread((char *) &dirent, sizeof dirent, 1, dir) == 1) {
434 if (dirent.d_ino == 0)
435 continue;
436 if (dirent.d_name[0] != 'E' || dirent.d_name[DIRSIZ - 1] != 0)
437 continue;
438 /*
439 * Got a file in the directory starting with E...
440 * Save a consed up name for the file to unlink
441 * later, and check that this is really a file
442 * we are looking for.
443 */
444 ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent.d_name));
445 if (yeah(nb)) {
446 /*
447 * Well, it is the file we are looking for.
448 * Is it more recent than any version we found before?
449 */
450 if (H.Time > besttime) {
451 /*
452 * A winner.
453 */
454 ignore(close(bestfd));
455 bestfd = dup(tfile);
456 besttime = H.Time;
457 CP(bestnb, nb);
458 }
459 /*
460 * Count versions so user can be told there are
461 * ``yet more pages to be turned''.
462 */
463 vercnt++;
464 }
465 ignore(close(tfile));
466 }
467 ignore(fclose(dir));
468}
469
470/*
471 * Given a candidate file to be recovered, see
472 * if its really an editor temporary and of this
473 * user and the file specified.
474 */
475yeah(name)
476 char *name;
477{
478
479 tfile = open(name, 2);
480 if (tfile < 0)
481 return (0);
482 if (read(tfile, (char *) &H, sizeof H) != sizeof H) {
483nope:
484 ignore(close(tfile));
485 return (0);
486 }
487 if (!eq(savedfile, file))
488 goto nope;
489 if (getuid() != H.Uid)
490 goto nope;
491 /*
492 * This is old and stupid code, which
493 * puts a word LOST in the header block, so that lost lines
494 * can be made to point at it.
495 */
44232d5b 496 ignorl(lseek(tfile, (long)(BUFSIZ*HBLKS-8), 0));
f3270895
MH
497 ignore(write(tfile, "LOST", 5));
498 return (1);
499}
500
501preserve()
502{
503
504}
505
506/*
507 * Find the true end of the scratch file, and ``LOSE''
508 * lines which point into thin air. This lossage occurs
509 * due to the sandbagging of i/o which can cause blocks to
510 * be written in a non-obvious order, different from the order
511 * in which the editor tried to write them.
512 *
513 * Lines which are lost are replaced with the text LOST so
514 * they are easy to find. We work hard at pretty formatting here
515 * as lines tend to be lost in blocks.
516 *
517 * This only seems to happen on very heavily loaded systems, and
518 * not very often.
519 */
520scrapbad()
521{
522 register line *ip;
523 struct stat stbuf;
524 off_t size, maxt;
525 int bno, cnt, bad, was;
526 char bk[BUFSIZ];
527
528 ignore(fstat(tfile, &stbuf));
529 size = stbuf.st_size;
44232d5b 530 maxt = (size >> SHFT) | (BNDRY-1);
f3270895
MH
531 bno = (maxt >> OFFBTS) & BLKMSK;
532#ifdef DEBUG
533 fprintf(stderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno);
534#endif
535
536 /*
537 * Look for a null separating two lines in the temp file;
538 * if last line was split across blocks, then it is lost
539 * if the last block is.
540 */
541 while (bno > 0) {
542 ignorl(lseek(tfile, (long) BUFSIZ * bno, 0));
543 cnt = read(tfile, (char *) bk, BUFSIZ);
544 while (cnt > 0)
545 if (bk[--cnt] == 0)
546 goto null;
547 bno--;
548 }
549null:
550
551 /*
552 * Magically calculate the largest valid pointer in the temp file,
553 * consing it up from the block number and the count.
554 */
555 maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1;
556#ifdef DEBUG
557 fprintf(stderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt);
558#endif
559
560 /*
561 * Now cycle through the line pointers,
562 * trashing the Lusers.
563 */
564 was = bad = 0;
565 for (ip = one; ip <= dol; ip++)
566 if (*ip > maxt) {
567#ifdef DEBUG
568 fprintf(stderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt);
569#endif
570 if (was == 0)
571 was = ip - zero;
44232d5b 572 *ip = ((HBLKS*BUFSIZ)-8) >> SHFT;
f3270895
MH
573 } else if (was) {
574 if (bad == 0)
575 fprintf(stderr, " [Lost line(s):");
576 fprintf(stderr, " %d", was);
577 if ((ip - 1) - zero > was)
578 fprintf(stderr, "-%d", (ip - 1) - zero);
579 bad++;
580 was = 0;
581 }
582 if (was != 0) {
583 if (bad == 0)
584 fprintf(stderr, " [Lost line(s):");
585 fprintf(stderr, " %d", was);
586 if (dol - zero != was)
587 fprintf(stderr, "-%d", dol - zero);
588 bad++;
589 }
590 if (bad)
591 fprintf(stderr, "]");
592}
593
594/*
595 * Aw shucks, if we only had a (void) cast.
596 */
597#ifdef lint
598Ignorl(a)
599 long a;
600{
601
602 a = a;
603}
604
605Ignore(a)
606 char *a;
607{
608
609 a = a;
610}
611
612Ignorf(a)
613 int (*a)();
614{
615
616 a = a;
617}
618
619ignorl(a)
620 long a;
621{
622
623 a = a;
624}
625#endif
626
627int cntch, cntln, cntodd, cntnull;
628/*
629 * Following routines stolen mercilessly from ex.
630 */
631putfile()
632{
633 line *a1;
634 register char *fp, *lp;
635 register int nib;
636
637 a1 = addr1;
638 clrstats();
639 cntln = addr2 - a1 + 1;
640 if (cntln == 0)
641 return;
642 nib = BUFSIZ;
643 fp = genbuf;
644 do {
645 getline(*a1++);
646 lp = linebuf;
647 for (;;) {
648 if (--nib < 0) {
649 nib = fp - genbuf;
650 if (write(io, genbuf, nib) != nib)
651 wrerror();
652 cntch += nib;
653 nib = 511;
654 fp = genbuf;
655 }
656 if ((*fp++ = *lp++) == 0) {
657 fp[-1] = '\n';
658 break;
659 }
660 }
661 } while (a1 <= addr2);
662 nib = fp - genbuf;
663 if (write(io, genbuf, nib) != nib)
664 wrerror();
665 cntch += nib;
666}
667
668wrerror()
669{
670
671 syserror();
672}
673
674clrstats()
675{
676
677 ninbuf = 0;
678 cntch = 0;
679 cntln = 0;
680 cntnull = 0;
681 cntodd = 0;
682}
683
684#define READ 0
685#define WRITE 1
686
687getline(tl)
688 line tl;
689{
690 register char *bp, *lp;
691 register int nl;
692
693 lp = linebuf;
694 bp = getblock(tl, READ);
695 nl = nleft;
696 tl &= ~OFFMSK;
697 while (*lp++ = *bp++)
698 if (--nl == 0) {
699 bp = getblock(tl += INCRMT, READ);
700 nl = nleft;
701 }
702}
703
704int read();
705int write();
706
707char *
708getblock(atl, iof)
709 line atl;
710 int iof;
711{
712 register int bno, off;
713
714 bno = (atl >> OFFBTS) & BLKMSK;
715 off = (atl << SHFT) & LBTMSK;
716 if (bno >= NMBLKS)
717 error(" Tmp file too large");
718 nleft = BUFSIZ - off;
719 if (bno == iblock) {
720 ichanged |= iof;
721 return (ibuff + off);
722 }
723 if (bno == oblock)
724 return (obuff + off);
725 if (iof == READ) {
726 if (ichanged)
727 blkio(iblock, ibuff, write);
728 ichanged = 0;
729 iblock = bno;
730 blkio(bno, ibuff, read);
731 return (ibuff + off);
732 }
733 if (oblock >= 0)
734 blkio(oblock, obuff, write);
735 oblock = bno;
736 return (obuff + off);
737}
738
739blkio(b, buf, iofcn)
740 short b;
741 char *buf;
742 int (*iofcn)();
743{
744
745 lseek(tfile, (long) (unsigned) b * BUFSIZ, 0);
44232d5b 746 if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ)
f3270895
MH
747 syserror();
748}
749
750syserror()
751{
752 extern int sys_nerr;
753 extern char *sys_errlist[];
754
755 dirtcnt = 0;
756 write(2, " ", 1);
757 if (errno >= 0 && errno <= sys_nerr)
758 error(sys_errlist[errno]);
759 else
760 error("System error %d", errno);
761 exit(1);
762}