aboutsummaryrefslogtreecommitdiff
path: root/sys/mtio
diff options
context:
space:
mode:
Diffstat (limited to 'sys/mtio')
-rw-r--r--sys/mtio/README45
-rw-r--r--sys/mtio/doc/mtio.hlp814
-rw-r--r--sys/mtio/doc/newdriver.notes517
-rw-r--r--sys/mtio/mkpkg48
-rw-r--r--sys/mtio/mtalloc.x64
-rw-r--r--sys/mtio/mtcache.com9
-rw-r--r--sys/mtio/mtcache.x199
-rw-r--r--sys/mtio/mtcap.x36
-rw-r--r--sys/mtio/mtclean.x110
-rw-r--r--sys/mtio/mtdealloc.x35
-rw-r--r--sys/mtio/mtdevall.x30
-rw-r--r--sys/mtio/mtencode.x44
-rw-r--r--sys/mtio/mtfile.x24
-rw-r--r--sys/mtio/mtfname.x29
-rw-r--r--sys/mtio/mtglock.x47
-rw-r--r--sys/mtio/mtgtyopen.x129
-rw-r--r--sys/mtio/mtio.com9
-rw-r--r--sys/mtio/mtio.h42
-rw-r--r--sys/mtio/mtlocknam.x40
-rw-r--r--sys/mtio/mtneedf.x26
-rw-r--r--sys/mtio/mtopen.x188
-rw-r--r--sys/mtio/mtparse.x126
-rw-r--r--sys/mtio/mtpos.x39
-rw-r--r--sys/mtio/mtrdlock.x93
-rw-r--r--sys/mtio/mtrewind.x41
-rw-r--r--sys/mtio/mtskip.x31
-rw-r--r--sys/mtio/mtstatus.x34
-rw-r--r--sys/mtio/mtupdlock.x188
-rw-r--r--sys/mtio/zardmt.x22
-rw-r--r--sys/mtio/zawrmt.x21
-rw-r--r--sys/mtio/zawtmt.x30
-rw-r--r--sys/mtio/zclsmt.x55
-rw-r--r--sys/mtio/zopnmt.x58
-rw-r--r--sys/mtio/zsttmt.x21
-rw-r--r--sys/mtio/zzdebug.x357
35 files changed, 3601 insertions, 0 deletions
diff --git a/sys/mtio/README b/sys/mtio/README
new file mode 100644
index 00000000..143917e6
--- /dev/null
+++ b/sys/mtio/README
@@ -0,0 +1,45 @@
+MTIO -- Magtape i/o. This directory contains the FIO device driver for the
+magtape devices plus MTOPEN (used to open a magtape device and hook it to
+FIO) plus a few other routines. See ./doc for additional info.
+
+
+USER ROUTINES
+
+ yes|no = mtfile (fname) # Is fname a magtape device?
+ yes|no = mtneedfileno (mtname) # No file number given?
+ gty = mtcap (mtname)
+ mtfname (mtname, file, outstr, maxch)
+
+ mtparse (mtname, device, fileno, recno, attrl, maxch)
+ mtencode (mtname, maxch, device, fileno, recno, attrl)
+
+ fd = mtopen (mtname, acmode, bufsize)
+ mtrewind (mtname, initcache)
+ mtposition (mtname, file, record)
+
+ Use CLOSE to close a file descriptor opened with mtopen.
+ Use GTYCLOSE to close a termcap descriptor opened with mtcap.
+ MTNAME is the full magtape device specification, i.e, "mt[...]".
+
+
+
+SYSTEM ROUTINES
+
+ mtallocate (mtname)
+ mtdeallocate (mtname, rewind_tape)
+ mtstatus (out, mtname)
+ mtclean (level, stale, out)
+
+
+
+FIO DRIVER
+
+ zopnmt (iodev, acmode, mtchan)
+ zclsmt (mtchan, status)
+ zardmt (mtchan, buf, maxbytes, offset)
+ zawrmt (mtchan, buf, nbytes, offset)
+ zawtmt (mtchan, status)
+ zsttmt (mtchan, what, lvalue)
+
+
+All other procedures are internal to the interface.
diff --git a/sys/mtio/doc/mtio.hlp b/sys/mtio/doc/mtio.hlp
new file mode 100644
index 00000000..e00643a5
--- /dev/null
+++ b/sys/mtio/doc/mtio.hlp
@@ -0,0 +1,814 @@
+.help mtio Aug83 "Magnetic Tape I/O"
+.sh
+1. Introduction
+
+ This document describes the Magnetic Tape I/O (MTIO) package,
+i.e., the interface by which IRAF programs access magnetic tapes.
+Included are the requirements and specifications for the package,
+and a discussion of the interface between MTIO and FIO.
+
+.sh
+2. MTIO Requirements
+.ls 4
+.ls (1)
+The machine independent part of the MTIO package shall be written in
+the SPP language in compliance with the standards and conventions of IRAF.
+.le
+.ls (2)
+The MTIO package shall provide a single interface to all magtape
+devices on all IRAF target machines.
+.le
+.ls (3)
+MTIO shall interface to FIO, making it possible to read and write a magtape
+file via the FIO interface in a device independent fashion.
+.le
+.ls (4)
+All functions shall take an error action if a hardware error occurs,
+or if an illegal function is requested.
+No hardware exception or trap shall occur while accessing a magtape
+file via MTIO which is not caught by MTIO and converted into an
+IRAF error action.
+.le
+.ls (5)
+All error actions shall be defined symbolically in the system error
+code file <syserr.h>. The MTIO error messages shall have the
+form SYS_MT<function>, i.e., SYS_MTOPEN. The error message strings
+shall be placed in the system error message file, "syserrmsg".
+.le
+.ls (6)
+The MTIO code shall be divided into a machine independent part and
+a machine dependent part. All machine independent function and error
+checking shall be done in the machine independent part, to minimize
+the size and complexity of the machine dependent part, and therefore
+maximize the transportability of the package.
+.le
+.ls (7)
+MTIO shall interface to FIO by implementing the standard FIO binary file
+z-routines ZOPNMT, ZCLSMT, ZARDMT, ZAWRMT, ZAWTMT, and ZSTTMT.
+The specifications for these routines are given elsewhere.
+.le
+
+.sh
+3. MTIO Specifications
+
+ A magtape file is opened with MTOPEN and then accessed as a binary
+file via the device independent FIO interface. Upon input, FIO merges
+all tape records together into a binary byte stream. Upon output,
+FIO writes records equal in size to the FIO buffers, unless FLUSH or
+CLOSE is called, causing a partially filled buffer to be flushed to tape.
+The data within a tape file may not be randomly accessed; only sequential
+accesses are permitted. When i/o is complete, CLOSE should be called
+to close the file and return all buffer space.
+
+ fd = mtopen (filename, access_mode, buf_size)
+
+The format of a nonblank magnetic tape consists of beginning-of-tape (BOT),
+followed by zero or more files, followed by end-of-tape (EOT).
+Each file consists of one or more records, followed by a tape mark (EOF).
+EOT is defined as two consecutive tape marks, i.e., as a file containing
+zero records. MTIO knows nothing about labeled tapes.
+
+Tape records need not all be the same length, but for transportability
+reasons records should be an integral number of computer words in length,
+and very short records should be avoided (short records are used by some
+operating systems for special purposes). The term "computer word"
+is not well defined; the size of a tape record in bytes should be evenly
+divisible by 2, 4, or 8, where the transportability increases with the
+size of the divisor. To avoid loss of data when reading a tape,
+the FIO buffer must be at least as large as the longest record on the tape.
+
+.sh
+3.1 Opening a Magtape
+
+ MTOPEN opens a file on the named magtape device. The following
+device naming convention determines the tape drive and density:
+
+ mt[a-z][800|1600|6250]
+
+The letter in the second field determines which drive is to be used,
+and the third field, which is optional, determines the density.
+If the density is not specified, a system or drive dependent default
+density will be selected. Thus, to access a file on drive A at 1600 bpi,
+one would open the file "mta1600".
+
+The significance of the terms "drive A", "drive B", etc., is installation
+dependent, and need not imply distinct physical drives. A tape drive may
+have to be allocated before it can be opened by MTIO; MTIO does not attempt
+to allocate devices.
+
+The device name may optionally be followed by a subscript specifying
+the index of the file to which the tape is to be positioned.
+Thus, opening "mta[3]" causes device A to be opened with the default
+density, positioned to BOF of the third file on the tape.
+The field N, as in "mta[N]" may have the following values:
+
+.ls 4
+.ls 12 absent
+If the entire subscript is absent, or if N is absent the tape is opened
+at the current position, i.e., the tape is not physically moved.
+.le
+.ls N >= 1
+If an integer number greater than or equal to 1 (one) is given,
+the tape is opened positioned to that file.
+.le
+.ls N == EOT
+If the file number is given as "EOT" or as "eot", the tape is opened
+positioned at EOT.
+.le
+.le
+
+If called with a non "mt"-prefixed file name, MTOPEN assumes that the file
+is a regular disk resident binary file, and attempts to open the named
+file. A program which normally reads directly from a magtape device may
+therefore be used to read from a regular binary file, or from the
+standard input (the special file "STDIN").
+
+The following access modes are recognized by MTOPEN:
+.ls 4
+.ls 12 READ_ONLY
+The device is opened for reading, positioned to the beginning of
+a file, BOT, EOT, or to the "current position".
+Any attempt to write to the device will cause an error.
+.le
+.ls WRITE_ONLY
+The device is opened for writing, positioned to the beginning of a file,
+BOT, EOT, or to the "current position". When the file is subsequently
+closed, a new EOT mark will be written. Existing tape files may be overwritten.
+.le
+.ls APPEND
+The device is opened for writing, positioned at the end of tape (EOT).
+Append mode must not be used with blank tapes. Note that the significance
+of APPEND mode is different for MTOPEN than for a regular file open;
+the tape is to be extended by adding a new file, whereas in FIO it is
+the file itself which is extended.
+.le
+.ls NEW_TAPE
+The device is opened for writing, positioned at BOT. Any existing
+data on the tape is overwritten. Recommended mode for blank tapes.
+.le
+.le
+
+FIO can read any tape with a maximum record size less than or equal
+to the buffer size. In buffered mode, FIO normally writes records equal
+in size to the FIO internal buffers. Smaller records may be written if
+FLUSH or CLOSE is called; records of any size may be written in unbuffered
+mode.
+
+The third argument to MTOPEN is used to set the FIO buffer size.
+If the buffer size is given as zero, a default value is used.
+The default value chosen depends on the mode of access and upon the
+maximum record size permitted by the host system. MTOPEN will automatically
+allocate the maximum size FIO buffer if the tape is opened for reading.
+The FIO buffer size may be changed in an FSET call anytime before
+the first buffered i/o on the file.
+
+.sh
+3.2 Ordinary I/O to a Magtape Device
+
+ Seeks are not permitted on magtape files. A tape may be opened for
+reading or for writing, but not for both at the same time. A magtape
+device is much like the standard input and output; STDIN is read only,
+STDOUT is write only, and seeking is not permitted on either. STDIN
+differs from ordinary files in that data may continue to be read after
+an EOF; the same is true of magtape files. If a read returning EOF is
+followed by another read on a magtape device, the second read will
+access the first record of the next file. Once EOT is reached, every
+read will return EOF. There is no way to advance beyond EOT on a read.
+An EOF mark may only be written by closing the tape (see next section).
+
+.sh
+3.3 Closing a Magtape
+
+ The CLOSE function is called either explicitly by the user task,
+or implicilty by the IRAF main upon normal or abnormal task termination.
+If the file was opened for writing, CLOSE flushes the output buffer and
+writes an EOT mark at the current position. A file opened for writing
+is left positioned ready to write the first record of the next file on
+the tape (i.e., after the EOF of the file just written). A file opened
+for reading, if closed immediately after reading EOF, is left positioned
+ready to read or write the first record of the next file on the tape.
+
+.sh
+3.4 Bytes and Records
+
+ Upon input, the size of a record will be rounded up to an integral
+number of "chars". No data will be lost, but one or more extra bytes of
+data may be added. A tape conversion program which must deal with
+odd-sized records must know the size of tape records to properly extract
+the data. FIO cannot write odd-sized records.
+
+The binary i/o routines do not transform the data in any way,
+including byte swapping. If byte swapping is necessary, the conversion
+program must do the byte swapping explicitly. The predefined machine
+constants BYTE_SWAP and WORD_SWAP indicate whether byte or word swapping
+is necessary on the local machine (word swapping refers to the two 16 bit
+words in a long integer). Floating point data should not be stored in
+binary on tape, unless the tape is to be read only by the machine which
+wrote it.
+
+Routines are available elsewhere in the PI for manipulating bytes.
+The routines BYTMOV, BYTSWP, and BYTPAK are particularly useful for cracking
+foreign tapes.
+
+.sh
+3.5 Low level I/O to a Magtape
+
+ The asynchronous, unbuffered FIO routines AREAD, AWRITE, and AWAIT
+may be used to perform record i/o on a magtape. A single tape record
+is read or written by each call; the FIO buffer is not used. AWAIT must
+be called after each read or write, before initiating the next i/o transfer.
+Although in general it is unwise to mix buffered and unbuffered i/o on
+the same file, it is safe to perform one or more unbuffered transfers
+immediately after opening a file, before performing any buffered i/o.
+
+If an application must do something peculiar, like write an odd-sized
+record, the MTIO z-routines may be called directly. The z-routines transfer
+data in units of machine bytes.
+
+.sh
+3.6 CL level tape commands
+
+ The four routines ALLOCATE, DEALLOCATE, REWIND and MTSTATUS will be
+available at the CL level. A tape drive ("mta", "mtb", etc.) must be
+explicitly allocated before it can be accessed with MTOPEN.
+A drive may be allocated to only one user at a time. MTIO keeps track
+of the position of an allocated tape; once a drive has been allocated,
+the tape must not be manually positioned by the user. Tapes can be
+changed without reallocating the drive provided REWIND is first used to
+rewind the tape. DEALLOCATE automatically rewinds the tape before
+deallocating the drive. MTSTATUS tells whether or not a particular drive
+has been allocated, and if so, gives the name of the owner, the density,
+whether not the device is physically open, and so on.
+
+.sh
+3.6 Example
+
+ The following program reads an ASCII card image file from the
+input tape and converts it to an ordinary text stream, which is written
+to the standard output. This program is insensitive to the number of
+cards per tape record, and may be used to read card image disk files
+as well as tape files.
+
+The following CL commands might be entered to read the second card image
+file on the 800 bpi input tape into file "myfile", converting to lower case:
+
+.ks
+.nf
+ cl> allocate mta
+ cl> rcardimage "mta800[2]" | lcase > myfile
+.fi
+.ke
+
+
+The source for the program "rcardimage" follows:
+
+
+.ks
+.nf
+# RCARDIMAGE -- CL callable task which reads a card image
+# file, writing the converted cards to the standard output.
+
+procedure rcardimage()
+
+char filename[SZ_FNAME]
+int input, mtopen()
+
+begin
+ call clgstr ("filename", filename, SZ_FNAME)
+ input = mtopen (filename, READ_ONLY, 0)
+ call read_card_image_file (input, STDOUT)
+ call close (input)
+end
+.fi
+.ke
+
+
+
+.ks
+.nf
+define SZ_CARD 80
+
+# READ_CARD_IMAGE_FILE -- Read the named card image file, convert
+# to a regular character stream, and write to the output file.
+
+procedure read_card_image_file (input, output)
+
+int input, output # input, output files
+char cardbuf[SZ_CARD] # raw card
+char linebuf[SZ_CARD+1] # add 1 char for newline
+int last_char
+int read()
+errchk read, putline
+
+begin
+ # Read successive cards until EOF is reached. Unpack the
+ # card, strip trailing whitespace, and write the processed
+ # line to the output file.
+
+ while (read (input, cardbuf, SZ_CARD) != EOF) {
+ call chrupk (cardbuf, 1, linebuf, 1, SZ_CARD)
+
+ # Strip trailing whitespace, add newline.
+ last_char = 0
+ for (ip=1; ip <= SZ_CARD; ip=ip+1)
+ if (! IS_WHITE (linebuf[ip]))
+ last_char = ip
+
+ linebuf[last_char+1] = '\n'
+ linebuf[last_char+2] = EOS
+
+ call putline (output, linebuf)
+ }
+end
+.fi
+.ke
+.endhelp
+
+
+.helpsys mtio Nov83 "MTIO Interface Detailed Design"
+.sh
+Strategies for Interfacing MTIO
+
+ The specifications for MTIO have been kept as simple as possible for maximum
+machine independence. There is only one high level call, MTOPEN, which is
+used to open the device; it does little more than call FIO. Thereafter
+everything is done through the six FIO z-routines:
+
+.nf
+ zopnmt (filespec, access_mode; channel|ERR)
+ zclsmt (channel)
+ zardmt (channel, buffer, maxbytes, one_indexed_byte_offset)
+ zawrmt (channel, buffer, nbytes, one_indexed_byte_offset)
+ zawtmt (channel; nbytes|ERR)
+ zsttmt (channel, parameter; long_value)
+.fi
+
+These z-routines may be written in any language so long as they are SPP (i.e.,
+Fortran) callable. The "filespec" argument is a packed string. The access
+modes and ERR code are system constants defined in <iraf.h>. Channel may be
+anything you wish. The file offsets will always be zero since the magtape
+device is a streaming (sequential) device.
+
+Be sure that the z-routines, if written partially in the SPP language, do not
+make calls to high level program interface routines, especially any which do
+i/o. Doing so violates the reentrancy restrictions of the SPP language (and
+of Fortran). It may also lead to problems with the way libraries are searched.
+More explicitly, any calls to error or iferr, to any of the FIO routines, or to
+any of the printf or scan routines are absolutely forbidden. It is ok to use
+the string primitives (because they do not do any i/o or call error), but it
+is best to avoid even those if possible. It is perfectly all right to call
+other z-routines provided they do not directly or indirectly call you back
+(none will call mtio).
+
+.sh
+Virtual Device Model
+
+ Though in general the z-routines may have to be completely rewritten for
+a new OS, in fact many systems have functionally similar primitive magtape
+interfaces. If your system can be described by the model defined below,
+the "z?*.x" files in this directory can be used unchanged, and all you need to
+provide are the equivalents of the "zz" prefixed files.
+
+ Basically, the model requires that the zz-routine which opens the magtape
+be capable of positioning the tape to the first record of any file, given the
+file number to which the tape is positioned at open time. The high level MTIO
+interface code is charged with keeping track of the position of the tape at
+all times, including while the tape is closed. The high level code does not
+explicitly move the tape, though it does implicitly move it during open and when
+it commands an i/o operation. The high level code assumes nothing about tape
+motions; all zz-primitives return status values stating how many records or
+files the tape moved in the last operation. Thus, the details of the function
+of a zz-routine can vary depending on the system, without requiring
+modifications to the z-routines.
+
+.ls 4
+.ls (1)
+The following open/close primitives are required. ZZOPMT opens a tape
+drive positioned ready to read or write the first record of the specified
+file.
+.ls zzopmt (drive,density,acmode, oldrec,oldfile; newfile; oschan|ERR)
+.br
+.ls 10 drive
+Logical drive number (1,2,3,...).
+.le
+.ls density
+Drive density. A positive integer, i.e., 800, 1600 or 6250. A value
+of zero implies that an OS default density is to be selected.
+.le
+.ls acmode
+Access mode. Read_only or write_only. Modes new_file and append are
+dealt with by the "newfile" file number parameter.
+.le
+.ls oldrecord
+The number of the record to which we are currently positioned in the
+oldfile. We have to know this to know whether or not the file has to
+be rewound. The first record is number one.
+.le
+.ls oldfile
+The number of the file to which the tape is currently positioned.
+.le
+.ls newfile
+The number of the file to be opened, where 1 specifies that the tape is
+to be rewound. If newfile <= 0, open at EOT. On output, contains the
+actual file number of the new file; this may differ from the requested
+value if EOT is encountered. It is not an error (as far as the zz-routine
+is concerned) to attempt to position to a file beyond EOT.
+.le
+.ls oschan
+The channel number by which the file is to be referred in calls to the
+other zz-routines, or ERR if the file cannot be opened. ZZOPMT should
+set oschan IMMEDIATELY AFTER PHYSICALLY OPENING THE DEVICE, so that the
+error recovery code can close the file if an interrupt occurs.
+.le
+.le
+
+.ls 4 zzclmt (oschan, access_mode; nfile)
+Close tape. Write a new EOT at the current position if writing.
+Returns the file number increment (in the event that a file mark or two
+has to be written).
+.le
+.le
+
+.ls (2)
+The following i/o primitives are required.
+.ls zzrdmt (oschan, buf, maxbytes)
+Initiate a read of up to maxbytes bytes from the next tape
+record into buffer buf. It is not an error if fewer than "maxbytes"
+bytes are read; in fact maxbytes will normally be larger than the largest
+record on the tape. In general it is difficult to tell if the tape record
+was larger than maxbytes; the user code should select a large maxbytes and
+consider it an error if the full maxbytes are read. If EOF is encountered,
+return a zero byte count in the next call to zzwtmt.
+.le
+.ls zzwrmt (oschan, buf, nbytes)
+Initiate a write of "nbytes" bytes to the tape. It is an error
+if all data is not written.
+.le
+.ls zzwtmt (oschan; nbytes|ERR, nrecord, nfile)
+Wait for i/o to complete, and return the number of bytes read or
+written in the last transfer; return 0 if we read a tape mark.
+Keep returning the same thing in redundant calls. If an error
+occurs the error status should be cleared when the next i/o
+transfer is initiated. Return nrecord=1 for each tape record read,
+but not for file marks. Return nfile=1 if a filemark is skipped.
+A read of 0 bytes signifies that EOF was seen, but does not necessarily
+imply that a tape mark has been skipped.
+.le
+.le
+.le
+
+.sh
+Program Structure
+
+ The drive must be allocated before MTOPEN is called; MTOPEN verifies
+that the drive has been allocated and then calls FIO to install the magtape
+device and open the desired file on the drive. Allocating a file involves
+a call to the OS to allocate and/or mount the device, followed by creation
+of the device lock file in the dev$ directory. The lock file is used to
+indicate that the drive has been allocated, and to keep track of the tape
+position when the drive is closed.
+
+
+.ks
+.nf
+ mtopen
+ mt_parse_filespec
+ mt_get_tape_position
+ various FIO routines
+ fopnbf
+ zopnmt
+ zzopmt
+ zsttmt
+ fset [to set buffer size]
+
+
+ Structure of the MTOPEN Procedure
+.fi
+.ke
+
+
+The ZCLSMT procedure is called by CLOSE to close a file opened with MTOPEN.
+CLOSE may be called by the user, by the IRAF main if the task completes w/o
+closing the file, or during error recovery. An error occurring during MTOPEN
+may result in a call to CLOSE. We do not have to worry about reentrancy here
+because none of the MTIO z-routines called by ZOPNMT call error (they will
+return an ERR status to FOPNBF and therefore terminate before ZCLSMT is called).
+
+
+.ks
+.nf
+ close
+ zclsmt
+ mt_update_lockfile
+ smark,salloc,sfree
+ fvfn_to_osfn
+ mktemp
+ zopntx
+ zputtx
+ zclstx
+ zfdele
+ zfrnam
+ fatal
+ zzclmt
+
+
+ Structure of the ZCLSMT procedure
+.fi
+.ke
+
+
+The i/o procedures keep track of the number of records read or written and
+ensure that we do not read past EOF or EOT. We need to keep track of the
+number of records written so that we can detect a null file write.
+
+
+.ks
+.nf
+ aread awrite
+ zardmt zawrmt
+ zzrdmt zzwrmt
+
+
+ await
+ zawtmt
+ zzwtmt
+
+
+ Structure of the I/O Procedures
+.fi
+.ke
+
+
+The final routine is the ZSTTMT status procedure. This routine is self
+contained, except that access to the MTIO common is required to get the
+access mode (which determines the default buffer size).
+
+
+.sh
+Semicode
+
+ We do as much of the work in MTOPEN as we can, since it is the only
+high level, machine independent routine we have (we can call anything in this
+routine without reentrancy problems). We check that the drive has been
+allocated (to us), allocate an mtio device descriptor, check that the same
+drive has not been reopened, parse the filespec and save the pieces in the
+descriptor (for later use by ZOPNMT), read in the current tape position from
+the lock file, and then call fopnbf which in turn calls ZOPNMT to open the
+drive and position to the desired file.
+
+
+.tp 8
+.nf
+int procedure mtopen (filespec, access_mode, bufsize)
+
+begin
+ if (file not mt-prefixed) {
+ call open to open regular binary file
+ return (file descriptor returned by open)
+ }
+
+ get mtio device descriptor slot but do not allocate it yet
+ if (slots all taken)
+ error: magtape device multiply opened (filespec)
+ call mt_parse_filespec to break out drivename, density,
+ filenumber and set up mtio device descriptor
+ if (drive is already open)
+ error: magtape device multiply opened (filespec)
+ check that the named drive has been allocated to us
+ if (drive not allocated to us)
+ error: magtape not allocated (filespec)
+ decode lock file to get old position of drive, save in
+ device descriptor
+
+ call fopnbf to install the magtape device and open file
+ if (bufsize is nonzero)
+ call fset to set non-default FIO buffer size
+
+ return (file descriptor)
+end
+.fi
+
+
+ZOPNMT is passed a mostly initialized magtape file descriptor in the mtio
+common by MTOPEN. The "filespec" string is not actually used by ZOPNMT.
+We physically open the file and set up the remaining fields in the tape file
+descriptor. NOTE: if an interrupt occurs during the call to ZZOPMT,
+ZCLSMT will be called to perform error recovery. If this happens the
+position of the tape is undefined.
+
+
+.ks
+.nf
+procedure zopnmt (filespec, access_mode; chan|ERR)
+
+[we are passed the mtio file descriptor in the mtio common]
+
+begin
+ set flag for ZCLSMT in case we are interrupted
+ call ZZOPMT to open tape positioned to the beginning of the
+ indicated file.
+
+ if (cannot open tape)
+ return (chan = ERR)
+ if (actual file < requested file)
+ if (reading)
+ set at-eof flag and at_eot flag, so that reads return EOF
+ else {
+ call zzclmt to close device
+ return (mtchan = ERR)
+ }
+
+ save oschan in descriptor
+ save actual file number in descriptor
+ initialize the remaining descriptor fields
+
+ return (chan = mtio descriptor index)
+end
+.fi
+.ke
+
+
+Opening a file for writing and then immediately closing it poses problems.
+There is no way to write a zero-length file on an unlabeled tape. We cannot
+just write a tape mark because that might mean writing a spurious double
+tapemark (false EOT) in the middle of the tape. We could just ignore the
+request and not write any file, but that is not quite right either (if a
+disk file is opened for writing and then immediately closed, a file is
+still created). We compromise by writing a single short record containing
+the packed ASCII character string "NULLFILE". The actual length of the
+physical nullfile record is machine dependent.
+
+We may or may not need to bump the file counter when the file is closed;
+the OS tells us which. If we have just written a file and ZZCLMT must
+write a tape mark, for example, we might be left positioned before EOT,
+between the tape marks, or (unlikely) after the tape marks.
+
+If an error (i.e., console interrupt) occurs while the tape is being
+positioned by ZZOPMT, we will be called by the error recovery system
+with a garbage mtchan argument. We must be able to detect this type
+of call and modify the lock file, marking the position of the tape
+as UNDEFINED. The next open call will then automatically rewind the
+tape, ensuring accurate positioning. In a normal close we write out
+a lockfile identifying the file and record to which the tape is positioned.
+
+
+.ks
+.nf
+procedure zclsmt (mtchan)
+
+begin
+ if (error recovery in progress for ZOPNMT)
+ mark current position as undefined
+ else {
+ if (file was opened for writing but nothing was written)
+ write out "null file" short record
+
+ call zzclmt (oschan, access_mode; nfiles)
+ file += nfiles
+ if (nfiles > 0)
+ record = 1
+ }
+
+ call mt_update_lockfile to save tape status, position
+ deallocate the mtio device descriptor slot
+end
+.fi
+.ke
+
+
+Ignore all read requests once EOF or EOT has been reached. The only operation
+possible at that point is to close the device; there is no rewind or backskip
+function for a streaming device. We do not "know" what would happen if we
+called ZZRDMT after reaching EOF; this is OS dependent, and we do not want
+to require any particular behavior (some systems would return the first
+record of the next file, others would keep returning EOF).
+
+
+.ks
+.nf
+procedure zardmt (mtchan, buf, maxbytes, offset)
+
+begin
+ if (not at EOF or EOT)
+ call zzrdmt (oschan, buf, maxbytes)
+end
+.fi
+.ke
+
+
+We cannot hit EOF or EOT on a write, so merely post the request and return.
+Ignore the "offset" parameter since magtape is a streaming device.
+
+
+.ks
+.nf
+procedure zawrmt (mtchan, buf, nbytes, offset)
+
+begin
+ call zzwrmt (oschan, buf, nbytes)
+end
+.fi
+.ke
+
+
+FIO insures that ZAWTMT is called once and only once after every asynchronous
+i/o read or write request. Once we hit EOF or EOT on a read, we return
+EOF (nchars = 0) on every subsequent request until the file is closed.
+After each real data transfer we update the record,file counters as
+directed by the OS.
+
+
+.ks
+.nf
+procedure zawtmt (mtchan; status)
+
+begin
+ if (reading and at EOF or EOT)
+ return (status = 0)
+
+ call zzwtmt (oschan, nchars, nrecords_skipped, nfiles_skipped)
+ if (nfiles_skipped > 0) {
+ set at-EOF flag
+ record = 1
+ }
+
+ file += nfiles_skipped # keep track of position
+ record += nrecords_skipped
+ nrecords += nrecords_skipped # count records rd|wr
+
+ status = nchars
+end
+.fi
+.ke
+
+.sh
+Error Recovery
+
+ Error recovery during magtape operations is tricky since we are trying
+to keep track of the position of the drive. If an error occurs while the
+tape is being positioned by ZZOPMT we must detect the condition and mark the
+position of the tape as indefinite (forcing a rewind on the next open).
+If an interrupt occurs while positioning to EOT for a write, we do not
+want ZZCLMT to write the new EOT mark somewhere in the middle of the tape,
+truncating the tape.
+
+ Interrupts or other errors while reading or writing are comparatively
+harmless. Interrupting a write results in a short but otherwise normal
+file on the tape; interrupting a read leaves us positioned to some
+arbitrary record within the file (harmless). Conceivably a read
+could be interrupted just as we were reading a tape mark, causing the
+file position to be lost. We have not protected against this.
+
+.sh
+Data Structures
+
+ The mtio device descriptor structure describes the status of each magtape
+device in use. The maximum number of magtape files which can be open at one
+time is the maximum number of tape drives. Only one file can be open on each
+drive at a time.
+
+
+.ks
+.nf
+ struct mtiodes {
+ int mt_drive # 1,2, etc. (c.t. a,b,...)
+ int mt_density # 0,800,1600,6250, etc.
+ int mt_oldfile # file number at open time
+ int mt_oldrecord # record number at open time
+ int mt_file # file being accessed (0=EOT)
+ int mt_record # next record to be accessed
+ int mt_nrecords # nrecords read/written
+ int mt_acmode # access mode
+ int mt_oschan # OS channel number
+ int mt_ateof # true when at EOF
+ int mt_ateot # true when at EOT
+ } mtiocom[MAX_TAPES]
+.fi
+.ke
+
+
+When a device is allocated, a lock file is created in the system logical device
+directory "dev$". For example, the lock file for magtape unit A is the
+file "dev$mta.lok". This is a human readable text file; it is typed out on
+the terminal when the user types "devstatus mta". The lock file is updated
+when the drive is closed and deleted when the drive is deallocated. The drive
+is automatically deallocated when the user logs out of the CL.
+
+
+.ks
+.nf
+Sample Lock File:
+
+ # Magtape unit 'mta' allocated to 'user' Sun 20:06:13 27-Nov-83
+ current file = 4
+ current record = 1
+ 72 records read, EOF seen
+.fi
+.ke
+
+
+The preliminary comments are written when the lock file is created, i.e.,
+when the device is allocated. Any number of comment lines are permitted.
+They are merely copied when ZCLSMT updates the lock file. The remaining
+records are written by ZCLSMT to record the current position of the tape,
+and to inform the user of the device status.
diff --git a/sys/mtio/doc/newdriver.notes b/sys/mtio/doc/newdriver.notes
new file mode 100644
index 00000000..3ef2ca7f
--- /dev/null
+++ b/sys/mtio/doc/newdriver.notes
@@ -0,0 +1,517 @@
+ NEW IRAF MAGTAPE DRIVER
+ October 1991
+ (design notes)
+
+
+1. User Interface
+
+1.1 Magtape Specification
+
+ Old format:
+
+ mtX.density[file.record]
+
+ New format:
+
+ mtX['['file[.record][:(param|param@|param=value):...]']']
+
+ e.g.,
+
+ mtexb1[4:nb:se@:ts=1200:so=/dev/ttya8]
+
+ mtX is used only as an index into the devices file, and is not
+ parsed. The tape density is no longer singled out as a special
+ parameter. If any parameters are specified, these parameters
+ override those given in the tapecap file entry.
+
+
+1.2 TAPECAP file
+
+ Current Format:
+
+ irafname [%NNNN] device aliases
+ e.g., mtb.9 nrst8 rst8 nrst0 rst0
+
+ Revised Format:
+
+ name[|alias|...]:cap=value:...:[tc=name:]
+
+ Device parameters are specified in termcap file format.
+
+ mta|mtexb1|Exabyte drive 1:\
+ :dv=nrst0:rd=rst0:\
+ :al=rst0,rst8,rst16,rst24,nrst0,nrst8,nrst16,nrst24:\
+ :tc=exb-sunst:
+ exb-sunst|Exabyte via SunOS ST driver:\
+ :bs#0:dt=Exabyte:fs#2200:mr#65535:
+
+ This feature will use the new GTY interface. Modifications are
+ required to 1) allow specification of parameters at open time which
+ will override those given in the file, and 2) make the use of a #
+ for numeric parameters optional (accept either # or =).
+
+
+1.3 I/O Monitoring
+
+ Device and tape type and capacity are given in tapecap.
+ Keep track of tape usage along with file position in lock file.
+ Lock file provides simple means to track usage. A real time
+ display may optionally be provided by the driver writing to
+ a tape monitoring window via a file or socket, with the name
+ given in tapecap or on the command line.
+
+
+2. Code Changes
+
+ OS,KI
+ zfiomt.c - all host versions are affected as calling sequence
+ has changed. Network interface is affected (new tape system
+ and old iraf kernel servers are incompatible).
+
+ The UNIX version of the new driver needs additional code to
+ optionally log status information to a file or special file.
+ Support for tcp/ip status logging as well?
+
+ ETC
+ Device allocation code is affected by the devices file syntax
+ changes.
+
+ MTIO
+ Must be changed to reflect magtape specfication and devices file
+ specification changes, i.e., the density parameter is omitted,
+ the device name is not quite the same thing, and a general
+ device parameter mechanism is added.
+
+ The feature to keep track of the amount of tape used is device
+ and host independent (given adequate parameterization in the
+ devices file) hence can be implemented in MTIO. Output to the
+ user, and preservation of tape usage status over device closes,
+ will be via the .lok file and DEVSTATUS.
+
+ The MTIO driver Z routines seem largely unaffected, except for
+ the status routine and the i/o routines, which need support added
+ for blocked devices.
+
+
+3. Device Driver
+
+3.1. Device Classes
+
+ generic generic device (open/close/read/write)
+ reel 800,1600,6250 bpi 1/2inch reel tape
+ cartridge various QIC formats - fixed size blocks
+ exabyte variable size blocks
+ dat variable size blocks
+
+
+3.2. Device Parameters
+
+[[NOT KEPT UP TO DATE - refer to os$zfiomt.c for latest version. ]]]]
+
+ CODE TYPE DEFAULT DESCRIPTION
+
+ bs i 0 device block size (0 if variable)
+ dn i none density (bpi)
+ dt s generic drive type
+ fs i 0 filemark size (Kb)
+ mr i 65535 maximum record size
+ or i 63360 optimum record size
+ rs i 0 record gap size (bytes)
+ ts i 0 tape capacity (Mb)
+ tt s unknown tape type
+
+ al s none device allocation info
+ dv s required no-rewind device file
+ rd s none rewind device file
+ so s none status output device file or socket
+
+ bo b no BSF positions to BOF
+ fc b no device does a FSF on CLRO
+ ir b no treat all read errors as EOF
+ nb b no device cannot backspace
+ nf b no rewind and space forward to backspace file
+ np b no disable all positioning ioctls
+ ow b no backspace and overwrite EOT at append
+ re b no read at EOT returns ERR
+ rf b no use BSR,FSR to space over filemarks
+ se b no device will position past EOT in a read
+ sk b no skip record forward after a read error
+ wc b no OPWR-CLWR at EOF writes null file
+
+ bf i builtin BSF ioctl code
+ br i builtin BSR ioctl code
+ ct i builtin MTIOCTOP code
+ ff i builtin FSF ioctl code
+ fr i builtin FSR ioctl code
+ ri i builtin REW ioctl code
+
+
+3.3. Host Level Device Operations
+
+ open (FILE I/O OPERATIONS)
+ close
+ read
+ write
+ wait (not used)
+
+ FSR (POSITIONING IOCTLS)
+ BSR
+ FSF
+ BSF
+ REW
+
+ WEOF (not used)
+ EOM (not used) (SunOS - space to end of media)
+
+
+3.4. Device Characteristics
+
+ open No known system/device dependencies.
+
+ close For a tape opened read-only, close positions to after
+ the filemark of the current file on some SysV systems.
+ This makes it impossible to rewind a device opened
+ no-rewind (after the open/rewind/close the tape is
+ left at the beginning of the second file). On such
+ systems the tape can only be rewound (left rewound
+ at close) by opening and closing the rewind device.
+
+ It is assumed that when a tape opened for writing is
+ closed an EOT mark is written, and the tape is left
+ positioned after the filemark of the last file
+ written, ready to write the next file (true on all
+ known systems, with some variations in how the EOT
+ is represented).
+
+ read A read where the record size exceeds the size of the
+ read request is assumed to be an error.
+
+ A read at end of file should return a zero byte
+ count and leave the file positioned after the file
+ mark. Some devices may return ERR when a tape mark
+ is read.
+
+ If the device block size is zero it is assumed that
+ records can be any size up to the max record size,
+ and that successive records can vary in size.
+
+ If the device block size is nonzero it is assumed
+ that the byte count for read and write requests (the
+ record size) should be a multiple of the device
+ block size. Multiple physical device blocks are
+ read or written to satisfy an i/o request. On a
+ read, all notion of the record size is lost, i.e.,
+ a read of N blocks will return N blocks regardless
+ of the blocking factor used in a write.
+
+ A read at EOT may leave the tape positioned after
+ the file mark just read or may result in a zero byte
+ count being returned with no affect on the tape
+ position.
+
+ Following a read error when reading a data record the
+ tape may be left 1) before the bad record, 2) after
+ the bad record, 3) in an undefined position, e.g.,
+ partway through the record.
+
+ write For variable record devices each write is assumed
+ to write a tape record the size of the output buffer.
+ For fixed block devices it is assumed that the size
+ of the write request must be an integral multiple of
+ the block size, and that multiple physical device
+ blocks will be written to satisfy the request.
+
+ wait Wait (asynchronous i/o) is not currently used and
+ is emulated in the driver. Some systems (Ultrix
+ and SunOS 4.1) provide facilities for multi-buffered
+ asynchronous i/o which the iraf driver may make use
+ of in the future.
+
+ FSR Forward skip record. Some systems permit a FSR over
+ a filemark and some do not.
+
+ BSR Backward skip record. Some systems permit a BSR over
+ a filemark and some do not.
+
+ FSF Forward skip file. The tape is assumed to be left
+ positioned after the filemark and before the first
+ record of the next file.
+
+ BSF Backward skip file. Some systems leave the tape
+ positioned on the BOT side of the filemark, others
+ leave the tape positioned to just before the first
+ record of the file following the filemark. A BSF with
+ a zero count may or may not rewind the current file.
+
+ REW Rewinds the tape, leaving the tape positioned to BOT.
+ No known system/device dependencies. See the note
+ on the "close" operation, above.
+
+ WEOF Not currently used.
+
+
+3.5. Driver Options
+
+ overwrite filemark (reel tapes only)
+ disable all backspace operations
+ skip record forward after a read error
+ i/o logging to specified output device
+
+
+3.6. Driver Functions
+
+ ZZOPMT - open and position to desired file
+ zmtopen
+ zmtclose
+ zmtposition
+ zmtrew
+ zmtfsf
+ zmtbsf
+ zmtfsr
+ zmtbsr
+
+ ZZCLMT - close
+ zmtclose
+
+ ZZRDMT - read next record
+ read
+ zmtbsf
+
+ ZZWRMT - write record
+ write
+
+ ZZWTMT - wait for i/o and return byte count
+
+ ZZRWMT - rewind tape
+ zmtopen
+ zmtclose
+
+ ZZSTMT - return device parameters
+
+
+3.7. Notes on Specific Devices
+
+3.7.1 Device Characteristics
+
+3.7.2 Exabyte Drivers
+
+ SunOS 4.1 ST Driver
+ BSF positions to BOT side of filemark (conventional behavior).
+ Driver does not allow positioning to after EOT.
+ Bug where driver loses track of file position is fixed.
+
+ SunOS 4.0.3 ST Driver
+ BSF positions to first record of file following file mark.
+ BSF 0 rewinds the current file.
+ Driver does not allow positioning to after EOT, i.e., when
+ positioning to EOT, after two successive zero reads,
+ it is NOT necessary to backspace over the filemark.
+ Driver tries to keep track of current file position but has
+ a bug which causes it to zero its counter when appending
+ a file to a tape within open/close.
+ This bug has two ramifications:
+ o Will not position to a file before where it thinks
+ BOT is.
+ o Will rewind to get to file it thinks is at BOT.
+
+ Earlier (SunOS 3.X) Sun drivers defaulted to fixed block mode, and
+ probably had other significant differences from the current
+ round of drivers.
+
+ Ciprico RT Driver
+ Both fixed and variable block device entries.
+ rfsd_pr_errlog can get set to 1|2|8 in driver (/dev/mem) to
+ turn on status messages. Variable is commonly set to 2
+ to get tape position readouts, but this will interfere
+ with iraf networking.
+ BSF positions to BOT side of filemark (conventional behavior).
+ EOT is indicated by two file marks, and a read of the second mark
+ will leave the tape positioned to after the mark, requiring
+ a backspace to position to EOT (conventional behavior).
+ Status (NOP) ioctl implemented, but file/record count always zero.
+
+ Sparcstation (4.1) ST driver
+ BSR over a filemark confuses the driver - looks like it thinks
+ it saw the EOF when reading in the forward direction.
+ The next read returns EOF and then the real EOF is read,
+ so 2 EOFs in a row are seen (looks like EOT). The file
+ count in the driver gets messed up.
+ BSF causes all subsequent reads to return immediately with ERR.
+ One (sometimes two!) REW ioctls are required to clear.
+ FSF and FSR work.
+
+ R-Squared driver for Exabyte (Sun-3 running 4.1)
+ Opens drive in fixed block mode.
+ Read at EOF returns IOERR (driver bug).
+ Status (NOP) ioctl not implemented.
+
+
+3.8. Pseudocode for Driver Functions
+
+
+# ZZOPMT -- Open the magtape device and position to the given file.
+
+procedure zzopmt (device, acmode, oldrec, oldfile, newfile, chan)
+begin
+ # Open device for positioning.
+ open no-rewind raw device read-only
+
+ # Do not move the tape if opened newfile=0.
+ if (newfile != 0) {
+ # Rewind to get to known position if position uncertain.
+ if (current position unknown) {
+ rewind tape
+ oldrec = 1
+ oldfile = 1
+ }
+
+ # Position to given file.
+ newfile = zmtfpos (chan, oldfile, oldrec, newfile)
+ }
+
+ # Reopen if necessary for i/o.
+ if (need write access)
+ reopen device for writing
+end
+
+
+# ZZCLMT -- Close the magtape device.
+
+procedure zzclmt (chan, acmode, nrecords, nfiles, status)
+begin
+ close device
+
+ nfiles = 0
+ nrecords = 0
+
+ if (acmode == read && device: FSF on close read-only)
+ nfiles = 1
+ else if (acmode == write && !(at BOF && device: no EOF if no write))
+ nfiles = 1
+end
+
+
+# ZZRDMT -- Initiate a read of the next tape record into the user buffer.
+
+procedure zzrdmt (chan, buf, maxbytes)
+begin
+ physically read tape
+ save read status for zzwtmt
+end
+
+
+# ZZWRMT -- Initiate a write of the next tape record.
+
+procedure zzwrmt (chan, buf, nbytes)
+begin
+ physically write record
+ save write status for zzwtmt
+end
+
+
+# ZZWTMT -- Wait for i/o to complete and return the byte count and the
+# change to the tape position in files and records.
+
+procedure zzwtmt (chan, nrecords, nfiles, nbytes)
+begin
+ nrecords = 0
+ nfiles = 0
+
+ if (io error) {
+ nbytes = ERR
+ } else if (read 0 bytes) {
+ if (at BOF) {
+ # At EOT.
+ if (device: read at EOT will go past EOT)
+ if (device: cannot backspace) {
+ zmtrew()
+ nfiles = -ARB
+ nrecords = -ARB
+ } else
+ zmtbsf (1)
+ } else
+ nfiles = 1
+ } else {
+ nrecords = 1
+ clear at BOF flag
+ }
+end
+
+
+# ZZRWMT -- Rewind the named device.
+
+procedure zzrwmt (device, status)
+begin
+ if (device: rewind device specified)
+ close (open (rewind-at-close device read-only))
+ else {
+ open no rewind device
+ zmtrew
+ close
+ }
+end
+
+
+# ZMTFPOS -- Position to the indicated file.
+
+int procedure zmtfpos (chan, oldfile, oldrec, newfile)
+begin
+ # Already positioned to desired file; don't do anything.
+ if (newfile == oldfile && oldrec == 1)
+ return (newfile)
+
+ # Move the tape.
+ if (newfile == 1) {
+ # Rewind.
+ zzrwmt()
+ } else if (newfile <= oldfile && newfile != EOT) {
+ # Backspace to desired file.
+ if (device: cannot backspace) {
+ zmtrew()
+ oldfile = 1
+ oldrec = 1
+ goto fwd_
+ } else if (device: BSF positions to BOF) {
+ zmtbsf (oldfile - newfile)
+ } else {
+ zmtbsf (oldfile - newfile + 1)
+ zmtfsf (1)
+ }
+ } else {
+ # Space forward to desired file or EOT.
+fwd_ while (oldfile < newfile || newfile == EOT) {
+ n = read (next record)
+ if (n == 0 && oldrec == 1) {
+ # At EOT.
+ if (device: read at EOT will go past EOT) {
+ if (device: cannot backspace) {
+ newfile = oldfile
+ zmtrew()
+ oldfile = 1
+ oldrec = 1
+ goto fwd_
+ } else
+ zmtbsf (1)
+ }
+ if (writing && device: overwrite EOF) {
+ if (!device: cannot backspace) {
+ if (device: BSF positions to BOF)
+ zmtbsf (0)
+ else {
+ zmtbsf (1)
+ zmtfsf (1)
+ }
+ }
+ }
+ break
+ } else if (n > 0)
+ zmtfsf (1)
+
+ oldfile++
+ oldrec = 1
+ }
+ newfile = oldfile
+ }
+
+ return (newfile)
+end
diff --git a/sys/mtio/mkpkg b/sys/mtio/mkpkg
new file mode 100644
index 00000000..2c0ce77f
--- /dev/null
+++ b/sys/mtio/mkpkg
@@ -0,0 +1,48 @@
+# Make the MTIO routines.
+
+$checkout libsys.a lib$
+$update libsys.a
+$checkin libsys.a lib$
+$exit
+
+zzdebug:
+ $checkout libsys.a lib$
+ $update libsys.a
+ $checkin libsys.a lib$
+
+ #$set XFLAGS = "$(XFLAGS) -qx"
+ $omake zzdebug.x
+ $link -z zzdebug.o
+ ;
+
+libsys.a:
+ #$set XFLAGS = "$(XFLAGS) -qx"
+
+ mtalloc.x mtio.h <time.h>
+ mtcache.x mtcache.com mtio.com mtio.h <config.h> <error.h>
+ mtcap.x mtio.h
+ mtclean.x mtio.h <finfo.h> <xalloc.h>
+ mtdealloc.x <error.h>
+ mtdevall.x <knet.h> <xalloc.h>
+ mtencode.x
+ mtfile.x
+ mtfname.x mtio.h
+ mtglock.x mtio.h
+ mtgtyopen.x mtio.h
+ mtlocknam.x mtio.h <chars.h> <ctype.h>
+ mtneedf.x mtio.h
+ mtopen.x mtio.com mtio.h <config.h> <fset.h> <knet.h> <mach.h>
+ mtparse.x mtio.h <config.h> <ctype.h>
+ mtpos.x mtio.h
+ mtrdlock.x mtio.com mtio.h <config.h>
+ mtrewind.x mtio.h <error.h>
+ mtskip.x <fset.h>
+ mtstatus.x
+ mtupdlock.x mtio.com mtio.h <config.h> <knet.h> <mach.h>
+ zardmt.x mtio.com mtio.h <config.h> <knet.h>
+ zawrmt.x mtio.com mtio.h <config.h> <knet.h>
+ zawtmt.x mtio.com mtio.h <config.h> <knet.h>
+ zclsmt.x mtio.com mtio.h <config.h> <knet.h>
+ zopnmt.x mtio.com mtio.h <config.h> <knet.h>
+ zsttmt.x mtio.com mtio.h <config.h> <fio.h> <knet.h> <mach.h>
+ ;
diff --git a/sys/mtio/mtalloc.x b/sys/mtio/mtalloc.x
new file mode 100644
index 00000000..2e21be1c
--- /dev/null
+++ b/sys/mtio/mtalloc.x
@@ -0,0 +1,64 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <time.h>
+include "mtio.h"
+
+# MTALLOCATE -- "Allocate" a tape drive by writing the lock file. We do
+# not actually call the OS to physically allocate the drive; that is done
+# by our caller (e.g., xallocate, in etc$xalloc.x). The lock file is no longer
+# used to gain exclusive access to the device; it is now used only to keep
+# track of the tape position after process termination.
+
+procedure mtallocate (mtname)
+
+char mtname[ARB] #I device name
+
+int fd, junk
+pointer sp, lockfile, mtowner, userid, timestr, device
+errchk open, mtparse
+long clktime()
+int open()
+
+begin
+ call smark (sp)
+ call salloc (device, SZ_FNAME, TY_CHAR)
+ call salloc (lockfile, SZ_FNAME, TY_CHAR)
+ call salloc (mtowner, SZ_FNAME, TY_CHAR)
+ call salloc (userid, SZ_FNAME, TY_CHAR)
+ call salloc (timestr, SZ_TIME, TY_CHAR)
+
+ # Get name of lockfile used by the given device.
+ call mtparse (mtname, Memc[device], SZ_FNAME, junk, junk, junk, 0)
+ call mt_glock (mtname, Memc[lockfile], SZ_FNAME)
+
+ # Open lock file and write out unit, owner, time allocated, etc.
+ # Overwrite any existing lockfile. We are called only after
+ # physically allocating the device at the host system level
+ # (and are not called if the device is already allocated), so this
+ # is safe.
+
+ iferr (call delete (Memc[lockfile]))
+ ;
+ fd = open (Memc[lockfile], NEW_FILE, TEXT_FILE)
+
+ call cnvtime (clktime(long(0)), Memc[timestr], SZ_TIME)
+ call getuid (Memc[userid], SZ_FNAME)
+
+ call fprintf (fd, "# Magtape unit %s status %s user %s\n")
+ call pargstr (Memc[device])
+ call pargstr (Memc[timestr])
+ call pargstr (Memc[userid])
+
+ # Assume initially that the tape position is undefined. This will
+ # cause the tape to be rewound on the first open, and thereafter
+ # the position will be defined unless an i/o error occurs.
+
+ call fprintf (fd, "file = -1\n")
+ call fprintf (fd, "record = -1\n")
+ call fprintf (fd, "nfiles = 0\n")
+ call fprintf (fd, "tapeused = 0 Kb\n")
+ call fprintf (fd, "pflags = 0\n")
+
+ call close (fd)
+ call sfree (sp)
+end
diff --git a/sys/mtio/mtcache.com b/sys/mtio/mtcache.com
new file mode 100644
index 00000000..b3533f3f
--- /dev/null
+++ b/sys/mtio/mtcache.com
@@ -0,0 +1,9 @@
+# MTIO savepos cache.
+
+int c_modified[SZ_CACHE]
+int c_mtdes[LEN_DEVPOS,SZ_CACHE]
+char c_device[SZ_DEVICE,SZ_CACHE]
+char c_iodev[SZ_IODEV,SZ_CACHE]
+char c_lkname[SZ_LKNAME,SZ_CACHE]
+
+common /mtcacm/ c_modified, c_mtdes, c_device, c_iodev, c_lkname
diff --git a/sys/mtio/mtcache.x b/sys/mtio/mtcache.x
new file mode 100644
index 00000000..87b68bdb
--- /dev/null
+++ b/sys/mtio/mtcache.x
@@ -0,0 +1,199 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <error.h>
+include "mtio.h"
+
+.help mtcache
+.nf _________________________________________________________________________
+MTCACHE -- Cache the magtape position (file and record) between file opens.
+The lock file is used for permanent storage, but is only read and written
+when necessary. When multiple tape files are accessed by a single task the
+tape position is kept in the cache between successive MTOPEN - CLOSE file
+accesses.
+
+ mt_getpos (mtname, mt)
+ mt_savepos (mt)
+ mt_sync (status)
+ mt_clrcache ()
+
+SAVEPOS updates the tape descriptor in the cache; SYNC updates the cache
+on disk in the lock file. SYNC is automatically called at task termination
+time (including during abnormal termination), but is not normally called
+when a tape file is closed.
+
+ERROR RECOVERY is a bit tricky. If error recovery takes place the ONERROR
+procedures are called first thing, before FIO cleanup takes place. That means
+that SYNC will be called while the magtape channel is still open, and before
+SAVEPOS has been called by ZCLSMT (which is called by fio_cleanup which is
+called by the main). If an error occurs on a magtape channel we want to
+record an undefined file position. We must see that SAVEPOS is called at
+file open time to mark the cache for updating, causing SYNC to write an
+undefined position lock file when the error occurs. ZCLSMT, when called
+during error recovery, should set the position to undefined but need not
+sync the cache.
+
+The cache is invalidated at process startup time and at SYNC time in case
+the user loads a new tape or runs a different magtape process while the
+current process is idling between tasks. In other words, the cache is only
+valid while the task using it is executing.
+.endhelp ___________________________________________________________________
+
+define SZ_CACHE MT_MAXTAPES
+
+
+# MT_GETPOS -- Return the device position descriptor of a drive given its
+# unit number.
+
+procedure mt_getpos (mtname, mt)
+
+char mtname[ARB] #I device name
+int mt #I MTIO descriptor
+
+int slot
+bool streq()
+include "mtcache.com"
+include "mtio.com"
+
+begin
+ # First look in the cache.
+ for (slot=1; slot <= SZ_CACHE; slot=slot+1)
+ if (streq (MT_LKNAME(mt), c_lkname[1,slot])) {
+ call amovi (c_mtdes[1,slot], MT_DEVPOS(mt), LEN_DEVPOS)
+ call strcpy (c_device[1,slot], MT_DEVICE(mt), SZ_DEVICE)
+ call strcpy (c_lkname[1,slot], MT_LKNAME(mt), SZ_LKNAME)
+ call strcpy (c_iodev[1,slot], MT_IODEV(mt), SZ_IODEV)
+ return
+ }
+
+ # Get the current position from the lock file, if there is one.
+ call mt_read_lockfile (mtname, mt)
+end
+
+
+# MT_SAVEPOS -- Save the current position in the cache. The entire descriptor
+# is saved since most of the information therein is needed for SYNC, even
+# though only a portion of the information will be used by GETPOS.
+
+procedure mt_savepos (mt)
+
+int mt #I MTIO descriptor
+
+int prev, slot
+bool streq(), strne()
+extern mt_sync()
+include "mtcache.com"
+include "mtio.com"
+data prev /0/
+define cache_ 91
+
+begin
+ # Post termination handler to sync the cache at task termination time.
+ call onerror (mt_sync)
+
+ # Do not update the cache if the file position is undefined.
+ if (MT_FILNO(mt) <= 0)
+ return
+
+ # Are we updating an entry already in the cache?
+ for (slot=1; slot <= SZ_CACHE; slot=slot+1)
+ if (streq (MT_LKNAME(mt), c_lkname[1,slot]))
+ goto cache_
+
+ # Add the entry to the cache. Resync the contents of the old slot
+ # if it is for a different drive and has been modified since it was
+ # last synced.
+
+ slot = prev + 1
+ if (slot > SZ_CACHE)
+ slot = 1
+ prev = slot
+
+ if (c_modified[slot] == YES && strne(MT_LKNAME(mt),c_lkname[1,slot])) {
+ call amovi (c_mtdes[1,slot], MT_DEVPOS(0), LEN_DEVPOS)
+ call strcpy (c_device[1,slot], MT_DEVICE(0), SZ_DEVICE)
+ call strcpy (c_lkname[1,slot], MT_LKNAME(0), SZ_LKNAME)
+ call strcpy (c_iodev[1,slot], MT_IODEV(0), SZ_IODEV)
+ call mt_update_lockfile (0)
+ }
+
+cache_
+ call amovi (MT_DEVPOS(mt), c_mtdes[1,slot], LEN_DEVPOS)
+ call strcpy (MT_DEVICE(mt), c_device[1,slot], SZ_DEVICE)
+ call strcpy (MT_LKNAME(mt), c_lkname[1,slot], SZ_LKNAME)
+ call strcpy (MT_IODEV(mt), c_iodev[1,slot], SZ_IODEV)
+ c_modified[slot] = YES
+end
+
+
+# MT_SYNC -- Update all modified entries in the cache. Set the position to
+# undefined (file=-1) if we are called during error recovery. We are called
+# at task termination by the IRAF Main.
+
+procedure mt_sync (status)
+
+int status #I task termination status
+
+int slot
+include "mtcache.com"
+include "mtio.com"
+
+begin
+ # Update the .lok files of any active devices.
+ for (slot=1; slot <= SZ_CACHE; slot=slot+1) {
+ if (c_modified[slot] == YES) {
+ # If called during error recovery mark the file position undef.
+ if (status != OK) {
+ call amovi (c_mtdes[1,slot], MT_DEVPOS(0), LEN_DEVPOS)
+ MT_FILNO(0) = -1
+ MT_RECNO(0) = -1
+ call amovi (MT_DEVPOS(0), c_mtdes[1,slot], LEN_DEVPOS)
+ }
+
+ # Update the lockfile.
+ call amovi (c_mtdes[1,slot], MT_DEVPOS(0), LEN_DEVPOS)
+ call strcpy (c_device[1,slot], MT_DEVICE(0), SZ_DEVICE)
+ call strcpy (c_lkname[1,slot], MT_LKNAME(0), SZ_LKNAME)
+ call strcpy (c_iodev[1,slot], MT_IODEV(0), SZ_IODEV)
+
+ # Ignore errors if we are called during error recovery.
+ iferr (call mt_update_lockfile (0))
+ if (status == OK)
+ call erract (EA_ERROR)
+
+ c_modified[slot] = NO
+ c_device[1,slot] = EOS
+ }
+ }
+
+ # Invalidate the cache when a task terminates.
+ call mt_clrcache()
+
+ # If we are called during error recovery, set the file position for
+ # all open tapes to undefined to prevent mt_savepos from being called
+ # by zclsmt when the tape file is closed during error recovery.
+
+ if (status != OK)
+ do slot = 1, MT_MAXTAPES {
+ MT_FILNO(slot) = -1
+ MT_RECNO(slot) = -1
+ }
+end
+
+
+# MT_CLRCACHE -- Initialize the cache.
+
+procedure mt_clrcache()
+
+int slot
+include "mtcache.com"
+
+begin
+ for (slot=1; slot <= SZ_CACHE; slot=slot+1) {
+ c_modified[slot] = NO
+ c_device[1,slot] = EOS
+ c_lkname[1,slot] = EOS
+ c_iodev[1,slot] = EOS
+ call aclri (c_mtdes[1,slot], LEN_DEVPOS)
+ }
+end
diff --git a/sys/mtio/mtcap.x b/sys/mtio/mtcap.x
new file mode 100644
index 00000000..d3f859fa
--- /dev/null
+++ b/sys/mtio/mtcap.x
@@ -0,0 +1,36 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "mtio.h"
+
+# MTCAP -- Return the tapecap descriptor for the given magtape device. The
+# device is specified by the full specification (as passed to mtopen), hence
+# there may be tapecap attributes in the device specification which override
+# those in the tapecap file.
+
+pointer procedure mtcap (mtname)
+
+char mtname[ARB] #I magtape device specification
+
+int fileno, recno
+pointer sp, device, devcap, cache_gty, gty
+pointer mt_gtyopen(), gtycaps(), gtyopen()
+errchk mtparse, mt_gtyopen
+
+begin
+ call smark (sp)
+ call salloc (device, SZ_DEVICE, TY_CHAR)
+ call salloc (devcap, SZ_DEVCAP, TY_CHAR)
+
+ call mtparse (mtname, Memc[device], SZ_DEVICE, fileno, recno,
+ Memc[devcap], SZ_DEVCAP)
+
+ # Do not return the cached MTIO device entry, as we do not want the
+ # application to close this with gtyclose. Open a new GTY descriptor
+ # using the capabilities in the cached entry.
+
+ cache_gty = mt_gtyopen (Memc[device], Memc[devcap])
+ gty = gtyopen ("", "", Memc[gtycaps(cache_gty)])
+
+ call sfree (sp)
+ return (gty)
+end
diff --git a/sys/mtio/mtclean.x b/sys/mtio/mtclean.x
new file mode 100644
index 00000000..5de01a7e
--- /dev/null
+++ b/sys/mtio/mtclean.x
@@ -0,0 +1,110 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <finfo.h>
+include <xalloc.h>
+include "mtio.h"
+
+# MTCLEAN -- Clean the lock file area. This routine is called periodically
+# by the system (e.g. during CL startup) to scan the lock file area and
+# deleted old lock files. This prevents old lock files which are no longer
+# valid from erroneously being used to indicate the current tape position.
+#
+# What mtclean does is similar to "delete tmp$mt*.lok" except that, since all
+# users store lok files in the same area, we don't want to delete all the
+# files indiscriminately. The default action is to delete only files for
+# unallocated devices, or for devices allocated to the current user for which
+# the lok file is more than STALE seconds old. It is harmless to delete a
+# lok file unnecessarily in that MTIO will recover automatically, but doing
+# so will force the tape to be rewound to regain a known position.
+
+procedure mtclean (level, stale, out)
+
+int level #I 0 for default; 1 to delete all .lok files
+int stale #I delete lok file if older than stale seconds
+int out #I if nonzero, print messages to this file
+
+int fd, status
+pointer list, ip, cp, device
+pointer sp, fname, owner, template, lbuf
+long fi[LEN_FINFO]
+
+pointer fntopn()
+long clktime()
+int gstrmatch(), xdevowner()
+int open(), getline(), finfo(), fntgfn()
+define del_ 91
+
+begin
+ call smark (sp)
+ call salloc (lbuf, SZ_LINE, TY_CHAR)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (owner, SZ_FNAME, TY_CHAR)
+ call salloc (template, SZ_FNAME, TY_CHAR)
+
+ # Get file name template matching all lock files.
+ call sprintf (Memc[template], SZ_FNAME, "%s%s*%s")
+ call pargstr (LOCKLDIR)
+ call pargstr (LOCKFILE)
+ call pargstr (LOCKEXTN)
+
+ # Open a file list.
+ list = fntopn (Memc[template])
+
+ # Examine each file in turn and delete if delete criteria satisfied.
+ while (fntgfn (list, Memc[fname], SZ_FNAME) != EOF) {
+
+ # If level is nonzero (force-delete) delete unconditionally.
+ if (level != 0)
+ goto del_
+
+ # Open lok file and get device name.
+ iferr (fd = open (Memc[fname], READ_ONLY, TEXT_FILE))
+ next
+ if (getline (fd, Memc[lbuf]) == EOF) {
+ call close (fd)
+ goto del_
+ }
+ if (gstrmatch (Memc[lbuf], "unit ", status, ip) <= 0) {
+ call close (fd)
+ goto del_
+ }
+ device = lbuf + ip
+ for (cp=device; Memc[cp] != EOS && Memc[cp] != ' '; cp=cp+1)
+ ;
+ Memc[cp] = EOS
+ call close (fd)
+
+ # Determine if the device is currently allocated. If the device
+ # is not allocated delete the lok file unconditionally. If the
+ # device is allocated to someone else leave the lok file alone.
+ # If the lok file is allocated to the current user delete it if
+ # the file is older than the stale value.
+
+ status = xdevowner (Memc[device], Memc[owner], SZ_FNAME)
+ switch (status) {
+ case DV_DEVFREE:
+ goto del_
+
+ case DV_DEVINUSE:
+ # Do nothing.
+
+ case DV_DEVALLOC:
+ # Delete the file if older than the stale value.
+ if (finfo (Memc[fname], fi) == ERR)
+ goto del_
+
+ if (clktime(FI_MTIME(fi)) > stale) {
+ # Delete the file.
+del_ if (out != NULL) {
+ call fprintf (out, "delete lok file %s\n")
+ call pargstr (Memc[fname])
+ }
+ iferr (call delete (Memc[fname]))
+ ;
+ }
+ }
+ }
+
+ call fntcls (list)
+ call sfree (sp)
+end
diff --git a/sys/mtio/mtdealloc.x b/sys/mtio/mtdealloc.x
new file mode 100644
index 00000000..2f98a972
--- /dev/null
+++ b/sys/mtio/mtdealloc.x
@@ -0,0 +1,35 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+
+# MTDEALLOCATE -- Deallocate a previously allocated tape drive. To deallocate
+# a drive we try to rewind (any errors, such as drive offline, will result in a
+# warning message), and then delete the lockfile. We do not call up the OS to
+# deallocate the drive; that is done at a higher level, usually XDEALLOCATE
+# (in etc$xalloc.x).
+
+procedure mtdeallocate (mtname, rewind_tape)
+
+char mtname[ARB] #I magtape specification
+int rewind_tape #I rewind before deallocating drive
+
+pointer sp, lockfile
+errchk mt_glock, syserrs
+
+begin
+ call smark (sp)
+ call salloc (lockfile, SZ_FNAME, TY_CHAR)
+
+ if (rewind_tape == YES)
+ iferr (call mtrewind (mtname, NO))
+ call erract (EA_WARN)
+
+ call mt_sync (OK)
+
+ call mt_glock (mtname, Memc[lockfile], SZ_FNAME)
+ iferr (call delete (Memc[lockfile]))
+ ;
+
+ call mt_clrcache()
+ call sfree (sp)
+end
diff --git a/sys/mtio/mtdevall.x b/sys/mtio/mtdevall.x
new file mode 100644
index 00000000..3f5620d8
--- /dev/null
+++ b/sys/mtio/mtdevall.x
@@ -0,0 +1,30 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <xalloc.h>
+include <knet.h>
+
+# MT_DEVALLOCATED -- Verify that the named host magtape device is allocated.
+# The IRAF device name must already have been translated to a host device
+# name before we are called.
+
+int procedure mt_devallocated (iodev)
+
+char iodev[ARB] #I host name of device
+pointer sp, pk_iodev, pk_owner
+int status
+
+begin
+ call smark (sp)
+ call salloc (pk_iodev, SZ_FNAME, TY_CHAR)
+ call salloc (pk_owner, SZ_FNAME, TY_CHAR)
+
+ # The following assumes that the node! prefix is in iodev.
+ call strpak (iodev, Memc[pk_iodev], SZ_FNAME)
+ call zdvown (Memc[pk_iodev], Memc[pk_owner], SZ_FNAME, status)
+
+ call sfree (sp)
+ if (status == DV_DEVALLOC)
+ return (YES)
+ else
+ return (NO)
+end
diff --git a/sys/mtio/mtencode.x b/sys/mtio/mtencode.x
new file mode 100644
index 00000000..b6bf6b0f
--- /dev/null
+++ b/sys/mtio/mtencode.x
@@ -0,0 +1,44 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# MTENCODE -- Construct a full magtape device specification. This routine is
+# the opposite of MTPARSE. If the file and record numbers are to be omitted
+# from the output mtname they should be passed as ERR.
+
+procedure mtencode (outstr, maxch, device, fileno, recno, attrl)
+
+char outstr[ARB] #O magtape device specification
+int maxch #I max chars out
+char device[ARB] #I device name (incl node)
+int fileno, recno #I file and record numbers, or ERR
+char attrl[ARB] #I tapecap attributes
+
+int op
+int gstrcpy()
+int itoc()
+
+begin
+ if (fileno != ERR || recno != ERR || attrl[1] != EOS) {
+ op = gstrcpy (device, outstr, maxch) + 1
+ outstr[op] = '['; op = op + 1
+ if (fileno != ERR) {
+ if (fileno == EOT)
+ op = op + gstrcpy ("EOT", outstr[op], maxch-op+1)
+ else
+ op = op + itoc (fileno, outstr[op], maxch-op+1)
+ }
+ if (recno != ERR) {
+ outstr[op] = '.'; op = op + 1
+ op = op + itoc (recno, outstr[op], maxch-op+1)
+ }
+ if (attrl[1] != EOS) {
+ if (attrl[1] != ':') {
+ outstr[op] = ':'
+ op = op + 1
+ }
+ op = op + gstrcpy (attrl, outstr[op], maxch-op+1)
+ }
+ outstr[op] = ']'; op = op + 1
+ outstr[op] = EOS
+ } else
+ call strcpy (device, outstr, maxch)
+end
diff --git a/sys/mtio/mtfile.x b/sys/mtio/mtfile.x
new file mode 100644
index 00000000..c07a550b
--- /dev/null
+++ b/sys/mtio/mtfile.x
@@ -0,0 +1,24 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define SZ_NODENAME 9
+
+# MTFILE -- Test a filename to see if it is the name of a magtape file.
+# A magtape file is characterized by the filename prefix "mt" (ingnoring
+# the nodename prefix if any).
+
+int procedure mtfile (fname)
+
+char fname[ARB] #I filename to be tested
+
+int ip, junk
+char nodename[SZ_NODENAME]
+int ki_extnode()
+
+begin
+ ip = ki_extnode (fname, nodename, SZ_NODENAME, junk) + 1
+
+ if (fname[ip] == 'm' && fname[ip+1] == 't')
+ return (YES)
+ else
+ return (NO)
+end
diff --git a/sys/mtio/mtfname.x b/sys/mtio/mtfname.x
new file mode 100644
index 00000000..7bb92408
--- /dev/null
+++ b/sys/mtio/mtfname.x
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "mtio.h"
+
+# MTFNAME -- Edit the input mtname (magtape file specification) to reference
+# the numbered file. The edited mtname is returned in the output string.
+
+procedure mtfname (mtname, fileno, outstr, maxch)
+
+char mtname[ARB] #I magtape device specification
+int fileno #I desired file number
+char outstr[ARB] #O output mtname string
+int maxch #I maxch chars out
+
+int ofileno, orecno
+pointer sp, device, devcap
+
+begin
+ call smark (sp)
+ call salloc (device, SZ_DEVICE, TY_CHAR)
+ call salloc (devcap, SZ_DEVCAP, TY_CHAR)
+
+ call mtparse (mtname, Memc[device], SZ_DEVICE, ofileno, orecno,
+ Memc[devcap], SZ_DEVCAP)
+ call mtencode (outstr, maxch,
+ Memc[device], fileno, orecno, Memc[devcap])
+
+ call sfree (sp)
+end
diff --git a/sys/mtio/mtglock.x b/sys/mtio/mtglock.x
new file mode 100644
index 00000000..a1abfef5
--- /dev/null
+++ b/sys/mtio/mtglock.x
@@ -0,0 +1,47 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "mtio.h"
+
+# MT_GLOCK -- Return the lockfile name for the given magtape device
+# specification. There can be many logical devices, defined in the tapecap
+# file, which refer to the same physical device. All entries for the same
+# physical device share the same i/o device file and lock file. If the
+# physical device is on a remote node the tapecap file on that node should
+# be accessed, and the node name must be included in the lockfile file name,
+# although the lock file is always written on the local node.
+
+procedure mt_glock (mtname, lockfile, maxch)
+
+char mtname[ARB] #I full magtape device spec
+char lockfile[ARB] #O receives lockfile name
+int maxch #I max chars out
+
+int filno, recno
+pointer sp, lkname, device, devcap, gty
+errchk mt_gtyopen, syserrs
+int gtygets()
+pointer mt_gtyopen()
+
+begin
+ call smark (sp)
+ call salloc (lkname, SZ_FNAME, TY_CHAR)
+ call salloc (device, SZ_FNAME, TY_CHAR)
+ call salloc (devcap, SZ_DEVCAP, TY_CHAR)
+
+ call mtparse (mtname,
+ Memc[device], SZ_FNAME, filno, recno, Memc[devcap], SZ_DEVCAP)
+
+ # The "lk" capability specifies the lock file root name.
+ gty = mt_gtyopen (Memc[device], Memc[devcap])
+ if (gtygets (gty, "lk", Memc[lkname], SZ_FNAME) <= 0) {
+ call eprintf ("missing `lk' parameter in tapecap entry for %s\n")
+ call pargstr (mtname)
+ call syserrs (SYS_MTTAPECAP, mtname)
+ }
+
+ call ki_xnode (Memc[device], Memc[lkname], SZ_FNAME)
+ call mt_lockname (Memc[lkname], lockfile, maxch)
+
+ call sfree (sp)
+end
diff --git a/sys/mtio/mtgtyopen.x b/sys/mtio/mtgtyopen.x
new file mode 100644
index 00000000..489fba2d
--- /dev/null
+++ b/sys/mtio/mtgtyopen.x
@@ -0,0 +1,129 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "mtio.h"
+
+# MT_GTYOPEN -- Given a logical device name, open the tapecap file and extract
+# the entry for the named device into an open GTY descriptor. The descriptor
+# pointer is returned as the function value. The last entry accessed is
+# cached indefinitely so that repeated references cause the tapecap file to
+# be scanned only once.
+#
+# The tapecap file may be defined in the user environment, otherwise it
+# defaults to the compiled in default TAPECAP (dev$tapecap). The format
+# of the "tapecap" environment variable is "filename[:devcap]", e.g.
+#
+# home$tapecap:so
+#
+#
+# would cause the file home$tapecap to be used as the tapecap file, adding the
+# device capabilities :so to each tapecap device access (any devcap fields
+# given on the command line will override these). Either the filename or
+# devcap field may be omitted. For example, reset tapecap = ":so" causes
+# the default tapecap file dev$tapecap to be used, but enables status output
+# logging in each magtape access.
+
+pointer procedure mt_gtyopen (device, ufields)
+
+char device[ARB] #I local device name (incl node)
+char ufields[ARB] #I optional user tapecap fields
+
+int len_ufields, junk
+char c_device[SZ_DEVICE]
+char c_ufields[SZ_DEVCAP]
+bool first_time, capseen, remote
+pointer c_gty, gty, sp, tapecap, devcap, ip, op
+pointer fname, dname, nname, tname
+
+bool streq()
+pointer gtyopen()
+int envfind(), stridxs(), strlen(), gstrcpy(), ki_gnode()
+errchk gtyopen, syserrs
+data first_time /true/
+
+begin
+ # First time initialization.
+ if (first_time) {
+ c_gty = NULL
+ first_time = false
+ }
+
+ # Check the cache.
+ if (c_gty != NULL && streq(device,c_device) && streq(ufields,c_ufields))
+ return (c_gty)
+
+ # Cache miss. Free the old descriptor.
+ if (c_gty != NULL)
+ call gtyclose (c_gty)
+
+ call smark (sp)
+ call salloc (tapecap, SZ_DEVCAP, TY_CHAR)
+ call salloc (devcap, SZ_DEVCAP, TY_CHAR)
+ call salloc (fname, SZ_PATHNAME, TY_CHAR)
+ call salloc (tname, SZ_PATHNAME, TY_CHAR)
+ call salloc (dname, SZ_FNAME, TY_CHAR)
+ call salloc (nname, SZ_FNAME, TY_CHAR)
+
+ # Get tapecap definition.
+ if (envfind ("tapecap", Memc[tapecap], SZ_DEVCAP) <= 0)
+ call strcpy (TAPECAP, Memc[tapecap], SZ_DEVCAP)
+
+ # Parse into filename and devcap fields.
+ op = fname
+ capseen = false
+ len_ufields = gstrcpy (ufields, Memc[devcap], SZ_DEVCAP)
+ for (ip=tapecap; Memc[ip] != EOS; ip=ip+1) {
+ if (Memc[ip] == ':' && !capseen) {
+ Memc[op] = EOS
+ op = devcap + len_ufields
+ capseen = true
+ }
+ Memc[op] = Memc[ip]
+ op = op + 1
+ }
+ Memc[op] = EOS
+
+ # Supply default filename if none given.
+ if (Memc[fname] == EOS)
+ call strcpy (TAPECAP, Memc[fname], SZ_PATHNAME)
+
+ # If no node is specified for the tapecap file, access the tapecap
+ # file on the same node as the magtape device file.
+
+ if (stridxs ("!", Memc[fname]) <= 0)
+ call ki_xnode (device, Memc[fname], SZ_PATHNAME)
+
+ # Get the node name the device is on.
+ remote = (ki_gnode (Memc[fname], Memc[nname], junk) != 0)
+
+ # Get the tapecap device name minus any node prefix.
+ call strcpy (device, Memc[dname], SZ_FNAME)
+ call ki_xnode ("", Memc[dname], SZ_FNAME)
+
+ # Open the tapecap entry. Try "tapecap.<node>" first then "tapecap".
+ # <node> is the hostname of the host the tape device is on, i.e. the
+ # network server on which the drive is located. The "tapecap.<node>"
+ # feature allows a shared common IRAF installation to support distinct
+ # tapecap files for different servers, falling back on the standard
+ # tapecap file if no node-specific file is found.
+
+ call strcpy (Memc[fname], Memc[tname], SZ_PATHNAME)
+ call strcat (".", Memc[tname], SZ_PATHNAME)
+ call strcat (Memc[nname], Memc[tname], SZ_PATHNAME)
+
+ iferr (gty = gtyopen (Memc[tname], Memc[dname], Memc[devcap]))
+ iferr (gty = gtyopen (Memc[fname], Memc[dname], Memc[devcap]))
+ call syserrs (SYS_MTTAPECAP, device)
+ if (gty == NULL)
+ call syserrs (SYS_MTTAPECAP, device)
+
+ # Update the cache.
+ if (strlen(ufields) <= SZ_DEVCAP) {
+ call strcpy (device, c_device, SZ_DEVICE)
+ call strcpy (Memc[devcap], c_ufields, SZ_DEVCAP)
+ c_gty = gty
+ }
+
+ call sfree (sp)
+ return (gty)
+end
diff --git a/sys/mtio/mtio.com b/sys/mtio/mtio.com
new file mode 100644
index 00000000..8accab3b
--- /dev/null
+++ b/sys/mtio/mtio.com
@@ -0,0 +1,9 @@
+# The MTIO Common.
+
+int new_mtchan # flag newly opened channel
+int mtdev[LEN_MTIODES,MT_MAXTAPES+1] # integer fields
+char mtnam[SZ_DEVICE,MT_MAXTAPES+1] # array of drive names
+char mtosn[SZ_IODEV,MT_MAXTAPES+1] # host name for device
+char mtlkn[SZ_LKNAME,MT_MAXTAPES+1] # lock file name
+
+common /mtiocom/ new_mtchan, mtdev, mtnam, mtosn, mtlkn
diff --git a/sys/mtio/mtio.h b/sys/mtio/mtio.h
new file mode 100644
index 00000000..a400a88a
--- /dev/null
+++ b/sys/mtio/mtio.h
@@ -0,0 +1,42 @@
+# MTIO.H -- Magtape i/o interface definitions. Note that the system config
+# file contains additional definitions (i.e., MT_MAXTAPES).
+
+define TAPECAP "dev$tapecap" # default tapecap file
+define LOCKLDIR "tmp$" # where the lock file goes
+define LOCKFILE "mt" # root lockfile name
+define LOCKEXTN ".lok" # lockfile extension
+define MT_MAGIC (-5417) # was zopnmt called by mtopen?
+define SZ_DEVICE 79 # max length of drive name
+define SZ_IODEV 79 # max length host device name
+define SZ_LKNAME 79 # max length lock file mame
+define SZ_DEVCAP 512 # max command line tapecap chars
+
+# MTIO device descriptor structure. The device descriptor is implemented
+# as the two dimensional integer array MTDEV, defined in the mtio common.
+# The DEVPOS substructure must agree with the driver, os$zfiomt.c.
+
+define MT_DEVICE mtnam[1,$1+1] # drive name
+define MT_IODEV mtosn[1,$1+1] # i/o device
+define MT_LKNAME mtlkn[1,$1+1] # lock file name
+
+define LEN_MTIODES 11
+define MT_DEVPOS MT_FILNO # devpos struct (passed to driver)
+define LEN_DEVPOS 5
+
+define MT_OSCHAN mtdev[1,$1+1] # OS channel or 0
+define MT_ACMODE mtdev[2,$1+1] # new access mode
+define MT_DEVCAP mtdev[3,$1+1] # pointer to tapecap entry for device
+define MT_FILE mtdev[4,$1+1] # new file number
+define MT_RECORD mtdev[5,$1+1] # new record number
+define MT_ATEOF mtdev[6,$1+1] # reached end of file on a read
+define MT_FILNO mtdev[7,$1+1] # old file number at open
+define MT_RECNO mtdev[8,$1+1] # old record number at open
+define MT_NFILES mtdev[9,$1+1] # nfiles on tape
+define MT_TAPEUSED mtdev[10,$1+1] # total tape used, bytes
+define MT_PFLAGS mtdev[11,$1+1] # i/o flags returned by driver
+
+# PFLAGS bitflags.
+define MF_ERR 001B # i/o error in last operation
+define MF_EOF 002B # tape mark seen in last operation
+define MF_EOT 004B # end of tape seen in last op
+define MF_EOR 010B # last op was a record advance
diff --git a/sys/mtio/mtlocknam.x b/sys/mtio/mtlocknam.x
new file mode 100644
index 00000000..0820645a
--- /dev/null
+++ b/sys/mtio/mtlocknam.x
@@ -0,0 +1,40 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <chars.h>
+include "mtio.h"
+
+# MT_LOCKNAME -- Generate the file name of the magtape lock file, given the
+# logical drive name. We are called from a z-routine, so do not use any high
+# level i/o routines. The generated lockfile name is of the form
+#
+# [node!]dir$mta.lok
+#
+# The lock file is maintained on the same node as the drive to which it
+# refers.
+
+procedure mt_lockname (device, lockfile, maxch)
+
+char device[ARB] #I device name
+char lockfile[maxch] #O receives generated lockfile name
+int maxch #I max chars out
+
+int ip, op
+int gstrcpy(), strlen()
+
+begin
+ lockfile[1] = EOS
+
+ # Copy the node name prefix, if any.
+ call ki_xnode (device, lockfile, maxch)
+ op = strlen (lockfile) + 1
+ ip = op
+
+ # Add the directory name prefix, "mt", and device name.
+ op = op + gstrcpy (LOCKLDIR, lockfile[op], maxch-op+1)
+ op = op + gstrcpy (LOCKFILE, lockfile[op], maxch-op+1)
+ op = op + gstrcpy (device[ip], lockfile[op], maxch-op+1)
+
+ # Add file extension.
+ op = op + gstrcpy (LOCKEXTN, lockfile[op], maxch-op+1)
+end
diff --git a/sys/mtio/mtneedf.x b/sys/mtio/mtneedf.x
new file mode 100644
index 00000000..9c334949
--- /dev/null
+++ b/sys/mtio/mtneedf.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "mtio.h"
+
+# MTNEEDFILENO -- Returns YES if no file number is specified in mtname, or
+# NO if a file is specified.
+
+int procedure mtneedfileno (mtname)
+
+char mtname[ARB] #I magtape device specification
+
+int fileno, recno
+pointer sp, device, devcap
+int btoi()
+
+begin
+ call smark (sp)
+ call salloc (device, SZ_DEVICE, TY_CHAR)
+ call salloc (devcap, SZ_DEVCAP, TY_CHAR)
+
+ call mtparse (mtname, Memc[device], SZ_DEVICE, fileno, recno,
+ Memc[devcap], SZ_DEVCAP)
+
+ call sfree (sp)
+ return (btoi(fileno == ERR))
+end
diff --git a/sys/mtio/mtopen.x b/sys/mtio/mtopen.x
new file mode 100644
index 00000000..fc5d6c4f
--- /dev/null
+++ b/sys/mtio/mtopen.x
@@ -0,0 +1,188 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <config.h>
+include <syserr.h>
+include <fset.h>
+include <mach.h>
+include "mtio.h"
+
+# MTOPEN -- Open a magtape file, or a regular binary file if the file name is
+# not "mt" prefixed. Access modes are restricted to read_only, write_only,
+# append, and new_tape. The buffer size argument specifies the size of the
+# FIO buffer used to access the tape; a system and device dependent default
+# is supplied if the buffer size is given as zero. If the device is a fixed
+# block size device the buffer size will be adjusted to an integral multiple
+# of the device block size. For variable size record devices, the buffer size
+# determines the size of the tape record on a write, or the maximum record
+# size on a read.
+#
+# The device to be accessed is specified as follows:
+#
+# [node!] mtX [ '[' file[.record] [:attr-list] ']' ]
+#
+# for example,
+#
+# mtexb1[4:nb:se@:ts=1200:so=/dev/ttya8]
+#
+# The "mt" prefix is required for the object to be considered a magtape device
+# reference. The device name returned is "mtX" as shown above; there must be
+# an entry for device mtX in the tapecap file in DEV.
+#
+# The file and record numbers are optional. Files and records are numbered
+# starting with 1. A sequence such as "mtX[eot]" will cause the tape to be
+# positioned to end of tape. "mtX[0]" causes the tape to be opened at the
+# current position, i.e., without being moved.
+#
+# The optional attr-list field consists of a sequence of colon-delimited
+# tapecap fields. These will override any values given in the tapecap entry
+# for the device. The syntax for attr-list is the same as in tapecap.
+#
+# If the filespec does not have the prefix "mt", we assume that the file is
+# a regular binary file and try to open that. If a tape file is specified
+# then the drive must be allocated before we are called. We allocate and
+# initialize an MTIO file descriptor and call FOPNBF to install the magtape
+# device in FIO and open the device/file.
+
+int procedure mtopen (mtname, acmode, bufsize)
+
+char mtname[ARB] #I device to be opened
+int acmode #I access mode
+int bufsize #I fio buffer size (record size) or 0
+
+bool first_time
+pointer sp, devcap, fname, gty
+int mt, fd, nskip, new_file, new_record
+
+bool streq()
+pointer mt_gtyopen(), gtycaps()
+int open(), fopnbf(), gtygets(), access()
+int mt_skip_record(), mtfile(), mt_devallocated()
+extern zopnmt(), zardmt(), zawrmt(), zawtmt(), zsttmt(), zclsmt()
+
+errchk open, fopnbf, fseti, syserrs, mtparse
+errchk mt_getpos, mt_skip_record, mt_gtyopen, gtygets, mt_glock, mtallocate
+data first_time /true/
+include "mtio.com"
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_PATHNAME, TY_CHAR)
+ call salloc (devcap, SZ_DEVCAP, TY_CHAR)
+
+ # Runtime initialization of the mtio file descriptor common.
+ # Make each file descriptor available for use.
+
+ if (first_time) {
+ call mt_clrcache()
+ do mt = 1, MT_MAXTAPES
+ MT_OSCHAN(mt) = NULL
+ first_time = false
+ }
+
+ # If regular binary file, we are done.
+ if (mtfile(mtname) == NO) {
+ call sfree (sp)
+ return (open (mtname, acmode, BINARY_FILE))
+ }
+
+ # Get mtio file descriptor slot, but do not allocate it until
+ # we are ready to open the file.
+
+ for (mt=1; mt <= MT_MAXTAPES && MT_OSCHAN(mt) != NULL; mt=mt+1)
+ ;
+ if (mt > MT_MAXTAPES)
+ call syserrs (SYS_MTMULTOPEN, mtname)
+
+ # Break mtname into drive name, file and record number, etc.
+ call mtparse (mtname, MT_DEVICE(mt), SZ_DEVICE,
+ new_file, new_record, Memc[devcap], SZ_DEVCAP)
+ if (new_record == ERR)
+ new_record = 1
+
+ # Get tapecap info.
+ gty = mt_gtyopen (MT_DEVICE(mt), Memc[devcap])
+ MT_DEVCAP(mt) = gtycaps (gty)
+ if (gtygets (gty, "dv", MT_IODEV(mt), SZ_IODEV) <= 0) {
+ call eprintf ("missing `dv' parameter in tapecap entry for %s\n")
+ call pargstr (mtname)
+ call syserrs (SYS_MTTAPECAP, mtname)
+ }
+ call ki_xnode (MT_DEVICE(mt), MT_IODEV(mt), SZ_IODEV)
+
+ # If the device has not been allocated, at least write out the
+ # lock file. This will not physically allocate the device, but
+ # the lock file is required to be able to access the device.
+
+ call mt_glock (mtname, MT_LKNAME(mt), SZ_LKNAME)
+ if (mt_devallocated (MT_IODEV(mt)) == NO)
+ if (access (MT_LKNAME(mt), 0,0) == NO)
+ call mtallocate (mtname)
+
+ # Get current tape position.
+ call mt_getpos (mtname, mt)
+
+ MT_FILE(mt) = new_file
+ MT_RECORD(mt) = new_record
+ MT_ATEOF(mt) = NO
+
+ # If tape is opened for writing but no file number is given, default
+ # to EOT. Defaulting to current file or BOT could result in
+ # destruction of the tape. Note that this default WILL RESULT IN TAPE
+ # RUNAWAY if used on a blank tape. Blank tapes must be explicitly
+ # written at file [1], or opened with access mode NEW_TAPE.
+
+ if ((acmode == WRITE_ONLY && MT_FILE(mt) == -1) || (acmode == APPEND)) {
+ MT_FILE(mt) = EOT
+ MT_RECORD(mt) = 1
+ } else if (acmode == NEW_TAPE) {
+ MT_FILE(mt) = 1
+ MT_RECORD(mt) = 1
+ }
+
+ # Make sure that we are not reopening a drive which is already open.
+ for (fd=1; fd <= MT_MAXTAPES; fd=fd+1)
+ if (fd != mt && MT_OSCHAN(fd) != NULL)
+ if (streq (MT_DEVICE(fd), MT_DEVICE(mt)))
+ call syserrs (SYS_MTMULTOPEN, mtname)
+
+ # Initialize the remaining fields in the file descriptor and open the
+ # device. ZOPNMT will position the tape. Note that we pass the index
+ # of the new mtio descriptor slot to ZOPNMT in the common. This is a
+ # bit ugly, but is safe enough, since we know that FOPNBF is going to
+ # call ZOPNMT.
+
+ switch (acmode) {
+ case READ_ONLY:
+ MT_ACMODE(mt) = READ_ONLY
+ case WRITE_ONLY, APPEND, NEW_TAPE:
+ MT_ACMODE(mt) = WRITE_ONLY
+ default:
+ call syserrs (SYS_MTACMODE, mtname)
+ }
+
+ new_mtchan = mt
+ fd = fopnbf (MT_IODEV(mt), acmode,
+ zopnmt, zardmt, zawrmt, zawtmt, zsttmt, zclsmt)
+
+ # Set the file buffer size (record size for variable block devices).
+ if (bufsize > 0)
+ call fseti (fd, F_BUFSIZE, bufsize)
+
+ # If the user specified a record offset, skip records up to there.
+ # Zero means leave positioned to old record.
+
+ if (MT_RECORD(mt) == 0)
+ MT_RECORD(mt) = MT_RECNO(mt)
+ if (MT_RECORD(mt) > 1) {
+ nskip = MT_RECORD(mt) - 1
+ MT_RECORD(mt) = 1
+ if (mt_skip_record (fd, nskip) != nskip)
+ call syserrs (SYS_MTSKIPREC, mtname)
+ }
+
+ call mt_savepos (mt)
+
+ call sfree (sp)
+ return (fd)
+end
diff --git a/sys/mtio/mtparse.x b/sys/mtio/mtparse.x
new file mode 100644
index 00000000..27622280
--- /dev/null
+++ b/sys/mtio/mtparse.x
@@ -0,0 +1,126 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <syserr.h>
+include <ctype.h>
+include "mtio.h"
+
+# MTPARSE -- Decode a virtual magtape file specification, returning the device
+# name, file and record to which the drive is to be positioned, and any special
+# device attributes (these will override the device defaults). The file and
+# record fields are returned as ERR if missing. Only the drive name field is
+# required.
+#
+# Magtape device syntax:
+#
+# [node!] mtX [ '[' file[.record] [:attr-list] ']' ]
+#
+# for example,
+#
+# mtexb1[4:nb:se@:ts=1200:so=/dev/ttya8]
+#
+# The "mt" prefix is required for the object to be considered a magtape
+# device reference. The device name returned is "mtX" as shown above; there
+# must be an entry for device mtX in the tapecap file in DEV.
+#
+# The file and record numbers are optional. Files and records are numbered
+# starting with 1. A sequence such as "mtX[eot]" will cause the tape to be
+# positioned to end of tape. "mtX[0]" causes the tape to be opened at the
+# current position, i.e., without being moved.
+#
+# The optional attr-list field consists of a sequence of colon-delimited
+# tapecap fields. These will override any values given in the tapecap
+# entry for the device. The syntax for attr-list is the same as in tapecap.
+
+procedure mtparse (mtname, device, sz_device, file, record, attrl, sz_attrl)
+
+char mtname[ARB] #I device specification
+char device[ARB] #O device name as in tapecap
+int sz_device #I max chars in device name
+int file #O file number or -1
+int record #O record number or -1
+char attrl[ARB] #O attribute list
+int sz_attrl #I max char in attribute list
+
+char eotstr[3]
+int ip, op, nchars, ival
+int ctoi(), strncmp(), ki_extnode()
+bool streq()
+define bad_ 91
+
+begin
+ # Extract the node name, if any, from the mtname.
+ ip = ki_extnode (mtname, device, sz_device, nchars) + 1
+ op = nchars + 1
+
+ # Verify that this is a magtape device specification.
+ if (strncmp (mtname[ip], "mt", 2) != 0)
+ goto bad_
+
+ # Extract the device name field.
+ while (mtname[ip] != EOS && mtname[ip] != '[') {
+ device[op] = mtname[ip]
+ op = min (sz_device, op + 1)
+ ip = ip + 1
+ }
+ device[op] = EOS
+
+ file = ERR
+ record = ERR
+ attrl[1] = EOS
+
+ # Process the [...] part of the device specification.
+ if (mtname[ip] == '[') {
+ ip = ip + 1
+
+ # Get the file number.
+ if (ctoi (mtname, ip, ival) > 0) {
+ file = ival
+ if (file < 0)
+ goto bad_
+ } else if (IS_ALPHA(mtname[ip])) {
+ call strcpy (mtname[ip], eotstr, 3)
+ call strlwr (eotstr)
+ if (streq (eotstr, "eot")) {
+ file = EOT
+ ip = ip + 3
+ } else
+ goto bad_
+ }
+
+ # Get the record number.
+ if (mtname[ip] == '.' || mtname[ip] == ',') {
+ ip = ip + 1
+ if (mtname[ip] == ']')
+ record = ERR
+ else if (ctoi (mtname, ip, ival) > 0) {
+ record = ival
+ if (record < 0)
+ goto bad_
+ }
+ }
+
+ # Get the device attribute list.
+ op = 1
+ if (mtname[ip] == ':') {
+ attrl[op] = mtname[ip]
+ op = max(1, min(sz_attrl, op + 1))
+ ip = ip + 1
+
+ while (mtname[ip] != EOS && mtname[ip] != ']') {
+ attrl[op] = mtname[ip]
+ op = max(1, min(sz_attrl, op + 1))
+ ip = ip + 1
+ }
+ }
+ attrl[op] = EOS
+
+ # Check for the ']' terminator.
+ if (mtname[ip] != ']')
+ goto bad_
+ }
+
+ return
+bad_
+ call syserrs (SYS_MTFILSPEC, mtname)
+end
diff --git a/sys/mtio/mtpos.x b/sys/mtio/mtpos.x
new file mode 100644
index 00000000..f88946ae
--- /dev/null
+++ b/sys/mtio/mtpos.x
@@ -0,0 +1,39 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "mtio.h"
+
+# MTPOSITION -- Position the device to the indicated file and record.
+# We are called to position the device by device name, not to position
+# an open magtape file.
+
+procedure mtposition (mtname, file, record)
+
+char mtname[ARB] #I device to be positioned
+int file #I desired file number
+int record #I desired record number
+
+int junk
+pointer sp, mtspec, device, devcap
+errchk mtparse, mtopen
+int mtopen()
+
+begin
+ call smark (sp)
+ call salloc (device, SZ_FNAME, TY_CHAR)
+ call salloc (mtspec, SZ_FNAME, TY_CHAR)
+ call salloc (devcap, SZ_DEVCAP, TY_CHAR)
+
+ # Get device name (including node! prefix) from mtname.
+ call mtparse (mtname,
+ Memc[device], SZ_FNAME, junk, junk, Memc[devcap], SZ_DEVCAP)
+
+ # Encode new mtname and open device to position to desired file.
+ # Note that we do not return until positioning is complete. Thus,
+ # "mtposition(device,1)" is a rewind with wait.
+
+ call mtencode (Memc[mtspec], SZ_FNAME,
+ Memc[device], file, record, Memc[devcap])
+ call close (mtopen (Memc[mtspec], READ_ONLY, 1))
+
+ call sfree (sp)
+end
diff --git a/sys/mtio/mtrdlock.x b/sys/mtio/mtrdlock.x
new file mode 100644
index 00000000..774fab19
--- /dev/null
+++ b/sys/mtio/mtrdlock.x
@@ -0,0 +1,93 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include "mtio.h"
+
+# MT_READ_LOCKFILE -- Read the magtape lock file to determine the current
+# position of the tape (the lock file is used to record the tape position
+# while the device is closed). If the lock file cannot be accessed or an
+# error occurs in reading it, return an undefined tape position.
+
+procedure mt_read_lockfile (mtname, mt)
+
+char mtname[ARB] #I device name
+int mt #I MTIO descriptor
+
+int fd, ip
+pointer sp, lockfile, lbuf
+int strmatch(), stridxs(), ctoi(), open(), getline()
+errchk open, getline
+include "mtio.com"
+define err_ 91
+
+begin
+ call smark (sp)
+ call salloc (lockfile, SZ_FNAME, TY_CHAR)
+ call salloc (lbuf, SZ_LINE, TY_CHAR)
+
+ # If the lock file cannot be accessed, return an undefined tape
+ # position but do not abort.
+
+ #call mt_lockname (MT_LKNAME(mt), Memc[lockfile], SZ_FNAME)
+ call strcpy (MT_LKNAME(mt), Memc[lockfile], SZ_FNAME)
+ iferr (fd = open (Memc[lockfile], READ_ONLY, TEXT_FILE)) {
+ fd = ERR
+ goto err_
+ }
+
+ # Get file number.
+ repeat {
+ if (getline (fd, Memc[lbuf]) == EOF)
+ goto err_
+ } until (strmatch (Memc[lbuf], "^file") > 0)
+ ip = stridxs ("=", Memc[lbuf]) + 1
+ if (ctoi (Memc[lbuf], ip, MT_FILNO(mt)) == 0)
+ goto err_
+
+ # Get record number.
+ if (getline (fd, Memc[lbuf]) == EOF)
+ goto err_
+ ip = stridxs ("=", Memc[lbuf]) + 1
+ if (ctoi (Memc[lbuf], ip, MT_RECNO(mt)) == 0)
+ goto err_
+
+ # Get total files on tape.
+ if (getline (fd, Memc[lbuf]) == EOF)
+ goto err_
+ ip = stridxs ("=", Memc[lbuf]) + 1
+ if (ctoi (Memc[lbuf], ip, MT_NFILES(mt)) == 0)
+ goto err_
+
+ # Get amount of tape used.
+ if (getline (fd, Memc[lbuf]) == EOF)
+ goto err_
+ ip = stridxs ("=", Memc[lbuf]) + 1
+ if (ctoi (Memc[lbuf], ip, MT_TAPEUSED(mt)) == 0)
+ goto err_
+
+ # Get pflags.
+ if (getline (fd, Memc[lbuf]) == EOF)
+ goto err_
+ ip = stridxs ("=", Memc[lbuf]) + 1
+ if (ctoi (Memc[lbuf], ip, MT_PFLAGS(mt)) == 0)
+ goto err_
+
+ call close (fd)
+ call sfree (sp)
+
+ return
+err_
+ if (fd != ERR)
+ call close (fd)
+
+ # Write a new lock file so that we can update the tape position
+ # later (the file must exist after the drive is opened).
+
+ call mtallocate (mtname)
+
+ # Return an undefined tape position.
+ MT_FILNO(mt) = -1
+ MT_RECNO(mt) = -1
+
+ call sfree (sp)
+end
diff --git a/sys/mtio/mtrewind.x b/sys/mtio/mtrewind.x
new file mode 100644
index 00000000..b7efcddb
--- /dev/null
+++ b/sys/mtio/mtrewind.x
@@ -0,0 +1,41 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+include "mtio.h"
+
+# MTREWIND -- Rewind the named magtape device. This is a synchronous
+# rewind. Rewind not only rewinds the device, it also initializes the
+# MTIO view of what is on the tape (number of files, total bytes used).
+# Hence, if the drive is left allocated but the tape is changed, or if
+# the position cache becomes inaccurate for any reason, a rewind will
+# initialize things without having to deallocate and reallocate the drive.
+
+procedure mtrewind (mtname, initcache)
+
+char mtname[ARB] #I device to be rewound
+int initcache #I discard positional information?
+
+pointer sp, fname
+int fd, mtopen()
+errchk mtfname
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+
+ # Init position cache.
+ if (initcache == YES) {
+ call mt_glock (mtname, Memc[fname], SZ_FNAME)
+ iferr (call delete (Memc[fname]))
+ ;
+ }
+
+ # Rewind device.
+ call mtfname (mtname, 1, Memc[fname], SZ_FNAME)
+ iferr (fd = mtopen (Memc[fname], READ_ONLY, 0))
+ call erract (EA_WARN)
+ else
+ call close (fd)
+
+ call sfree (sp)
+end
diff --git a/sys/mtio/mtskip.x b/sys/mtio/mtskip.x
new file mode 100644
index 00000000..f51948a4
--- /dev/null
+++ b/sys/mtio/mtskip.x
@@ -0,0 +1,31 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <fset.h>
+
+# MT_SKIP_RECORD -- Skip records on an opened file. Return the actual number
+# of records skipped; stop if EOF is reached.
+
+int procedure mt_skip_record (fd, nrecords)
+
+int fd #I magtape device
+int nrecords #I number of records to skip
+
+pointer sp, buf
+int n, bufsize
+errchk aread, await
+int await(), fstati()
+
+begin
+ call smark (sp)
+ bufsize = fstati (fd, F_BUFSIZE)
+ call salloc (buf, bufsize, TY_CHAR)
+
+ for (n=1; n <= nrecords; n=n+1) {
+ call aread (fd, Memc[buf], bufsize, 0)
+ if (await (fd) == EOF)
+ break
+ }
+
+ call sfree (sp)
+ return (n-1)
+end
diff --git a/sys/mtio/mtstatus.x b/sys/mtio/mtstatus.x
new file mode 100644
index 00000000..967d25df
--- /dev/null
+++ b/sys/mtio/mtstatus.x
@@ -0,0 +1,34 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# MTSTATUS -- Print the status of an allocated magtape device, i.e., print the
+# lock file as a text file. Called by the DEVSTATUS task.
+
+procedure mtstatus (out, mtname)
+
+int out #I output file
+char mtname[ARB] #I magtape specification
+
+int in
+pointer sp, lockfile
+errchk open, fcopyo
+int open(), access()
+
+begin
+ call smark (sp)
+ call salloc (lockfile, SZ_FNAME, TY_CHAR)
+
+ call mt_sync (OK)
+
+ call mt_glock (mtname, Memc[lockfile], SZ_FNAME)
+ if (access (Memc[lockfile], 0, 0) == NO) {
+ call fprintf (out, "tape position for %s is undefined\n")
+ call pargstr (mtname)
+ } else {
+ # Print the lockfile.
+ in = open (Memc[lockfile], READ_ONLY, TEXT_FILE)
+ call fcopyo (in, out)
+ call close (in)
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/mtio/mtupdlock.x b/sys/mtio/mtupdlock.x
new file mode 100644
index 00000000..654d3cbc
--- /dev/null
+++ b/sys/mtio/mtupdlock.x
@@ -0,0 +1,188 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <mach.h>
+include <config.h>
+include "mtio.h"
+
+# MT_UPDATE_LOCKFILE -- Update the current file position in the lockfile.
+# This information is returned by the host specific driver at close time.
+# We are called from a z-routine so we must access the lockfile using only
+# low level OS interface routines to avoid recursion. We may be called during
+# error recovery (as well as from a z-routine), so any errors are fatal.
+
+procedure mt_update_lockfile (mt)
+
+int mt #I device slot
+
+extern mt_sync()
+pointer sp, lockfile, tempfile, lbuf, ip, op, extn
+int old_lockfile, new_lockfile, junk, status, nlines
+errchk fmapfn
+include "mtio.com"
+define oline_ 91
+define err_ 92
+
+begin
+ call smark (sp)
+ call salloc (lockfile, SZ_PATHNAME, TY_CHAR)
+ call salloc (tempfile, SZ_PATHNAME, TY_CHAR)
+ call salloc (lbuf, SZ_LINE, TY_CHAR)
+
+ # Catch any errors in the following section and convert them
+ # into fatal errors.
+
+ iferr {
+ # Try to avoid generating any non-host legal filenames in the
+ # following code, to avoid any need to access the VFN mapping
+ # file. Generate temp file in the same directory on the same
+ # node as the lockfile so that we can easily rename the tempfile
+ # to be the new lockfile.
+
+ #call mt_lockname (MT_LKNAME(mt), Memc[lockfile], SZ_PATHNAME)
+ call strcpy (MT_LKNAME(mt), Memc[lockfile], SZ_PATHNAME)
+
+ # Make tempfile name.
+ extn = NULL
+ op = tempfile
+ for (ip=lockfile; Memc[ip] != EOS; ip=ip+1) {
+ if (Memc[ip] == '.')
+ extn = op
+ Memc[op] = Memc[ip]
+ op = op + 1
+ }
+
+ if (extn == NULL)
+ extn = op
+ call strcpy (".tlk", Memc[extn], 4)
+
+ # Map the filenames.
+ call fmapfn (Memc[tempfile], Memc[tempfile], SZ_PATHNAME)
+ call fmapfn (Memc[lockfile], Memc[lockfile], SZ_PATHNAME)
+
+ } then
+ goto err_
+
+ # Overwrite any existing tempfile.
+ call zfdele (Memc[tempfile], junk)
+ call zopntx (Memc[tempfile], NEW_FILE, new_lockfile)
+ if (new_lockfile == ERR)
+ goto err_
+
+ # Open old lockfile, if any, and copy the comments section.
+ call zopntx (Memc[lockfile], READ_ONLY, old_lockfile)
+ if (old_lockfile == ERR) {
+oline_ call strcpy ("# Magtape unit ", Memc[lbuf], SZ_LINE)
+ call strcat (MT_DEVICE(mt), Memc[lbuf], SZ_LINE)
+ call strcat (" status\n", Memc[lbuf], SZ_LINE)
+ call mt_putline (new_lockfile, Memc[lbuf])
+ } else {
+ nlines = 0
+ repeat {
+ call zgettx (old_lockfile, Memc[lbuf], SZ_LINE, status)
+ if (status <= 0) {
+ if (nlines == 0) {
+ call zclstx (old_lockfile, status)
+ goto oline_
+ } else
+ break
+ } else
+ nlines = nlines + 1
+ Memc[lbuf+status] = EOS
+ if (Memc[lbuf] == '#')
+ call mt_putline (new_lockfile, Memc[lbuf])
+ } until (Memc[lbuf] != '#')
+ call zclstx (old_lockfile, status)
+ }
+
+ # Everything else we write from here on is new stuff. Discard rest
+ # of old lockfile.
+
+ # Save current file and record numbers.
+ if (MT_FILNO(mt) == -1)
+ MT_RECNO(mt) = -1
+ call mt_savekeyword (new_lockfile, "file", MT_FILNO(mt))
+ if (MT_NFILES(mt) > 0 && (MT_FILNO(mt) == MT_NFILES(mt) + 1))
+ call mt_putline (new_lockfile, " (EOT)")
+ call mt_putline (new_lockfile, "\n")
+ call mt_savekeyword (new_lockfile, "record", MT_RECNO(mt))
+ call mt_putline (new_lockfile, "\n")
+ call mt_savekeyword (new_lockfile, "nfiles", MT_NFILES(mt))
+ call mt_putline (new_lockfile, "\n")
+ call mt_savekeyword (new_lockfile, "tapeused", MT_TAPEUSED(mt))
+ call mt_putline (new_lockfile, "\n")
+ call mt_savekeyword (new_lockfile, "pflags", MT_PFLAGS(mt))
+ call mt_putline (new_lockfile, "\n")
+
+ # Install the new lockfile.
+ call zflstx (new_lockfile, status)
+ if (status == ERR)
+ goto err_
+ call zclstx (new_lockfile, status)
+ if (status == ERR)
+ goto err_
+
+ call zfdele (Memc[lockfile], status)
+ call zfrnam (Memc[tempfile], Memc[lockfile], status)
+ if (status == ERR)
+ goto err_
+
+ call sfree (sp)
+ return
+
+err_
+ # If an error of any sort occurs, it is fatal.
+ call onerror_remove (mt_sync)
+ call zfdele (Memc[tempfile], status)
+ call zfdele (Memc[lockfile], status)
+ call fatal (0, "Fatal error writing magtape device lockfile")
+end
+
+
+# MT_SAVEKEYWORD -- Write a "keyword = value" status line into the lockfile.
+
+procedure mt_savekeyword (fd, keyword, value)
+
+int fd # output file
+char keyword[ARB] # name of keyword
+int value # value of keyword
+char numbuf[MAX_DIGITS]
+int junk, itoc()
+
+begin
+ junk = itoc (value, numbuf, MAX_DIGITS)
+
+ call mt_putline (fd, keyword)
+ call mt_putline (fd, " = ")
+ call mt_putline (fd, numbuf)
+end
+
+
+# MT_PUTLINE -- Put a text string to the lockfile. Do not write line
+# to lockfile until a newline is seen.
+
+procedure mt_putline (fd, text)
+
+int fd
+char text[ARB]
+
+extern mt_sync()
+char lbuf[SZ_LINE]
+int ip, op, status
+data op /1/
+
+begin
+ for (ip=1; text[ip] != EOS; ip=ip+1) {
+ lbuf[op] = text[ip]
+ op = min (SZ_LINE, op) + 1
+ if (text[ip] == '\n') {
+ call zputtx (fd, lbuf, op-1, status)
+ if (status == ERR) {
+ call onerror_remove (mt_sync)
+ call fatal (0,
+ "Fatal error writing magtape device lockfile")
+ }
+ op = 1
+ }
+ }
+end
diff --git a/sys/mtio/zardmt.x b/sys/mtio/zardmt.x
new file mode 100644
index 00000000..ac5833be
--- /dev/null
+++ b/sys/mtio/zardmt.x
@@ -0,0 +1,22 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <config.h>
+include "mtio.h"
+
+# ZARDMT -- MTIO asynchronous read primitive. Initiate a read of up to
+# maxbytes bytes into the user buffer.
+
+procedure zardmt (mtchan, buf, maxbytes, offset)
+
+int mtchan #I i/o channel
+char buf[ARB] #O output data buffer
+int maxbytes #I max bytes to read
+long offset #I file offset
+
+include "mtio.com"
+
+begin
+ if (MT_ATEOF(mtchan) == NO)
+ call zzrdmt (MT_OSCHAN(mtchan), buf, maxbytes, offset)
+end
diff --git a/sys/mtio/zawrmt.x b/sys/mtio/zawrmt.x
new file mode 100644
index 00000000..5caf2b70
--- /dev/null
+++ b/sys/mtio/zawrmt.x
@@ -0,0 +1,21 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <config.h>
+include "mtio.h"
+
+# ZAWRMT -- MTIO asynchronous write primitive. Initiate a write of nbytes
+# bytes to the tape.
+
+procedure zawrmt (mtchan, buf, nbytes, offset)
+
+int mtchan #I i/o channel
+char buf[ARB] #I data to be written
+int nbytes #I number of bytes of data
+long offset #I file offset
+
+include "mtio.com"
+
+begin
+ call zzwrmt (MT_OSCHAN(mtchan), buf, nbytes, offset)
+end
diff --git a/sys/mtio/zawtmt.x b/sys/mtio/zawtmt.x
new file mode 100644
index 00000000..5599a81e
--- /dev/null
+++ b/sys/mtio/zawtmt.x
@@ -0,0 +1,30 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <config.h>
+include "mtio.h"
+
+# ZAWTMT -- Wait for the last i/o transfer to complete, update tape position
+# counters, return nbytes|status to caller.
+
+procedure zawtmt (mtchan, status)
+
+int mtchan #I i/o channel
+int status #O status (nbytes transferred or ERR)
+
+include "mtio.com"
+
+begin
+ # The "sticky" EOF should not be necessary but is needed due to the
+ # way FIO behaves when it hits EOF on a blocked file. In some
+ # circumstances (depends upon the file length) two reads are made and
+ # if the second read does not return zero EOF will not be detected.
+
+ if (MT_ATEOF(mtchan) == YES)
+ status = 0
+ else {
+ call zzwtmt (MT_OSCHAN(mtchan), MT_DEVPOS(mtchan), status)
+ if (status == 0)
+ MT_ATEOF(mtchan) = YES
+ }
+end
diff --git a/sys/mtio/zclsmt.x b/sys/mtio/zclsmt.x
new file mode 100644
index 00000000..7c4dcbf7
--- /dev/null
+++ b/sys/mtio/zclsmt.x
@@ -0,0 +1,55 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <knet.h>
+include "mtio.h"
+
+# ZCLSMT -- Close a magtape file and device. Update lockfile so that we
+# will know where the tape is positioned next time we open the device.
+# Deallocate the mtio descriptor so that it may be reused again.
+#
+# We are being called during error recovery if "new_mtchan" is not null.
+# If MT_OSCHAN has also been set, then ZZOPMT was interrupted, probably while
+# trying to position the tape, and the position of the tape is indefinite.
+# Close the tape with acmode=read so that no tape marks are written, and write
+# the lockfile with file = -1 to signify that the position is indefinite.
+
+procedure zclsmt (mtchan, status)
+
+int mtchan #I i/o channel
+int status #O close status
+
+int mt
+bool error_recovery
+include "mtio.com"
+
+begin
+ # Called by error recovery while positioning tape? (during open)
+ if (new_mtchan != NULL) {
+ mt = new_mtchan
+ if (MT_OSCHAN(mt) != NULL)
+ call zzclmt (MT_OSCHAN(mt), MT_DEVPOS(mt), status)
+
+ call mt_savepos (mt)
+ call mt_sync (ERR)
+ new_mtchan = NULL
+
+ } else {
+ mt = mtchan
+
+ # If a task aborts while a tape file is open, mt_sync will
+ # already have been called to update the position,
+ # and the current file will have been set to undefined (-1).
+
+ error_recovery = (MT_FILNO(mt) == -1)
+
+ # Close device. This clobbers MT_FILNO (see above).
+ call zzclmt (MT_OSCHAN(mt), MT_DEVPOS(mt), status)
+
+ # Update the tape position if not recovering from an abort.
+ if (!error_recovery)
+ call mt_savepos (mt)
+ }
+
+ MT_OSCHAN(mt) = NULL
+end
diff --git a/sys/mtio/zopnmt.x b/sys/mtio/zopnmt.x
new file mode 100644
index 00000000..1c55e1ae
--- /dev/null
+++ b/sys/mtio/zopnmt.x
@@ -0,0 +1,58 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <config.h>
+include "mtio.h"
+
+# ZOPNMT -- Open magtape device at the specifed file. We are called indirectly
+# by MTOPEN (via fopnbf), which sets up a new mtio device decriptor pointed
+# to by NEW_MTCHAN, and passes it via the mtio common.
+
+procedure zopnmt (iodev, acmode, mtchan)
+
+char iodev[ARB] #I PACKED i/o device name string
+int acmode #I file access mode
+int mtchan #O return value (mt descriptor index)
+
+int mt
+pointer sp, pk_devcap
+include "mtio.com"
+define err_ 91
+
+begin
+ call smark (sp)
+ call salloc (pk_devcap, SZ_DEVCAP, TY_CHAR)
+
+ # Pick up index of mt descriptor slot set up by MTOPEN. Make sure
+ # that we were called by MTOPEN and not somebody else.
+
+ mt = new_mtchan
+ if (mt < 1 || mt > MT_MAXTAPES)
+ goto err_
+
+ # Open the device.
+ call strpak (Memc[MT_DEVCAP(mt)], Memc[pk_devcap], SZ_DEVCAP)
+ call zzopmt (iodev, MT_ACMODE(mt), Memc[pk_devcap], MT_DEVPOS(mt),
+ MT_FILE(mt), MT_OSCHAN(mt))
+ if (MT_OSCHAN(mt) == ERR)
+ goto err_
+
+ # If "new_mtchan" is nonzero when ZCLSMT is called, it implies that
+ # CLOSE was called during error recovery due to an interrupt of ZZOPMT
+ # and the position of the tape is undefined. Clear the flag since the
+ # open is now complete and we were not interrupted.
+
+ new_mtchan = NULL
+ MT_FILNO(mt) = MT_FILE(mt)
+ call mt_savepos (mt)
+
+ mtchan = mt
+ call sfree (sp)
+ return
+
+err_
+ # Z-routines can only return ERR in the event of an error.
+ MT_OSCHAN(mt) = NULL
+ call sfree (sp)
+ mtchan = ERR
+end
diff --git a/sys/mtio/zsttmt.x b/sys/mtio/zsttmt.x
new file mode 100644
index 00000000..252c9735
--- /dev/null
+++ b/sys/mtio/zsttmt.x
@@ -0,0 +1,21 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <mach.h>
+include <config.h>
+include <fio.h>
+include "mtio.h"
+
+# ZSTTMT -- Get magtape device or device driver parameters and settings.
+
+procedure zsttmt (mtchan, what, lvalue)
+
+int mtchan #I mtio descriptor
+int what #I status parameter to be returned
+long lvalue #O returned status value
+
+include "mtio.com"
+
+begin
+ call zzstmt (MT_OSCHAN(mtchan), what, lvalue)
+end
diff --git a/sys/mtio/zzdebug.x b/sys/mtio/zzdebug.x
new file mode 100644
index 00000000..cf02e610
--- /dev/null
+++ b/sys/mtio/zzdebug.x
@@ -0,0 +1,357 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <xalloc.h>
+include <mach.h>
+include <fset.h>
+
+task alloc = t_allocate,
+ dealloc = t_deallocate,
+ status = t_status,
+ mtpos = t_mtposition,
+ wtestfile = t_wtestfile,
+ mtexamine = t_mtexamine,
+ mtcopy = t_mtcopy,
+ rew = t_rewind
+
+
+.help testmtio
+.nf __________________________________________________________________________
+MTIO test routines. Assorted routines for verification of MTIO.
+
+ alloc Allocate a drive.
+
+ dealloc Deallocate a drive.
+
+ status Print drive status
+
+ mtpos Position to the indicated file and record.
+
+ wtestfile Writes a test file. The number of records and the
+ range of record sizes may be specified. The contents
+ of a record are determined by its size.
+
+ mtexamine Examines the structure of a tape. Tells the number of
+ files on the tape, the number of records in each file,
+ the sizes of the records, and optionally dumps the
+ contents of an indicated range of records from each
+ file.
+
+ mtcopy Fast binary copy. Copies a binary disk or tape file
+ to a binary disk or tape file using all the FIO
+ defaults.
+.endhelp _____________________________________________________________________
+
+
+# ALLOCATE -- Allocate a drive.
+
+procedure t_allocate()
+
+int junk, status
+char drive[SZ_FNAME]
+char owner[SZ_FNAME]
+int xallocate(), xdevowner()
+
+begin
+ call clgstr ("drive", drive, SZ_FNAME)
+ status = xallocate (drive)
+
+ switch (status) {
+ case OK:
+ call printf ("device allocated successfully\n")
+ case ERR:
+ call printf ("cannot allocate device\n")
+ case DV_DEVFREE:
+ call printf ("device is free and may be allocated\n")
+ case DV_DEVALLOC:
+ call printf ("device is already allocated\n")
+ case DV_DEVINUSE:
+ junk = xdevowner ("drive", owner, SZ_FNAME)
+ call printf ("device is already allocated to `%s'\n")
+ call pargstr (owner)
+ case DV_DEVNOTFOUND:
+ call printf ("device not found\n")
+ default:
+ call printf ("unknown status %d\n")
+ call pargi (status)
+ }
+end
+
+
+# DEALLOCATE -- Deallocate a drive.
+
+procedure t_deallocate()
+
+int junk, status
+char drive[SZ_FNAME]
+char owner[SZ_FNAME]
+
+bool clgetb()
+int xdeallocate(), xdevowner()
+
+begin
+ call clgstr ("drive", drive, SZ_FNAME)
+ status = xdeallocate (drive, clgetb ("rewind"))
+
+ switch (status) {
+ case OK:
+ call printf ("device deallocated successfully\n")
+ case ERR:
+ call printf ("cannot deallocate device\n")
+ case DV_DEVFREE:
+ call printf ("device is free and may be allocated\n")
+ case DV_DEVALLOC:
+ call printf ("device is already allocated\n")
+ case DV_DEVINUSE:
+ junk = xdevowner ("drive", owner, SZ_FNAME)
+ call printf ("device is already allocated to `%s'\n")
+ call pargstr (owner)
+ case DV_DEVNOTFOUND:
+ call printf ("device not found\n")
+ default:
+ call printf ("unknown status %d\n")
+ call pargi (status)
+ }
+end
+
+
+# STATUS -- Print drive status.
+
+procedure t_status()
+
+int status
+char drive[SZ_FNAME]
+char owner[SZ_FNAME]
+int xdevowner()
+
+begin
+ call clgstr ("drive", drive, SZ_FNAME)
+ status = xdevowner (drive, owner, SZ_FNAME)
+
+ switch (status) {
+ case OK:
+ call printf ("device deallocated successfully\n")
+ case ERR:
+ call printf ("cannot deallocate device\n")
+ case DV_DEVFREE:
+ call printf ("device is free and may be allocated\n")
+ case DV_DEVALLOC:
+ call printf ("device is already allocated\n")
+ case DV_DEVINUSE:
+ call printf ("device is allocated to `%s'\n")
+ call pargstr (owner)
+ case DV_DEVNOTFOUND:
+ call printf ("device not found\n")
+ default:
+ call printf ("unknown status %d\n")
+ call pargi (status)
+ }
+end
+
+
+# MTPOS -- Position to the indicated file and record.
+
+procedure t_mtposition()
+
+char drive[SZ_FNAME]
+int clgeti()
+
+begin
+ call clgstr ("drive", drive, SZ_FNAME)
+ call mtposition (drive, clgeti("file"), clgeti("record"))
+end
+
+
+# WTESTFILE -- Write a test file to the tape. Specify file [1] to write to
+# a new tape. If no file number is given, the file is appended to the tape.
+# Specify the number of records to be written and the range of sizes in bytes
+# of the records. Each byte of a record will contain the size of the record
+# modulus 256.
+
+procedure t_wtestfile()
+
+char mtname[SZ_FNAME]
+int nrecords
+int min_recsize, max_recsize
+
+pointer buf
+long seed
+int fd, i, recsize, oschan, status
+int clgeti(), mtopen(), fstati()
+real urand()
+data seed /123/
+
+begin
+ # Get tapefile name and open file for writing.
+ call clgstr ("mtname", mtname, SZ_FNAME)
+ fd = mtopen (mtname, WRITE_ONLY, 1)
+ oschan = fstati (fd, F_CHANNEL)
+
+ nrecords = max (0, clgeti ("nrecords"))
+ min_recsize = max (1, clgeti ("min_recsize"))
+ max_recsize = max (min_recsize, clgeti ("max_recsize"))
+
+ call calloc (buf, max_recsize, TY_CHAR)
+
+ # Records are written by directly calling ZAWRMT, so that we can
+ # write odd size records.
+
+ do i = 1, nrecords {
+ recsize = int ((max_recsize - min_recsize) * urand (seed)) +
+ min_recsize
+ call zawrmt (oschan, Memc[buf], recsize, 0)
+ call zawtmt (oschan, status)
+ if (status == ERR)
+ call error (1, "write error")
+ }
+
+ call mfree (buf, TY_CHAR)
+ call close (fd)
+end
+
+
+# MTEXAMINE -- Examine the structure of a tape filesystem or a file. If no file
+# number is given, all files are examined.
+
+procedure t_mtexamine()
+
+int fileno, nrecords
+char mtname[SZ_FNAME], mtfile[SZ_FNAME]
+int strlen(), mt_examine()
+
+begin
+ call clgstr ("mtname", mtname, SZ_FNAME)
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ if (mtname[strlen(mtname)] == ']') {
+ call strcpy (mtname, mtfile, SZ_FNAME)
+ nrecords = mt_examine (STDOUT, mtname)
+
+ } else {
+ fileno = 1
+ repeat {
+ call sprintf (mtfile, SZ_FNAME, "%s[%d]")
+ call pargstr (mtname)
+ call pargi (fileno)
+ fileno = fileno + 1
+ } until (mt_examine (STDOUT, mtfile) == 0)
+ }
+end
+
+
+# MT_EXAMINE -- Examine a magtape file. Print file number, then count
+# successive records. When the record size changes, print the number of
+# records encountered with the old size. When all done, print the total
+# number of records and bytes. Return the number of records in the file.
+
+int procedure mt_examine (out, mtfile)
+
+int out # output stream
+char mtfile[ARB] # magtape file to be examined
+
+pointer buf
+int in, nrecords, totrecords, totbytes, bufsize, recsize, last_recsize
+errchk mtopen, read, fstati, printf, pargi
+int mtopen(), read(), fstati()
+
+begin
+ in = mtopen (mtfile, READ_ONLY, 0)
+ bufsize = fstati (in, F_BUFSIZE)
+ call malloc (buf, bufsize, TY_CHAR)
+
+ call fprintf (out, " File %s:\n")
+ call pargstr (mtfile)
+
+ totrecords = 0
+ nrecords = 0
+ totbytes = 0
+ last_recsize = 0
+
+ # Describe record composition of file.
+ while (read (in, Memc[buf], bufsize) != EOF) {
+ recsize = fstati (in, F_SZBBLK)
+ if (nrecords == 0) { # first record
+ nrecords = 1
+ last_recsize = recsize
+ } else if (recsize == last_recsize) {
+ nrecords = nrecords + 1
+ } else {
+ call fprintf (out, "\t%d %d-byte records\n")
+ call pargi (nrecords)
+ call pargi (last_recsize)
+ nrecords = 1
+ last_recsize = recsize
+ }
+ totrecords = totrecords + 1
+ totbytes = totbytes + recsize
+ }
+
+ if (nrecords > 0) {
+ call fprintf (out, "\t%d %d-byte records\n")
+ call pargi (nrecords)
+ call pargi (last_recsize)
+ }
+
+ # Print total count of records, bytes.
+ call fprintf (out, "\tTotal %d records, %d bytes\n")
+ call pargi (totrecords)
+ call pargi (totbytes)
+
+ call mfree (buf, TY_CHAR)
+ call close (in)
+
+ return (totrecords)
+end
+
+
+# MTCOPY -- Copy a binary file from magtape or disk to magtape or disk,
+# using all the default FIO and MTIO pararameters. If the output file is
+# a magtape, all records (except possibly the last record in the file) will
+# be the same size. If input tape records are not commensurate with the size
+# of a CHAR they will be zero-padded to an integral number of chars upon
+# input.
+
+procedure t_mtcopy()
+
+pointer buf
+int in, out, bufsize, acmode
+char infile[SZ_FNAME], outfile[SZ_FNAME]
+int mtopen(), fstati(), read(), mtfile()
+
+begin
+ call clgstr ("infile", infile, SZ_FNAME)
+ call clgstr ("outfile", outfile, SZ_FNAME)
+
+ in = mtopen (infile, READ_ONLY, 0)
+
+ # If output file is a disk file, create a new file, but do not
+ # create a new tape if writing to tape.
+
+ acmode = NEW_FILE
+ if (mtfile(outfile) == YES)
+ acmode = WRITE_ONLY
+ out = mtopen (outfile, acmode, 0)
+
+ bufsize = fstati (in, F_BUFSIZE)
+ call malloc (buf, bufsize, TY_CHAR)
+
+ while (read (in, Memc[buf], bufsize) != EOF)
+ call write (out, Memc[buf], fstati (in, F_NCHARS))
+
+ call mfree (buf, TY_CHAR)
+ call close (in)
+ call close (out)
+end
+
+
+# REWIND -- Rewind the tape.
+
+procedure t_rewind()
+
+char mtname[SZ_FNAME]
+bool clgetb()
+int btoi()
+
+begin
+ call clgstr ("mtname", mtname, SZ_FNAME)
+ call mtrewind (mtname, btoi(clgetb("initialize")))
+end