1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2025 Poul-Henning Kamp, <phk@FreeBSD.org>
5 * Copyright (c) 1985, 1987, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <assert.h>
31 #include <err.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <paths.h>
35 #include <signal.h>
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41 #include <sysexits.h>
42 #include <unistd.h>
43
44 #include <sys/endian.h>
45 #include <sys/mtio.h>
46 #include <sys/stat.h>
47 #include <sys/sysctl.h>
48 #include <sys/uio.h>
49
50 #include <libutil.h>
51
52 #define MAXREC (1024 * 1024)
53 #define NOCOUNT (-2)
54
55 enum operation {READ, VERIFY, COPY, COPYVERIFY};
56
57 // Stuff the tape_devs need to know about
58 static int filen;
59 static uint64_t record;
60
61 //---------------------------------------------------------------------
62
63 class tape_dev {
64 size_t max_read_size;
65 public:
66 int fd;
67 char *name;
68 enum direction {SRC, DST} direction;
69
70 tape_dev(int file_handle, const char *spec, bool destination);
71
72 virtual ssize_t read_blk(void *dst, size_t len);
73 virtual ssize_t verify_blk(void *dst, size_t len, size_t expected);
74 virtual void write_blk(const void *src, size_t len);
75 virtual void file_mark(void);
76 virtual void rewind(void);
77 };
78
tape_dev(int file_handle,const char * spec,bool destination)79 tape_dev::tape_dev(int file_handle, const char *spec, bool destination)
80 {
81 assert(file_handle >= 0);
82 fd = file_handle;
83 name = strdup(spec);
84 assert(name != NULL);
85 direction = destination ? DST : SRC;
86 max_read_size = 0;
87 }
88
89 ssize_t
read_blk(void * dst,size_t len)90 tape_dev::read_blk(void *dst, size_t len)
91 {
92 ssize_t retval = -1;
93
94 if (max_read_size == 0) {
95 max_read_size = len;
96 while (max_read_size > 0) {
97 retval = read(fd, dst, max_read_size);
98 if (retval >= 0 || (errno != EINVAL && errno != EFBIG))
99 break;
100 if (max_read_size < 512)
101 errx(1, "Cannot find a sane max blocksize");
102
103 // Reduce to next lower power of two
104 int i = flsl((long)max_read_size - 1L);
105 max_read_size = 1UL << (i - 1);
106 }
107 } else {
108 retval = read(fd, dst, (size_t)max_read_size);
109 }
110 if (retval < 0) {
111 err(1, "read error, %s, file %d, record %ju",
112 name, filen, (uintmax_t)record);
113 }
114 return (retval);
115 }
116
117 ssize_t
verify_blk(void * dst,size_t len,size_t expected)118 tape_dev::verify_blk(void *dst, size_t len, size_t expected)
119 {
120 (void)expected;
121 return read_blk(dst, len);
122 }
123
124 void
write_blk(const void * src,size_t len)125 tape_dev::write_blk(const void *src, size_t len)
126 {
127 assert(len > 0);
128 ssize_t nwrite = write(fd, src, len);
129 if (nwrite < 0 || (size_t) nwrite != len) {
130 if (nwrite == -1) {
131 warn("write error, file %d, record %ju",
132 filen, (intmax_t)record);
133 } else {
134 warnx("write error, file %d, record %ju",
135 filen, (intmax_t)record);
136 warnx("write (%zd) != read (%zd)", nwrite, len);
137 }
138 errx(5, "copy aborted");
139 }
140 return;
141 }
142
143 void
file_mark(void)144 tape_dev::file_mark(void)
145 {
146 struct mtop op;
147
148 op.mt_op = MTWEOF;
149 op.mt_count = (daddr_t)1;
150 if (ioctl(fd, MTIOCTOP, (char *)&op) < 0)
151 err(6, "tape op (write file mark)");
152 }
153
154 void
rewind(void)155 tape_dev::rewind(void)
156 {
157 struct mtop op;
158
159 op.mt_op = MTREW;
160 op.mt_count = (daddr_t)1;
161 if (ioctl(fd, MTIOCTOP, (char *)&op) < 0)
162 err(6, "tape op (rewind)");
163 }
164
165 //---------------------------------------------------------------------
166
167 class tap_file: public tape_dev {
168 public:
tap_file(int file_handle,const char * spec,bool dst)169 tap_file(int file_handle, const char *spec, bool dst) :
170 tape_dev(file_handle, spec, dst) {};
171 ssize_t read_blk(void *dst, size_t len);
172 void write_blk(const void *src, size_t len);
173 void file_mark(void);
174 virtual void rewind(void);
175 };
176
177 static
full_read(int fd,void * dst,size_t len)178 ssize_t full_read(int fd, void *dst, size_t len)
179 {
180 // Input may be a socket which returns partial reads
181
182 ssize_t retval = read(fd, dst, len);
183 if (retval <= 0 || (size_t)retval == len)
184 return (retval);
185
186 char *ptr = (char *)dst + retval;
187 size_t left = len - (size_t)retval;
188 while (left > 0) {
189 retval = read(fd, ptr, left);
190 if (retval <= 0)
191 return (retval);
192 left -= (size_t)retval;
193 ptr += retval;
194 }
195 return ((ssize_t)len);
196 }
197
198 ssize_t
read_blk(void * dst,size_t len)199 tap_file::read_blk(void *dst, size_t len)
200 {
201 char lbuf[4];
202
203 ssize_t nread = full_read(fd, lbuf, sizeof lbuf);
204 if (nread == 0)
205 return (0);
206
207 if ((size_t)nread != sizeof lbuf)
208 err(EX_DATAERR, "Corrupt tap-file, read hdr1=%zd", nread);
209
210 uint32_t u = le32dec(lbuf);
211 if (u == 0 || (u >> 24) == 0xff)
212 return(0);
213
214 if (u > len)
215 err(17, "tapfile blocksize too big, 0x%08x", u);
216
217 size_t alen = (u + 1) & ~1;
218 assert (alen <= len);
219
220 ssize_t retval = full_read(fd, dst, alen);
221 if (retval < 0 || (size_t)retval != alen)
222 err(EX_DATAERR, "Corrupt tap-file, read data=%zd", retval);
223
224 nread = full_read(fd, lbuf, sizeof lbuf);
225 if ((size_t)nread != sizeof lbuf)
226 err(EX_DATAERR, "Corrupt tap-file, read hdr2=%zd", nread);
227
228 uint32_t v = le32dec(lbuf);
229 if (u == v)
230 return (u);
231 err(EX_DATAERR,
232 "Corrupt tap-file, headers differ (0x%08x != 0x%08x)", u, v);
233 }
234
235 void
write_blk(const void * src,size_t len)236 tap_file::write_blk(const void *src, size_t len)
237 {
238 struct iovec iov[4];
239 uint8_t zero = 0;
240 int niov = 0;
241 size_t expect = 0;
242 char tbuf[4];
243
244 assert((len & ~0xffffffffULL) == 0);
245 le32enc(tbuf, (uint32_t)len);
246
247 iov[niov].iov_base = tbuf;
248 iov[niov].iov_len = sizeof tbuf;
249 expect += iov[niov].iov_len;
250 niov += 1;
251
252 iov[niov].iov_base = (void*)(uintptr_t)src;
253 iov[niov].iov_len = len;
254 expect += iov[niov].iov_len;
255 niov += 1;
256
257 if (len & 1) {
258 iov[niov].iov_base = &zero;
259 iov[niov].iov_len = 1;
260 expect += iov[niov].iov_len;
261 niov += 1;
262 }
263
264 iov[niov].iov_base = tbuf;
265 iov[niov].iov_len = sizeof tbuf;
266 expect += iov[niov].iov_len;
267 niov += 1;
268
269 ssize_t nwrite = writev(fd, iov, niov);
270 if (nwrite < 0 || (size_t)nwrite != expect)
271 errx(17, "write error (%zd != %zd)", nwrite, expect);
272 }
273
274 void
file_mark(void)275 tap_file::file_mark(void)
276 {
277 char tbuf[4];
278 le32enc(tbuf, 0);
279 ssize_t nwrite = write(fd, tbuf, sizeof tbuf);
280 if ((size_t)nwrite != sizeof tbuf)
281 errx(17, "write error (%zd != %zd)", nwrite, sizeof tbuf);
282 }
283
284 void
rewind(void)285 tap_file::rewind(void)
286 {
287 off_t where;
288 if (direction == DST) {
289 char tbuf[4];
290 le32enc(tbuf, 0xffffffff);
291 ssize_t nwrite = write(fd, tbuf, sizeof tbuf);
292 if ((size_t)nwrite != sizeof tbuf)
293 errx(17,
294 "write error (%zd != %zd)", nwrite, sizeof tbuf);
295 }
296 where = lseek(fd, 0L, SEEK_SET);
297 if (where != 0 && errno == ESPIPE)
298 err(EX_USAGE, "Cannot rewind sockets and pipes");
299 if (where != 0)
300 err(17, "lseek(0) failed");
301 }
302
303 //---------------------------------------------------------------------
304
305 class file_set: public tape_dev {
306 public:
file_set(int file_handle,const char * spec,bool dst)307 file_set(int file_handle, const char *spec, bool dst) :
308 tape_dev(file_handle, spec, dst) {};
309 ssize_t read_blk(void *dst, size_t len);
310 ssize_t verify_blk(void *dst, size_t len, size_t expected);
311 void write_blk(const void *src, size_t len);
312 void file_mark(void);
313 void rewind(void);
314 void open_next(bool increment);
315 };
316
317 void
open_next(bool increment)318 file_set::open_next(bool increment)
319 {
320 if (fd >= 0) {
321 assert(close(fd) >= 0);
322 fd = -1;
323 }
324 if (increment) {
325 char *p = strchr(name, '\0') - 3;
326 if (++p[2] == '9') {
327 p[2] = '0';
328 if (++p[1] == '9') {
329 p[1] = '0';
330 if (++p[0] == '9') {
331 errx(EX_USAGE,
332 "file-set sequence overflow");
333 }
334 }
335 }
336 }
337 if (direction == DST) {
338 fd = open(name, O_RDWR|O_CREAT, DEFFILEMODE);
339 if (fd < 0)
340 err(1, "Could not open %s", name);
341 } else {
342 fd = open(name, O_RDONLY, 0);
343 }
344 }
345
346 ssize_t
read_blk(void * dst,size_t len)347 file_set::read_blk(void *dst, size_t len)
348 {
349 (void)dst;
350 (void)len;
351 errx(EX_SOFTWARE, "That was not supposed to happen");
352 }
353
354 ssize_t
verify_blk(void * dst,size_t len,size_t expected)355 file_set::verify_blk(void *dst, size_t len, size_t expected)
356 {
357 (void)len;
358 if (fd < 0)
359 open_next(true);
360 if (fd < 0)
361 return (0);
362 ssize_t retval = read(fd, dst, expected);
363 if (retval == 0) {
364 assert(close(fd) >= 0);
365 fd = -1;
366 }
367 return (retval);
368 }
369
370 void
write_blk(const void * src,size_t len)371 file_set::write_blk(const void *src, size_t len)
372 {
373 if (fd < 0)
374 open_next(true);
375 ssize_t nwrite = write(fd, src, len);
376 if (nwrite < 0 || (size_t)nwrite != len)
377 errx(17, "write error (%zd != %zd)", nwrite, len);
378 }
379
380 void
file_mark(void)381 file_set::file_mark(void)
382 {
383 if (fd < 0)
384 return;
385
386 off_t where = lseek(fd, 0UL, SEEK_CUR);
387
388 int i = ftruncate(fd, where);
389 if (i < 0)
390 errx(17, "truncate error, %s to %jd", name, (intmax_t)where);
391 assert(close(fd) >= 0);
392 fd = -1;
393 }
394
395 void
rewind(void)396 file_set::rewind(void)
397 {
398 char *p = strchr(name, '\0') - 3;
399 p[0] = '0';
400 p[1] = '0';
401 p[2] = '0';
402 open_next(false);
403 }
404
405 //---------------------------------------------------------------------
406
407 class flat_file: public tape_dev {
408 public:
flat_file(int file_handle,const char * spec,bool dst)409 flat_file(int file_handle, const char *spec, bool dst) :
410 tape_dev(file_handle, spec, dst) {};
411 ssize_t read_blk(void *dst, size_t len);
412 ssize_t verify_blk(void *dst, size_t len, size_t expected);
413 void write_blk(const void *src, size_t len);
414 void file_mark(void);
415 virtual void rewind(void);
416 };
417
418 ssize_t
read_blk(void * dst,size_t len)419 flat_file::read_blk(void *dst, size_t len)
420 {
421 (void)dst;
422 (void)len;
423 errx(EX_SOFTWARE, "That was not supposed to happen");
424 }
425
426 ssize_t
verify_blk(void * dst,size_t len,size_t expected)427 flat_file::verify_blk(void *dst, size_t len, size_t expected)
428 {
429 (void)len;
430 return (read(fd, dst, expected));
431 }
432
433 void
write_blk(const void * src,size_t len)434 flat_file::write_blk(const void *src, size_t len)
435 {
436 ssize_t nwrite = write(fd, src, len);
437 if (nwrite < 0 || (size_t)nwrite != len)
438 errx(17, "write error (%zd != %zd)", nwrite, len);
439 }
440
441 void
file_mark(void)442 flat_file::file_mark(void)
443 {
444 return;
445 }
446
447 void
rewind(void)448 flat_file::rewind(void)
449 {
450 errx(EX_SOFTWARE, "That was not supposed to happen");
451 }
452
453 //---------------------------------------------------------------------
454
455 enum e_how {H_INPUT, H_OUTPUT, H_VERIFY};
456
457 static tape_dev *
open_arg(const char * arg,enum e_how how,int rawfile)458 open_arg(const char *arg, enum e_how how, int rawfile)
459 {
460 int fd;
461
462 if (!strcmp(arg, "-") && how == H_OUTPUT)
463 fd = STDOUT_FILENO;
464 else if (!strcmp(arg, "-"))
465 fd = STDIN_FILENO;
466 else if (how == H_OUTPUT)
467 fd = open(arg, O_RDWR|O_CREAT, DEFFILEMODE);
468 else
469 fd = open(arg, O_RDONLY);
470
471 if (fd < 0)
472 err(EX_NOINPUT, "Cannot open %s:", arg);
473
474 struct mtop mt;
475 mt.mt_op = MTNOP;
476 mt.mt_count = 1;
477 int i = ioctl(fd, MTIOCTOP, &mt);
478
479 if (i >= 0)
480 return (new tape_dev(fd, arg, how == H_OUTPUT));
481
482 size_t alen = strlen(arg);
483 if (alen >= 5 && !strcmp(arg + (alen - 4), ".000")) {
484 if (how == H_INPUT)
485 errx(EX_USAGE,
486 "File-sets files cannot be used as source");
487 return (new file_set(fd, arg, how == H_OUTPUT));
488 }
489
490 if (how != H_INPUT && rawfile)
491 return (new flat_file(fd, arg, how == H_OUTPUT));
492
493 return (new tap_file(fd, arg, how == H_OUTPUT));
494 }
495
496 //---------------------------------------------------------------------
497
498 static tape_dev *input;
499 static tape_dev *output;
500
501 static size_t maxblk = MAXREC;
502 static uint64_t lastrec, fsize, tsize;
503 static FILE *msg;
504 static ssize_t lastnread;
505 static struct timespec t_start, t_end;
506
507 static void
report_total(FILE * file)508 report_total(FILE *file)
509 {
510 double dur = (t_end.tv_nsec - t_start.tv_nsec) * 1e-9;
511 dur += t_end.tv_sec - t_start.tv_sec;
512 uintmax_t tot = tsize + fsize;
513 fprintf(file, "total length: %ju bytes", tot);
514 fprintf(file, " time: %.0f s", dur);
515 tot /= 1024;
516 fprintf(file, " rate: %.1f kB/s", (double)tot/dur);
517 fprintf(file, "\n");
518 }
519
520 static void
sigintr(int signo __unused)521 sigintr(int signo __unused)
522 {
523 (void)signo;
524 (void)clock_gettime(CLOCK_MONOTONIC, &t_end);
525 if (record) {
526 if (record - lastrec > 1)
527 fprintf(msg, "records %ju to %ju\n",
528 (intmax_t)lastrec, (intmax_t)record);
529 else
530 fprintf(msg, "record %ju\n", (intmax_t)lastrec);
531 }
532 fprintf(msg, "interrupt at file %d: record %ju\n",
533 filen, (uintmax_t)record);
534 report_total(msg);
535 exit(1);
536 }
537
538 #ifdef SIGINFO
539 static volatile sig_atomic_t want_info;
540
541 static void
siginfo(int signo)542 siginfo(int signo)
543 {
544 (void)signo;
545 want_info = 1;
546 }
547
548 static void
check_want_info(void)549 check_want_info(void)
550 {
551 if (want_info) {
552 (void)clock_gettime(CLOCK_MONOTONIC, &t_end);
553 fprintf(stderr, "tcopy: file %d record %ju ",
554 filen, (uintmax_t)record);
555 report_total(stderr);
556 want_info = 0;
557 }
558 }
559
560 #else /* !SIGINFO */
561
562 static void
check_want_info(void)563 check_want_info(void)
564 {
565 }
566
567 #endif
568
569 static char *
getspace(size_t blk)570 getspace(size_t blk)
571 {
572 void *bp;
573
574 assert(blk > 0);
575 if ((bp = malloc(blk)) == NULL)
576 errx(11, "no memory");
577 return ((char *)bp);
578 }
579
580 static void
usage(void)581 usage(void)
582 {
583 fprintf(stderr,
584 "usage: tcopy [-crvx] [-l logfile] [-s maxblk] [src [dest]]\n"
585 );
586 exit(1);
587 }
588
589 static void
progress(ssize_t nread)590 progress(ssize_t nread)
591 {
592 if (nread != lastnread) {
593 if (lastnread != 0 && lastnread != NOCOUNT) {
594 if (lastrec == 0 && nread == 0)
595 fprintf(msg, "%ju records\n",
596 (uintmax_t)record);
597 else if (record - lastrec > 1)
598 fprintf(msg, "records %ju to %ju\n",
599 (uintmax_t)lastrec,
600 (uintmax_t)record);
601 else
602 fprintf(msg, "record %ju\n",
603 (uintmax_t)lastrec);
604 }
605 if (nread != 0)
606 fprintf(msg,
607 "file %d: block size %zd: ", filen, nread);
608 (void) fflush(msg);
609 lastrec = record;
610 }
611 if (nread > 0) {
612 fsize += (size_t)nread;
613 record++;
614 } else {
615 if (lastnread <= 0 && lastnread != NOCOUNT) {
616 fprintf(msg, "eot\n");
617 return;
618 }
619 fprintf(msg,
620 "file %d: eof after %ju records: %ju bytes\n",
621 filen, (uintmax_t)record, (uintmax_t)fsize);
622 filen++;
623 tsize += fsize;
624 fsize = record = lastrec = 0;
625 lastnread = 0;
626 }
627 lastnread = nread;
628 }
629
630 static void
read_or_copy(void)631 read_or_copy(void)
632 {
633 int needeof;
634 ssize_t nread, prev_read;
635 char *buff = getspace(maxblk);
636
637 (void)clock_gettime(CLOCK_MONOTONIC, &t_start);
638 needeof = 0;
639 for (prev_read = NOCOUNT;;) {
640 check_want_info();
641 nread = input->read_blk(buff, maxblk);
642 progress(nread);
643 if (nread > 0) {
644 if (output != NULL) {
645 if (needeof) {
646 output->file_mark();
647 needeof = 0;
648 }
649 output->write_blk(buff, (size_t)nread);
650 }
651 } else {
652 if (prev_read <= 0 && prev_read != NOCOUNT) {
653 break;
654 }
655 needeof = 1;
656 }
657 prev_read = nread;
658 }
659 (void)clock_gettime(CLOCK_MONOTONIC, &t_end);
660 report_total(msg);
661 free(buff);
662 }
663
664 static void
verify(void)665 verify(void)
666 {
667 char *buf1 = getspace(maxblk);
668 char *buf2 = getspace(maxblk);
669 int eot = 0;
670 ssize_t nread1, nread2;
671 filen = 0;
672 tsize = 0;
673
674 assert(output != NULL);
675 (void)clock_gettime(CLOCK_MONOTONIC, &t_start);
676
677 while (1) {
678 check_want_info();
679 nread1 = input->read_blk(buf1, (size_t)maxblk);
680 nread2 = output->verify_blk(buf2, maxblk, (size_t)nread1);
681 progress(nread1);
682 if (nread1 != nread2) {
683 fprintf(msg,
684 "tcopy: tapes have different block sizes; "
685 "%zd != %zd.\n", nread1, nread2);
686 exit(1);
687 }
688 if (nread1 > 0 && memcmp(buf1, buf2, (size_t)nread1)) {
689 fprintf(msg, "tcopy: tapes have different data.\n");
690 exit(1);
691 } else if (nread1 > 0) {
692 eot = 0;
693 } else if (eot++) {
694 break;
695 }
696 }
697 (void)clock_gettime(CLOCK_MONOTONIC, &t_end);
698 report_total(msg);
699 fprintf(msg, "tcopy: tapes are identical.\n");
700 fprintf(msg, "rewinding\n");
701 input->rewind();
702 output->rewind();
703
704 free(buf1);
705 free(buf2);
706 }
707
708 int
main(int argc,char * argv[])709 main(int argc, char *argv[])
710 {
711 enum operation op = READ;
712 int ch;
713 unsigned long maxphys = 0;
714 size_t l_maxphys = sizeof maxphys;
715 int64_t tmp;
716 int rawfile = 0;
717
718 setbuf(stderr, NULL);
719
720 if (!sysctlbyname("kern.maxphys", &maxphys, &l_maxphys, NULL, 0UL))
721 maxblk = maxphys;
722
723 msg = stdout;
724 while ((ch = getopt(argc, argv, "cl:rs:vx")) != -1)
725 switch((char)ch) {
726 case 'c':
727 op = COPYVERIFY;
728 break;
729 case 'l':
730 msg = fopen(optarg, "w");
731 if (msg == NULL)
732 errx(EX_CANTCREAT, "Cannot open %s", optarg);
733 setbuf(msg, NULL);
734 break;
735 case 'r':
736 rawfile = 1;
737 break;
738 case 's':
739 if (expand_number(optarg, &tmp)) {
740 warnx("illegal block size");
741 usage();
742 }
743 if (tmp <= 0) {
744 warnx("illegal block size");
745 usage();
746 }
747 maxblk = tmp;
748 break;
749 case 'v':
750 op = VERIFY;
751 break;
752 case 'x':
753 if (msg == stdout)
754 msg = stderr;
755 break;
756 case '?':
757 default:
758 usage();
759 }
760 argc -= optind;
761 argv += optind;
762
763 switch(argc) {
764 case 0:
765 if (op != READ)
766 usage();
767 break;
768 case 1:
769 if (op != READ)
770 usage();
771 break;
772 case 2:
773 if (op == READ)
774 op = COPY;
775 if (!strcmp(argv[1], "-")) {
776 if (op == COPYVERIFY)
777 errx(EX_USAGE,
778 "Cannot copy+verify with '-' destination");
779 if (msg == stdout)
780 msg = stderr;
781 }
782 if (op == VERIFY)
783 output = open_arg(argv[1], H_VERIFY, 0);
784 else
785 output = open_arg(argv[1], H_OUTPUT, rawfile);
786 break;
787 default:
788 usage();
789 }
790
791 if (argc == 0) {
792 input = open_arg(_PATH_DEFTAPE, H_INPUT, 0);
793 } else {
794 input = open_arg(argv[0], H_INPUT, 0);
795 }
796
797 if ((signal(SIGINT, SIG_IGN)) != SIG_IGN)
798 (void) signal(SIGINT, sigintr);
799
800 #ifdef SIGINFO
801 (void)signal(SIGINFO, siginfo);
802 #endif
803
804 if (op != VERIFY) {
805 if (op == COPYVERIFY) {
806 assert(output != NULL);
807 fprintf(msg, "rewinding\n");
808 input->rewind();
809 output->rewind();
810 }
811
812 read_or_copy();
813
814 if (op == COPY || op == COPYVERIFY) {
815 assert(output != NULL);
816 output->file_mark();
817 output->file_mark();
818 }
819 }
820
821 if (op == VERIFY || op == COPYVERIFY) {
822
823 if (op == COPYVERIFY) {
824 assert(output != NULL);
825 fprintf(msg, "rewinding\n");
826 input->rewind();
827 output->rewind();
828 input->direction = tape_dev::SRC;
829 output->direction = tape_dev::SRC;
830 }
831
832 verify();
833 }
834
835 if (msg != stderr && msg != stdout)
836 report_total(stderr);
837
838 return(0);
839 }
840