/* gdsi_read.c

	Copyright (c) Kapteyn Laboratorium Groningen 1990, 1991
	All Rights Reserved.

	This source contains the code to access the GDS data sets.
	It read/writes single precision floating point numbers to/from
	disk. At the moment no conversion of floating point type is done,
	but the software has already been included (although it will not
	be compiled). It is assumed that the type of floating point on
	the current machine is defined in OS_FLOATING_TYPE, and that the
	type of floating point in the data set can be obtained from the
	set descriptor. At the moment the first 8 bits of spare_long[0]
	are used to determine the floating point type (gives us 256
	possible types). The routines spfpfl and spfplf take care of the
	conversion.

	No locking is done. Applications can read/write the same data
	set without checking whether another application is reading or
	writing the same data. If locking is wanted, it could simply be
	implemented by placing a call to gds_lock_c before and
	gds_unlock_c after the call to read/write.

*/


#define	FPC				/* floating point conversion is wanted */

#include	"errno.h"		/* <errno.h> */
#include	"stdio.h"		/* <stdio.h> */
#include	"stdlib.h"		/* <stdlib.h> */
#include	"string.h"		/* <string.h> */

#define	lseek	LSEEK
#define	open	OPEN

#include	<sys/file.h>		/* from system */
#if	!defined(O_RDONLY)
#include	<fcntl.h>
#endif

#undef	lseek
#undef	open

#include	"gipsyc.h"		/* GIPSY symbols and definitions */
#include	"error.h"		/* defines error_c */
#include	"gdsparams.h"		/* some GDS parameters */
#include        "gdserrors.h"           /* GDS error codes */
#include	"gdsc_grid.h"		/* defines gdsc_grid_c */
#include	"gdsc_ndims.h"		/* defines gdsc_ndims_c */
#include	"gdsc_range.h"		/* defines gdsc_range_c */
#include	"nelc.h"		/* defines nelc_c */
#include	"setnfblank.h"		/* defines setnfblank_c */
#include        "gds___error.h"
#include        "gdsd_basic.h"
#include        "gds_ftype.h"
#include        "presentn.h"
#ifdef	FPC				/* floating point conversion */
#include	"spfpfl.h"		/* define spfpfl_c */
#include	"spfplf.h"		/* define spfplf_c */
#endif					/* floating point conversion */

#define	NFLOATS	4096			/* number of floats in block */

#define	E_TID	0			/* tid not used */
#define	R_TID	1			/* tid used for reads */
#define	W_TID	2			/* tid used for writes */

extern	int	close( );		/* unix close */
extern	int	fsync( );		/* unix synchronization */
extern	int	lseek( );		/* unix seek */
extern	int	open( );		/* unix open */
extern	int	read( );		/* unix read */
extern	int	write( );		/* unix write */

/* tell replaced by more common lseek */
/* extern	int	tell( );*/		/* unix tell */

typedef	struct {			/* bookkeeping per transfer */
   fint	dims;				/* dimension of database */
   fint	fptr;				/* points to next file position */
   fint	left[GDS_MAXDIM];		/* counters along sub database */
   fint	size[GDS_MAXDIM];		/* size of sub database */
   fint	step[GDS_MAXDIM];		/* step when overflow */
   fint	todo;				/* number of pixels still to handle */
#ifdef	FPC				/* floating point conversion */
   fint	type;				/* floating point type */
#endif					/* floating point conversion */
   int	ufid;				/* unix file descriptor */
   int	qtid;				/* tid for reads/writes */
} tid_struct;

static	fint		ntid = 0;	/* number of table entries */
static	tid_struct	*tids = NULL;	/* table with transfer id's */

static	float		floats[NFLOATS];	/* buffer with floats */


/*
 * gdsi_close is called by the lower level gds routines when the
 * file is closed. It is used to clean up the transfer id bookkeeping.
 */

static	void	gdsi_close( int fi )	/* image file descriptor */
{
   fint	itid;			/* tid counter */
   for (itid = 0; itid < ntid; itid++) {
      if (tids[itid].qtid != E_TID && tids[itid].ufid == fi) {
         tids[itid].todo = 0;	/* nothing left to transfer */
         tids[itid].qtid = E_TID;	/* empty tid */
      }
   }
}


