From fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4 Mon Sep 17 00:00:00 2001 From: Joseph Hunkeler Date: Wed, 8 Jul 2015 20:46:52 -0400 Subject: Initial commit --- unix/os/zfiomt.c | 1911 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1911 insertions(+) create mode 100644 unix/os/zfiomt.c (limited to 'unix/os/zfiomt.c') diff --git a/unix/os/zfiomt.c b/unix/os/zfiomt.c new file mode 100644 index 00000000..e3c3a890 --- /dev/null +++ b/unix/os/zfiomt.c @@ -0,0 +1,1911 @@ +/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _AIX +#include +#define MTIOCTOP STIOCTOP +#define MTBSF STRSF +#define MTBSR STRSR +#define MTFSF STFSF +#define MTFSR STFSR +#define MTREW STREW +#define MTWEOF STWEOF +#define mtop stop +#define mt_op st_op +#define mt_count st_count +#else +#ifndef MACOSX +#include +#endif +#endif + +/* Define if status logging to sockets is desired. */ +#define TCPIP + +#ifdef TCPIP +#include +#include +#include +#include +#define DEFPORT 5138 +#endif + +#define import_kernel +#define import_knames +#define import_zfstat +#define import_stdarg +#define import_spp +#include + +/* + * ZFIOMT.C -- Programmable magtape kernel interface for UNIX/IRAF systems. + * This file contains only the lowest level i/o routines. Most of the + * functionality of the iraf magtape i/o system is provided by the routines + * in the VOS interfaces MTIO and ETC. The dev$tapecap file is used to + * describe the magtape devices present on the local host system, and to + * characterize the behavior of each device. + * + * The behavior of this driver is controlled by parameters given in the + * entry for a magtape device in the tapecap file. The following parameters + * are defined (not all of which are necessarily used by the driver). + * + * CODE TYPE DEFAULT DESCRIPTION + * + * bs i 0 device block size (0 if variable) + * dn s 0 density + * dt s generic drive type + * fb i 1 default FITS blocking factor (recsize=fb*2880) + * fe i 0 time to FSF equivalent in file Kb + * fs i 0 approximate filemark size (bytes) + * mr i 65535 maximum record size + * or i 63360 optimum record size + * rs i 0 approximate record gap size (bytes) + * ts i 0 tape capacity (Mb) + * tt s unknown tape type + * + * al s none device allocation info + * dv s required i/o (no-rewind) device file + * lk s required lock file root name (uparm$mt.lok) + * rd s none rewind device file + * so s none status output device file or socket + * + * bo b no BSF positions to BOF + * ce b no ignore close status on CLRO + * eo b no do not write double EOT on CLWO (VMS) + * fc b no device does a FSF on CLRO + * ir b no treat all read errors as EOF + * mf b no enable multifile FSF for forward positioning + * nb b no device cannot backspace + * nf b/i no/0 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 + * ro b no rewind on every open to define position + * rr b no rewind at close-readonly to define position + * se b no device will position past EOT in a read + * sk b no skip record forward after a read error + * ue b no force update of EOT (search for EOT) + * wc b no OPWR-CLWR at EOF writes null file + * + * ct i builtin MTIOCTOP code + * bf i builtin BSF ioctl code + * br i builtin BSR ioctl code + * ff i builtin FSF ioctl code + * fr i builtin FSR ioctl code + * ri i builtin REW ioctl code + * + * + * Many of these parameters are optional. Some are used by the high level MTIO + * code rather than the host level driver. + * + * The externally callable driver routines are the following. + * + * ZZOPMT (device, acmode, devcap, devpos, newfile, chan) + * ZZRDMT (chan, buf, maxbytes, offset) + * ZZWRMT (chan, buf, nbytes, offset) + * ZZWTMT (chan, devpos, status) + * ZZSTMT (chan, param, value) + * ZZCLMT (chan, devpos, status) + * ZZRWMT (device, devcap, status) + * + * Here, "device" is the name by which the device is known to the driver, + * acmode is the access mode, devcap is the tapecap device entry, devpos is a + * structure giving the current tape position, amount of tape used, etc, and + * newfile is the requested file. The driver will position to the indicated + * file at open time. The devpos structure is passed in to the driver at open + * time and returned to the client at close time. While i/o is in progress + * the driver is responsible for keeping track of the device position. The + * client is responsible for maintaining the position information while the + * device is closed. If the position is uncertain devpos should be passed in + * with a negative file number and the driver will rewind to reestablish a + * known position. + * + * The devpos structure (struct _mtpos) has the following fields: + * + * int filno file number + * int recno record number + * int nfiles number of files on tape + * int tapeused total amount of storage used (Kb) + * int pflags bitflags describing last i/o operation + * + * FILNO and RECNO are negative if the position is undefined (unknown). File + * number 1 is the first file. NFILES is the total number of files on the + * tape. NFILES less than or equal to zero indicates that the number of files + * is unknown; the driver will set nfiles when EOT is seen. The tape can be + * positioned to EOT by opening the device at file NFILES+1. TAPEUSED refers + * to the total amount of tape used at EOT, regardless of the current tape + * position. The driver will keep track of tape usage using the device + * attributes specified in the tapecap entry. TAPEUSED less than or equal to + * zero indicates that the amount of tape used is unknown. Both the tape + * capacity and TAPEUSED are given in Kb (1024 byte blocks). + * + * + * The following bitflags are defined: + * + * MF_ERR i/o error occurred in last operation + * MF_EOF a tape mark was seen in the last operation + * MF_EOT end of tape seen in the last operation + * MF_EOR a record advance occurred in the last operation + * + * The PFLAGS field is output only, and is cleared at the beginning of each + * i/o request. + */ + +extern int errno; +typedef unsigned int U_int; + +#define CONSOLE "/dev/console" +#define MAX_ERRIGNORE 10 /* max errs before skiprec */ +#define MAX_ERRCNT 20 /* max errs before EOF */ +#define MAXDEV 8 /* max open magtape devices */ +#define MAXREC 64512 /* default maximum record size */ +#define OPTREC 64512 /* default optimum record size */ +#define SHORTREC 20 /* short record for dummy files */ +#define RDONLY 0 /* read only */ +#define WRONLY 1 /* write only */ + +/* Tape position information (must agree with client struct). This is input + * by the client at open time and is only modified locally by the driver. + */ +struct _mtpos { + int filno; /* current file (1=first) */ + int recno; /* current record (1=first) */ + int nfiles; /* number of files on tape */ + int tapeused; /* total tape used (Kb) */ + int pflags; /* i/o status bitflags (output) */ +}; + +/* MTPOS bitflags. */ +#define MF_ERR 0001 /* i/o error occurred in last operation */ +#define MF_EOF 0002 /* a tape mark was seen in the last operation */ +#define MF_EOT 0004 /* end of tape seen in the last operation */ +#define MF_EOR 0010 /* a record advance occurred in the last operation */ + +/* General magtape device information, for status output. */ +struct mtdev { + FILE *statusout; /* status out or NULL */ + int blksize; /* device block size */ + int recsize; /* last record size */ + int maxrec; /* maximum record size */ + int optrec; /* optimum record size */ + int tapesize; /* tape capacity (Kb) */ + int eofsize; /* filemark size, bytes */ + int gapsize; /* interrecord gap size, bytes */ + int maxbsf; /* BSF vs rewind-FSF threshold */ + char density[SZ_FNAME]; /* tape density, bpi */ + char devtype[SZ_FNAME]; /* drive type */ + char tapetype[SZ_FNAME]; /* tape type */ + char statusdev[SZ_FNAME]; /* status output device */ +}; + +/* Magtape device descriptor. */ +#define get_mtdesc(fd) ((struct mtdesc *)zfd[fd].fp) +#define set_mtdesc(fd,mp) zfd[fd].fp = (FILE *)mp +#define ateot(pp) (pp->nfiles>0 && pp->filno==pp->nfiles+1 && pp->recno==1) +#define spaceused(pp) ((pp->nfiles==0 || pp->filno>pp->nfiles) && !ateot(pp)) + +struct mtdesc { + XINT *chan; /* file descriptor open device */ + int flags; /* device characteristics */ + int acmode; /* access mode */ + int errcnt; /* i/o error count */ + int nbytes; /* status of last i/o transfer */ + int tbytes; /* byte portion of tapeused */ + int mtrew; /* REW ioctl code */ + int mtbsr, mtfsr; /* BSR,FSR ioctl codes */ + int mtbsf, mtfsf; /* BSF,FSF ioctl codes */ + U_int mtioctop; /* MTIOCTOP code */ + struct _mtpos mtpos; /* position information */ + struct mtdev mtdev; /* drive type information */ + char iodev[SZ_FNAME]; /* i/o device */ + char nr_device[SZ_FNAME]; /* no-rewind-on-close device */ + char rw_device[SZ_FNAME]; /* rewind-on-close device */ +}; + +/* Parameter codes. */ +#define P_AL 1 /* allocation stuff */ +#define P_BF 2 /* MTBSF */ +#define P_BO 3 /* BSF positions to BOF */ +#define P_BR 4 /* MTBSR */ +#define P_BS 5 /* block size */ +#define P_CE 6 /* ignore close status on CLRO */ +#define P_CT 7 /* MTIOCTOP */ +#define P_DN 8 /* density */ +#define P_DT 9 /* drive type id string */ +#define P_DV 10 /* no rewind device */ +#define P_EO 11 /* do not write double EOT on CLWO (VMS) */ +#define P_FC 12 /* device does FSF on close readonly */ +#define P_FF 13 /* MTFSF */ +#define P_FR 14 /* MTFSR */ +#define P_FS 15 /* filemark size, Kb */ +#define P_IR 16 /* map read errors to EOF */ +#define P_MF 17 /* enable multifile FSF for fwd positioning */ +#define P_MR 18 /* max record (i/o transfer) size */ +#define P_NB 19 /* backspace not allowed */ +#define P_NF 20 /* rewind and space forward to posn back */ +#define P_NP 21 /* disable file positioning */ +#define P_OR 22 /* optimum record size */ +#define P_OW 23 /* optimize EOT (9tk drives) */ +#define P_RD 24 /* rewind device */ +#define P_RE 25 /* read at EOT returns ERR */ +#define P_RF 26 /* use record skip ioctls to skip filemarks */ +#define P_RI 27 /* MTREW */ +#define P_RO 28 /* rewind on every open to define position */ +#define P_RR 29 /* rewind after close-readonly */ +#define P_RS 30 /* interrecord gap size, bytes */ +#define P_SE 31 /* BSF needed after read at EOT */ +#define P_SK 32 /* skip record forward after read error */ +#define P_SO 33 /* status output device or socket */ +#define P_TS 34 /* tape size, Mb */ +#define P_TT 35 /* tape type id string */ +#define P_UE 36 /* force update of EOT (search for EOT) */ +#define P_WC 37 /* open-write/close creates dummy EOT */ + +/* Tapecap device characteristic bitflags. */ +#define BO 00000001 /* BSF positions to BOF */ +#define CE 00000002 /* ignore close status on CLRO */ +#define EO 00000004 /* do not write double EOT on CLWO (VMS) */ +#define FC 00000010 /* defines does a FSF on CLRO */ +#define IR 00000020 /* treat all read errors as EOF */ +#define MF 00000040 /* enable multifile FSF for fwd positioning */ +#define NB 00000100 /* device cannot backspace */ +#define NF 00000200 /* rewind and space forward to position back */ +#define NP 00000400 /* disable file positioning */ +#define OW 00001000 /* backspace and overwrite at append */ +#define RD 00002000 /* rewind-on-close device specified */ +#define RE 00004000 /* read at EOT can signal error */ +#define RF 00010000 /* use BSR,FSR to space over filemarks */ +#define RO 00020000 /* rewind on every open to define position */ +#define RR 00040000 /* rewind after close-readonly */ +#define SE 00100000 /* read at EOT leaves past tape mark */ +#define SK 00200000 /* skip record forward after a read error */ +#define UE 00400000 /* force update of EOT (search for EOT) */ +#define WC 01000000 /* CLWR at EOF writes null file */ + +/* Device characteristic codes. */ +#define PNAME(a,b) ((((int)(a))<<8)+(int)(b)) + +/* Tape drives aren't supported on Mac systems currently. +*/ +#ifndef MACOSX + +/* Device flag table. */ +static struct mtchar { + int pname; /* 2 byte parameter name code */ + int pcode; /* parameter number */ + int bitflag; /* flag bit */ + int valset; /* value has been set */ +} devpar[] = { + { PNAME('a','l'), P_AL, 0, 0 }, + { PNAME('b','f'), P_BF, 0, 0 }, + { PNAME('b','o'), P_BO, BO, 0 }, + { PNAME('b','r'), P_BR, 0, 0 }, + { PNAME('b','s'), P_BS, 0, 0 }, + { PNAME('c','e'), P_CE, CE, 0 }, + { PNAME('c','t'), P_CT, 0, 0 }, + { PNAME('d','n'), P_DN, 0, 0 }, + { PNAME('d','t'), P_DT, 0, 0 }, + { PNAME('d','v'), P_DV, 0, 0 }, + { PNAME('e','o'), P_EO, EO, 0 }, + { PNAME('f','c'), P_FC, FC, 0 }, + { PNAME('f','f'), P_FF, 0, 0 }, + { PNAME('f','r'), P_FR, 0, 0 }, + { PNAME('f','s'), P_FS, 0, 0 }, + { PNAME('i','r'), P_IR, IR, 0 }, + { PNAME('m','f'), P_MF, MF, 0 }, + { PNAME('m','r'), P_MR, 0, 0 }, + { PNAME('n','b'), P_NB, NB, 0 }, + { PNAME('n','f'), P_NF, NF, 0 }, + { PNAME('n','p'), P_NP, NP, 0 }, + { PNAME('o','r'), P_OR, 0, 0 }, + { PNAME('o','w'), P_OW, OW, 0 }, + { PNAME('r','d'), P_RD, 0, 0 }, + { PNAME('r','e'), P_RE, RE, 0 }, + { PNAME('r','f'), P_RF, RF, 0 }, + { PNAME('r','i'), P_RI, 0, 0 }, + { PNAME('r','o'), P_RO, RO, 0 }, + { PNAME('r','r'), P_RR, RR, 0 }, + { PNAME('r','s'), P_RS, 0, 0 }, + { PNAME('s','e'), P_SE, SE, 0 }, + { PNAME('s','k'), P_SK, SK, 0 }, + { PNAME('s','o'), P_SO, 0, 0 }, + { PNAME('t','s'), P_TS, 0, 0 }, + { PNAME('t','t'), P_TT, 0, 0 }, + { PNAME('u','e'), P_UE, UE, 0 }, + { PNAME('w','c'), P_WC, WC, 0 }, + { 0, 0, 0, 0 }, +}; + + +static int zmtgetfd(); +static int zmtbsr(), zmtbsf(), zmtfsr(), zmtfsf(); +static int zmtclose(), zmtfpos(), zmtrew(); + +static int zmtopen (char *dev, int u_acmode); +static int zmtclose (int fd); +static struct mtdesc *zmtdesc (char *device, int acmode, char *devcap, + struct _mtpos *devpos); +static int zmtfpos (struct mtdesc *mp, int newfile); +static int zmtrew (int fd); +static void zmtfls (struct mtdesc *mp); +static void zmtfree (struct mtdesc *mp); +static int zmtfsf (int fd, int nfiles); +static int zmtbsf (int fd, int nfiles); +static int zmtfsr (int fd, int nrecords); +static int zmtbsr (int fd, int nrecords); + +static void zmtdbgn (struct mtdesc *mp, const char *argsformat, ... ); +static void zmtdbg (struct mtdesc *mp, char *msg); +static void zmtdbgopen (struct mtdesc *mp); +static void zmtdbgclose (struct mtdesc *mp); + + + + +/* ZZOPMT -- Open the named magtape device and position to the given file. + * On output, "newfile" contains the number of the file actually opened, + * which may be less than what was requested if EOT is reached. + */ +int +ZZOPMT ( + PKCHAR *device, /* device name */ + XINT *acmode, /* access mode: read_only or write_only for tapes */ + PKCHAR *devcap, /* tapecap entry for device */ + XINT *devpos, /* pointer to tape position info struct */ + XINT *newfile, /* file to be opened or EOT */ + XINT *chan /* OS channel of opened file */ +) +{ + register int fd; + register struct mtdesc *mp; + struct _mtpos *pp; + + /* Open the main device descriptor. */ + mp = zmtdesc ((char *)device, *acmode, (char *)devcap, + (struct _mtpos *)devpos); + if (mp == NULL) { + *chan = XERR; + return (XERR); + } + + zmtdbgn (mp, "open device %s\n", (char *)device); + + /* Save the channel pointer for the delayed open used for file + * positioning. If file positioning is needed the device will + * be opened read-only, so that an interrupt occurring while seeking + * to EOT for writing will not result in truncation of the tape! + * BE SURE TO RETURN OSCHAN as soon as the device is physically + * opened, so that the error recovery code can close the file if + * we are interrupted. + */ + mp->chan = chan; + *chan = 0; + + /* Initialize the descriptor. */ + mp->errcnt = 0; + mp->tbytes = 0; + pp = &mp->mtpos; + + /* Zero counters if position is undefined. */ + if (pp->filno < 1 || pp->recno < 1) { + pp->nfiles = 0; + pp->tapeused = 0; + } + + /* Zero counters if new tape? */ + if (mp->acmode == WRITE_ONLY && (*newfile == 0 || *newfile == 1)) { + pp->nfiles = 0; + pp->tapeused = 0; + } + + /* Zero tapeused counter if rewinding and nfiles is still unknown. */ + if (pp->nfiles == 0 && *newfile == 1) + pp->tapeused = 0; + + /* Status output. */ + zmtdbgn (mp, "devtype = %s", mp->mtdev.devtype); + zmtdbgn (mp, "tapetype = %s", mp->mtdev.tapetype); + zmtdbgn (mp, "tapesize = %d", mp->mtdev.tapesize); + zmtdbgn (mp, "density = %s", + mp->mtdev.density[0] ? mp->mtdev.density : "na"); + zmtdbgn (mp, "blksize = %d", mp->mtdev.blksize); + zmtdbgn (mp, "acmode = %s", mp->acmode == READ_ONLY ? "read" : + ((*newfile < 0) ? "append" : "write")); + zmtdbgn (mp, "file = %d%s", pp->filno, ateot(pp) ? " (EOT)" : ""); + zmtdbgn (mp, "record = %d", pp->recno); + zmtdbgn (mp, "nfiles = %d", pp->nfiles); + zmtdbgn (mp, "tapeused = %d", pp->tapeused); + zmtfls (mp); + + /* Position to the desired file. Do not move the tape if newfile=0 + * or if NP (no-position) is specified for the device. Rewind the tape + * to get to a known position if current tape position is undefined. + */ + if (*newfile == 0 || (mp->flags & NP)) { + zmtdbg (mp, "file positioning is disabled\n"); + zmtfls (mp); + } + if (*newfile) { + /* Rewind if current position uncertain. */ + if ((mp->flags & RO) || pp->filno < 1 || pp->recno < 1) { + if (!(mp->flags & NP)) + if ((fd = zmtgetfd (mp)) == ERR || zmtrew(fd) == ERR) + goto err; + pp->filno = pp->recno = 1; + } + + /* Position to the desired file. NP disables file positioning, + * in which case we assume the user knows what they are doing + * and we are already there. + */ + if (mp->flags & NP) + *newfile = (*newfile < 0) ? pp->filno : *newfile; + else if ((*newfile = zmtfpos (mp, *newfile)) == XERR) + goto err; + } + + /* Reopen file with write permission if necessary. */ + if (mp->acmode == WRITE_ONLY) { + if (*chan) { + zmtclose (*chan); + zmtdbg (mp, "reopen for writing\n"); + } + (*chan) = fd = zmtopen (mp->iodev, WRONLY); + if (fd != ERR) + zmtdbgn (mp, + "device %s opened on descriptor %d\n", mp->iodev, fd); + } else + fd = zmtgetfd (mp); + + if (fd == ERR) + goto err; + set_mtdesc(fd,mp); + mp->errcnt = 0; + + zmtdbgn (mp, "file = %d%s", pp->filno, ateot(pp) ? " (EOT)" : ""); + zmtdbgn (mp, "record = %d", mp->mtpos.recno); + zmtfls (mp); + + if (mp->acmode == WRITE_ONLY) + zmtdbg (mp, "writing...\n"); + else + zmtdbg (mp, "reading...\n"); + + return (XOK); +err: + /* Error exit. */ + zmtfree (mp); + *chan = XERR; + + return (*chan); +} + + +/* ZZCLMT -- Close magtape. Write a new EOT mark at the current position + * if tape is open for writing, leaving tape positioned ready to write the + * next file. + */ +int +ZZCLMT (XINT *chan, XINT *devpos, XINT *o_status) +{ + register int fd; + register struct mtdesc *mp; + register struct _mtpos *pp; + int status, eof_seen, eor_seen, eot_seen; + + /* Since open files are closed during error recovery and an interrupt + * can occur while closing a magtape file, it is possible that ZZCLMT + * twice, after the host file has been closed and the MP descriptor + * freed. Watch out for a bad channel number or mp=NULL. + */ + *o_status = XERR; + if ((fd = *chan) <= 0) + return (XERR); + if ((mp = get_mtdesc(fd)) == NULL) + return (XERR); + pp = &mp->mtpos; + + eof_seen = 0; + eor_seen = 0; + eot_seen = 0; + status = OK; + + /* Close file and update tape position. + */ + if (mp->acmode == READ_ONLY) { + /* Rewind if the rewind-after-read flag is set. This is used on + * devices that can leave the tape in an undefined position after + * a file read. + */ + if (mp->flags & RR) { + if (zmtrew(fd) == ERR) + status = ERR; + else { + pp->filno = pp->recno = 1; + zmtdbgn (mp, "file = %d", pp->filno); + zmtdbgn (mp, "record = %d", pp->recno); + } + } + + /* Close device. */ + status = zmtclose (fd); + if (mp->flags & CE) + status = 0; + + /* On some SysV systems closing a tape opened RO causes a skip + * forward to BOF of the next file on the tape. This does not + * occur though when the device is closed after reading EOF on + * a file. + */ + if ((mp->flags & FC) && pp->recno > 1) + eof_seen = 1; + + } else if (pp->recno > 1) { + /* Close WRONLY other than at BOF always writes EOT, advancing + * the file position by one file. + */ + status = zmtclose (fd); + eof_seen = 1; + eot_seen = 1; + + } else if (mp->flags & WC) { + /* If a tape is opened for writing and then closed without any + * data being written, a tape mark is written resulting in a + * dummy EOT in the middle of the tape if writing continues. + * Backspace over the the extra tape mark if possible, otherwise + * write a short record. This will result in an extra dummy + * file being written to the tape but the only alternative is to + * rewind and space forward, or abort on an error. + */ + register int flags = mp->flags; + int blksize = mp->mtdev.blksize; + int bufsize; + char *bufp; + + if ((flags & NB) || ((flags & BO) && !(flags & RF))) { + bufsize = blksize ? blksize : SHORTREC; + bufsize = max (bufsize, SHORTREC); + if ((bufp = malloc(bufsize)) == NULL) { + zmtclose (fd); + status = ERR; + } else { + zmtdbg (mp, "no data - null file written\n"); + zmtfls (mp); + strcpy (bufp, "[NULLFILE]"); + write (fd, bufp, bufsize); + free (bufp); + status = zmtclose (fd); + eof_seen = 1; + } + } else { + /* Close and write EOT, reopen RDONLY and backspace over it. */ + status = (zmtclose(fd) == ERR); + if (status || (fd = zmtopen (mp->iodev, RDONLY)) == ERR) + status = ERR; + else { + status = ((flags & RF) ? zmtbsr : zmtbsf)(fd, 1); + status = (zmtclose(fd) == ERR) ? ERR : status; + } + eof_seen = 1; + } + eot_seen = 1; + } else { + status = zmtclose (fd); + eof_seen = 0; + eot_seen = 1; + } + + /* Update position information and write status output. */ + pp->pflags = status ? MF_ERR : 0; + if (eot_seen) { + pp->pflags |= MF_EOT; + } + if (eof_seen) { + pp->pflags |= MF_EOF; + pp->filno++; + pp->recno = 1; + if (mp->acmode == WRITE_ONLY) { + pp->nfiles = pp->filno - 1; + zmtdbgn (mp, "nfiles = %d", pp->nfiles); + } + if (mp->acmode == WRITE_ONLY || spaceused(pp)) + mp->tbytes += mp->mtdev.eofsize; + zmtdbgn (mp, "record = %d", pp->recno); + zmtdbgn (mp, "file = %d%s", pp->filno, ateot(pp) ? " (EOT)" : ""); + } + if (eor_seen) { + pp->pflags |= MF_EOR; + pp->recno++; + if (mp->acmode == WRITE_ONLY || spaceused(pp)) + mp->tbytes += mp->mtdev.gapsize; + zmtdbgn (mp, "record = %d", pp->recno); + } + + pp->tapeused += ((mp->tbytes + 512) / 1024); + zmtdbgn (mp, "tapeused = %d", pp->tapeused); + zmtfls (mp); + + *((struct _mtpos *)devpos) = *pp; + *o_status = status ? XERR : XOK; + zmtfree (mp); + + return (status); +} + + +/* ZZRDMT -- Read next tape record. We are supposed to be asynchronous, + * so save read status for return by next call to ZZWTMT. Read returns + * zero byte count if EOF is seen, as required by the specs, so we need + * do nothing special in that case. Tape is left positioned just past the + * tape mark. + */ +int +ZZRDMT ( + XINT *chan, + XCHAR *buf, + XINT *maxbytes, + XLONG *offset /* fixed block devices only */ +) +{ + register int fd = *chan, mb = (int)*maxbytes; + register struct mtdesc *mp = get_mtdesc(fd); + register struct _mtpos *pp = &mp->mtpos; + int status; + + if (mp->mtdev.blksize && (mb % mp->mtdev.blksize)) { + zmtdbgn (mp, + "read request %d not a multiple of device block size\n", mb); + zmtfls (mp); + } + + /* Position to the desired record (fixed block devices only). */ +/* + if (mp->mtdev.blksize && *offset > 0) { + int blkno, oldblk; + blkno = *offset / mp->mtdev.blksize + 1; + oldblk = mp->mtpos.recno; + if (blkno != oldblk) { + zmtdbgn (mp, "position to block %d\n", blkno); + if (blkno > oldblk) { + if (zmtfsr (fd, blkno - oldblk) == ERR) { + status = ERR; + goto done; + } + } else { + if ((mp->flags & NB) || zmtbsr(fd,oldblk-blkno) == ERR) { + status = ERR; + goto done; + } + } + mp->mtpos.recno = blkno; + } + } + */ + + /* Map read error to EOF if RE is set and we are reading the first + * record of a file (i.e. are positioned to EOT) or if IR is set. + */ + status = read (fd, (char *)buf, mb); + if (status == ERR) { + if ((mp->flags & RE) && pp->recno == 1) { + status = 0; + } else if (mp->flags & IR) { + zmtdbg (mp, "read error converted to zero read (EOF)\n"); + zmtfls (mp); + status = 0; + } + } + + /* If an error occurs on the read we assume that the tape has advanced + * beyond the bad record, and that the next read will return the next + * record on the tape. If this is not true and a read error loop + * occurs, we try skipping a record forward. If we continue to get + * read errors, we give up and return a premature EOF on the file. + */ + if (status == ERR) { + zmtdbgn (mp, "read error, errno = %d\n", errno); + zmtfls (mp); + if ((mp->errcnt)++ >= MAX_ERRCNT) + status = 0; /* give up; return EOF */ + else if ((mp->flags & SK) || mp->errcnt >= MAX_ERRIGNORE) + zmtfsr (fd, 1); + } + + mp->nbytes = status; + if (status >= 0 && mp->mtdev.recsize != status) + zmtdbgn (mp, "recsize = %d", mp->mtdev.recsize = status); + zmtfls (mp); + + return (status); +} + + +/* ZZWRMT -- Write next tape record. We are supposed to be asynchronous, + * so save write status for return by next call to ZZWTMT. + */ +int +ZZWRMT ( + XINT *chan, + XCHAR *buf, + XINT *nbytes, + XLONG *offset /* ignored on a write */ +) +{ + register int fd = *chan, nb = *nbytes; + register struct mtdesc *mp = get_mtdesc(fd); + int blksize = mp->mtdev.blksize; + + /* If writing to a blocked device, promote partial blocks to a + * full device block. + */ + if (blksize > 0 && (nb % blksize)) { + nb += blksize - (nb % blksize); + zmtdbgn (mp, "partial record promoted from %d to %d bytes\n", + *nbytes, nb); + } + + if (mp->mtdev.recsize != nb) + zmtdbgn (mp, "recsize = %d", mp->mtdev.recsize = nb); + if ((mp->nbytes = write (fd, (char *)buf, nb)) != nb) { + zmtdbgn (mp, "write error, status=%d, errno=%d\n", + mp->nbytes, errno); + mp->nbytes = ERR; + } + zmtfls (mp); + + return (XOK); +} + + +/* ZZWTMT -- "Wait" for i/o transfer to complete, and return the number of + * bytes transferred or XERR. A read at EOF returns a byte count of zero. + */ +int +ZZWTMT ( + XINT *chan, + XINT *devpos, + XINT *o_status +) +{ + register int fd = *chan; + register struct mtdesc *mp = get_mtdesc(fd); + register struct _mtpos *pp = &mp->mtpos; + register int flags = mp->flags; + int status, eof_seen, eor_seen, eot_seen; + + eof_seen = 0; + eor_seen = 0; + eot_seen = 0; + status = OK; + + if ((status = mp->nbytes) == ERR) { /* i/o error */ + status = ERR; + } else if (status == 0) { /* saw EOF */ + if (pp->recno <= 1) { + /* A read of zero (EOF) at the beginning of a file signals + * EOT. The file number does not change. + */ + pp->nfiles = pp->filno - 1; + zmtdbgn (mp, "nfiles = %d", pp->nfiles); + zmtdbgn (mp, "file = %d%s", + pp->filno, ateot(pp) ? " (EOT)" : ""); + zmtfls (mp); + eot_seen = 1; + + /* If the device allows us to read past the second filemark + * (SE) we must backspace over the filemark or any further + * reads could result in tape runaway. + */ + if (flags & SE) { + if ((flags & NB) || ((flags & FC) && !(flags & RF))) { + /* Cannot backspace; must rewind and space forward. */ + if (zmtrew(fd) == ERR) + status = ERR; + else if (zmtfsf(fd,pp->filno-1) == ERR) + status = ERR; + } else { + /* BSR is preferable if we can use it. */ + if ((((flags & RF) ? zmtbsr : zmtbsf)(fd, 1)) < 0) + status = ERR; + } + } + } else + eof_seen = 1; + } else + eor_seen = 1; + + /* Update position records and output status info. */ + pp->pflags = (status < 0) ? MF_ERR : 0; + if (eot_seen) + pp->pflags |= MF_EOT; + if (eof_seen) { + pp->filno++; + pp->recno = 1; + pp->pflags |= MF_EOF; + zmtdbg (mp, "record = 1"); + if (spaceused(pp)) + mp->tbytes += mp->mtdev.eofsize; + zmtdbgn (mp, "file = %d%s", pp->filno, ateot(pp) ? " (EOT)" : ""); + } + if (eor_seen) { + pp->pflags |= MF_EOR; + if (mp->mtdev.blksize > 0) + pp->recno += (status / mp->mtdev.blksize); + else + pp->recno++; + if (spaceused(pp)) + mp->tbytes += mp->mtdev.gapsize; + zmtdbgn (mp, "record = %d", pp->recno); + } + + if (status >= 0 && spaceused(pp)) { + mp->tbytes += status; + pp->tapeused += mp->tbytes / 1024; + mp->tbytes %= 1024; + zmtdbgn (mp, "tapeused = %d", pp->tapeused); + } + + *((struct _mtpos *)devpos) = *pp; + *o_status = (status < 0) ? XERR : status; + zmtfls (mp); + + return (status); +} + + +/* ZZSTMT -- Query a device or device driver parameter. + */ +int +ZZSTMT (XINT *chan, XINT *param, XLONG *lvalue) +{ + register int fd = *chan; + register struct mtdesc *mp = get_mtdesc(fd); + /* register struct _mtpos *pp = &mp->mtpos; */ + + + switch (*param) { + case FSTT_BLKSIZE: + /* Zero for variable size record devices, nonzero for fixed + * block size devices. + */ + (*lvalue) = mp->mtdev.blksize; + break; + case FSTT_FILSIZE: + /* When reading there is no way to know the file size, so set + * it to the largest possible value to make all reads in bounds. + * When appending a file the file starts out zero length, so + * set the file size to zero for a write access. + */ + if (mp->acmode == READ_ONLY) + (*lvalue) = MAX_LONG; + else + (*lvalue) = 0; + break; + case FSTT_OPTBUFSIZE: + (*lvalue) = mp->mtdev.optrec; + break; + case FSTT_MAXBUFSIZE: + (*lvalue) = mp->mtdev.maxrec; + break; + default: + (*lvalue) = XERR; + } + + return (*lvalue); +} + + +/* ZZRWMT -- Rewind the tape. This routine is in principle asynchronous but + * this is not the case for most unix systems (unless the host driver does + * asynchronous rewind with synchronization internally). + * + * This routine is not part of the normal binary file driver. + */ +int +ZZRWMT ( + PKCHAR *device, /* device name */ + PKCHAR *devcap, /* tapecap entry for device */ + XINT *o_status +) +{ + register struct mtdesc *mp; + register int fd; + int status; + + /* Open the main device descriptor. */ + mp = zmtdesc ((char *)device, READ_ONLY, (char *)devcap, NULL); + if (mp == NULL) { + *o_status = ERR; + return (XERR); + } + + /* If a rewind-on-close device is defined for this device use that + * to do the rewind, otherwise open the no-rewind device RDONLY and do + * an explicit rewind. The RD device can also be used to avoid an + * error condition if the device does not support the MTREW ioctl. + */ + if (mp->flags & RD) { + if ((fd = zmtopen (mp->rw_device, RDONLY)) == ERR) + status = ERR; + else + status = zmtclose (fd); + } else { + if ((fd = zmtopen (mp->iodev, RDONLY)) == ERR) { + status = ERR; + } else if (mp->flags & FC) { + /* Device does a FSF when closed read-only, making it + * impossible to leave the tape rewound after the close. + * Return ERR to cause MTIO to mark the position undefined, + * forcing a rewind the next time the tape is opened for i/o. + */ + static FILE *tty = NULL; + if (!tty && (tty = fopen (CONSOLE, "a")) != NULL) { + fprintf (tty, "cannot rewind device %s: ", (char *)device); + fprintf (tty, "add RD capability to dev$devices entry\n"); + fclose (tty); + } + status = ERR; + } else { + /* Normal rewind. */ + status = zmtrew (fd); + status = zmtclose(fd) ? ERR : status; + } + } + + zmtdbg (mp, "file = 1"); + zmtdbg (mp, "record = 1"); + zmtfls (mp); + *o_status = status ? XERR : XOK; + zmtfree (mp); + + return (status); +} + + + +/* + * INTERNAL INTERFACE ROUTINES. + * ---------------------------- + */ + +/* ZMTGETFD -- Open tape read-only, if not already open, and return the + * file descriptor as the function value. If the tape is already open + * the only action is to return the file descriptor. This routine is used + * to delay the device open during file positioning operations, so that + * it can be skipped if it is not necessary to move the tape. + */ +static int +zmtgetfd (mp) +register struct mtdesc *mp; +{ + register int fd; + + if (*mp->chan > 0) + return (*mp->chan); + + *mp->chan = fd = zmtopen (mp->iodev, RDONLY); + if (fd >= MAXOFILES) { + zmtclose (fd); + fd = ERR; + } + + if (fd == ERR) + zmtdbgn (mp, "failed to open device %s\n", mp->iodev); + else { + zmtdbgn (mp, "device %s opened on descriptor %d\n", mp->iodev, fd); + set_mtdesc (fd, mp); + mp->errcnt = 0; + mp->tbytes = 0; + } + + return (fd); +} + + +/* ZMTOPEN -- Convert the magtape device name into a unix pathname and open + * the drive. Do not move the tape. + * + * Devices can be specified as + * + * devname system device name in /dev or /dev/rmt + * /devpath full pathname of device + * ~/devpath user home directory relative pathname + * + * Returns the unix file descriptor or ERR. + */ +static int +zmtopen ( + char *dev, /* device name or pathname */ + int u_acmode /* read only or write only for tapes */ +) +{ + char path[SZ_PATHNAME+1]; + int fd = ERR; + + /* If the device name is already a pathname leave it alone, else + * prepend the /dev/ or /dev/rmt prefix. The device file can be + * in the user's home directory if ~/dev is specified. + */ + if (dev[0] == '/') { + /* Full pathname. */ + fd = open (dev, u_acmode); + + } else if (dev[0] == '~' && dev[1] == '/') { + /* User home directory relative pathname. */ + struct passwd *pwd; + pwd = getpwuid (getuid()); + if (pwd != NULL) { + strcpy (path, pwd->pw_dir); + strcat (path, &dev[1]); + endpwent(); + fd = open (path, u_acmode); + } + } else { + /* System device. */ + strcpy (path, "/dev/"); + strcat (path, dev); + if ((fd = open (path, u_acmode)) == ERR) { + /* If that fails take a look in /dev/rmt too, since this + * is where some SysV systems like to hide raw magtape device + * files. + */ + strcpy (path, "/dev/rmt/"); + strcat (path, dev); + fd = open (path, u_acmode); + } + } + + return (fd); +} + + +/* ZMTCLOSE -- Close a magtape device. + */ +static int zmtclose (int fd) +{ + register struct mtdesc *mp = get_mtdesc(fd); + zmtdbg (get_mtdesc(fd), "close device\n"); + zmtfls (mp); + return (close (fd)); +} + + +/* ZMTDESC -- Allocate and initialize the main magtape device descriptor. + */ +static struct mtdesc * +zmtdesc ( + char *device, /* host device to be used for i/o */ + int acmode, /* iraf file access mode code */ + char *devcap, /* tapecap entry for device */ + struct _mtpos *devpos /* device position info (or NULL ptr) */ +) +{ + register struct mtdesc *mp; + register struct mtdev *dp; + register struct mtchar *pp; + register char *ip, *op; + int pname; + + /* Allocate and initialize the device descriptor. */ + if ((mp = (struct mtdesc *) calloc (1, sizeof(*mp))) == NULL) + return (NULL); + + dp = &mp->mtdev; + dp->maxrec = MAXREC; + dp->optrec = OPTREC; + strcpy (dp->devtype, "generic"); + strcpy (dp->tapetype, "unknown"); + + mp->acmode = acmode; + strcpy (mp->iodev, device); + mp->mtioctop = MTIOCTOP; + mp->mtbsr = MTBSR; + mp->mtbsf = MTBSF; + mp->mtfsr = MTFSR; + mp->mtfsf = MTFSF; + mp->mtrew = MTREW; + + if (devpos) { + mp->mtpos = *devpos; + mp->mtpos.pflags = 0; + } + + /* Prepare to scan tapecap entry. */ + for (pp=devpar; pp->pname; pp++) + pp->valset = 0; + + /* Process the tapecap entry. This is a sequence of entries of the + * form "nn=value", where the NN is a two character name, the "=" is + * actually either `=' or `#', and where successive entries are + * delimited by colons. For example, ":dv=nrst0:rd=rst0:bs#0:...". + */ + for (ip=devcap; *ip; ) { + while (*ip && *ip != ':') + ip++; + if (*ip == ':') + ip++; + else + break; + pname = PNAME(ip[0],ip[1]); + + ip += 2; + if (*ip == '=' || *ip == '#') + ip++; + + for (pp=devpar; pp->pname; pp++) { + if (pp->pname == pname) { + /* If multiple entries are given for the parameter ignore + * all but the first. + */ + if (pp->valset) + continue; + else + mp->flags |= pp->bitflag; + + /* Check for a negated entry (e.g., ":ir@:"). */ + if (*ip == '@') { + mp->flags &= ~pp->bitflag; + pp->valset++; + continue; + } + + /* Check for a string valued parameter. */ + switch (pp->pcode) { + case P_DV: op = mp->nr_device; break; + case P_RD: op = mp->rw_device; break; + case P_DN: op = mp->mtdev.density; break; + case P_DT: op = mp->mtdev.devtype; break; + case P_TT: op = mp->mtdev.tapetype; break; + case P_SO: op = mp->mtdev.statusdev; break; + default: op = NULL; + } + + if (op != NULL) { + int nchars; + + /* String valued parameters. */ + for (nchars=0; *ip && *ip != ':'; ip++, nchars++) { + if (*ip == '\\' && isdigit(*(ip+1))) { + int n, i; + for (n=i=0; i < 3; i++) + n = n * 10 + (*(++ip) - '0'); + *op++ = n; + } else + *op++ = *ip; + } + *op = EOS; + pp->valset++; + + /* Default if no string value given but entry was + * found, e.g., ":so:". + */ + if (!nchars) + if (pp->pcode == P_SO) + strcpy (mp->mtdev.statusdev, ","); + break; + + } else if (*ip != ':') { + /* Numeric parameters. */ + int n = 0; + + while (*ip && *ip != ':') { + if (isdigit (*ip)) + n = n * 10 + (*ip - '0'); + ip++; + } + + switch (pp->pcode) { + case P_CT: mp->mtioctop = n; break; + case P_BF: mp->mtbsf = n; break; + case P_BR: mp->mtbsr = n; break; + case P_FF: mp->mtfsf = n; break; + case P_FR: mp->mtfsr = n; break; + case P_RI: mp->mtrew = n; break; + + case P_BS: dp->blksize = n; break; + case P_FS: dp->eofsize = n; break; + case P_MR: dp->maxrec = n; break; + case P_NF: dp->maxbsf = n; break; + case P_OR: dp->optrec = n; break; + case P_RS: dp->gapsize = n; break; + case P_TS: dp->tapesize = n; break; + default: /* ignore (bitflags) */ + ; + } + + pp->valset++; + break; + } + } + } + } + + /* Apply some obvious constraints. */ + if (dp->blksize) { + dp->maxrec = dp->maxrec / dp->blksize * dp->blksize; + dp->optrec = dp->optrec / dp->blksize * dp->blksize; + } + if (dp->maxrec > 0 && dp->optrec > dp->maxrec) + dp->optrec = dp->maxrec; + + zmtdbgopen (mp); + return (mp); +} + + +/* ZMTFREE -- Free the magtape device descriptor. + */ +static void +zmtfree (struct mtdesc *mp) +{ + zmtdbgclose (mp); + free (mp); +} + + +/* ZMTFPOS -- Position to the indicated file. The first file is #1. + * A negative newfile number signifies EOT. + */ +static int +zmtfpos ( + register struct mtdesc *mp, + int newfile /* file we want to position to */ +) +{ + register struct _mtpos *pp = &mp->mtpos; + register int flags = mp->flags; + int oldfile, oldrec, maxrec; + char *buf = NULL; + int fd, status, n; + + oldfile = pp->filno; + oldrec = pp->recno; + + if (newfile > 0) + zmtdbgn (mp, "position to file %d\n", newfile); + else if (newfile < 0) + zmtdbg (mp, "position to end of tape\n"); + else + return (oldfile); + + /* If we are positioning to EOT and UE is not set to force a search + * for EOT, use the nfiles information in the position descriptor to + * position to just before the EOT marker. + */ + if (newfile < 0 && !(flags&UE) && pp->nfiles > 0) { + newfile = pp->nfiles + 1; + zmtdbgn (mp, "end of tape is file %d\n", newfile); + } + zmtfls (mp); + + /* Don't do anything if already positioned to desired file and no + * further positioning is necessary. + */ + if (newfile == oldfile && oldrec == 1 && (!(flags & OW) || + newfile < pp->nfiles + 1 || mp->acmode == READ_ONLY)) + return (newfile); + + /* It is necessary to move the tape. Open the device if it has + * not already been opened. + */ + if ((fd = zmtgetfd(mp)) < 0) + return (ERR); + + /* Move the tape. */ + if (newfile == 1) { + if (zmtrew(fd) < 0) + return (ERR); + + } else if (newfile <= oldfile && newfile > 0) { + /* Backspace to the desired file. */ + if ((flags & NB) || + ((flags & NF) && oldfile - newfile > mp->mtdev.maxbsf)) { + + /* Device cannot backspace or is slow to backspace; must + * rewind and space forward. + */ + if (zmtrew(fd) < 0) + return (ERR); + oldfile = oldrec = 1; + zmtdbgn (mp, "file = %d", oldfile); + zmtfls (mp); + goto fwd; + } else if (flags & BO) { + /* BSF positions to BOF. */ + if (zmtbsf (fd, oldfile - newfile) < 0) + return (ERR); + } else { + /* BSF positions to BOT side of filemark. */ + if (zmtbsf (fd, oldfile - newfile + 1) < 0) + return (ERR); + else if (zmtfsf (fd, 1) < 0) + return (ERR); + } + + } else if (newfile < 0 && !(flags & UE) && + (pp->nfiles > 0 && oldfile == pp->nfiles+1)) { + + /* Already at EOT. */ + newfile = oldfile; + if ((flags & OW) && mp->acmode == WRITE_ONLY) + goto oweot; + + } else { + /* Space forward to desired file or EOT. + */ +fwd: + /* Fast file skip forward to numbered file. Used only when + * positioning to a numbered file, as opposed to positioning + * to EOT and the number of files on the tape is unknown. + * A multifile FSF is much faster on some devices than skipping + * a file at a time. It is also an atomic, uninterruptable + * operation so may be undesirable on devices where file + * positioning takes a long time, and could result in tape + * runaway in an attempt to position beyond EOT (if the host + * device driver cannot detect this). Fast skip is enabled if + * the MF (multifile-file) flag is set. + */ + if (newfile > oldfile && (flags & MF)) { + if (zmtfsf (fd, newfile - oldfile) < 0) + return (ERR); + + oldfile = newfile; + if ((flags & OW) && mp->acmode == WRITE_ONLY) + goto oweot; + else + goto done; + } + + /* Get a read buffer as large as the largest possible record, + * for variable record size devices, or the size of a device + * block for fixed block devices. + */ + if (mp->mtdev.blksize) + maxrec = mp->mtdev.blksize; + else { + maxrec = mp->mtdev.maxrec; + if (maxrec <= 0) + maxrec = MAXREC; + } + if (buf == NULL && !(buf = malloc(maxrec))) + return (ERR); + + /* Skip file forward one file at a time. This is tricky as we + * must be able to detect EOT when spacing forward or we risk + * tape runaway. Detecting EOT portably requires looking for + * a double EOT. We FSF to the next file and then read the + * first record; a read of zero (or ERR on some devices) signals + * a tape mark and hence double EOF or EOT. + */ + while (oldfile < newfile || newfile < 0) { + /* Test if the next record is a data record or filemark. */ + n = read (fd, buf, maxrec); + + /* Check for EOT, signified by two consecutive logical + * filemarks, or equivalently, a zero length file. On + * some systems a read at EOT might be an error, so treat + * a read error the same as EOF if the RE capability is set. + * (the IR flag causes all read errors to be treated as EOF + * and is intended only to try to workaround host driver bugs). + */ + if (n < 0 && !(flags & (RE|IR))) { + goto err; + } else if (n <= 0 && oldrec == 1) { + /* At EOT. Leave the tape between the filemarks if such + * a concept applies to the current device. If SE is + * not specified for the device, we are already there. + */ + + pp->nfiles = (newfile=oldfile) - 1; + zmtdbgn (mp, "nfiles = %d", pp->nfiles); + zmtdbg (mp, "at end of tape\n"); + zmtfls (mp); + + if (flags & SE) { + /* Cannot backspace? */ + if (flags & NB) { + newfile = oldfile; + if (zmtrew (fd) < 0) + goto err; + if (zmtfsf (fd, newfile - 1) < 0) + goto err; + oldrec = 1; + break; + } else { + if ((((flags & RF) ? zmtbsr:zmtbsf)(fd, 1)) < 0) + goto err; + } + } +oweot: + /* On some devices, e.g., 1/2 inch reel tape, the space + * between the two filemarks marking EOT can be large and + * we can get more data on the tape if we back up over the + * first filemark and then space forward over it, leaving + * the tape just after the first filemark rather than just + * before the second one. The OW (overwrite) capability + * enables this. + */ + if ((flags & OW) && !(flags & NB) && + mp->acmode == WRITE_ONLY) { + + if (flags & RF) { + status = zmtbsr (fd, 1); + status = (zmtfsr (fd, 1) < 0) ? ERR : status; + } else if (flags & BO) { + /* This may not actually do anything, depending + * upon the host driver... */ + status = zmtbsf (fd, 0); + } else { + status = zmtbsf (fd, 1); + status = (zmtfsf (fd, 1) < 0) ? ERR : status; + } + if (status < 0) + goto err; + } + + break; + + } else if (n > 0) { + if (zmtfsf (fd, 1) < 0) { +err: free (buf); + return (ERR); + } + } + + oldfile++; + oldrec = 1; + zmtdbgn (mp, "file = %d", oldfile); + zmtfls (mp); + } + + /* Set newfile to the file we actually ended up positioned to. */ + newfile = oldfile; + free (buf); + } +done: + /* Update position descriptor */ + pp->filno = newfile; + pp->recno = 1; + + return (newfile); +} + + +/* ZMTREW -- Rewind the tape. + */ +static int +zmtrew (int fd) +{ + register struct mtdesc *mp = get_mtdesc(fd); + struct mtop mt_rewind; + int status; + + mt_rewind.mt_op = mp->mtrew; + mt_rewind.mt_count = 1; + + zmtdbg (mp, "rewinding..."); + zmtfls (mp); + status = ioctl (fd, mp->mtioctop, (char *)&mt_rewind); + zmtdbgn (mp, "%s\n", status < 0 ? "failed" : "done"); + zmtfls (mp); + + return (status); +} + + +/* ZMTFSF -- Skip file forward. + */ +static int +zmtfsf (int fd, int nfiles) +{ + register struct mtdesc *mp = get_mtdesc(fd); + struct mtop mt_fwdskipfile; + int status; + + mt_fwdskipfile.mt_op = mp->mtfsf; + mt_fwdskipfile.mt_count = nfiles; + + zmtdbgn (mp, "skip %d file%s forward...", nfiles, + nfiles > 1 ? "s" : ""); + zmtfls (mp); + status = ioctl (fd, mp->mtioctop, (char *)&mt_fwdskipfile); + zmtdbgn (mp, "%s\n", status < 0 ? "failed" : "done"); + zmtfls (mp); + + return (status); +} + + +/* ZMTBSF -- Skip file backward. + */ +static int +zmtbsf (int fd, int nfiles) +{ + register struct mtdesc *mp = get_mtdesc(fd); + struct mtop mt_backskipfile; + int status; + + mt_backskipfile.mt_op = mp->mtbsf; + mt_backskipfile.mt_count = nfiles; + + zmtdbgn (mp, "skip %d file%s backward...", nfiles, + nfiles > 1 ? "s" : ""); + zmtfls (mp); + status = ioctl (fd, mp->mtioctop, (char *)&mt_backskipfile); + zmtdbgn (mp, "%s\n", status < 0 ? "failed" : "done"); + zmtfls (mp); + + return (status); +} + + +/* ZMTFSR -- Skip record forward. + */ +static int +zmtfsr (int fd, int nrecords) +{ + register struct mtdesc *mp = get_mtdesc(fd); + struct mtop mt_fwdskiprecord; + int status; + + mt_fwdskiprecord.mt_op = mp->mtfsr; + mt_fwdskiprecord.mt_count = nrecords; + + zmtdbgn (mp, "skip %d record%s forward...", nrecords, + nrecords > 1 ? "s" : ""); + zmtfls (mp); + status = ioctl (fd, mp->mtioctop, (char *)&mt_fwdskiprecord); + zmtdbgn (mp, "%s\n", status < 0 ? "failed" : "done"); + zmtfls (mp); + + return (status); +} + + +/* ZMTBSR -- Skip record backward. + */ +static int +zmtbsr (int fd, int nrecords) +{ + register struct mtdesc *mp = get_mtdesc(fd); + struct mtop mt_backskiprecord; + int status; + + mt_backskiprecord.mt_op = mp->mtbsr; + mt_backskiprecord.mt_count = nrecords; + + zmtdbgn (mp, "skip %d record%s backward...", nrecords, + nrecords > 1 ? "s" : ""); + zmtfls (mp); + status = ioctl (fd, mp->mtioctop, (char *)&mt_backskiprecord); + zmtdbgn (mp, "%s\n", status < 0 ? "failed" : "done"); + zmtfls (mp); + + return (status); +} + + +/* + * I/O logging routines. + * + * zmtdbgopen (mp) + * zmtdbg (mp, msg) + * zmtdbgn (mp, fmt, arg) + * zmtdbgn (mp, fmt, arg1, arg2) + * zmtdbg3 (mp, fmt, arg1, arg2, arg3) + * zmtfls (mp) + * zmtdbgclose (mp) + * + * Output may be written to either a file, if an absolute file pathname is + * given, or to a socket specified as "host[,port]". + */ + +#ifdef TCPIP +static SIGFUNC sigpipe = NULL; +static int nsockets = 0; +static int s_port[MAXDEV]; +static FILE *s_fp[MAXDEV]; +static int s_fd[MAXDEV]; +static int s_checksum[MAXDEV]; +#endif + +/* ZMTDBGOPEN -- Attempt to open a file or socket for status logging. + */ +static void zmtdbgopen (struct mtdesc *mp) +{ +#ifndef TCPIP + if (!mp->mtdev.statusdev[0] || mp->mtdev.statusout) + return; + if (mp->mtdev.statusdev[0] == '/') + mp->mtdev.statusout = fopen (mp->mtdev.statusdev, "a"); +#else + register char *ip, *op; + struct sockaddr_in sockaddr; + int port, isfile, sum, s, i; + char host[SZ_FNAME]; + struct hostent *hp; + FILE *fp = (FILE *) NULL; + + + /* Status logging disabled. */ + if (!mp->mtdev.statusdev[0]) + return; + + /* Status device already open. This is only possible in repeated + * calls to zmtdbgopen after a zzopmt. + */ + if (mp->mtdev.statusout) { + if (nsockets > 0 && !sigpipe) + sigpipe = (SIGFUNC) signal (SIGPIPE, SIG_IGN); + return; + } + + /* Compute statusdev checksum. */ + for (sum=0, ip = mp->mtdev.statusdev; *ip; ip++) + sum += (sum + *ip); + + /* Log status output to a file if a pathname is specified. + */ + for (isfile=0, ip=mp->mtdev.statusdev; *ip; ip++) + if ((isfile = (*ip == '/'))) + break; + if (isfile) { + mp->mtdev.statusout = fopen (mp->mtdev.statusdev, "a"); + if (mp->mtdev.statusout) { + for (i=0; i < MAXDEV; i++) + if (!s_fp[i]) { + s_fp[i] = mp->mtdev.statusout; + s_port[i] = s_fd[i] = 0; + s_checksum[i] = sum; + break; + } + } + return; + } + + /* If the entry is of the form "host" or "host,port" then status output + * is written to a socket connected to the specified host and port. + */ + for (ip=mp->mtdev.statusdev, op=host; *ip && *ip != ','; ) + *op++ = *ip++; + *op = EOS; + if (!host[0]) + strcpy (host, "localhost"); + if (*ip == ',') + ip++; + port = (isdigit(*ip)) ? atoi(ip) : DEFPORT; + + /* Is port already open in cache? */ + s = 0; + for (i=0; i < MAXDEV; i++) + if (s_port[i] == port) { + if (s_checksum[i] != sum) { + fclose (s_fp[i]); + s_fd[i] = s_port[i] = s_checksum[i] = 0; + s_fp[i] = NULL; + } else { + s = s_fd[i]; + fp = s_fp[i]; + } + break; + } + + if (!s) { + if ((hp = gethostbyname(host)) == NULL) + return; + if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) + return; + + bzero ((char *)&sockaddr, sizeof(sockaddr)); + bcopy ((char *)hp->h_addr,(char *)&sockaddr.sin_addr, hp->h_length); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons((short)port); + if (connect (s,(struct sockaddr *)&sockaddr,sizeof(sockaddr)) < 0) { + close (s); + return; + } + + fp = fdopen (s, "w"); + for (i=0; i < MAXDEV; i++) + if (!s_fp[i]) { + s_port[i] = port; + s_fd[i] = s; + s_fp[i] = fp; + s_checksum[i] = sum; + break; + } + } + + /* Ignore signal generated if server goes away unexpectedly. */ + nsockets++; + if (!sigpipe) + sigpipe = signal (SIGPIPE, SIG_IGN); + + mp->mtdev.statusout = fp; + zmtdbgn (mp, "iodev = %s", mp->iodev); + if (gethostname (host, SZ_FNAME) == 0) + zmtdbgn (mp, "host = %s", host); +#endif +} + + +/* ZMTDBGCLOSE -- Close the status output. Called at ZZCLMT time. If the + * status output is a socket merely flush the output and restore the original + * sigpipe signal handler if the reference count for the process goes to zero. + * If the output is a file always close the file. If the debug output is + * changed from a socket to a file during the execution of a process this + * will leave a socket open, never to be closed, but this is not likely to + * be worth fixing since the status output device, if used, should change + * infrequently. + */ +static void zmtdbgclose (struct mtdesc *mp) +{ + register int i; + + if (mp->mtdev.statusout) { + fflush (mp->mtdev.statusout); + for (i=0; i < MAXDEV; i++) { + if (s_fp[i] == mp->mtdev.statusout) { + if (s_port[i]) + nsockets--; + else { + fclose (mp->mtdev.statusout); + s_checksum[i] = 0; + s_fp[i] = NULL; + } + break; + } + } + + if (sigpipe && nsockets <= 0) { +#ifdef AUX + signal (SIGPIPE, (sigfunc_t)sigpipe); +#else + signal (SIGPIPE, sigpipe); +#endif + sigpipe = (SIGFUNC) NULL; + nsockets = 0; + } + } +} + +static void zmtdbg (struct mtdesc *mp, char *msg) +{ + register FILE *out; + register char *ip; + + if ((out = mp->mtdev.statusout)) { + for (ip=msg; *ip; ip++) { + if (*ip == '\n') { + putc ('\\', out); + putc ('n', out); + } else + putc (*ip, out); + } + putc ('\n', out); + } +} + +static void zmtfls (struct mtdesc *mp) +{ + FILE *out; + if ((out = mp->mtdev.statusout)) + fflush (out); +} + +static void zmtdbgn ( struct mtdesc *mp, const char *argsformat, ... ) +{ + va_list ap; + char obuf[SZ_LINE]; + va_start (ap, argsformat); + vsnprintf (obuf, SZ_LINE, argsformat, ap); + va_end (ap); + obuf[SZ_LINE-1]='\0'; + zmtdbg (mp, obuf); +} + + + +#else + +int ZZOPMT ( + PKCHAR *device, /* device name */ + XINT *acmode, /* access mode: read_only or write_only for tapes */ + PKCHAR *devcap, /* tapecap entry for device */ + XINT *devpos, /* pointer to tape position info struct */ + XINT *newfile, /* file to be opened or EOT */ + XINT *chan /* OS channel of opened file */ +) +{ + return (XERR); +} + + +int ZZCLMT (XINT *chan, XINT *devpos, XINT *o_status) +{ + return (XERR); +} + + +int ZZRDMT ( + XINT *chan, + XCHAR *buf, + XINT *maxbytes, + XLONG *offset /* fixed block devices only */ +) +{ + return (XERR); +} + + +int ZZWRMT ( + XINT *chan, + XCHAR *buf, + XINT *nbytes, + XLONG *offset /* ignored on a write */ +) +{ + return (XERR); +} + + +int ZZWTMT ( + XINT *chan, + XINT *devpos, + XINT *o_status +) +{ + return (XERR); +} + + +int ZZSTMT (XINT *chan, XINT *param, XLONG *lvalue) +{ + return (XERR); +} + + +int ZZRWMT ( + PKCHAR *device, /* device name */ + PKCHAR *devcap, /* tapecap entry for device */ + XINT *o_status +) +{ + return (XERR); +} + + +#endif + + -- cgit