char *vers = "copytape version 1.2, 15 May 87";

/* copytape -- tape-to-tape copy program */
/*
/* F. da Cruz, Columbia University Center for Computing Activities, May 1987 */
/*
/* Parts cribbed from Jim Guyton's readtape and writetape programs, and */
/* from ansitar.  If run with debug printout, output tape is slightly /*
/* longer than input tape. */

/* Suggested minor improvements: check that input & output devices are */ 
/* really tapes.  Really check that input & output tape not same device. */ 
/* Add some statistics.  Use dynamic buffers & pointers, rather than fixed, */
/* to save space.  Etc. */

/* Includes */

#include <stdio.h>			/* standard i/o */
#include <sys/types.h>			/* data types needed by below */
#include <sys/mtio.h>			/* magtape i/o */
#include <sys/ioctl.h>			/* i/o control */

/* I/O descriptors, flags, etc. */

int in, out;				/* input, output tapes */
int teof;				/* input tape eof flag */
int debug = 0, dv = 0;			/* command line option flags */
long copyfile();			/* forward dcl of non-int function */

/* Tape stuff */

char *indef = "/dev/rmt12";		/* default input drive */
char *outdef = "/dev/rmt13";		/* default output drive */
char *intape = "";
char *outape = "";
struct mtop mtx;			/* tape i/o structure for ioctl */

/* Buffer stuff */

#define BLKSIZE 30*1024			/* I/O buffer size */
#define BLOCKS 10			/* Number of buffers */
int maxblock = BLKSIZE;
char buf[BLOCKS][BLKSIZE];		/* Declare buffer array */
int buflen[BLOCKS];			/* & array of data lengths per buf */

char errbuf[100];			/* Buffer for making error strings */

/* Main program */

main(argc,argv) int argc; char *argv[]; {

    long ct, files, bytes = 0L;
    int filenum = 1;
    char x, *xp;

    while (--argc > 0) {		/* Parse options */
	argv++;
	if (**argv == '-') {
	    x = *(*argv+1);		/* Option letter */
	    xp = *argv+1;		/* Point past dash */
	    while (x) {			/* Loop thru bundled options */
		switch(x) {
		  case 'd':		/* Debugging display */
		    debug = 1;
		    break;
		  case 'f':		/* From device */
		    if (*(xp+1)) fatal("Invalid argument bundling");
		    intape = *++argv;
		    argc--;
		    if (*intape == '-' || argc <= 0)
		      fatal("-f argument missing");
		    break;
		  case 't':		/* To device */
		    if (*(xp+1)) fatal("Invalid argument bundling");
		    outape = *++argv;
		    argc--;
		    if (*outape == '-' || argc <= 0)
		      fatal("-t argument missing");
		    break;
		  case 'v':		/* Version info */
		    dv = 1;
		    break;
		  default  : usage();
	        }
		x = *++xp;		/* See if options are bundled */
	    }
	} else usage();
    }
    if (debug || dv) {			/* Display version info if asked */
	fprintf(stderr,"%s\n",vers);
	fprintf(stderr,"%d buffers, %d bytes each\n\n",BLOCKS,maxblock);
    }
  
    if (!*intape) intape = indef;	/* Supply defaults */
    if (!*outape) outape = outdef;
    if (strcmp(intape,"0") == 0) intape = indef;
    if (strcmp(intape,"1") == 0) intape = outdef;
    if (strcmp(outape,"0") == 0) outape = indef;
    if (strcmp(outape,"1") == 0) outape = outdef;
    if (strcmp(outape,intape) == 0) {
	fprintf(stderr,"You must specify two different tape drives\n");
	exit(1);
    }
    if (debug) {
	fprintf(stderr,"input drive: %s\n",intape);
	fprintf(stderr,"output drive: %s\n",outape);
    }
    in = open(intape, 0);		/* Open input tape */
    if (in > 0)
      printf("Reading from tape '%s'\n", intape);
    else {
	sprintf(errbuf,"Can't open tape %s for reading",intape);
	fatal(errbuf);
    }
    if (rew(in) < 0) 			/* Rewind to load point */
       fatal("Can't rewind input tape");

    out = open(outape, 1);		/* Open output tape */
    if (out > 0)
      printf("Writing to tape '%s'\n", outape);
    else {
	sprintf(errbuf,"Can't open tape %s for writing",outape);
	fatal(errbuf);
    }
    if (rew(out) < 0) 			/* Rewind to load point */
       fatal("Can't rewind output tape");

    system("date");			/* Say when starting */
    ct = files = 1;			/* Initialize counters */
    while (ct) {			/* Copy each file on tape */
      if (debug) fprintf(stderr,"\nfile %ld:", files);
      ct = copyfile();			/* Copy the file */
      files++;				/* Count the file */
      bytes += ct;			/* Accumulate total byte count */
    }
    tapemark(out);			/* Write final tapemark */
    close(out);				/* Close tape drives */
    close(in);
    system("date");			/* When done, print messages */
    printf("\nDone, copied %ld bytes in %ld file(s)\n", bytes, (files - 1));
    exit(0);
}

long					/* Copy a file from in to out */
copyfile() {				/* Returns number of bytes read */

    int i, blks;			/* Loop control, block count */
    long ct = 0;			/* Character count */

    teof = 0;				/* Copy tape blocks till tape eof */
					/* Do several buffers at a time */
    while (!teof) {

	for (blks = i = 0; i < BLOCKS; i++) { /* Read some buffers */

	    if ((buflen[i] = read(in, buf[i], BLKSIZE)) < 0)
	      fatal("I/O error reading tape");
	    else if (buflen[i] > 0) {
		blks++;			/* Count this block */
		if (debug) fprintf(stderr," %d:%d",i,buflen[i]);
	    } else {			/* Count of 0 means end of tape file */
		if (debug) fprintf(stderr," eof"); 
		teof = 1;		/* Flag tape file eof. */
		break;
	    }
	}
	if (debug) fprintf(stderr," [%d]",blks); /* # of blocks read */

	for (i = 0; i < blks; i++) {	/* Write out the buffers. */

	    if (debug) fprintf(stderr," w%d:%d",i,buflen[i]);
	    if (write(out, buf[i], buflen[i]) != buflen[i])
	      fatal("Write error");
	    ct += buflen[i];		/* Keep track of # of bytes written */
	    buflen[i] = 0;
	}
    }
    if (debug) fprintf(stderr," tm");
    if (tapemark(out) < 0)		/* Write file mark. */
      fatal("Can't write tape mark");
    return(ct);				/* Return output byte count. */
}

tapemark(tf) int tf; {			/* Write a tapemark (file mark) */
    mtx.mt_count = 1;
    mtx.mt_op = MTWEOF;
    return(ioctl(tf, MTIOCTOP, (char *) &mtx));
}

rew(tf) int tf; {			/* Rewind tape */
    mtx.mt_op = MTREW;
    return(ioctl(tf, MTIOCTOP, (char *) &mtx));
}

fatal(s) char *s; {			/* Fatal error message and exit */
    perror(s);				/* Include last system error */
    exit(1);
}

usage() {
    fprintf(stderr,"usage: copytape [-d -v -f intape -t outtape]\n");
    fprintf(stderr,"-d=debug, -v=version, -f=from, -t=to\n");
    fprintf(stderr,"intape default %s, outtape default %s\n",indef,outdef);
    fprintf(stderr,"maximum blocksize: %ld.\n",maxblock);
    fprintf(stderr,"number of blocks buffered: %d.\n",BLOCKS);
    exit(1);
}