/*
 * initfptr sets up the bookkeeping for the data transfer. We keep
 * track of the number of pixels along each box axis, the number of
 * pixels to transfer, the parts to skip and the position in the data file.
 * initfptr returns the setting in the transfer struct.
 */

static	tid_struct	initfptr( fchar set ,	/* the set name */
                                  fint  *cwlo ,	/* lower c.w. */
                                  fint  *cwup )	/* upper c.w. */
{
   fint		b1;			/* lower box limit */
   fint		b2;			/* upper box limit */
   fint		f1;			/* lower frame limit */
   fint		f2;			/* upper frame limit */
   fint		ftotal = 1;		/* pixel counter */
   fint		gerror = 0;		/* GDS error return */
   fint		n;			/* counter */
   fint		zero = 0;		/* just zero */
   tid_struct	r;			/* return value */

   r.dims = gdsc_ndims_c( set, &zero );	/* get number of axes */
   r.fptr = 0;				/* reset file pointer */
   r.todo = 1;				/* at least one */
   if (!tobool( presentn_c( cwlo ) )) {	/* argument not present */
      b1 = 0;				/* whole set */
   } else {				/* argument present */
      b1 = (*cwlo);			/* from argument list */
   }
   if (!tobool(presentn_c( cwup ) )) {	/* argument not present */
      gdsc_range_c( set, &b1, &b1, &b2, &gerror );
   } else {				/* argument present */
      b2 = (*cwup);			/* from argument list */
   }
   gdsc_range_c( set, &zero, &f1, &f2, &gerror );
   for (n = 0; n < r.dims; n++) {	/* loop to initialize the struct */
      fint	axnum = n + 1;		/* the axis number */
      fint	blo;			/* lower box grid */
      fint	bup;			/* upper box grid */
      fint	bsize;			/* number of box grids */
      fint	flo;			/* lower frame grid */
      fint	fup;			/* upper frame grid */
      fint	fsize;			/* number of frame grids */

      blo = gdsc_grid_c( set, &axnum, &b1, &gerror );
      bup = gdsc_grid_c( set, &axnum, &b2, &gerror );
      /*
       * The following statement checks whether the programmer
       * wants to read/write in reverse direction. Since that is
       * not allowed, we silently change the direction.
       * Only reads and writes along increasing axis coordinates
       * are allowed!
       */
      if (blo > bup) { fint sav = blo; blo = bup; bup = sav; }
      flo = gdsc_grid_c( set, &axnum, &f1, &gerror );
      fup = gdsc_grid_c( set, &axnum, &f2, &gerror );
      bsize = bup - blo + 1;		/* size of box */
      fsize = fup - flo + 1;		/* size of frame */
      r.fptr += ftotal * ( blo - flo );	/* update file pointer */
      r.left[n] = bsize;		/* left to do on this line */
      r.size[n] = bsize;		/* length of line */
      r.step[n] = ftotal * ( fsize - bsize );
      r.todo *= bsize;			/* update */
      ftotal *= fsize;			/* update */
   }
   if (gerror < 0) gds___error_c( &gerror );
   return( r );				/* return to caller */
}


/*
 * movefptr places the pointer to the next file position inside the
 * box if the current file position is outside this box. It is not checked
 * whether the transfer is completed. This has to be checked by the
 * calling routine.
 * movefptr returns the number of file positions advanced. Note that
 * file positions must be multiplied by sizeof( float ) to get the
 * position in bytes.
 */

static	fint	movefptr( tid_struct *r )	/* the transfer struct */
{
   fint	n = 0;				/* loop counter */
   fint	fptr = r->fptr;			/* current file position */

   while (r->left[n] == 0 && n < r->dims) {	/* loop trough dimensions */
      r->left[n] = r->size[n];		/* reset */
      r->fptr += r->step[n];		/* move file pointer forward */
      r->left[++n] -= 1;		/* one less left */
   }
   return( r->fptr - fptr );		/* return pointer movement */
}


/*
 * nextfptr returns the next file position where we can find data to
 * transfer. It returns the number of pixels which can be transfered.
 * This routine is called by the actual read and write routines in a loop.
 * Loop control is done with the number of pixels left to transfer.
 */

