/* tape.c, begun 3/10/92 */
 /* for DEC alpha machine */
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "ana_structures.h"
#include <math.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/mtio.h>
#include <fcntl.h>
 extern	struct sym_desc sym[];
 extern	int	ana_type_size[];
 extern	int	edb2_base;
 union	types_ptr { byte *b; short *w; int *l; float *f; double *d;};		
 extern	int scrat[];		/* scratch storage, also used in anatest.c */
 
						 /* tape things */
 struct  mtop    rew = {MTREW, 1 };
 struct  mtop    unl = {MTOFFL, 1 };
 struct  mtop    mtweof = {MTWEOF, 1 };
 struct  mtop    tape_op = {MTFSR, 1 };
 struct  mtget   ti;
 int	tape_lun, byte_count, io_status;
 int	tape_messages=1;
 static	int	tape_fd[4] = {0,0,0,0}, neof[4], neot[4];
#define	MAXTAPE	4
 /*------------------------------------------------------------------------- */
int ana_tape_status(narg,ps)            /* print tape status */
 int     narg, ps[];
 {
  int   fd, j;
  printf("tape status: number of drives in table = %d\n", MAXTAPE+1);
  for (j=0;j <= MAXTAPE;j++) {
  if ( (fd = tape_fd[j]) > 0 ) {
  printf("\ndrive # %d  ",j);
#if __sgi
  if ( ioctl(fd, MTIOCGET, &ti) ) perror("tape_status:");
  printf("\ncurrent file # %d", ti.mt_fileno);
#endif
  } }
 return 1;
 }
 /*------------------------------------------------------------------------- */
int tape_setup(narg,ps)		/* for internal use */
 int	narg, ps[];
 {
 char ana_tape[] = "ANA_TAPE0";
 char *ana_tape_dev;
 errno = 0;
 tape_lun = int_arg( ps[0] );		/* get tape logical unit number */
 if (tape_lun < 0 || tape_lun > MAXTAPE) {
  printf("illegal tape unit specified, only 0 or 1 legal for PORE2\n");
  return -1; }
 /* printf("in tape_setup\n"); */
 if (tape_fd[tape_lun] == 0) {		/* new ? */
 neof[tape_lun] = neot[tape_lun] = 0;
 ana_tape[8] = (char) ( (byte) '0' + tape_lun);
 printf("ana_tape = %s\n", ana_tape);
 ana_tape_dev = getenv(ana_tape);
 printf("ana_tape_dev = %s\n", ana_tape_dev);
 if (ana_tape_dev == NULL) {
   printf("TAPE I/O: no environmental variable for %s\n", ana_tape);
   return -1;
 }
 tape_fd[tape_lun] = open(ana_tape_dev, O_RDWR);
 }
 if (tape_fd[tape_lun] < 0) { perror("tape error:");  return -1; }
 return tape_fd[tape_lun];
 }
 /*------------------------------------------------------------------------- */
int check_tape_io(iq)
 int	iq;
 {
 io_status = errno;
 if (iq) printf("return status of ioctl = %d\n", iq);
 if (errno) perror("tape error:");
 if (errno == 0) io_status = 1; else io_status = errno;
 return 1;
 }
 /*------------------------------------------------------------------------- */
int ana_rewind(narg,ps)				/* rewind a tape drive */
 int	narg, ps[];
 {
 int	fd;
 if ( (fd = tape_setup(narg,ps)) < 0) return -1;;
 return check_tape_io( ioctl(fd, MTIOCTOP, &rew) );
 }
 /*------------------------------------------------------------------------- */
int ana_weof(narg,ps)				/* write an eof on tape drive */
 int	narg, ps[];
 {
 int	fd;
 if ( (fd = tape_setup(narg,ps)) < 0) return -1;;
 return check_tape_io( ioctl(fd, MTIOCTOP, &mtweof) );
 }
 /*------------------------------------------------------------------------- */
int ana_unload(narg,ps)				/* unload a tape drive */
 int	narg, ps[];
 {
 int	fd;
 if ( (fd = tape_setup(narg,ps)) < 0) return -1;;
 if ( ioctl(fd, MTIOCTOP, &unl) ) perror("tape unload:");
 if ( close(fd) ) perror("tape close:");
 tape_fd[tape_lun] = 0;
 return 1;
 }
 /*------------------------------------------------------------------------- */
