Bell 32V release
[unix-history] / usr / src / cmd / tar.c
CommitLineData
8c28b243
TL
1#include <stdio.h>
2#include <sys/types.h>
3#include <sys/stat.h>
4#include <sys/dir.h>
5#include <signal.h>
6
7char *sprintf();
8char *strcat();
9daddr_t bsrch();
10#define TBLOCK 512
11#define NBLOCK 20
12#define NAMSIZ 100
13union hblock {
14 char dummy[TBLOCK];
15 struct header {
16 char name[NAMSIZ];
17 char mode[8];
18 char uid[8];
19 char gid[8];
20 char size[12];
21 char mtime[12];
22 char chksum[8];
23 char linkflag;
24 char linkname[NAMSIZ];
25 } dbuf;
26} dblock, tbuf[NBLOCK];
27
28struct linkbuf {
29 ino_t inum;
30 dev_t devnum;
31 int count;
32 char pathname[NAMSIZ];
33 struct linkbuf *nextp;
34} *ihead;
35
36struct stat stbuf;
37
38int rflag, xflag, vflag, tflag, mt, cflag, mflag;
39int term, chksum, wflag, recno, first, linkerrok;
40int freemem = 1;
41int nblock = 1;
42
43daddr_t low;
44daddr_t high;
45
46FILE *tfile;
47char tname[] = "/tmp/tarXXXXXX";
48
49
50char *usefile;
51char magtape[] = "/dev/mt1";
52
53char *malloc();
54
55main(argc, argv)
56int argc;
57char *argv[];
58{
59 char *cp;
60 int onintr(), onquit(), onhup(), onterm();
61
62 if (argc < 2)
63 usage();
64
65 tfile = NULL;
66 usefile = magtape;
67 argv[argc] = 0;
68 argv++;
69 for (cp = *argv++; *cp; cp++)
70 switch(*cp) {
71 case 'f':
72 usefile = *argv++;
73 break;
74 case 'c':
75 cflag++;
76 rflag++;
77 break;
78 case 'u':
79 mktemp(tname);
80 if ((tfile = fopen(tname, "w")) == NULL) {
81 fprintf(stderr, "Tar: cannot create temporary file (%s)\n", tname);
82 done(1);
83 }
84 fprintf(tfile, "!!!!!/!/!/!/!/!/!/! 000\n");
85 /* FALL THROUGH */
86 case 'r':
87 rflag++;
88 if (nblock != 1) {
89noupdate:
90 fprintf(stderr, "Blocked tapes cannot be updated (yet)\n");
91 done(1);
92 }
93 break;
94 case 'v':
95 vflag++;
96 break;
97 case 'w':
98 wflag++;
99 break;
100 case 'x':
101 xflag++;
102 break;
103 case 't':
104 tflag++;
105 break;
106 case 'm':
107 mflag++;
108 break;
109 case '-':
110 break;
111 case '0':
112 case '1':
113 magtape[7] = *cp;
114 usefile = magtape;
115 break;
116 case 'b':
117 nblock = atoi(*argv++);
118 if (nblock > NBLOCK || nblock <= 0) {
119 fprintf(stderr, "Invalid blocksize. (Max %d)\n", NBLOCK);
120 done(1);
121 }
122 if (rflag && !cflag)
123 goto noupdate;
124 break;
125 case 'l':
126 linkerrok++;
127 break;
128 default:
129 fprintf(stderr, "tar: %c: unknown option\n", *cp);
130 usage();
131 }
132
133 if (rflag) {
134 if (cflag && tfile != NULL) {
135 usage();
136 done(1);
137 }
138 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
139 signal(SIGINT, onintr);
140 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
141 signal(SIGHUP, onhup);
142 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
143 signal(SIGQUIT, onquit);
144/*
145 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
146 signal(SIGTERM, onterm);
147*/
148 if (strcmp(usefile, "-") == 0) {
149 if (cflag == 0) {
150 fprintf(stderr, "Can only create standard output archives\n");
151 done(1);
152 }
153 mt = dup(1);
154 }
155 else if ((mt = open(usefile, 2)) < 0) {
156 if (cflag == 0 || (mt = creat(usefile, 0666)) < 0) {
157 fprintf(stderr, "tar: cannot open %s\n", usefile);
158 done(1);
159 }
160 }
161 dorep(argv);
162 }
163 else if (xflag) {
164 if (strcmp(usefile, "-") == 0)
165 mt = dup(0);
166 else if ((mt = open(usefile, 0)) < 0) {
167 fprintf(stderr, "tar: cannot open %s\n", usefile);
168 done(1);
169 }
170 doxtract(argv);
171 }
172 else if (tflag) {
173 if ((mt = open(usefile, 0)) < 0) {
174 fprintf(stderr, "tar: cannot open %s\n", usefile);
175 done(1);
176 }
177 dotable();
178 }
179 else
180 usage();
181 done(0);
182}
183
184usage()
185{
186 fprintf(stderr, "tar: usage tar -{txru}[cvfblm] [tapefile] [blocksize] file1 file2...\n");
187 done(1);
188}
189
190dorep(argv)
191char *argv[];
192{
193 register char *cp, *cp2;
194 char wdir[60];
195
196 if (!cflag) {
197 getdir();
198 do {
199 passtape();
200 getdir();
201 } while (!endtape());
202 if (tfile != NULL) {
203 char buf[200];
204
205 strcat(buf, "sort +0 -1 +1nr ");
206 strcat(buf, tname);
207 strcat(buf, " -o ");
208 strcat(buf, tname);
209 sprintf(buf, "sort +0 -1 +1nr %s -o %s; awk '$1 != prev {print; prev=$1}' %s >%sX;mv %sX %s",
210 tname, tname, tname, tname, tname, tname);
211 fflush(tfile);
212 system(buf);
213 freopen(tname, "r", tfile);
214 fstat(fileno(tfile), &stbuf);
215 high = stbuf.st_size;
216 }
217 }
218
219 getwdir(wdir);
220 while (*argv && ! term) {
221 cp2 = *argv;
222 for (cp = *argv; *cp; cp++)
223 if (*cp == '/')
224 cp2 = cp;
225 if (cp2 != *argv) {
226 *cp2 = '\0';
227 chdir(*argv);
228 *cp2 = '/';
229 cp2++;
230 }
231 putfile(*argv++, cp2);
232 chdir(wdir);
233 }
234 putempty();
235 putempty();
236 flushtape();
237 if (linkerrok == 1)
238 for (; ihead != NULL; ihead = ihead->nextp)
239 if (ihead->count != 0)
240 fprintf(stderr, "Missing links to %s\n", ihead->pathname);
241}
242
243endtape()
244{
245 if (dblock.dbuf.name[0] == '\0') {
246 backtape();
247 return(1);
248 }
249 else
250 return(0);
251}
252
253getdir()
254{
255 register struct stat *sp;
256 int i;
257
258 readtape( (char *) &dblock);
259 if (dblock.dbuf.name[0] == '\0')
260 return;
261 sp = &stbuf;
262 sscanf(dblock.dbuf.mode, "%o", &i);
263 sp->st_mode = i;
264 sscanf(dblock.dbuf.uid, "%o", &i);
265 sp->st_uid = i;
266 sscanf(dblock.dbuf.gid, "%o", &i);
267 sp->st_gid = i;
268 sscanf(dblock.dbuf.size, "%lo", &sp->st_size);
269 sscanf(dblock.dbuf.mtime, "%lo", &sp->st_mtime);
270 sscanf(dblock.dbuf.chksum, "%o", &chksum);
271 if (chksum != checksum()) {
272 fprintf(stderr, "directory checksum error\n");
273 done(2);
274 }
275 if (tfile != NULL)
276 fprintf(tfile, "%s %s\n", dblock.dbuf.name, dblock.dbuf.mtime);
277}
278
279passtape()
280{
281 long blocks;
282 char buf[TBLOCK];
283
284 if (dblock.dbuf.linkflag == '1')
285 return;
286 blocks = stbuf.st_size;
287 blocks += TBLOCK-1;
288 blocks /= TBLOCK;
289
290 while (blocks-- > 0)
291 readtape(buf);
292}
293
294putfile(longname, shortname)
295char *longname;
296char *shortname;
297{
298 int infile;
299 long blocks;
300 char buf[TBLOCK];
301 register char *cp, *cp2;
302 struct direct dbuf;
303 int i, j;
304
305 infile = open(shortname, 0);
306 if (infile < 0) {
307 fprintf(stderr, "tar: %s: cannot open file\n", longname);
308 return;
309 }
310
311 fstat(infile, &stbuf);
312
313 if (tfile != NULL && checkupdate(longname) == 0) {
314 close(infile);
315 return;
316 }
317 if (checkw('r', shortname) == 0) {
318 close(infile);
319 return;
320 }
321
322 if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
323 for (i = 0, cp = buf; *cp++ = longname[i++];);
324 *--cp = '/';
325 cp++;
326 i = 0;
327 chdir(shortname);
328 while (read(infile, (char *)&dbuf, sizeof(dbuf)) > 0 && !term) {
329 if (dbuf.d_ino == 0) {
330 i++;
331 continue;
332 }
333 if (strcmp(".", dbuf.d_name) == 0 || strcmp("..", dbuf.d_name) == 0) {
334 i++;
335 continue;
336 }
337 cp2 = cp;
338 for (j=0; j < DIRSIZ; j++)
339 *cp2++ = dbuf.d_name[j];
340 *cp2 = '\0';
341 close(infile);
342 putfile(buf, cp);
343 infile = open(".", 0);
344 i++;
345 lseek(infile, (long) (sizeof(dbuf) * i), 0);
346 }
347 close(infile);
348 chdir("..");
349 return;
350 }
351 if ((stbuf.st_mode & S_IFMT) != S_IFREG) {
352 fprintf(stderr, "tar: %s is not a file. Not dumped\n", longname);
353 return;
354 }
355
356 tomodes(&stbuf);
357
358 cp2 = longname;
359 for (cp = dblock.dbuf.name, i=0; (*cp++ = *cp2++) && i < NAMSIZ; i++);
360 if (i >= NAMSIZ) {
361 fprintf(stderr, "%s: file name too long\n", longname);
362 close(infile);
363 return;
364 }
365
366 if (stbuf.st_nlink > 1) {
367 struct linkbuf *lp;
368 int found = 0;
369
370 for (lp = ihead; lp != NULL; lp = lp->nextp) {
371 if (lp->inum == stbuf.st_ino && lp->devnum == stbuf.st_dev) {
372 found++;
373 break;
374 }
375 }
376 if (found) {
377 strcpy(dblock.dbuf.linkname, lp->pathname);
378 dblock.dbuf.linkflag = '1';
379 sprintf(dblock.dbuf.chksum, "%6o", checksum());
380 writetape( (char *) &dblock);
381 if (vflag) {
382 fprintf(stderr, "a %s ", longname);
383 fprintf(stderr, "link to %s\n", lp->pathname);
384 }
385 lp->count--;
386 close(infile);
387 return;
388 }
389 else {
390 lp = (struct linkbuf *) malloc(sizeof(*lp));
391 if (lp == NULL) {
392 if (freemem) {
393 fprintf(stderr, "Out of memory. Link information lost\n");
394 freemem = 0;
395 }
396 }
397 else {
398 lp->nextp = ihead;
399 ihead = lp;
400 lp->inum = stbuf.st_ino;
401 lp->devnum = stbuf.st_dev;
402 lp->count = stbuf.st_nlink - 1;
403 strcpy(lp->pathname, longname);
404 }
405 }
406 }
407
408 blocks = (stbuf.st_size + (TBLOCK-1)) / TBLOCK;
409 if (vflag) {
410 fprintf(stderr, "a %s ", longname);
411 fprintf(stderr, "%ld blocks\n", blocks);
412 }
413 sprintf(dblock.dbuf.chksum, "%6o", checksum());
414 writetape( (char *) &dblock);
415
416 while ((i = read(infile, buf, TBLOCK)) > 0 && blocks > 0) {
417 writetape(buf);
418 blocks--;
419 }
420 close(infile);
421 if (blocks != 0 || i != 0)
422 fprintf(stderr, "%s: file changed size\n", longname);
423 while (blocks-- > 0)
424 putempty();
425}
426
427
428
429doxtract(argv)
430char *argv[];
431{
432 long blocks, bytes;
433 char buf[TBLOCK];
434 char **cp;
435 int ofile;
436
437 for (;;) {
438 getdir();
439 if (endtape())
440 break;
441
442 if (*argv == 0)
443 goto gotit;
444
445 for (cp = argv; *cp; cp++)
446 if (prefix(*cp, dblock.dbuf.name))
447 goto gotit;
448 passtape();
449 continue;
450
451gotit:
452 if (checkw('x', dblock.dbuf.name) == 0) {
453 passtape();
454 continue;
455 }
456
457 checkdir(dblock.dbuf.name);
458
459 if (dblock.dbuf.linkflag == '1') {
460 unlink(dblock.dbuf.name);
461 if (link(dblock.dbuf.linkname, dblock.dbuf.name) < 0) {
462 fprintf(stderr, "%s: cannot link\n", dblock.dbuf.name);
463 continue;
464 }
465 if (vflag)
466 fprintf(stderr, "%s linked to %s\n", dblock.dbuf.name, dblock.dbuf.linkname);
467 continue;
468 }
469 if ((ofile = creat(dblock.dbuf.name, stbuf.st_mode & 07777)) < 0) {
470 fprintf(stderr, "tar: %s - cannot create\n", dblock.dbuf.name);
471 passtape();
472 continue;
473 }
474
475 chown(dblock.dbuf.name, stbuf.st_uid, stbuf.st_gid);
476
477 blocks = ((bytes = stbuf.st_size) + TBLOCK-1)/TBLOCK;
478 if (vflag)
479 fprintf(stderr, "x %s, %ld bytes, %ld tape blocks\n", dblock.dbuf.name, bytes, blocks);
480 while (blocks-- > 0) {
481 readtape(buf);
482 if (bytes > TBLOCK) {
483 if (write(ofile, buf, TBLOCK) < 0) {
484 fprintf(stderr, "tar: %s: HELP - extract write error\n", dblock.dbuf.name);
485 done(2);
486 }
487 } else
488 if (write(ofile, buf, (int) bytes) < 0) {
489 fprintf(stderr, "tar: %s: HELP - extract write error\n", dblock.dbuf.name);
490 done(2);
491 }
492 bytes -= TBLOCK;
493 }
494 close(ofile);
495 if (mflag == 0) {
496 time_t timep[2];
497
498 timep[0] = time(NULL);
499 timep[1] = stbuf.st_mtime;
500 utime(dblock.dbuf.name, timep);
501 }
502 }
503}
504
505dotable()
506{
507 for (;;) {
508 getdir();
509 if (endtape())
510 break;
511 if (vflag)
512 longt(&stbuf);
513 printf("%s", dblock.dbuf.name);
514 if (dblock.dbuf.linkflag == '1')
515 printf(" linked to %s", dblock.dbuf.linkname);
516 printf("\n");
517 passtape();
518 }
519}
520
521putempty()
522{
523 char buf[TBLOCK];
524 char *cp;
525
526 for (cp = buf; cp < &buf[TBLOCK]; )
527 *cp++ = '\0';
528 writetape(buf);
529}
530
531longt(st)
532register struct stat *st;
533{
534 register char *cp;
535 char *ctime();
536
537 pmode(st);
538 printf("%3d/%1d", st->st_uid, st->st_gid);
539 printf("%7D", st->st_size);
540 cp = ctime(&st->st_mtime);
541 printf(" %-12.12s %-4.4s ", cp+4, cp+20);
542}
543
544#define SUID 04000
545#define SGID 02000
546#define ROWN 0400
547#define WOWN 0200
548#define XOWN 0100
549#define RGRP 040
550#define WGRP 020
551#define XGRP 010
552#define ROTH 04
553#define WOTH 02
554#define XOTH 01
555#define STXT 01000
556int m1[] = { 1, ROWN, 'r', '-' };
557int m2[] = { 1, WOWN, 'w', '-' };
558int m3[] = { 2, SUID, 's', XOWN, 'x', '-' };
559int m4[] = { 1, RGRP, 'r', '-' };
560int m5[] = { 1, WGRP, 'w', '-' };
561int m6[] = { 2, SGID, 's', XGRP, 'x', '-' };
562int m7[] = { 1, ROTH, 'r', '-' };
563int m8[] = { 1, WOTH, 'w', '-' };
564int m9[] = { 2, STXT, 't', XOTH, 'x', '-' };
565
566int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
567
568pmode(st)
569register struct stat *st;
570{
571 register int **mp;
572
573 for (mp = &m[0]; mp < &m[9];)
574 select(*mp++, st);
575}
576
577select(pairp, st)
578int *pairp;
579struct stat *st;
580{
581 register int n, *ap;
582
583 ap = pairp;
584 n = *ap++;
585 while (--n>=0 && (st->st_mode&*ap++)==0)
586 ap++;
587 printf("%c", *ap);
588}
589
590checkdir(name)
591register char *name;
592{
593 register char *cp;
594 int i;
595 for (cp = name; *cp; cp++) {
596 if (*cp == '/') {
597 *cp = '\0';
598 if (access(name, 01) < 0) {
599 if (fork() == 0) {
600 execl("/bin/mkdir", "mkdir", name, 0);
601 execl("/usr/bin/mkdir", "mkdir", name, 0);
602 fprintf(stderr, "tar: cannot find mkdir!\n");
603 done(0);
604 }
605 while (wait(&i) >= 0);
606 chown(name, stbuf.st_uid, stbuf.st_gid);
607 }
608 *cp = '/';
609 }
610 }
611}
612
613onintr()
614{
615 signal(SIGINT, SIG_IGN);
616 term++;
617}
618
619onquit()
620{
621 signal(SIGQUIT, SIG_IGN);
622 term++;
623}
624
625onhup()
626{
627 signal(SIGHUP, SIG_IGN);
628 term++;
629}
630
631onterm()
632{
633 signal(SIGTERM, SIG_IGN);
634 term++;
635}
636
637tomodes(sp)
638register struct stat *sp;
639{
640 register char *cp;
641
642 for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
643 *cp = '\0';
644 sprintf(dblock.dbuf.mode, "%6o ", sp->st_mode & 07777);
645 sprintf(dblock.dbuf.uid, "%6o ", sp->st_uid);
646 sprintf(dblock.dbuf.gid, "%6o ", sp->st_gid);
647 sprintf(dblock.dbuf.size, "%11lo ", sp->st_size);
648 sprintf(dblock.dbuf.mtime, "%11lo ", sp->st_mtime);
649}
650
651checksum()
652{
653 register i;
654 register char *cp;
655
656 for (cp = dblock.dbuf.chksum; cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++)
657 *cp = ' ';
658 i = 0;
659 for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
660 i += *cp;
661 return(i);
662}
663
664checkw(c, name)
665char *name;
666{
667 if (wflag) {
668 printf("%c ", c);
669 if (vflag)
670 longt(&stbuf);
671 printf("%s: ", name);
672 if (response() == 'y'){
673 return(1);
674 }
675 return(0);
676 }
677 return(1);
678}
679
680response()
681{
682 char c;
683
684 c = getchar();
685 if (c != '\n')
686 while (getchar() != '\n');
687 else c = 'n';
688 return(c);
689}
690
691checkupdate(arg)
692char *arg;
693{
694 char name[100];
695 long mtime;
696 daddr_t seekp;
697 daddr_t lookup();
698
699 rewind(tfile);
700 for (;;) {
701 if ((seekp = lookup(arg)) < 0)
702 return(1);
703 fseek(tfile, seekp, 0);
704 fscanf(tfile, "%s %lo", name, &mtime);
705 if (stbuf.st_mtime > mtime)
706 return(1);
707 else
708 return(0);
709 }
710}
711
712done(n)
713{
714 unlink(tname);
715 exit(n);
716}
717
718prefix(s1, s2)
719register char *s1, *s2;
720{
721 while (*s1)
722 if (*s1++ != *s2++)
723 return(0);
724 if (*s2)
725 return(*s2 == '/');
726 return(1);
727}
728
729getwdir(s)
730char *s;
731{
732 int i;
733 int pipdes[2];
734
735 pipe(pipdes);
736 if ((i = fork()) == 0) {
737 close(1);
738 dup(pipdes[1]);
739 execl("/bin/pwd", "pwd", 0);
740 execl("/usr/bin/pwd", "pwd", 0);
741 fprintf(stderr, "pwd failed!\n");
742 printf("/\n");
743 exit(1);
744 }
745 while (wait((int *)NULL) != -1)
746 ;
747 read(pipdes[0], s, 50);
748 while(*s != '\n')
749 s++;
750 *s = '\0';
751 close(pipdes[0]);
752 close(pipdes[1]);
753}
754
755#define N 200
756int njab;
757daddr_t
758lookup(s)
759char *s;
760{
761 register i;
762 daddr_t a;
763
764 for(i=0; s[i]; i++)
765 if(s[i] == ' ')
766 break;
767 a = bsrch(s, i, low, high);
768 return(a);
769}
770
771daddr_t
772bsrch(s, n, l, h)
773daddr_t l, h;
774char *s;
775{
776 register i, j;
777 char b[N];
778 daddr_t m, m1;
779
780 njab = 0;
781
782loop:
783 if(l >= h)
784 return(-1L);
785 m = l + (h-l)/2 - N/2;
786 if(m < l)
787 m = l;
788 fseek(tfile, m, 0);
789 fread(b, 1, N, tfile);
790 njab++;
791 for(i=0; i<N; i++) {
792 if(b[i] == '\n')
793 break;
794 m++;
795 }
796 if(m >= h)
797 return(-1L);
798 m1 = m;
799 j = i;
800 for(i++; i<N; i++) {
801 m1++;
802 if(b[i] == '\n')
803 break;
804 }
805 i = cmp(b+j, s, n);
806 if(i < 0) {
807 h = m;
808 goto loop;
809 }
810 if(i > 0) {
811 l = m1;
812 goto loop;
813 }
814 return(m);
815}
816
817cmp(b, s, n)
818char *b, *s;
819{
820 register i;
821
822 if(b[0] != '\n')
823 exit(2);
824 for(i=0; i<n; i++) {
825 if(b[i+1] > s[i])
826 return(-1);
827 if(b[i+1] < s[i])
828 return(1);
829 }
830 return(b[i+1] == ' '? 0 : -1);
831}
832
833readtape(buffer)
834char *buffer;
835{
836 int i, j;
837
838 if (recno >= nblock || first == 0) {
839 if (first == 0 && nblock == 1)
840 j = NBLOCK;
841 else
842 j = nblock;
843 if ((i = read(mt, tbuf, TBLOCK*j)) < 0) {
844 fprintf(stderr, "Tar: tape read error\n");
845 done(3);
846 }
847 if (first == 0) {
848 if ((i % TBLOCK) != 0) {
849 fprintf(stderr, "Tar: tape blocksize error\n");
850 done(3);
851 }
852 i /= TBLOCK;
853 if (rflag && i != 1) {
854 fprintf(stderr, "Tar: Cannot update blocked tapes (yet)\n");
855 done(4);
856 }
857 if (i != nblock && i != 1) {
858 fprintf(stderr, "Tar: blocksize = %d\n", i);
859 nblock = i;
860 }
861 }
862 recno = 0;
863 }
864 first = 1;
865 copy(buffer, &tbuf[recno++]);
866 return(TBLOCK);
867}
868
869writetape(buffer)
870char *buffer;
871{
872 first = 1;
873 if (recno >= nblock) {
874 if (write(mt, tbuf, TBLOCK*nblock) < 0) {
875 fprintf(stderr, "Tar: tape write error\n");
876 done(2);
877 }
878 recno = 0;
879 }
880 copy(&tbuf[recno++], buffer);
881 if (recno >= nblock) {
882 if (write(mt, tbuf, TBLOCK*nblock) < 0) {
883 fprintf(stderr, "Tar: tape write error\n");
884 done(2);
885 }
886 recno = 0;
887 }
888 return(TBLOCK);
889}
890
891backtape()
892{
893 lseek(mt, (long) -TBLOCK, 1);
894 if (recno >= nblock) {
895 recno = nblock - 1;
896 if (read(mt, tbuf, TBLOCK*nblock) < 0) {
897 fprintf(stderr, "Tar: tape read error after seek\n");
898 done(4);
899 }
900 lseek(mt, (long) -TBLOCK, 1);
901 }
902}
903
904flushtape()
905{
906 write(mt, tbuf, TBLOCK*nblock);
907}
908
909copy(to, from)
910register char *to, *from;
911{
912 register i;
913
914 i = TBLOCK;
915 do {
916 *to++ = *from++;
917 } while (--i);
918}