static	fint	nextfptr( tid_struct *r ,	/* transfer structure */
                          fint       *nbuf ,	/* number of pixels to do */
                          fint       *np )	/* pixels we can do */
{
   fint	fptr = r->fptr;			/* current file position */
   fint	step;				/* the step size */

   if ((*nbuf) > r->todo) (*nbuf) = r->todo;
   (*np) = 0;				/* reset */
   do {					/* loop */
      if (r->left[0] < (*nbuf)) {	/* not enough left on this line */
         (*np) += step = r->left[0];	/* the step */
      } else {				/* enough on this line */
         (*np) += step = (*nbuf);	/* the step */
      }
      (*nbuf) -= step;			/* decrease */
      r->todo -= step;			/* decrease */
      r->left[0] -= step;		/* decrease */
      r->fptr += step;			/* increase */
   } while (movefptr( r ) == 0 && (*nbuf));	/* etc */
   return( fptr );			/* return old file position */
}


/*
 * gdsi_open tries to get the image file descriptor which is stored in the
 * set status block. This ensures that we have only one file descriptor per set.
 * gdsi_open returns the transfer id or an error code.
 * At the moment the memory associated with transfer id's is not released
 * if the transfer has been completed!!
 */

static	fint	gdsi_open( fchar set ,		/* name of set */
                           fint  *cwlo ,	/* lower c.w. of box */
                           fint  *cwup ,	/* upper c.w. of box */
                           int   qtid )		/* read or write tid */
{
   fint		itid = 0;		/* transfer id */
   tid_struct	newtid;			/* transfer struct */
   int          fi;                     /* image file descriptor */
   fint         err_i=0;

   fi = gds___image(set, gdsi_close);
   if (!fi) return GDS_IOPENFAIL;
   newtid = initfptr( set, cwlo, cwup );/* get new tid struct */
   newtid.ufid = fi;			/* image file descriptor */
   newtid.qtid = qtid;			/* set type of tid */
#ifdef	FPC				/* floating point conversion */
   /*
    * get type of floating point.
    */
   newtid.type = gds_ftype_c(set, &err_i);
#endif					/* floating point conversion */
   while (itid < ntid && tids[itid].qtid != E_TID) itid++;
   if (itid == ntid) {			/* no unused transfer struct found */
      tid_struct	*newtidptr;	/* create new one */

      newtidptr = realloc( tids, ( ntid + 1 ) * sizeof( tid_struct ) );
      if (newtidptr == NULL) {		/* error */
         return( GDS_ALLOCFAIL );	/* severe error */
      } else {				/* okay */
         tids = newtidptr;		/* new pointer */
         ntid += 1;			/* increase */
      }
   }
   memcpy( &tids[itid], &newtid, sizeof( tid_struct ) );
   return( itid + 1 );			/* return tranfer id */
}


/*

#>            gdsi_read.dc2

Function:     GDSI_READ

Purpose:      Reads data from (part of) a set.

Category:     DATA, IMAGE-IO

File:         gdsi_read.c

Author:       K.G. Begeman

Use:          CALL GDSI_READ( SET,                Input    CHARACTER*(*)
                              CLOW,               Input    INTEGER
                              CUPP,               Input    INTEGER
                              BUFFER,             Output   REAL ARRAY
                              BUF_LEN,            Input    INTEGER
                              PIXELS_DONE,        Output   INTEGER
                              TRANS_ID )          In/Out   INTEGER

              SET           set name
              CLOW          coordinate word of lower left corner of the
                            subset to read.
                            If omitted: read the whole set.
              CUPP          coordinate word of upper right corner of the
                            subset to read.
                            If omitted: CLOW is a subset to read.
              BUFFER        array where pixels are stored.
              BUF_LEN       max. number of pixels to read.
                            If omitted: 1 is assumed.
              PIXELS_DONE   number of pixels actually read in one call.
                            Not cumulative for a complete transfer.
                            If omitted: PIXELS_DONE must be equal to BUF_LEN,
                                        otherwise error code -30 will be
                                        returned in TRANS_ID.
              TRANS_ID      transfer identification.
                            = 0  read successful completed.
                            > 0  unique indentifer if read could not be
                                 completed. This unmodified TRANS_ID must be
                                 used until the read has been completed with
                                 TRANS_ID = 0.
                            < 0  a GDS error has been detected.
                                 Some special GDSI_READ error codes:
                                 -30 argument PIXELS_DONE omitted, but was
                                     unequal to BUF_LEN.
                                 -31 illegal transfer identifier.
                                 -32 Unable to allocate enough memory.
                                 -36 cannot open data file.
                                 -38 maximum open sets exceeded.

Related Docs: gdsi_write.dc2, gdsi_cancel.dc2.

Updates:      May 21, 1987: WZ, istalled.
              Dec 21, 1989: WZ, migrated to C.
              Jul  5, 1990: KGB, modified document.
              Oct 31, 1990: WZ, 'naxis', etc. from setsta -> dsc_file.
              Aug 17, 1991: KGB, new algorithm, replaced fopen by open.
              Jan 31, 1994: JPT, attempt to open read-only if read-write fails.
              Mar 24, 1994: JPT, modified for GDS server.
              Jan 15, 1998: VOG, replaced tell() by more common lseek()

#<

Fortran to C interface:

@ subroutine gdsi_read( character, integer, integer, real, integer, integer, integer )

*/