int ana_skipr(narg,ps)				/* skip records */
 int	narg, ps[];
 {
 int	fd, nr;
 if ( (fd = tape_setup(narg,ps)) < 0) return -1;;
 if (narg > 1 ) nr = int_arg( ps[1] ); else nr = 1;
 if (nr == 0)  return 1;
 if (nr > 0) {
 tape_op.mt_op = MTFSR;	tape_op.mt_count = nr;
 } else {
 tape_op.mt_op = MTBSR;	tape_op.mt_count = -nr;
 }
 return check_tape_io( ioctl(fd, MTIOCTOP, &tape_op) );
 }
 /*------------------------------------------------------------------------- */
int ana_skipf(narg,ps)				/* skip files */
 int	narg, ps[];
 {
 int	fd, nf;
 if ( (fd = tape_setup(narg,ps)) < 0) return -1;;
 if (narg > 1 ) nf = int_arg( ps[1] ); else nf = 1;
 if (nf == 0)  return 1;
 if (nf > 0) {
 tape_op.mt_op = MTFSF;	tape_op.mt_count = nf;
 } else {
 tape_op.mt_op = MTBSF;	tape_op.mt_count = -nf;
 }
 return check_tape_io( ioctl(fd, MTIOCTOP, &tape_op) );
 }
 /*------------------------------------------------------------------------- */
int ana_taprd(narg,ps)				/* read tape record */
 int	narg, ps[];
 {
 int	fd, nbr, iq, j, nd, n;
 struct	ahead	*h;
 union	types_ptr q1;
 if ( (fd = tape_setup(narg,ps)) < 0) return -1;;
				 /* get the size of the input array to load */
 iq = ps[1];
 if ( sym[iq].class != 4 ) return execute_error(66);
 h = (struct ahead *) sym[iq].spec.array.ptr;
 q1.l = (int *) ((char *)h + sizeof(struct ahead));
 nd = h->ndim;
 n = ana_type_size[sym[iq].type];
 for(j=0;j<nd;j++) n *= h->dims[j];
 nbr = read (fd, q1.b, n);
 printf("number read in = %d\n",nbr);
 byte_count = nbr;
 if (nbr != n) {printf("TAPRD - variable/record size mismatch\n");
   if (nbr == -1 && errno == ENOMEM) { errno = 0; byte_count = n;
    printf("record larger than variable size of %d byte\n",n); }
   else { if (tape_messages) {   /* only if wanted */
   printf("variable size = %d bytes, record = %d bytes\n",n, nbr);
   } }
 }
 io_status = errno;
 if (errno > 0 ) perror("taprd");
 return 1;
 }
 /*------------------------------------------------------------------------- */
int ana_tapwrt(narg,ps)				/* write tape record */
 int	narg, ps[];
 {
 int	fd, nbr, iq, j, nd, n;
 struct	ahead	*h;
 union	types_ptr q1;
 if ( (fd = tape_setup(narg,ps)) < 0) return -1;;
				 /* get the size of the input array to load */
 iq = ps[1];
 if ( sym[iq].class != 4 ) return execute_error(66);
 h = (struct ahead *) sym[iq].spec.array.ptr;
 q1.l = (int *) ((char *)h + sizeof(struct ahead));
 nd = h->ndim;
 n = ana_type_size[sym[iq].type]; errno = 0;
 for(j=0;j<nd;j++) n *= h->dims[j];
 /*printf("tapwrt, n = %d\n", n);*/
 nbr = write(fd, q1.b, n);
 byte_count = nbr;				/* want this for write */
 io_status = errno;
 if (errno) perror("tapwrt");
 if (nbr != n) { printf("tape write problem\n");  return -1; }
 return 1;
 }
 /*------------------------------------------------------------------------- */
