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