void	gdsi_read_c( fchar set ,		/* the set name */
                     fint  *cwlo ,		/* lower coordinate word */
                     fint  *cwup ,		/* upper coordinate word */
                     float *data ,		/* the data buffer */
                     fint  *size ,		/* size of buffer above */
                     fint  *done ,		/* number done */
                     fint  *tid )		/* transfer id */
{
   fint		ltid;			/* local transfer id */
   fint		ndone;			/* number of pixels done */
   fint		nleft;			/* number of pixels left */
   fint		nread;			/* number done */
   fint		ntotl;			/* number to do */
   int		f;			/* file descriptor */
   tid_struct	*ctid;			/* transfer struct */

   if (tobool(presentn_c( tid ))) {	/* argument present */
      ltid = (*tid);			/* then copy it */
   } else {				/* argument not present */
      ltid = 0;				/* make it zero */
   }
   if (ltid < 0) {			/* error from previous call */
      gds___error_c( tid );		/* generate standard errors */
   } else if (ltid > ntid) {		/* illegal tid */
      (*tid) = -31;			/* error */
      return;				/* return to caller */
   }
   if (ltid == 0) ltid = gdsi_open( set, cwlo, cwup, R_TID );
   if (ltid < 0) {			/* a fatal error */
      if (tobool(presentn_c( tid ))) {	/* argument was present */
         (*tid) = ltid;			/* return to caller */
      } else {				/* we do the error handling */
         gds___error_c( &ltid );	/* gds error routine */
      }
      return;				/* return to caller */
   }
   if (tobool(presentn_c( size ))) {	/* argument defined */
      nleft = (*size);			/* get it */
   } else {				/* argument not defined */
      nleft = 1;			/* default */
   }
   ctid = &tids[ltid-1];		/* get the struct */
   f = ctid->ufid;			/* get file descriptor */
   nread = 0;				/* reset */
   ntotl = nleft;			/* number to do */
   do {
      fint	eptr;			/* end position */
      fint	fptr = nextfptr( ctid, &nleft, &ndone );
      fint	nptr;			/* number of positions */
      int	nd;			/* number of pixels done */
      int	nr;			/* number read */

      /*lseek( f, 0, SEEK_END );*/		/* put at end of file */
      eptr = lseek(f, 0, SEEK_END) / sizeof( float );	/* get end position */
      nptr = eptr - fptr;		/* number of positions */
      if (nptr > ndone) nptr = ndone;	/* decrease */
      if (nptr > 0) {			/* we can read */
         lseek( f, fptr * sizeof( float ), SEEK_SET );
         nr = nptr * sizeof( float );	/* number of bytes */
#if	0
         nd = read( f, &data[nread], nr );	/* do the read */
#else
         while ((nd = read( f, &data[nread], nr )) == -1 && errno == EINTR);
#endif
         if (nd == -1) {		/* read error */
            char	*estr;		/* pointer to error string */
            fint	fatal = 4;	/* fatal error */

            estr = strerror( errno );
            error_c( &fatal, tofchar( estr ) );
         } else if (nd != nr) {		/* should not happen */
            fint	fatal = 4;	/* fatal error */

            error_c( &fatal, tofchar( "DATA READ ERROR!!" ) );
         }
#ifdef	FPC				/* floating point conversion */
         if (OS_FLOATING_TYPE != ctid->type) {
            spfpfl_c( &ctid->type, &data[nread], &data[nread], &nptr );
         }
#endif					/* floating point conversion */
         nread += nptr;			/* update */
         ndone -= nptr;			/* update */
      }
      if (ndone) {			/* not yet done */
         setnfblank_c( &data[nread], &ndone );	/* fill with blanks */
         nread += ndone;		/* update */
      }
   } while (nleft);			/* until all done */
   if (!ctid->todo) {			/* all done */
      ltid = 0;				/* free transfer id */
      ctid->qtid = E_TID;		/* make it empty */
   }
   if (tobool(presentn_c( done ))) {	/* argument present */
      (*done) = nread;			/* return to caller */
   } else {				/* argument not present */
      if (nread != ntotl) ltid = -30;	/* error code */
   }
   if (tobool(presentn_c( tid ))) {	/* argument present */
      (*tid) = ltid;			/* return transfer id */
   } else if (ltid) {			/* not present and no zero */
      ltid = -30;			/* error code */
      gds___error_c( &ltid );		/* GDS error routine */
   }
}


