home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume1 / 8707 / 36 < prev    next >
Encoding:
Internet Message Format  |  1990-07-13  |  14.2 KB

  1. From: merlin@hqda-ai.UUCP (David S. Hayes)
  2. Newsgroups: comp.sources.misc
  3. Subject: copytape -- copy magtapes (BSD or magtape ioctls thereof)
  4. Message-ID: <2831@ncoast.UUCP>
  5. Date: 9 Jul 87 02:56:03 GMT
  6. Sender: allbery@ncoast.UUCP
  7. Lines: 543
  8. Approved: allbery@ncoast.UUCP
  9. X-Archive: comp.sources.misc/8707/36
  10.  
  11. [Good news for me, sort of; Plexus Sys5 1.5 has the BSD magtape ioctls listed
  12. somewhere in /usr/include/sys.  Even if you're not BSD, you may have them;
  13. they are something of a de-facto standard.  ++bsa]
  14.  
  15.      Here's a baby I whipped up for myself some time ago.  It
  16. duplicates magtapes.  Any magtapes.  Any format, any character
  17. set, any (semi-reasonable) block size.  Basically, it's the Unix
  18. equivalent of a nybble-copier.
  19.  
  20.      This runs on Sun equipment, using the 4.2BSD ioctl calls for
  21. magtape operations.  My manuals don't say whether System V has
  22. these, but it should not be difficult.  All that is required is
  23. the ability to write a tape mark.  I wouldn't really have sent
  24. this in, but I recently found that a particular manufacturer, who
  25. shall go unnamed, wants $500 for this from their consulting
  26. services division.
  27.  
  28.      Now, everybody who was going to buy from that *other* vendor,
  29. please send 10% to me :-), care of "David S. Hayes Retirement Fund".
  30. [Remember, don't send cash through the electronic mails.]
  31.  
  32. David S. Hayes, The Merlin of Avalon    PhoneNet:  (202) 694-6900
  33. UUCP:  *!seismo!sundc!hqda-ai!merlin    ARPA: merlin%hqda-ai@seismo.css.gov
  34.  
  35. # This is a shell archive.  Remove anything before this line, then
  36. # unpack it by saving it in a file and typing "sh file".  (Files
  37. # unpacked will be owned by you and have default permissions.)
  38. #
  39. # This archive contains:
  40. # Makefile copytape.1 copytape.5 copytape.c
  41. #
  42. #!/bin/sh
  43. echo x - Makefile
  44. cat > "Makefile" << '//E*O*F Makefile//'
  45. MAN1 =    /usr/man/man1
  46. MAN5 =    /usr/man/man5
  47. BIN =    /usr/local/bin
  48.  
  49. copytape:    copytape.c
  50.     cc -O -o copytape copytape.c
  51.  
  52. install:    copytape
  53.     install -s -m 0511 copytape ${BIN}
  54.  
  55. man:    man1 man5
  56.  
  57. man1:    ${MAN1}/copytape.1
  58.     cp copytape.1 ${MAN1}
  59.  
  60. man5:    ${MAN5}/copytape.5
  61.     cp copytape.5 ${MAN5}
  62. //E*O*F Makefile//
  63.  
  64. echo x - copytape.1
  65. cat > "copytape.1" << '//E*O*F copytape.1//'
  66. .TH COPYTAPE 1 "25 June 1986"
  67. .\"@(#)copytape.1 1.0 86/07/08 AICenter; by David S. Hayes
  68. .SH NAME
  69. copytape \- duplicate magtapes
  70. .SH SYNOPSIS
  71. .B copytape
  72. \[\-f\]
  73. \[\-t\]
  74. \[\-s\fInnn\fP\]
  75. \[\-l\fInnn\fP\]
  76. \[\-v\]
  77. .I
  78. \[input \[output\]\]
  79. .SH DESCRIPTION
  80. .LP
  81. .I copytape
  82. duplicates magtapes.  It is intended for duplication of
  83. bootable or other non-file-structured (non-tar-structured)
  84. magtapes on systems with only one tape drive.
  85. .I copytape
  86. is blissfully ignorant of tape formats.  It merely makes
  87. a bit-for-bit copy of its input.
  88. .PP
  89. In normal use,
  90. .I copytape
  91. would be run twice.  First, a boot tape is copied to an
  92. intermediate disk file.  The file is in a special format that
  93. preserves the record boundaries and tape marks.  On the second
  94. run, 
  95. .I copytape
  96. reads this file and generates a new tape.  The second step
  97. may be repeated if multiple copies are required.  The typical
  98. process would look like this:
  99. .sp
  100. .RS +.5i
  101. tutorial% copytape /dev/rmt8 tape.tmp
  102. .br
  103. tutorial% copytape tape.tmp /dev/rmt8
  104. .br
  105. tutorial% rm tape.tmp
  106. .RE
  107. .PP
  108. .I copytape
  109. copies from the standard input to the standard output, unless
  110. input and output arguments are provided.  It will automatically
  111. determine whether its input and output are physical tapes, or
  112. data files.  Data files are encoded in a special (human-readable)
  113. format.
  114. .PP
  115. Since
  116. .I copytape
  117. will automatically determine what sort of thing its input
  118. and output are, a twin-drive system can duplicate a tape in
  119. one pass.  The command would be
  120. .RS +.5i
  121. tutorial% copytape /dev/rmt8 /dev/rmt9
  122. .RE
  123. .SH OPTIONS
  124. .TP 3
  125. .RI \-s nnn
  126. Skip tape marks.  The specified number of tape marks are skipped
  127. on the input tape, before the copy begins.  By default, nothing is
  128. skipped, resulting in a copy of the complete input tape.  Multiple
  129. tar(1) and dump(1) archives on a single tape are normally
  130. separated by a single tape mark.  On ANSI or IBM labelled tapes,
  131. each file has three associated tape marks.  Count carefully.
  132. .TP 3
  133. .RI \-l nnn
  134. Limit.  Only nnn files (data followed by a tape mark), at most,
  135. are copied.  This can be used to terminate a copy early.  If the
  136. skip option is also specified, the files skipped do not count
  137. against the limit.
  138. .TP 3
  139. \-f
  140. >From tape.  The input is treated as though it were a physical
  141. tape, even if it is a data file.  This option can be used
  142. to copy block-structured device files other than magtapes.
  143. .TP 3
  144. \-t
  145. To tape.  The output is treated as though it were a physical
  146. tape, even if it is a data file.  Normally, data files mark
  147. physical tape blocks with a (human\-readable) header describing
  148. the block.  If the \-t option is used when the output is
  149. actually a disk file, these headers will not be written.
  150. This will extract all the information from the tape, but
  151. .I copytape
  152. will not be able to duplicate the original tape based on
  153. the resulting data file.
  154. .TP 3
  155. \-v
  156. Verbose.
  157. .I copytape
  158. does not normally produce any output on the control terminal.
  159. The verbose option will identify the input and output files,
  160. tell whether they are physical tapes or data files, and
  161. announce the size of each block copied.  This can produce
  162. a lot of output on even relatively short tapes.  It is
  163. intended mostly for diagnostic work.
  164. .SH FILES
  165. /dev/rmt*
  166. .SH "SEE ALSO"
  167. ansitape(1), dd(1), tar(1), mtio(4), copytape(5)
  168. .SH AUTHOR
  169. David S. Hayes, Site Manager, US Army Artificial Intelligence Center.
  170. Originally developed September 1984 at Rensselaer Polytechnic Institute,
  171. Troy, New York.
  172. Revised July 1986.  This software is in the public domain.
  173. .SH BUGS
  174. .LP
  175. .I copytape
  176. treats two successive file marks as logical end-of-tape.
  177. .LP
  178. The intermediate data file can consume huge amounts of
  179. disk space.  A full 2400-foot reel can burn 50 megabytes.
  180. This is not strictly speaking a bug, but users should
  181. be aware of the possibility.  Check disk space with
  182. .I df(1)
  183. before starting
  184. .IR copytape .
  185. Caveat Emptor!
  186. //E*O*F copytape.1//
  187.  
  188. echo x - copytape.5
  189. cat > "copytape.5" << '//E*O*F copytape.5//'
  190. .TH COPYTAPE 5  "8 August 1986"
  191. .SH NAME
  192. copytape \- copytape intermediate data file format
  193. .SH DESCRIPTION
  194. .I copytape
  195. duplicates magtapes on single\-tape systems by making
  196. an intermediate copy of the tape in a disk file.
  197. This disk file has a special format that preserves
  198. the block boundaries and tape marks of the original
  199. physical tape.
  200. .PP
  201. Each block is preceded by a header identifying what
  202. sort of block it is.  In the case of data blocks,
  203. the length of the data is also given.  Each header is
  204. on a separate text line, followed by a newline character.
  205. .sp
  206. .TP 3
  207. CPTP:BLK \fInnnnn\fP
  208. .ti -3
  209. \fIdata\fP\\n
  210. .sp
  211. A data block is identified by the keyword
  212. .IR BLK .
  213. The length of the block is given in a five\-character
  214. numeric field.  The field is zero\-padded on the left if
  215. less than five characters are needed.  The header is
  216. followed by a newline character.
  217. The original data follows.  The data may have any characters
  218. in it, since
  219. .I copytape
  220. uses an fread(3) to extract it.
  221. The data is followed by a newline, to make the file easy
  222. to view with an editor.
  223. .TP 3
  224. CPTP:MRK
  225. A tape mark was encountered in the original tape.
  226. .TP 3
  227. CPTP:EOT
  228. When two consecutive tape marks are encountered,
  229. .I copytape
  230. treats the second as a logical end\-of\-tape.  On
  231. output, both MRK and EOT generate
  232. a physical tape mark.
  233. .I copytape
  234. stops processing after copying an EOT.
  235. .SH "SEE ALSO"
  236. mtio(4)
  237. .SH BUGS
  238. Some weird tapes may not use two consecutive tape marks
  239. as logical end\-of\-tape.
  240. //E*O*F copytape.5//
  241.  
  242. echo x - copytape.c
  243. cat > "copytape.c" << '//E*O*F copytape.c//'
  244.  
  245. /*
  246.  * COPYTAPE.C
  247.  *
  248.  * This program duplicates magnetic tapes, preserving the
  249.  * blocking structure and placement of tape marks.
  250.  *
  251.  * This program was updated at
  252.  *
  253.  *    U.S. Army Artificial Intelligence Center
  254.  *    HQDA (Attn: DACS-DMA)
  255.  *    Pentagon
  256.  *    Washington, DC  20310-0200
  257.  *
  258.  *    Phone: (202) 694-6900
  259.  *
  260.  **************************************************
  261.  *
  262.  *    THIS PROGRAM IS IN THE PUBLIC DOMAIN
  263.  *
  264.  **************************************************
  265.  *
  266.  * July 1986        David S. Hayes
  267.  *        Made data file format human-readable.
  268.  *
  269.  * April 1985        David S. Hayes
  270.  *        Original Version.
  271.  */
  272.  
  273. #include <stdio.h>
  274. #include <sys/types.h>
  275. #include <sys/ioctl.h>
  276. #include <sys/mtio.h>
  277. #include <sys/file.h>
  278.  
  279. extern int      errno;
  280.  
  281. #define BUFLEN        65536    /* max tape block size */
  282. #define TAPE_MARK    -100    /* return record length if we read a
  283.                  * tape mark */
  284. #define END_OF_TAPE    -101    /* 2 consecutive tape marks */
  285. #define FORMAT_ERROR    -102    /* data file munged */
  286.  
  287. int             totape = 0,    /* treat destination as a tape drive */
  288.                 fromtape = 0;    /* treat source as a tape drive */
  289.  
  290. int             verbose = 0;    /* tell what we're up to */
  291.  
  292. char           *source = "stdin",
  293.                *dest = "stdout";
  294.  
  295. char            tapebuf[BUFLEN];
  296.  
  297. main(argc, argv)
  298.     int             argc;
  299.     char           *argv[];
  300. {
  301.     int             from = 0,
  302.                     to = 1;
  303.     int             len;    /* number of bytes in record */
  304.     int             skip = 0;    /* number of files to skip before
  305.                  * copying */
  306.     unsigned int    limit = 0xffffffff;
  307.     int             i;
  308.     struct mtget    status;
  309.  
  310.     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
  311.     switch (argv[i][1]) {
  312.       case 's':        /* skip option */
  313.         skip = atoi(&argv[i][2]);
  314.         break;
  315.  
  316.       case 'l':
  317.         limit = atoi(&argv[i][2]);
  318.         break;
  319.  
  320.       case 'f':        /* from tape option */
  321.         fromtape = 1;
  322.         break;
  323.  
  324.       case 't':        /* to tape option */
  325.         totape = 1;
  326.         break;
  327.  
  328.       case 'v':        /* be wordy */
  329.         verbose = 1;
  330.         break;
  331.  
  332.       default:
  333.         fprintf(stderr, "usage: copytape [-f] [-t] [-lnn] [-snn] [-v] from to\n");
  334.         exit(-1);
  335.     }
  336.     }
  337.  
  338.     if (i < argc) {
  339.     from = open(argv[i], O_RDONLY);
  340.     source = argv[i];
  341.     if (from == -1) {
  342.         perror("copytape: input open failed");
  343.         exit(-1);
  344.     }
  345.     i++;;
  346.     }
  347.     if (i < argc) {
  348.     to = open(argv[i], O_WRONLY | O_CREAT | O_TRUNC, 0666);
  349.     dest = argv[i];
  350.     if (to == -1) {
  351.         perror("copytape: output open failed");
  352.         exit(-1);
  353.     }
  354.     i++;
  355.     }
  356.     if (i < argc)
  357.     perror("copytape: extra arguments ignored");
  358.  
  359.     /*
  360.      * Determine if source and/or destination is a tape device. Try to
  361.      * issue a magtape ioctl to it.  If it doesn't error, then it was a
  362.      * magtape. 
  363.      */
  364.  
  365.     errno = 0;
  366.     ioctl(from, MTIOCGET, &status);
  367.     fromtape |= errno == 0;
  368.     errno = 0;
  369.     ioctl(to, MTIOCGET, &status);
  370.     totape |= errno == 0;
  371.     errno = 0;
  372.  
  373.     if (verbose) {
  374.     fprintf(stderr, "copytape: from %s (%s)\n",
  375.         source, fromtape ? "tape" : "data");
  376.     fprintf(stderr, "          to %s (%s)\n",
  377.         dest, totape ? "tape" : "data");
  378.     }
  379.  
  380.     /*
  381.      * Skip number of files, specified by -snnn, given on the command
  382.      * line. This is used to copy second and subsequent files on the
  383.      * tape. 
  384.      */
  385.  
  386.     if (verbose && skip) {
  387.     fprintf(stderr, "copytape: skipping %d input files\n", skip);
  388.     }
  389.     for (i = 0; i < skip; i++) {
  390.     do {
  391.         len = input(from);
  392.     } while (len > 0);
  393.     if (len == FORMAT_ERROR) {
  394.         perror(stderr, "copytape: format error on skip");
  395.         exit(-1);
  396.     };
  397.     if (len == END_OF_TAPE) {
  398.         fprintf(stderr, "copytape: only %d files in input\n", i);
  399.         exit(-1);
  400.     };
  401.     };
  402.  
  403.     /*
  404.      * Do the copy. 
  405.      */
  406.  
  407.     len = 0;
  408.     while (limit && !(len == END_OF_TAPE || len == FORMAT_ERROR)) {
  409.     do {
  410.         do {
  411.         len = input(from);
  412.         if (len == FORMAT_ERROR)
  413.             perror("copytape: data format error - block ignored");
  414.         } while (len == FORMAT_ERROR);
  415.  
  416.         output(to, len);
  417.  
  418.         if (verbose) {
  419.         switch (len) {
  420.           case TAPE_MARK:
  421.             fprintf(stderr, "  copied MRK\n");
  422.             break;
  423.  
  424.           case END_OF_TAPE:
  425.             fprintf(stderr, "  copied EOT\n");
  426.             break;
  427.  
  428.           default:
  429.             fprintf(stderr, "  copied %d bytes\n", len);
  430.         };
  431.         };
  432.     } while (len > 0);
  433.     limit--;
  434.     }
  435.     exit(0);
  436. }
  437.  
  438. /*
  439.  * Input up to 64K from a file or tape. If input file is a tape, then
  440.  * do markcount stuff.  Input record length will be supplied by the
  441.  * operating system. 
  442.  */
  443.  
  444. input(fd)
  445.     int             fd;
  446. {
  447.     static          markcount = 0;    /* number of consecutive tape
  448.                      * marks */
  449.     int             len,
  450.                     l2,
  451.                     c;
  452.     char            header[40];
  453.  
  454.     if (fromtape) {
  455.     len = read(fd, tapebuf, BUFLEN);
  456.     if (len == 0) {
  457.         if (++markcount == 2)
  458.         return END_OF_TAPE;
  459.         else
  460.         return TAPE_MARK;
  461.     }
  462.     markcount = 0;        /* reset tape mark count */
  463.     return len;
  464.     }
  465.     /* Input is really a data file. */
  466.     l2 = read(fd, header, 5);
  467.     if (l2 != 5 || strncmp(header, "CPTP:", 5) != 0)
  468.     return FORMAT_ERROR;
  469.  
  470.     l2 = read(fd, header, 4);
  471.     if (strncmp(header, "BLK ", 4) == 0) {
  472.     l2 = read(fd, header, 6);
  473.     if (l2 != 6)
  474.         return FORMAT_ERROR;
  475.     header[6] = '\0';
  476.     len = atoi(header);
  477.     l2 = read(fd, tapebuf, len);
  478.     if (l2 != len)
  479.         return FORMAT_ERROR;
  480.     read(fd, header, 1);    /* skip trailing newline */
  481.     } else if (strncmp(header, "MRK\n", 4) == 0)
  482.     return TAPE_MARK;
  483.     else if (strncmp(header, "EOT\n", 4) == 0)
  484.     return END_OF_TAPE;
  485.     else
  486.     return FORMAT_ERROR;
  487.  
  488.     return len;
  489. }
  490.  
  491. /*
  492.  * Copy a buffer out to a file or tape. 
  493.  *
  494.  * If output is a tape, write the record.  A length of zero indicates that
  495.  * a tapemark should be written. 
  496.  *
  497.  * If not a tape, write len to the output file, then the buffer.  
  498.  */
  499.  
  500. output(fd, len)
  501.     int             fd,
  502.                     len;
  503. {
  504.     struct mtop     op;
  505.     char            header[20];
  506.  
  507.     if (totape && (len == TAPE_MARK || len == END_OF_TAPE)) {
  508.     op.mt_op = MTWEOF;
  509.     op.mt_count = 1;
  510.     ioctl(fd, MTIOCTOP, &op);
  511.     return;
  512.     }
  513.     if (!totape) {
  514.     switch (len) {
  515.       case TAPE_MARK:
  516.         write(fd, "CPTP:MRK\n", 9);
  517.         break;
  518.  
  519.       case END_OF_TAPE:
  520.         write(fd, "CPTP:EOT\n", 9);
  521.         break;
  522.  
  523.       case FORMAT_ERROR:
  524.         break;
  525.  
  526.       default:
  527.         sprintf(header, "CPTP:BLK %05d\n", len);
  528.         write(fd, header, 15);
  529.         write(fd, tapebuf, len);
  530.         write(fd, "\n", 1);
  531.     }
  532.     } else
  533.     write(fd, tapebuf, len);
  534. }
  535. //E*O*F copytape.c//
  536.  
  537. echo Possible errors detected by \'wc\' [hopefully none]:
  538. temp=/tmp/shar$$
  539. trap "rm -f $temp; exit" 0 1 2 3 15
  540. cat > $temp <<\!!!
  541.       17      37     284 Makefile
  542.      120     643    3860 copytape.1
  543.       50     259    1475 copytape.5
  544.      294     940    6405 copytape.c
  545.      481    1879   12024 total
  546. !!!
  547. wc  Makefile copytape.1 copytape.5 copytape.c | sed 's=[^ ]*/==' | diff -b $temp -
  548. exit 0
  549.