home *** CD-ROM | disk | FTP | other *** search
/ minnie.tuhs.org / unixen.tar / unixen / PDP-11 / Distributions / ucb / spencer_2bsd.tar.gz / 2bsd.tar / src / ex / exrecover.c < prev    next >
C/C++ Source or Header  |  1980-02-17  |  15KB  |  757 lines

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