/*

#>            gdsi_write.dc2

Function:     GDSI_WRITE

Purpose:      Writes data to (part of) an set.

Category:     DATA, IMAGE-IO

File:         gdsi_read.c

Author:       K.G. Begeman

Use:          CALL  GDSI_WRITE( SET,             Input       CHARACTER*(*)
                                CLOW,            Input       INTEGER
                                CUPP,            Input       INTEGER
                                BUFFER,          Input       REAL ARRAY
                                BUF_LEN,         Input       INTEGER
                                PIXELS_DONE,     Output      INTEGER
                                TRANS_ID )       In/Out      INTEGER

              SET           set name
              CLOW          coordinate word of lower left corner of the
                            subset to write.
                            If omitted: write the whole set.
              CUPP          coordinate word of upper right corner of the
                            subset to write.
                            If omitted: CLOW is a subset to write.
              BUFFER        array with pixels to write.
              BUF_LEN       max. number of pixels to write.
                            If omitted: 1 is assumed.
              PIXELS_DONE   number of pixels actually written in one call.
                            Not cumulative for a complete transfer.
                            If omitted: PIXELS_DONE must be equal to BUF_LEN,
                                        otherwise error code -30 will be
                                        returned in TRANS_ID.
              TRANS_ID      transfer identification.
                            = 0  write successful completed.
                            > 0  unique indentifer if write could not be
                                 completed. This unmodified TRANS_ID must be
                                 used until the write has been completed with
                                 TRANS_ID = 0.
                            < 0  a GDS error has been detected.
                                 Some special GDSI_WRITE error codes:
                                 -30 argument PIXELS_DONE omitted, but was
                                     unequal to BUF_LEN.
                                 -31 illegal transfer identifier.
                                 -32 Unable to allocate enough memory.
                                 -36 cannot open data file.
                                 -38 maximum open sets exceeded.

Related Docs: gdsi_read.dc2, gdsi_cancel.dc2.

Updates:      May 21, 1987: WZ, installed.
              Dec 21, 1989: WZ, migrated to C.
              Jul  5, 1990: KGB, modified document.
              Oct 31, 1990: WZ, 'naxis', etc. from setsta -> dsc_file.
              Aug 17, 1991: KGB, new algorithm, replace fopen by open.
              Jan 31, 1994: JPT, attempt to open read-only if read-write fails.

#<

Fortran to C interface:

@ subroutine gdsi_write( character, integer, integer, real, integer, integer, integer )

*/