int ana_tapebufout(narg,ps)			/* write tape record */
 /*the call is TAPEBUFOUT,tape#,array,[recsize,offset,len]
 the defaults for offset and len are just the whole array (or what is left of
 it if just offset is specified) and the default for recsize is 16384 bytes
 intended for dumping large amounts of data */
 int	narg, ps[];
 {
 int	fd, nbr, iq, j, nd, n, recsize, nb, ic, offset, len;
 struct	ahead	*h;
 char	*ptr;
 union	types_ptr q1;
 if ( (fd = tape_setup(narg,ps)) < 0) return -1;;
				 /* get the size of the output array */
 iq = ps[1];
 if ( sym[iq].class != 4 ) return execute_error(66);
 h = (struct ahead *) sym[iq].spec.array.ptr;
 ptr = (char *) ((char *)h + sizeof(struct ahead));
 nd = h->ndim;
 nb = n = ana_type_size[sym[iq].type];
 errno = 0;
 for(j=0;j<nd;j++) n *= h->dims[j];
 recsize = 16384;  offset = 0;
 if (narg >2) recsize = int_arg(ps[2]);
 /* may want a different offset and/or length */
 if (narg >3) offset = int_arg(ps[3]);
 offset *= nb;		/* offset in bytes */
 n = n - offset;	/* reduce the count */
 ptr = ptr + offset;	/* starting address for writing */
 if (offset > n) {printf("offset exceeds array size, %d %d\n",offset, n);
	 return -1; }
 if (narg >4) { len = int_arg(ps[4]);  len = len * nb;
	 n = MIN(len, n - offset); }
 if (n < 14) {
   printf("data size too small for a tape read/write, use >= 14, %d\n", n);
   return -1; }
 ic = n;
 /*printf("in tapebufout, fd = %d, n = %d, recordsize = %d\n",fd,n,recsize);*/
 while (ic > 0) {
 recsize = MIN(recsize, ic);
 if (errno) perror("error before tapbufout");
 errno = 0;
 /*printf("number to write = %d, address = %lx\n",recsize, ptr);*/
 nbr = write (fd, ptr, recsize);
 byte_count = nbr;				/* want this for write */
 io_status = errno;
 /*printf("number written = %d\n",nbr);*/
 if (errno) perror("tapbufout");
 if (nbr <= 0 ) {					/*problem */
 	printf("nbr = %d\n",nbr);
	if (nbr == 0) { printf("returned 0 while writing array\n");  }
	else { perror("tapebufout, nbr<0"); } return 0; }
 ic -= nbr;	ptr = ptr + nbr;
 }
 return 1;
 }
 /*------------------------------------------------------------------------- */
int ana_tapebufin(narg,ps)			/* read tape record */
 /*the call is TAPEBUFIN,tape#,array,[recsize,offset,len]
 the defaults for offset and len are just the whole array (or what is left of
 it if just offset is specified) and the default for recsize is 32768 bytes
 intended for reading large amounts of data, I/O is asynchronous to allow other
 activities in the meantime, such as reading from another tape or otherwise
 acquiring or processing data
 */
 int	narg, ps[];
 {
 int	fd, nbr, iq, j, nd, n, recsize, nb, ic, offset, len;
 struct	ahead	*h;
 union	types_ptr q1;
 if ( (fd = tape_setup(narg,ps)) < 0) return -1;;
				 /* get the size of the input array to load */
 iq = ps[1];
 if ( sym[iq].class != 4 ) return execute_error(66);
 h = (struct ahead *) sym[iq].spec.array.ptr;
 q1.l = (int *) ((char *)h + sizeof(struct ahead));
 nd = h->ndim;
 nb = n = ana_type_size[sym[iq].type]; errno = 0;
 for(j=0;j<nd;j++) n *= h->dims[j];	/* n is size of array in bytes */
 recsize = 32768;  offset = 0;
 if (narg >2) recsize = int_arg(ps[2]);
 /* may want a different offset and/or length */
 if (narg >3) { offset = int_arg(ps[3]);
   offset *= nb;		/* offset in bytes */
   if (offset > n) {printf("offset exceeds array size, %d %d\n",offset, n);
	 return -1; }
   n = n - offset;
   }
 if (narg >4) { len = int_arg(ps[4]);  len = len * nb;
	 n = MIN(len, n); } /* note that offset was already subtracted from
	 			arg. # 3 */
 if (n < 14) {
   printf("data size too small for a tape read/write, use >= 14, %d\n", n);
   return -1; }
 q1.b += offset;
 ic = n;
 printf("in tapebuferin, fd = %d, n = %d, recordsize = %d\n",fd,n,recsize);
 while (ic > 0) {
 recsize = MIN(recsize, ic);
 errno = 0;
 nbr = read (fd, q1.b, recsize);
 printf("number read in = %d\n",nbr);
 /* the number read will be -1 for errors but a record larger than recsize
 is also tagged as an error, this often happens on the last record */
 printf("errno = %d\n",errno);
 if (errno) perror("tapebufin_a");
 if (nbr == 0) { printf("EOF while reading array\n"); perror("tapebufin"); }
 if (nbr < 0 ) {
  if (errno == 12) {
  /* record larger than requested recsize, OK if last one */
     if (ic == recsize) { nbr = recsize; } else {
       printf("record sizes seem to be larger than the specified max size\n");
       printf("try again with a larger record size parameter\n"); return 0; }
   } else { perror("tapebufin"); return 0; } }
 ic -= nbr;	q1.b += nbr;
 }
 return 1;
 }
 /*------------------------------------------------------------------------- */
int ana_wait_for_tape(narg,ps)			/* wait */
 int	narg, ps[];
 {
 /* a dummy version, we'll need this when we do asynchronous tape i/o */
 return 1;
 }
 /*------------------------------------------------------------------------- */
