/*===========================================================================
  Copyright (C) 1995-2005 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.COPYRIGHT   (c)  1995-2005  European Southern Observatory
.IDENT       dataio.c
.LAUGUAGE    C
.AUTHOR      P.Grosbol   ESO/IPG
.KEYWORDS    data I/O routines, tape, stream
.COMMENT     buffered I/O for data format IHAP/FITS routiens
.VERSION     1.0  1988-Dec-10 : Creation,   PJG 
.VERSION     1.1  1989-Jan-17 : Add count of output blocks,   PJG 
.VERSION     1.2  1989-May-24 : Specify blocksize for disk/tape, PJG 
.VERSION     1.4  1989-Nov-06 : Upgrade for fixed-block devices , PJG 
.VERSION     1.5  1990-Feb-04 : Intiate cvb and change call-seq., PJG 
.VERSION     1.6  1990-Mar-30 : Insert error messages for OS-calls, PJG 
.VERSION     1.8  1991-Jan-25 : Change include files. FO
.VERSION     1.9  1991-Nov-19 : Correct error in write with fix-block, PJG
.VERSION     2.0  1993-Oct-28 : Update to new SC and prototypes, PJG
.VERSION     2.1  1994-Jun-29 : Check error only in first read, PJG
.VERSION     2.2  1995-Jan-23 : Skip 'osmsg' for osdopen, PJG
.VERSION     2.3  1996-Jan-09 : Check read status for disk files, PJG
 050922		last modif

---------------------------------------------------------------------*/

#include   <stdio.h>

#include   <filedef.h>
#include   <computer.h>
#include   <fitsfmt.h>
#include   <fitsdef.h>
#include   <midas_def.h>

#define   MXBUF        30720       /* size of internal buffer       */

char               *osmsg();       /* OS error message              */

static    int           fdi = -1;       /* file descriptor of input file */
static    int           gdi;	 	/* backup of fdi */
static    int           fdo = -1;       /* file descriptor of output file */
static    int           gdo;	 	/* backup of fdo */
static    int            fd = -1;       /* file descriptor of tape */
static    int          apos;       /* absolute position of tape     */
static    int           fmt;       /* format of data file FITS/IHAP */
static    int          widx;       /* current write index in buffer */
static    int          ridx;       /* current read index in buffer  */
static    int          rlvb;       /* last of valid byte in read buffer  */
static    int          wlvb;       /* last of valid byte in write buffer  */
static    int        rbsize;       /* block size of reads           */
static    int        wbsize;       /* block size of writes          */
static    int         devbs;       /* device block size             */
static    int         mxbuf;       /* actual size of buffer         */
static    int          nopb;       /* no. of output bytes           */
static    int           lrs;       /* last read status              */
static    char          dev;       /* type of input device          */
static    char       *rbptr = (char *) 0;       /* pointer to read buffer  */
static    char       *wbptr = (char *) 0;       /* pointer to write buffer */

/*

*/

#ifdef __STDC__
int dopen(char * name , int iomode , char type , int den)
#else
int dopen(name,iomode,type,den)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE       open device 
.RETURN        file descriptor - if error status of open call
---------------------------------------------------------------------*/
char        *name;             /* IN: name of device to open         */
int        iomode;             /* IN: I/O open mode 0:read, 1:write  */
char         type;             /* IN: device type - Block, Stream    */
int           den;             /* IN: tape density for tapes (B)     */
#endif
{
int  myfd;




dev = type; apos = 0; devbs = 1;
mxbuf = (MAXIO1 < MXBUF) ? MAXIO1 : MXBUF;  /* determine buffer size */

if (dev == 'S') 
   {                           /* open disk file      */
   osfop('F',FITSLR);               /* force 2880 byte fix records */
   myfd = osdopen(name,iomode);
   if (myfd == -1) return (-1);

   if (iomode == 0)
      {
      gdi = fdi = myfd;
      }
   else
      {
      gdo = fdo = myfd;
      }
   }
else
   {                              /* open block dev/tape         */
   myfd = osuopen(name,iomode,den);
   if (myfd == -1)
      {
      SCTPUT(osmsg()); return (-1);
      }
   devbs = osubsize(myfd);
   mxbuf = (mxbuf/devbs) * devbs;
   fd = myfd;
   }

/* allocate data buffer */

if (iomode == 0)
   {
   if (rbptr == (char *) 0) rbptr = (char *)osmmget(mxbuf);
   }
else
   {
   if (wbptr == (char *) 0) wbptr = (char *)osmmget(mxbuf);
   }

return myfd;
}