void	gdsi_write_c( fchar set ,		/* the set name */
                      fint  *cwlo ,		/* lower coordinate word */
                      fint  *cwup ,		/* upper coordinate word */
                      float *data ,		/* the data to write */
                      fint  *size ,		/* size of buffer above */
                      fint  *done ,		/* number done */
                      fint  *tid )		/* transfer id */
{
   fint		ltid;			/* local transfer id */
   fint		ndone;			/* number of pixels done */
   fint		nleft;			/* number of pixels left */
   fint		ntotl;			/* number of pixels to do */
   fint		nwrit;			/* counts number of pixels done */
   int		f;			/* file descriptor */
   tid_struct	*ctid;			/* transfer struct */

   if (tobool(presentn_c( tid ))) {	/* argument present */
      ltid = (*tid);			/* then copy it */
   } else {				/* argument not present */
      ltid = 0;				/* make it zero */
   }
   if (ltid < 0) {			/* error from previous call */
      gds___error_c( tid );		/* generate standard errors */
   } else if (ltid > ntid) {		/* illegal tid */
      (*tid) = -31;			/* error */
      return;				/* return to caller */
   }
   if (ltid == 0) ltid = gdsi_open( set, cwlo, cwup, W_TID );
   if (ltid < 0) {			/* a fatal error */
      if (tobool(presentn_c( tid ))) {	/* argument was present */
         (*tid) = ltid;			/* return to caller */
      } else {				/* we do the error handling */
         gds___error_c( &ltid );	/* gds error routine */
      }
      return;				/* return to caller */
   }
   if (tobool(presentn_c( size ))) {	/* argument defined */
      nleft = (*size);			/* get it */
   } else {				/* argument not defined */
      nleft = 1;			/* default */
   }
   ctid = &tids[ltid-1];		/* get the struct */
   f = ctid->ufid;			/* get file descriptor */
   ntotl = nleft;			/* number to do */
   nwrit = 0;				/* reset */
   do {
      fint	eptr;			/* end position in image */
      fint	fptr = nextfptr( ctid, &nleft, &ndone );
      int	nd;			/* number of pixels done */
      int	nw;			/* number of writes */

     /* lseek( f, 0, SEEK_END );*/		/* set at end position */
      eptr = lseek(f, 0, SEEK_END) / sizeof( float );	/* get end position */
      if (fptr > eptr) {		/* we skip a part */
         fint	nfloats = NFLOATS;	/* floats in buffer */

         setnfblank_c( floats, &nfloats );	/* fill with blanks */
#ifdef	FPC				/* floating point conversion */
         if (OS_FLOATING_TYPE != ctid->type) {
            spfplf_c( &ctid->type, floats, floats, &nfloats );
         }
#endif					/* floating point conversion */
         while (eptr < fptr) {		/* blank fill loop */
            nfloats = fptr - eptr;	/* size of hole */
            if (nfloats > NFLOATS) nfloats = NFLOATS;
            nw = nfloats * sizeof( float );	/* number of bytes */
#if	0
            nd = write( f, floats, nw );/* do the write */
#else
            while ((nd = write( f, floats, nw )) == -1 && errno == EINTR);
#endif
            if (nd == -1) {		/* write error */
               char	*estr;		/* pointer to error string */
               fint	fatal = 4;	/* fatal error */

               estr = strerror( errno );
               error_c( &fatal, tofchar( estr ) );
            } else if (nd != nw) {	/* should not happen */
               fint	fatal = 4;	/* fatal error */

               error_c( &fatal, tofchar( "DATA WRITE ERROR!!" ) );
            }
            eptr += nfloats;		/* new end position */
         }
      }
      lseek( f, fptr * sizeof( float ), SEEK_SET );
#ifdef	FPC				/* floating point conversion */
      if (OS_FLOATING_TYPE != ctid->type) {
         fint	pleft = ndone;
         fint	pstep = NFLOATS;
         fint	pwrit = nwrit;

         while (pleft) {
            if (pstep > pleft) pstep = pleft;
            spfplf_c( &ctid->type, &data[pwrit], floats, &pstep );
            nw = pstep * sizeof( float );	/* number of bytes */
#if	0
            nd = write( f, floats, nw );/* do the write */
#else
            while ((nd = write( f, floats, nw )) == -1 && errno == EINTR);
#endif
            if (nd == -1) {		/* write error */
               char	*estr;		/* pointer to error string */
               fint	fatal = 4;	/* fatal error */

               estr = strerror( errno );
               error_c( &fatal, tofchar( estr ) );
            } else if (nd != nw) {	/* should not happen */
               fint	fatal = 4;	/* fatal error */

               error_c( &fatal, tofchar( "DATA WRITE ERROR!!" ) );
            }
            pwrit += pstep;
            pleft -= pstep;
         }
      } else {
         nw = ndone * sizeof( float );	/* number of bytes */
#if	0
         nd = write( f, &data[nwrit], nw );	/* do the write */
#else
         while ((nd = write( f, &data[nwrit], nw )) == -1 && errno == EINTR);
#endif
         if (nd == -1) {		/* write error */
            char	*estr;		/* pointer to error string */
            fint	fatal = 4;	/* fatal error */

            estr = strerror( errno );
            error_c( &fatal, tofchar( estr ) );
         } else if (nd != nw) {		/* should not happen */
            fint	fatal = 4;	/* fatal error */
            error_c( &fatal, tofchar( "DATA WRITE ERROR!!" ) );
         }
      }
#else					/* CURRENTLY IMPLEMENTED */
      nw = ndone * sizeof( float );	/* number of bytes */
#if	0
      nd = write( f, &data[nwrit], nw );/* do the write */
#else
      while ((nd = write( f, &data[nwrit], nw )) == -1 && errno == EINTR);
#endif
      if (nd == -1) {			/* write error */
         char	*estr;			/* pointer to error string */
         fint	fatal = 4;		/* fatal error */

         estr = strerror( errno );
         error_c( &fatal, tofchar( estr ) );
      } else if (nd != nw) {		/* should not happen */
         fint	fatal = 4;		/* fatal error */
         error_c( &fatal, tofchar( "DATA WRITE ERROR!!" ) );
      }
#endif
      nwrit += ndone;			/* update */
   } while (nleft);			/* until nothing left to do */
#if	0
   /*
    * because of a sun bug, we have to check here whether the same file is
    * open for read also. If this is true, we have to do a fsync to avoid
    * erroneous reads. This will slow down the process by about a factor 3.
    */
   {
      int	itid;

      for (itid = 0; itid < ntid; itid++) {
         if (tids[itid].qtid == R_TID && tids[itid].ufid == f) break;
      }
      if (itid < ntid) fsync( f );
   }
#endif
   if (!ctid->todo) {			/* all done */
      ltid = 0;				/* free transfer id */
      ctid->qtid = E_TID;		/* make it empty */
   }
   if (tobool(presentn_c( done ))) {	/* argument present */
      (*done) = nwrit;			/* return to caller */
   } else {				/* argument not present */
      if (nwrit != ntotl) ltid = -30;	/* error code */
   }
   if (tobool(presentn_c( tid ))) {	/* argument present */
      (*tid) = ltid;			/* return transfer id */
   } else if (ltid) {			/* not present and no zero */
      ltid = -30;			/* error code */
      gds___error_c( &ltid );		/* GDS error routine */
   }
}


