# mt --- Software Tools Subsystem magnetic tape interface include "mt_def.r.i" include MT_COMMON ARG_DECL integer csw, file, mode, rec integer blk_spec, conv_spec, check_blk, check_unit, pos_spec, position, read_write, unit_spec string usage _ "Usage: mt [] [-p ] [-(r|w) [-b ] [-c ] {}] [-v]" procedure bomb forward if (unit_spec (Unit) == ERR) # get unit number, if specified call error (usage) if (check_unit (csw) == ERR) # see if drive is usable bomb PARSE_COMMAND_LINE ("pbcnrwv"s, usage) if (ARG_PRESENT (r) && ARG_PRESENT (w)) call error (usage) Conv = DEFAULT_CONV Recsize = DEFAULT_RECSIZE Blksize = DEFAULT_BLKSIZE Corrected = 0 Recovered = 0 Verbose = NO if (ARG_PRESENT (v)) Verbose = YES if (ARG_PRESENT (b) # parse blocking specification && blk_spec (ARG_TEXT (b), Recsize, Blksize) == ERR) bomb if (ARG_PRESENT (c) # parse conversion specification && conv_spec (ARG_TEXT (c), Conv) == ERR) bomb if (Conv == BINARY) # binary i/o is done 2 bytes per word Blksize = (Blksize + 1) / 2 if (check_blk (Conv, Recsize, Blksize) == ERR) bomb if (ARG_PRESENT (p)) # do positioning if (pos_spec (ARG_TEXT (p), mode, file, rec) == ERR || position (mode, file, rec) == ERR) bomb Baddr (1) = loc (Buffer_1) # initialize buffer pointers Baddr (2) = loc (Buffer_2) if (ARG_PRESENT (r)) # read files from tape csw = read_write (READ) elif (ARG_PRESENT (w)) # write files to tape csw = read_write (WRITE) if (csw == ERR) bomb if (Recovered ~= 0) { call print (ERROUT, "*i recovered i/o errors"s, Recovered) if (Corrected ~= 0) call print (ERROUT, " (*i CRC corrections)"s, Corrected) call putch (NEWLINE, ERROUT) } stop # bomb --- exit with error status procedure bomb { call seterr (1000) stop } end # ascii_to_ebcdic --- convert ASCII to EBCDIC subroutine ascii_to_ebcdic (buf, len) character buf (ARB) integer len integer i character table (128) data table / _ 8r000, 8r001, 8r002, 8r003, 8r067, 8r055, 8r056, 8r057, 8r026, 8r005, 8r045, 8r013, 8r014, 8r015, 8r016, 8r017, 8r020, 8r021, 8r022, 8r023, 8r074, 8r075, 8r062, 8r046, 8r030, 8r031, 8r077, 8r047, 8r034, 8r035, 8r036, 8r037, 8r100, 8r132, 8r177, 8r173, 8r133, 8r154, 8r120, 8r175, 8r115, 8r135, 8r134, 8r116, 8r153, 8r140, 8r113, 8r141, 8r360, 8r361, 8r362, 8r363, 8r364, 8r365, 8r366, 8r367, 8r370, 8r371, 8r172, 8r136, 8r114, 8r176, 8r156, 8r157, 8r174, 8r301, 8r302, 8r303, 8r304, 8r305, 8r306, 8r307, 8r310, 8r311, 8r321, 8r322, 8r323, 8r324, 8r325, 8r326, 8r327, 8r330, 8r331, 8r342, 8r343, 8r344, 8r345, 8r346, 8r347, 8r350, 8r351, 8r112, 8r340, 8r117, 8r137, 8r155, 8r171, 8r201, 8r202, 8r203, 8r204, 8r205, 8r206, 8r207, 8r210, 8r211, 8r221, 8r222, 8r223, 8r224, 8r225, 8r226, 8r227, 8r230, 8r231, 8r242, 8r243, 8r244, 8r245, 8r246, 8r247, 8r250, 8r251, 8r300, 8r152, 8r320, 8r241, 8r007/ do i = 1, len buf (i) = table (and (buf (i), 127) + 1) return end # blk_spec --- parse blocking specification argument for mt integer function blk_spec (str, recsz, blksz) integer recsz, blksz character str (ARB) integer i integer ctoi i = 1 recsz = ctoi (str, i) if (str (i) == '/'c || str (i) == '.'c) { i += 1 blksz = ctoi (str, i) * recsz } else blksz = recsz if (str (i) ~= EOS) { call remark ("syntax: -b [/]"s) return (ERR) } return (OK) end # check_blk --- check validity of block size integer function check_blk (conv, recsz, blksz) integer conv, recsz, blksz integer maxb, maxw if (conv == BINARY) { maxw = MAX_BLKSIZE maxb = MAX_BLKSIZE * 2 } else { maxw = MAX_BLKSIZE - 1 maxb = maxw } if (blksz > maxw) { call print (ERROUT, "maximum block size is *i bytes*n"s, maxb) return (ERR) } return (OK) end # pos_spec --- parse positioning arguments integer function pos_spec (str, mode, file, rec) character str (ARB) integer mode, file, rec integer i integer ctoi if (str (1) == '+'c || str (1) == '-'c) { if (str (1) == '+'c) mode = FORWARD else mode = REVERSE i = 2 } else { mode = ABSOLUTE i = 1 } file = ctoi (str, i) if (file == 0 && mode == ABSOLUTE) file = 1 if (str (i) == '.'c || str (i) == '/'c) { i += 1 rec = ctoi (str, i) } else rec = 0 if (rec == 0 && mode == ABSOLUTE) rec = 1 if (str (i) ~= EOS) { call remark ("syntax: -p [+|-][/]"s) return (ERR) } return (OK) end # check_unit --- check validity and usability of specified tape drive integer function check_unit (csw) integer csw include MT_COMMON integer mtscheck, mtstat csw = 0 select when (Unit < MIN_UNIT || Unit > MAX_UNIT) call print (ERROUT, "units are *i to *i*n"s, MIN_UNIT, MAX_UNIT) when (mtscheck (mtstat (csw)) == ERR) ; ifany return (ERR) return (OK) end # conv_spec --- parse conversion specification argument for mt integer function conv_spec (str, conv) integer conv character str (ARB) select (str (1)) when ('a'c, 'A'c) conv = ASCII when ('b'c, 'B'c) conv = BINARY when ('e'c, 'E'c) conv = EBCDIC else { call remark ("syntax: -c (a[scii] | b[inary] | e[bcdic])"s) return (ERR) } return (OK) end # ebcdic_to_ascii --- translate EBCDIC to internal ASCII subroutine ebcdic_to_ascii (buf, len) character buf (ARB) integer len character table (256) integer i define (o,8r77) # '?'c without parity data table / _ NUL , SOH , STX , ETX , o, HT , o, DEL , o, o, o, VT , FF , CR , SO , SI , DLE , DC1 , DC2 , DC3 , o, o, BS , o, CAN , EM , o, o, FS , GS , RS , US , o, o, o, o, o, LF , ETB , ESC , o, o, o, o, o, ENQ , ACK , BEL , o, o, SYN , o, o, o, o, EOT , o, o, o, o, DC4 , NAK , o, SUB , ' 'c, o, o, o, o, o, o, o, o, o, '['c, '.'c, '<'c, '('c, '+'c, ']'c, '&'c, o, o, o, o, o, o, o, o, o, '!'c, '$'c, '*'c, ')'c, ';'c, '^'c, '-'c, '/'c, o, o, o, o, o, o, o, o, '|'c, ','c, '%'c, '_'c, '>'c, '?'c, o, o, o, o, o, o, o, o, o, '`'c, ':'c, '#'c, '@'c, "'"c, '='c, '"'c, o, 'a'c, 'b'c, 'c'c, 'd'c, 'e'c, 'f'c, 'g'c, 'h'c, 'i'c, o, o, o, o, o, o, o, 'j'c, 'k'c, 'l'c, 'm'c, 'n'c, 'o'c, 'p'c, 'q'c, 'r'c, o, o, o, o, o, o, o, '~'c, 's'c, 't'c, 'u'c, 'v'c, 'w'c, 'x'c, 'y'c, 'z'c, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, '{'c, 'A'c, 'B'c, 'C'c, 'D'c, 'E'c, 'F'c, 'G'c, 'H'c, 'I'c, o, o, o, o, o, o, '}'c, 'J'c, 'K'c, 'L'c, 'M'c, 'N'c, 'O'c, 'P'c, 'Q'c, 'R'c, o, o, o, o, o, o, '\'c, o, 'S'c, 'T'c, 'U'c, 'V'c, 'W'c, 'X'c, 'Y'c, 'Z'c, o, o, o, o, o, o, '0'c, '1'c, '2'c, '3'c, '4'c, '5'c, '6'c, '7'c, '8'c, '9'c, o, o, o, o, o, o/ undefine (o) do i = 1, len buf (i) = table (buf (i) + 1) return end # getbuf --- read and convert one buffer from disk file integer function getbuf (bufn, fd) integer bufn, fd include MT_COMMON character fill integer nb, nw, i integer readf, readln if (Conv == BINARY) { select (bufn) when (1) { nw = readf (Buffer_1, Blksize, fd) if (nw <= 0) return (EOF) while (nw < Blksize) { nw += 1 Buffer_1 (nw) = BINARY_FILL } Buflen (1) = Blksize } when (2) { nw = readf (Buffer_2, Blksize, fd) if (nw <= 0) return (EOF) while (nw < Blksize) { nw += 1 Buffer_2 (nw) = BINARY_FILL } Buflen (2) = Blksize } } else { # we are reading a text file select (bufn) when (1) { for (nb = 0; nb < Blksize; nb += Recsize) if (readln (fd, Buffer_1 (nb + 1), Recsize) == EOF) break if (nb == 0) return (EOF) select (Conv) when (ASCII) { fill = ASCII_FILL do i = 1, nb Buffer_1 (i) &= 8r177 } when (EBCDIC) { fill = EBCDIC_FILL call ascii_to_ebcdic (Buffer_1, nb) } while (nb < Blksize) { nb += 1 Buffer_1 (nb) = fill } Buflen (1) = Blksize } when (2) { for (nb = 0; nb < Blksize; nb += Recsize) if (readln (fd, Buffer_2 (nb + 1), Recsize) == EOF) break if (nb == 0) return (EOF) select (Conv) when (ASCII) { fill = ASCII_FILL do i = 1, nb Buffer_2 (i) &= 8r177 } when (EBCDIC) { fill = EBCDIC_FILL call ascii_to_ebcdic (Buffer_2, nb) } while (nb < Blksize) { nb += 1 Buffer_2 (nb) = fill } Buflen (2) = Blksize } } DB { DB local junk, code DB integer junk, code DB call prwf$$ (2, 3, Baddr (bufn), Buflen (bufn), intl (0), DB junk, code) DB } return (OK) end # mtscheck --- check for unit online and ready integer function mtscheck (csw) integer csw select when (~ ONLINE (csw)) call remark ("drive is off line"s) when (~ READY (csw)) call remark ("drive is not ready"s) ifany return (ERR) return (OK) end # mtcheck --- check for errors and retry if necessary integer function mtcheck (rw, bufn, csw) integer rw, bufn, csw include MT_COMMON integer r integer mtscheck, mtstat logical corr procedure print_error_bits forward procedure retry forward call mtwait (Bufstat (1, bufn)) csw = Bufstat (CSW, bufn) if (mtscheck (csw) == ERR) return (ERR) if (~ ERROR_BITS (csw)) { Bufok (bufn) = YES Buflen (bufn) = Bufstat (NWR, bufn) return (OK) } corr = FALSE # don't try CRC correction yet do r = 1, MAX_RETRYS; { call mtskipr (REVERSE) retry } do r = 1, MAX_RETRYS; { call mtskipr (REVERSE) if (~ AT_BOT (mtstat (csw))) { call mtskipr (REVERSE) # draw bad spot across tape cleaner call mtskipr (FORWARD) } retry } if (rw == READ) { # now try CRC correction corr = TRUE retry } call print (ERROUT, "Block *5,-10i:"s, Block) print_error_bits call remark (" Unrecovered"s) Bufok (bufn) = NO return (OK) # print_error_bits --- display error status in readable form procedure print_error_bits { if (VPERR (csw)) call putlin (" VPE"s, ERROUT) if (RUNAWAY (csw)) call putlin (" RUN"s, ERROUT) if (CRCERR (csw)) call putlin (" CRC"s, ERROUT) if (LRCERR (csw)) call putlin (" LRC"s, ERROUT) if (DMARANGE (csw)) call putlin (" DMA"s, ERROUT) if (UNCERR (csw)) call putlin (" UNC"s, ERROUT) if (RAWERR (csw)) call putlin (" RAW"s, ERROUT) } # retry --- retry the troublesome operation procedure retry { call mtio (rw, bufn, corr) # retry operation call mtwait (Bufstat (1, bufn)) # wait for it to finish csw = Bufstat (CSW, bufn) # get status word if (mtscheck (csw) == ERR) # check for ONLINE and READY return (ERR) if (~ ERROR_BITS (csw)) { # block successfully transferred Recovered += 1 if (corr) Corrected += 1 Bufok (bufn) = YES Buflen (bufn) = Bufstat (NWR, bufn) return (OK) } } end # mtio --- perform raw i/o operations for mt subroutine mtio (rw, bufn, corr) integer rw, bufn logical corr include MT_COMMON integer instr, len select (rw) when (READ) { len = MAX_BLKSIZE if (Conv == BINARY) instr = NTREAD2 else instr = NTREAD1 if (corr) instr ^= NTCORRBIT } when (WRITE) { len = Buflen (bufn) if (Conv == BINARY) instr = NTWRITE2 else instr = NTWRITE1 } call t$mt (Unit, Baddr (bufn), len, instr, Bufstat (1, bufn)) return end # mtread --- perform read operations for mt integer function mtread (fd) integer fd include MT_COMMON integer bufn, csw integer on_eofr, on_eovr, mtcheck Block = 1 bufn = 1 call mtio (READ, bufn, FALSE) # read first block while (mtcheck (READ, bufn, csw) ~= ERR) { if (AT_EOF (csw)) # check for end of file return (on_eofr (csw)) if (AT_EOT (csw)) { # check for end of reel if (on_eovr (csw) == ERR) break call mtrewind call new_reel (READ) } call mtio (READ, NEXT_BUF (bufn), FALSE) # start reading next blk # if (Bufok (bufn) ~= NO) # now process previous block call putbuf (bufn, fd) bufn = NEXT_BUF (bufn) Block += 1 } return (ERR) end # mtrewind --- rewind tape subroutine mt_rewind include MT_COMMON integer status (8) save status call t$mt (Unit, loc (0), 0, REWIND, status) return end # mtskipf --- seek in either direction for file mark subroutine mtskipf (direction) integer direction include MT_COMMON integer instr, status (8) save status if (direction == FORWARD) instr = NTSKIPF else instr = NTBACKF call t$mt (Unit, loc (0), 0, instr, status) return end # mtskipr --- skip one record, forward or reverse subroutine mtskipr (direction) integer direction include MT_COMMON integer instr, status (8) save status if (direction == REVERSE) instr = NTBACKR else instr = NTSKIPR call t$mt (Unit, loc (0), 0, instr, status) return end # mtstat --- read status of tape drive integer function mtstat (csw) integer csw include MT_COMMON integer status (8) save status call t$mt (Unit, loc (0), 0, READ_STATUS, status) if (REWINDING (status (CSW))) { call mtrewind # will hang until previous rewind is done call t$mt (Unit, loc (0), 0, READ_STATUS, status) } csw = status (CSW) return (csw) end # mtwait --- wait for previous operation to complete subroutine mtwait (notdone) bool notdone integer csw while (notdone) call mtstat (csw) return end # mtwrite --- perform write operations for mt integer function mtwrite (fd) integer fd include MT_COMMON integer csw, bufn, stat integer getbuf, mtcheck, on_eofw, on_eovw Block = 1 bufn = 1 stat = getbuf (bufn, fd) while (stat ~= EOF) { call mtio (WRITE, bufn, FALSE) stat = getbuf (NEXT_BUF (bufn), fd) if (mtcheck (WRITE, bufn, csw) == ERR) return (ERR) if (AT_EOT (csw)) { # end of reel sensed if (stat == EOF && on_eofw (csw) == ERR || on_eovw (csw) == ERR) return (ERR) call mtrewind if (stat ~= EOF) call new_reel (WRITE) else return (OK) } bufn = NEXT_BUF (bufn) Block += 1 } return (on_eofw (csw)) end # mtfmark --- write a file mark subroutine mtfmark include MT_COMMON integer status (8) save status call t$mt (Unit, loc (0), 0, NTFMARK, status) return end # new_reel --- prompt user to mount a new reel subroutine new_reel (rw) integer rw include MT_COMMON integer csw integer input, check_unit repeat { while (input (STDIN, "unit: *i"s, Unit) == EOF) ; select when (check_unit (csw) == ERR) ; when (AT_EOT (csw)) call remark ("tape is at end of reel"s) when (rw == WRITE && FILE_PROTECT (csw)) call remark ("tape is write protected"s) when (rw == WRITE && IN_MID_FILE (csw)) call remark ("tape is in mid file"s) else break } return end # on_eofr --- do end of file processing for read operations integer function on_eofr (csw) integer csw csw = 0 return (OK) end # on_eofw --- do end of file processing for write operations integer function on_eofw (csw) integer csw integer mtstat, mtscheck call mtfmark call mtfmark if (mtscheck (mtstat (csw)) == ERR) return (ERR) call mtskipf (REVERSE) return (mtscheck (mtstat (csw))) end # on_eovr --- do end of volume processing for read operations integer function on_eovr (csw) integer csw csw = 0 return (OK) end # on_eovw --- do end of volume processing for write operations integer function on_eovw (csw) integer csw integer mtscheck, mtstat call mtfmark call mtfmark return (mtscheck (mtstat (csw))) end # position --- do tape positioning for mt integer function position (mode, file, rec) integer mode, file, rec include MT_COMMON integer i, csw integer mtstat, mtscheck select (mode) when (ABSOLUTE) { call mtrewind for (i = 1; i < file; i += 1) { if (mtscheck (mtstat (csw)) == ERR) return (ERR) if (AT_EOT (csw)) { call remark ("end of tape"s) return (ERR) } call mtskipf (FORWARD) } for (i = 1; i < rec; i += 1) { if (mtscheck (mtstat (csw)) == ERR) return (ERR) select when (AT_EOT (csw)) call remark ("end of tape"s) when (i > 1 && AT_EOF (csw)) call remark ("end of file"s) ifany return (ERR) call mtskipr (FORWARD) } } when (FORWARD) { for (i = 1; i <= file; i += 1) { if (mtscheck (mtstat (csw)) == ERR) return (ERR) if (AT_EOT (csw)) { call remark ("end of tape"s) return (ERR) } call mtskipf (FORWARD) } for (i = 1; i <= rec; i += 1) { if (mtscheck (mtstat (csw)) == ERR) return (ERR) select when (AT_EOT (csw)) call remark ("end of tape"s) when (i > 1 && AT_EOF (csw)) call remark ("end of file"s) ifany return (ERR) call mtskipr (FORWARD) } } when (REVERSE) { if (file ~= 0 || rec == 0) call mtskipf (REVERSE) for (i = 1; i <= file; i += 1) { if (mtscheck (mtstat (csw)) == ERR) return (ERR) if (AT_BOT (csw)) { call remark ("beginning of tape"s) return (ERR) } call mtskipf (REVERSE) } if (rec == 0 && ~ AT_BOT (mtstat (csw))) call mtskipf (FORWARD) else for (i = 1; i <= rec; i += 1) { if (mtscheck (mtstat (csw)) == ERR) return (ERR) select when (AT_BOT (csw)) call remark ("beginning of tape"s) when (i > 1 && AT_EOF (csw)) call remark ("beginning of file"s) ifany return (ERR) call mtskipr (REVERSE) } } if (mtscheck (mtstat (csw)) == ERR) return (ERR) return (OK) end # putbuf --- convert, deblock and write one buffer to disk file subroutine putbuf (bufn, fd) integer bufn, fd include MT_COMMON integer nb if (Conv == BINARY) { select (bufn) when (1) call writef (Buffer_1, Buflen (1), fd) when (2) call writef (Buffer_2, Buflen (2), fd) } else { select (bufn) when (1) { if (Conv == ASCII) for (nb = 1; nb <= Buflen (1); nb += 1) Buffer_1 (nb) |= 8r200 else call ebcdic_to_ascii (Buffer_1, Buflen (1)) for (nb = 0; nb + Recsize <= Buflen (1); nb += Recsize) call writeln (fd, Buffer_1 (nb + 1), Recsize) if (nb < Buflen (1)) call writeln (fd, Buffer_1 (nb + 1), Buflen (1) - nb) } when (2) { if (Conv == ASCII) for (nb = 1; nb <= Buflen (2); nb += 1) Buffer_2 (nb) |= 8r200 else call ebcdic_to_ascii (Buffer_2, Buflen (2)) for (nb = 0; nb + Recsize <= Buflen (2); nb += Recsize) call writeln (fd, Buffer_2 (nb + 1), Recsize) if (nb < Buflen (2)) call writeln (fd, Buffer_2 (nb + 1), Buflen (2) - nb) } } return end # read_write --- perform read/write operations for mt integer function read_write (rw) integer rw include MT_COMMON integer fd, csw, stat, state (4) integer gfnarg, create, open, mtread, mtwrite, mtstat, equal character arg (MAXLINE) if (AT_EOT (mtstat (csw))) { call remark ("tape is at end of reel"s) return } if (rw == WRITE) select when (FILE_PROTECT (csw)) call remark ("tape is not writable"s) when (IN_MID_FILE (csw)) call remark ("tape is in mid-file"s) ifany return stat = OK state (1) = 1 while (stat ~= ERR) select (gfnarg (arg, state)) when (EOF) break when (ERR) { call print (ERROUT, "*s: bad file name*n"s, arg) stat = ERR } when (OK) if (rw == READ) { # tape to disk select when (equal (arg, "/dev/stdin1"s) ~= NO) call scopy ("/dev/stdout1"s, 1, arg, 1) when (equal (arg, "/dev/stdin2"s) ~= NO) call scopy ("/dev/stdout2"s, 1, arg, 1) when (equal (arg, "/dev/stdin3"s) ~= NO) call scopy ("/dev/stdout3"s, 1, arg, 1) fd = create (arg, WRITE) if (fd == ERR) { stat = ERR call print (ERROUT, "*s: can't create*n"s, arg) } else { stat = mtread (fd) call close (fd) if (Verbose == YES) call print (ERROUT, "*s: *i blocks read from tape*n"s, arg, Block - 1) } } else { # disk to tape fd = open (arg, READ) if (fd == ERR) { call print (ERROUT, "*s: can't open*n"s, arg) stat = ERR } else { stat = mtwrite (fd) call close (fd) if (Verbose == YES) call print (ERROUT, "*s: *i blocks written to tape *n"s, arg, Block - 1) } } return (stat) end # readln --- character read primitive integer function readln (fd, buf, len) integer fd, buf (ARB), len integer getlin bool long_line data long_line /FALSE/ readln = getlin (buf, fd, len + 1) if (long_line && readln == 1 && buf (1) == NEWLINE) readln = getlin (buf, fd, len + 1) if (readln == EOF) return if (buf (readln) == NEWLINE) { long_line = FALSE readln -= 1 } else long_line = TRUE while (readln < len) { readln += 1 buf (readln) = ' 'c } return end # unit_spec --- parse unit specification argument integer function unit_spec (unit) integer unit integer i integer ctoi, getarg character arg (MAXARG) if (getarg (1, arg, MAXARG) ~= EOF && arg (1) ~= '-'c) { i = 1 unit = ctoi (arg, i) if (arg (i) ~= EOS) # syntax error return (ERR) call delarg (1) # get rid of the argument } else unit = DEFAULT_UNIT return (OK) end # writeln --- character write primitive subroutine writeln (fd, buf, len) integer fd, buf (ARB), len integer i, temp for (i = len; i > 0; i -= 1) if (buf (i) ~= ' 'c) break temp = buf (i + 1) buf (i + 1) = EOS call putlin (buf, fd) buf (i + 1) = temp call putch (NEWLINE, fd) return end