/* * COPYTAPE.C * * This program duplicates magnetic tapes, preserving the * blocking structure and placement of tape marks. * * This program was updated at * * U.S. Army Artificial Intelligence Center * HQDA (Attn: DACS-DMA) * Pentagon * Washington, DC 20310-0200 * * Phone: (202) 694-6900 * * Apparently, some copytape version was, on 1991-12-13, available from * oak.oakland.edu in /pub2/unix-c/tapes/copytape.tar.Z. Whether or * not it's still available from there now I have no idea. * * On 2000-10-19, I was told source and manpage were available from * http://sources.isc.org/utils/backup/copytape.txt, though I did * not check this myself. * ************************************************** * * THIS PROGRAM IS IN THE PUBLIC DOMAIN * ************************************************** * * May 2005 Mouse * Added -T. * * October 1998 Mouse * Ported to NetBSD and wgcc - note, USE_RMT code untested. * * July 1986 David S. Hayes * Made data file format human-readable. * * April 1985 David S. Hayes * Original Version. */ #include #include #include #include #include #include #include #include extern const char *__progname; /* max tape block size we're prepared to handle */ #define BUFLEN 262144 /* "out-of-band" length values, returned by input() and used by output() */ #define TAPE_MARK -100 /* represents a tape mark */ #define END_OF_TAPE -101 /* two consecutive tape marks */ #define FORMAT_ERROR -102 /* mangled data file */ static int fromtape; /* treat source as a tape drive */ static int totape; /* treat destination as a tape drive */ #define T_FILE 1 #define T_TAPE 2 #define T_PIPE 3 static int verbose; /* be chatty */ static const char *source; /* printable name of data source */ static const char *dest; /* printable name of data sink */ static char tapebuf[BUFLEN]; /* tape buffer (duh!) */ #ifdef USE_RMT #define ISRMT 0x10000000 #define FD 0x0fffffff static int t_ioctl(int fd, unsigned int ioc, char *arg) { if (fd & ISRMT) return(rmt_ioctl(fd&FD,ioc,arg)); return(ioctl(fd&FD,ioc,arg)); } static int t_read(int fd, void *buf, int n) { if (fd & ISRMT) return(rmt_read(fd&FD,buf,n)); return(read(fd&FD,buf,n)); } static int t_write(int fd, const void *buf, int n) { if (fd & ISRMT) return(rmt_write(fd&FD,buf,n)); return(write(fd&FD,buf,n)); } #else /* ie, without USE_RMT */ #define t_read read #define t_write write #define t_ioctl ioctl #endif /* USE_RMT */ /* All writing needs to be error-checked. */ static void o_write(int fd, const void *buf, int nb) { int n; n = t_write(fd,buf,nb); if (n < 0) { fprintf(stderr,"%s: write: %s\n",__progname,strerror(errno)); exit(1); } if (n != nb) { fprintf(stderr,"%s: short write: wanted %d, did %d\n",__progname,nb,n); exit(1); } } /* When reading from a file, read(...,n,...) is guaranteed to return n bytes if they are present. When reading from a pipe or a network connection, this is not the case. Hence.... */ static int Read(int fd, void *buf, int n) { int left; char *bp; int did; int nr; bp = buf; left = n; did = 0; while (left > 0) { nr = t_read(fd,bp,left); if (nr < 0) { if (did == 0) { did = -1; } break; } else if (nr == 0) { break; } else { did += nr; bp += nr; left -= nr; } } return(did); } static int opentape(const char *tf, char mode) { #ifdef USE_RMT const char *colon; const char *slash; #endif int rv; #ifdef USE_RMT if ((colon=index(tf,':')) && (!(slash=index(tf,'/')) || (slash > colon))) { char *t; int l; l = colon - tf; t = malloc(l+1); bcopy(tf,t,l); rv = rmt_open(t,colon+1,(mode=='r')?O_RDONLY:O_RDWR); if (rv < 0) { fprintf(stderr,"%s: %s on %s: %s\n",__progname,colon+1,t,strerror(errno)); } else { rv |= ISRMT; } free(t); } else #endif { rv = open(tf,(mode=='r')?O_RDONLY:(O_WRONLY|O_CREAT|O_TRUNC),0666); if (rv < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,tf,strerror(errno)); } } return(rv); } /* for the benefit of debuggers */ static int FMTERR(void) { return(FORMAT_ERROR); } /* * Input up to 256K from a file or tape. If input file is a tape, then * do markcount stuff. Input record length will be supplied by the * operating system. */ static int input(int fd) { static int markcount = 0; int len; int l2; char header[40]; switch (fromtape) { case T_TAPE: len = t_read(fd,&tapebuf[0],BUFLEN); switch (len) { case -1: perror("copytape: can't read input"); return(END_OF_TAPE); break; case 0: if (++markcount == 2) return(END_OF_TAPE); return(TAPE_MARK); break; default: markcount = 0; /* reset tape mark count */ return(len); } break; case T_FILE: /* Input is really a data file. */ l2 = Read(fd,&header[0],5); if (l2 == 0) return(END_OF_TAPE); if ((l2 != 5) || strncmp(&header[0],"CPTP:",5)) return(FMTERR()); l2 = Read(fd,&header[0],4); if (l2 != 4) return(FMTERR()); if (! strncmp(&header[0],"BLK ",4)) { l2 = Read(fd,&header[0],7); if (l2 != 7) return(FMTERR()); header[6] = '\0'; len = atoi(&header[0]); l2 = Read(fd,&tapebuf[0],len); if (l2 != len) return(FMTERR()); Read(fd,&header[0],1); /* trailing newline */ } else if (! strncmp(&header[0],"MRK\n",4)) { return(TAPE_MARK); } else if (! strncmp(&header[0],"EOT\n",4)) { return(END_OF_TAPE); } /* Sigh. There's at least one copytape version out there that writes utterly incompatible data files. Fortunately the format is not human-incomprehensible. It writes "CPTP:BLOCK %d\n" instead of "CPTP:BLK %06d\n", "CPTP:TAPE_MARK\n" instead of "CPTP:MRK\n", and "CPTP:END_OF_TAPE\n" instead of "CPTP:EOT\n". The biggest pain here is the variable-size number on the BLOCK lines. */ else if (! strncmp(&header[0],"BLOC",4)) { l2 = Read(fd,&header[0],2); if ((l2 != 2) || strncmp(&header[0],"K ",2)) return(FMTERR()); len = 0; while (1) { l2 = Read(fd,&header[len],1); if (l2 != 1) return(FMTERR()); if (header[len] == '\n') break; if ((header[len] < '0') || (header[len] > '9')) return(FMTERR()); if (len++ > 20) return(FMTERR()); } header[len] = '\0'; len = atoi(&header[0]); l2 = Read(fd,&tapebuf[0],len); if (l2 != len) return(FMTERR()); Read(fd,&header[0],1); /* trailing newline */ } else if (! strncmp(&header[0],"TAPE",4)) { l2 = Read(fd,&header[0],6); if ((l2 != 6) || strncmp(&header[0],"_MARK\n",6)) return(FMTERR()); return(TAPE_MARK); } else if (! strncmp(&header[0],"END_",4)) { l2 = Read(fd,&header[0],8); if ((l2 != 8) || strncmp(&header[0],"OF_TAPE\n",8)) return(FMTERR()); return(END_OF_TAPE); } /* Anything else is a format error. */ else { return(FMTERR()); } return(len); break; } abort(); } /* * Copy a buffer out to a file or tape. * * If output is a tape, write the record. A length of zero indicates that * a tapemark should be written. * * If not a tape, write len to the output file, then the buffer. */ static void output(int fd, int len) { struct mtop op; char header[20]; switch (totape) { default: abort(); break; case T_TAPE: switch (len) { case TAPE_MARK: case END_OF_TAPE: op.mt_op = MTWEOF; op.mt_count = 1; if (t_ioctl(fd,MTIOCTOP,&op) < 0) { fprintf(stderr,"%s: MTWEOF 1: %s\n",__progname,strerror(errno)); exit(1); } return; break; case FORMAT_ERROR: return; break; } break; case T_FILE: switch (len) { case TAPE_MARK: o_write(fd,"CPTP:MRK\n",9); break; case END_OF_TAPE: o_write(fd,"CPTP:EOT\n",9); break; case FORMAT_ERROR: break; default: sprintf(header,"CPTP:BLK %06d\n",len); o_write(fd,header,strlen(header)); o_write(fd,&tapebuf[0],len); o_write(fd,"\n",1); break; } return; break; case T_PIPE: switch (len) { case TAPE_MARK: case END_OF_TAPE: case FORMAT_ERROR: return; break; } break; } o_write(fd,&tapebuf[0],len); } static const char *sourcetext(int s) { switch (s) { case T_FILE: return("file"); break; case T_TAPE: return("tape"); break; case T_PIPE: return("pipe"); break; } abort(); } int main(int, char **); int main(int ac, char **av) { int from; int to; int len; int skip; unsigned int limit; int i; struct mtget status; fromtape = T_FILE; totape = T_FILE; verbose = 0; source = "stdin"; dest = "stdout"; from = 0; to = 1; skip = 0; limit = ~0U; for (i=1;(i 0); switch (len) { case FORMAT_ERROR: fprintf(stderr,"%s: format error during skip\n",__progname); exit(1); break; case END_OF_TAPE: fprintf(stderr,"%s: only %d files in input\n",__progname,i); exit(1); break; } } /* * Do the copy. */ len = 0; while (limit && !((len == END_OF_TAPE) || (len == FORMAT_ERROR))) { do { while (1) { len = input(from); if (len != FORMAT_ERROR) break; fprintf(stderr,"%s: data format error - block ignored\n",__progname); } output(to,len); if (verbose) { switch (len) { case TAPE_MARK: fprintf(stderr," copied MRK\n"); break; case END_OF_TAPE: fprintf(stderr," copied EOT\n"); break; default: fprintf(stderr," copied %d bytes\n",len); break; } } } while (len > 0); limit --; } exit(0); }