/*

#>            gdsi_cancel.dc2

Function:     GDSI_CANCEL

Purpose:      Cancels a transfer started with GDSI_READ or GDSI_WRITE.

Category:     DATA, IMAGE-IO

File:         gdsi_read.c

Author:       K.G. Begeman

Use:          CALL GDSI_CANCEL( SET,           Input       CHARACTER*(*)
                                TRANS_ID )     Input       INTEGER

              SET           set name
              TRANS_ID      transfer indentification.
                            This identifier was returned by a previous call
                            GDSI_READ or GDSI_WRITE ( always > 0 ).

Related Docs: gdsi_read.dc2, gdsi_write.dc2.

Updates:      Dec 21, 1989: WZ, migrated to C.
              Jul  5, 1990: KGB, modified document.
              Oct 31, 1990: WZ, 'naxis', etc. from setsta -> dsc_file.
              Aug 17, 1991: KGB, new algorithm, replace fopen by open.
              Jan 31, 1994: JPT, attempt to open read-only if read-write fails.

#<

Fortran to C interface:

@ subroutine gdsi_cancel( character, integer )

*/

void	gdsi_cancel_c( fchar set ,		/* the set name */
                       fint  *tid )		/* the transfer id */
{
   int		f;			/* file descriptor */
   tid_struct	*ctid;			/* transfer struct */

   if ((*tid) < 0) {			/* error from previous call */
      gds___error_c( tid );		/* generate standard errors */
   } else if ((*tid) > ntid) {		/* illegal tid */
      (*tid) = -31;			/* error */
      return;				/* return to caller */
   }
   if ((*tid) == 0) return;		/* already done */
   ctid = &tids[(*tid)-1];		/* get transfer struct */
   f = ctid->ufid;			/* get file descriptor */
   if (ctid->todo) {			/* not yet finished */
      (*tid) = 0;			/* reset */
      ctid->todo = 0;			/* now its finished */
      ctid->qtid = E_TID;		/* make it empty */
   }
}