int drinit()
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE       initiate read buffer and check data format 
.RETURN        data format = 1:FITS, 0:Unknown, -1: NO data
---------------------------------------------------------------------*/
{
char   *pc,*str;



ridx = 0; rlvb = 0; lrs = 1;
if (dev == 'S') 
   {
   rlvb = osdread(fdi,rbptr,mxbuf);
   if (rlvb<mxbuf) lrs = 0;
   }
else
   rlvb = osuread(fd,rbptr,mxbuf);

if (rlvb<0)
   {
   SCTPUT(osmsg()); 
   return -1;
   }

pc = rbptr; str = "SIMPLE  =";
while ((*pc) == (*str)) pc++, str++;

if (*pc==' ' && !(*str))
   { 					/* check if FITS format */
   rbsize = (mxbuf/FITSLR) * FITSLR;	/* define best read size  */
   if (dev!='S') 
      {
      rbsize = (devbs==1) ? rbsize+4 : ((mxbuf-FITSLR)/devbs)*devbs;
      }
   cvinit(); fmt = FITS; 
   return fmt;
   }

return 0;
}

int dread(ppbuf,no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE       read 'no' bytes from device
.RETURN        no. of bytes read, -1: EOF, -2:error
---------------------------------------------------------------------*/
char    **ppbuf;                /* pointer to buffer pointer         */
int          no;                /* no. of bytes to read              */
{
register char  *pbs,*pbe;

int      nb,n;


if (rlvb < ridx+no)
   {			 	/* too few data - read next block    */
   n = rlvb - ridx; rlvb = n;
   pbs = rbptr; pbe = &rbptr[ridx];
   while (n--) *pbs++ = *pbe++;		/* move bytes to start of buffer */
   ridx = 0;
   while (lrs && rlvb<no) 
      {					/* read enough data for request  */
      if (dev == 'S')
         {
         nb = rbsize - rlvb;		/* devbs = 1 */
	 n = osdread(fdi,pbs,nb);
	 if (n<nb) lrs = 0;
         }
      else
         {
         nb = ((rbsize-rlvb-1)/devbs + 1) * devbs;
         n = osuread(fd,pbs,nb);
         }
      
      if (n > 0)
         {
         rlvb += n; pbs += n;
         }
      else
         return -1;
      }
   if (rlvb < no) no = rlvb;
   }

*ppbuf = &rbptr[ridx];
ridx += no;

return  no;
}

int dwinit(bf)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE       initiate write buffer and define blocking factor
.RETURN        always 0
---------------------------------------------------------------------*/
int          bf;                /* FITS blocking factor              */
{
widx = 0; wlvb = 0; nopb = 0;

if (bf<1)
   wbsize = FITSLR;
else
   wbsize = (10<bf) ? 10*FITSLR : bf*FITSLR;

if (mxbuf<wbsize) wbsize = (mxbuf/FITSLR) * FITSLR;
if (dev!='S' && devbs!=1) wbsize = (mxbuf/devbs)*devbs;

cvinit();
return 0;
}

int dwrite(pbuf,no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE       write 'no' bytes to device
.RETURN        no. of bytes transfered or <0 error
---------------------------------------------------------------------*/
char      *pbuf;                /* pointer to data to be written     */
int          no;                /* no. of bytes to write             */

{
char      *pc;

int      nn,n;

if (no<1) return 0;

pc = &wbptr[widx];
widx += no; nn = no; nopb += no;
if (widx<wbsize) 
   {					/* space in buffer - transfer */
   while (no--) *pc++ = *pbuf++;
   }

else 
   {
   n = wbsize - widx + no; no -= n;
   while (n--) *pc++ = *pbuf++;

   if (dev == 'S')
      n = osdwrite(fdo,wbptr,wbsize);
   else
      n = osuwrite(fd,wbptr,wbsize);
   if (n != wbsize)
      {
      if (n<0) SCTPUT(osmsg());
      SCTPUT("Error: I/O-error in writing block");
      widx -= no; return -1;
      }

   while (wbsize<no) 
      {
      if (dev == 'S')
         n = osdwrite(fdo,pbuf,wbsize);
      else
         n = osuwrite(fd,pbuf,wbsize);
      if (n != wbsize) 
         {
         if (n<0) SCTPUT(osmsg());
         SCTPUT("Error: I/O-error in writing block");
         widx -= no; return -1;
         }
      pbuf += n; no -= n;
      }

   widx = no; pc = wbptr;
   while (no--) *pc++ = *pbuf++;
   }

return nn;
}


int ddcopy()
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE       write 'no' bytes to device
.RETURN        no. of bytes transfered or <0 error
---------------------------------------------------------------------*/
{
char *pcin, *pcout;

int  n;



/*  we need read + write buffer */

if (rbptr == (char *) 0) rbptr = (char *)osmmget(mxbuf);
if (wbptr == (char *) 0) wbptr = (char *)osmmget(mxbuf);

n = 1;				/* kick off the loop */
while (n>0)
   {  
   n = dread(&pcin,FITSLR);
   if (n > 0)
      {
      pcout = pcin;
      dwrite(pcout,n);
      }
   }


(void) dweof();
return 0;
}


int dclose(fid)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE       spool buffer to device and close it
.RETURN        status  0: OK, -1: error
---------------------------------------------------------------------*/
int  fid;		/* IN: file id returned from `dopen' */

{
if (dev != 'S') 		/* for tape ignore fid */
   {
   osuclose(fd, 0);

   if (rbptr != (char *) 0) 
      {
      (void) osmmfree(rbptr);
      rbptr = (char *) 0;
      }
   if (wbptr != (char *) 0)
      {
      (void) osmmfree(wbptr);
      wbptr = (char *) 0;
      }
   return 0;
   }

 
/* for FITS files on disk */

if ((fid == fdi) || (fid == gdi))
   {
   if (fdi != -1) osdclose(fdi);
   fdi = -1;
   if (rbptr != (char *) 0) 		/* clear read buffer */
      {
      (void) osmmfree(rbptr);
      rbptr = (char *) 0;
      }
   }
else if ((fid == fdo) || (fid == gdo))
   {
   if (fdo != -1) osdclose(fdo);
   fdo = -1;
   if (wbptr != (char *) 0)		/* clear write buffer */
      {
      (void) osmmfree(wbptr);
      wbptr = (char *) 0;
      }
    }
else
   {
   char  tmp[80];

   (void) sprintf(tmp,"(FITS) dclose: bad file id (%d) passed...",fid);
   SCTPUT(tmp);
   if (rbptr != (char *) 0) 
      {
      (void) osmmfree(rbptr);
      rbptr = (char *) 0;
      }
   if (wbptr != (char *) 0)
      {
      (void) osmmfree(wbptr);
      wbptr = (char *) 0;
      }
   return (-1);
   }
return 0;
}



int dapos(no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE       position on an absolute file no. on a magnetic tape
.RETURN        status  0: OK, -1: error
---------------------------------------------------------------------*/
int            no;                 /* absolute file no. on tape      */

{

if (dev == 'S') return -1;       /* cannot position Stream         */

apos = (no<0) ? osufseek(fd,0,FILE_END) : osufseek(fd,no,FILE_START);
if (apos<0) { SCTPUT(osmsg()); return -1; }
return 0;
}

int dskip(no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE       skip files on a magnetic tape
.RETURN        status  0: OK, -1: error
---------------------------------------------------------------------*/
int            no;                 /* no. of files to be skipped     */
{
  if (dev == 'S') return -1;       /* cannot position stream         */
  if (no) apos = osufseek(fd,no,FILE_CURRENT);
  if (apos<0) { SCTPUT(osmsg()); return -1; }
  return 0;
}

#ifdef __STDC__
int dbfill(char val)
#else
int dbfill(val)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE       fill remaining part of FITS record with 'val'
.RETURN        status always 0: OK
---------------------------------------------------------------------*/
char         val;                  /* IN: value to fill block with   */
#endif
{
  char       *pc;
  int         nx;

  nx = ((nopb-1)/FITSLR+1)*FITSLR - nopb;
  nopb += nx;
  pc = &wbptr[widx];
  while (nx--) { *pc++ = val; widx++; }

  return 0;
}

int dweof()
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE       flush data in internal buffer and close current file
.RETURN        no. of records written, -1: error
---------------------------------------------------------------------*/
{
int     n,i;

n = (0<widx) ? (((widx-1)/devbs)+1)*devbs : 0;
for (i=widx; i<n; i++) wbptr[i] = '\0';

if (dev == 'S') 
   {
   if (fdo != -1) 
      {
      n = osdwrite(fdo,wbptr,n);
      osdclose(fdo);
      }
   fdo = -1;
   } 
else 
   {
   if (fd != -1) 
      {
      n = osuwrite(fd,wbptr,n);
      if (n<0) { SCTPUT(osmsg()); osufclose(fd); return -1; }
      osufclose(fd);
      }
   fd = -1;
   }
nopb += n - widx;
  
return nopb/FITSLR;
}
 
