/****************************************************************************
 * NCSA HDF                                                                 *
 * Software Development Group                                               *
 * National Center for Supercomputing Applications                          *
 * University of Illinois at Urbana-Champaign                               *
 * 605 E. Springfield, Champaign IL 61820                                   *
 *                                                                          *
 * For conditions of distribution and use, see the accompanying             *
 * hdf/COPYING file.                                                        *
 *                                                                          *
 ****************************************************************************/

#ifdef RCSID
static char RcsId[] = "@(#)$Revision: 1.77 $";
#endif

/* $Id: dfsd.c,v 1.77 1999/06/16 21:10:44 dwells Exp $ */

#ifdef HAVE_PABLO
#define PABLO_mask ID_dfsd_c
#endif
 
/*-----------------------------------------------------------------------------
 File:  dfsd.c

 Purpose:
    Routines for input and output of numeric data group

 Invokes:
    df.c dfgroup.c dfkit.c dfi.h df.h dfsd.h

 Public functions:
    DFSDgetdims      - get rank and dim sizes
    DFSDgetdatastrs  - get label, unit, format and coord system of data
    DFSDgetdimstrs   - get label, unit and format for a dimension
    DFSDgetdatalen   -
    DFSDgetdimlen    -
    DFSDgetdimscale  - get scale for a dimension
    DFSDgetrange     - get max and min of data
    DFSDgetdata      - get data values
    DFSDgetNT        - get file number type for reading
    DFSDpre32sdg     - tests, without calling DFSDsdginfo,  whether or
                       not the SDG/ref written with 3.1
    DFSDsetlengths   - set lengths of label, unit, format strings on gets
    DFSDsetdims      - set rank and dim sizes
    DFSDsetdatastrs  - set data label, unit, format and coord system
    DFSDsetdimstrs   - set dim labels, units and formats
    DFSDsetdimscale  - set scale for a dimension
    DFSDsetrange     - set max and min of data
    DFSDsetorder     - set array order to C or FORTRAN order
    DFSDsetNT        - set number type to be written out
    DFSDputdata      - output data, data info, and display info
    DFSDrestart      - forget info about last file accessed - restart from
                       beginning
    DFSDndatasets    - return number of SDGs in file
    DFSDclear        - forget all info set by DFSDset* routines
    DFSDlastref      - get reference number of last SDG read or written
    DFSDgetslice     - get part of the data, specified as a slice
    DFSDstartslice   - set up to write SD
    DFSDputslice     - write specified number of data items to file
    DFSDendslice     - end of series of writes, write out SDG
    DFSDwriteref     - set reference number to be used in next SDS write slab
    DFSDsetfillvalue - set fill value to be used in next SDS written
    DFSDgetfillvalue - return fill value from SDS that is about to be read
    DFSDstartslab    - set up to write slabs
    DFSDwriteslab    - write hyperslab of values
    DFSDendslab      - end of series of hyperslab writes
    DFSDreadslab     - get part of the data, specified as a slab

Lower level functions:
    DFSDgetsdg  - read SDG into struct
    DFSDputsdg  - write SDG to file

Private functions:
    DFSDIopen      - open or reopen file
    DFSDIsdginfo   - find next sdg in file
    DFSDIisndg     - was currently read sdg written by HDF3.2
    DFSDIrefresh   - get info of next sdg if necessary
    DFSDIgetrrank  - get rank of the currently read sdg
    DFSDIgetwrank  - get rank of the sdg to be written
    DFSDIclear     - clear sdg data structure of all info
    DFSDIgetdata   - read data from file
    DFSDIputdata   - write data to file
    DFSDIgetslice  - get slice
    DFSDIputslice  - put slice
    DFSDIendslice  -
    DFSDIsetnsdg_t - set up nsdg table
    DFSDInextnsdg  - get next nsdg from nsdg table
    DFSDIgetndg    - read NDG into struct
    DFSDIputndg    - write NDG to file

Fortran stub functions:
    dsisdas - set data label, unit, format and coord system
    dsisdis - set dim labels, units and formats

 Remarks:
    These functions will be copied into dfsd.c after debugging.
    This version assumes that all the values are floating point.
 *---------------------------------------------------------------------------*/

#include "hdf.h"
#include "dfsd.h"

/* MMM: make this definition correct and move to hfile.h, or wherever. */
#define DF_NOFILE 0

#define LABEL   0
#define UNIT    1
#define FORMAT  2
#define COORDSYS 3

#define NFGSDG_TYPE_SDG 0   /* a pure SDG  */
#define NFGSDG_TYPE_NFG 1   /* a pure NDG  */
#define NFGSDG_TYPE_SDGNDG 2    /* an SDG in NDG */

/* Init NSDG table header      */
PRIVATE DFnsdg_t_hdr *nsdghdr = NULL;

/* initialize aid to -1 and numbertype to DFNT_NONE.   S. Xu    */
PRIVATE DFSsdg Readsdg =        /* struct for reading */
{
    {(uint16) 0, (uint16) 0}, (intn) 0, NULL, NULL,
    {NULL, NULL, NULL},
    {NULL, NULL, NULL}, NULL,
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    (int32) DFNT_NONE, DFNTF_NONE, (int32) -1, (int32) 0, (int32) 0,
    (float64) 1.0, (float64) 0.0, (float64) 0.0, (float64) 0.0,
    (int32) -1,
    {0}, 0
};

PRIVATE DFSsdg Writesdg =       /* struct for writing */
{
    {(uint16) 0, (uint16) 0}, (intn) 0, NULL, NULL,
    {NULL, NULL, NULL},
    {NULL, NULL, NULL}, NULL,
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    (int32) DFNT_NONE, DFNTF_NONE, (int32) -1, (int32) 0, (int32) 0,
    (float64) 1.0, (float64) 0.0, (float64) 0.0, (float64) 0.0,
    (int32) -1,
    {0}, 0
};

PRIVATE uint16 Writeref = 0;    /* ref of next SDG/NDG to write to file */
PRIVATE intn Newdata = (-1);    /* Values in Readsdg fresh? */
                /* -1 : no descriptor read */
                /* 1 : descriptor read */
PRIVATE intn Nextsdg = 1;       /* Signal if DFSDgetdata should get the */
                /* next SDG/NDG */
PRIVATE int32 Sfile_id = DF_NOFILE;     /* pointer to file for slice writes */
PRIVATE int32 *Sddims;          /*dims written so far in slice write */

PRIVATE struct
  {                             /* Indicators of status (s) of info:    */
      intn        dims;         /* s = -1: there is no info in this category */
      intn        nt;           /* s = 0: info was set, but not yet written */
      intn        coordsys;     /* s > 0: info was set and written with ref no.s */
      intn        luf[3];
      intn        scales;
      intn        maxmin;
      intn        transpose;    /* It should be taken out!!!            !!!  */
      intn        cal;
      intn        fill_value;
      intn        new_ndg;
  }
Ref =
{
    -1, -1, -1,
    {
        -1, -1, -1
    }
    ,-1, -1, -1, -1, -1, -1
};

PRIVATE intn Maxstrlen[4] =
{DFS_MAXLEN, DFS_MAXLEN, DFS_MAXLEN, DFS_MAXLEN};
PRIVATE intn Ismaxmin = 0;      /* is there a max/min value on read?  */
PRIVATE intn FileTranspose = 0; /* is the data in column major order? */
PRIVATE intn Fortorder = 0;     /* should data be written col major?  */
PRIVATE intn IsCal = 0;         /* has calibration info been set?     */

/* In ver. 3.2 numbertype and file number format (subclass) are included  */
/* in DFSsdg, and  fileNTsize is local to functions .           */
/* static int fileNT=DFNTF_IEEE,         default: all IEEE       */
/*           fileNTsize=4,                       */
/*           outNT=DFNTF_IEEE,           default output: IEEE */
/*           outNTsize=4,                        */
/*           userNT=DFNTF_IEEE ;         default */

PRIVATE uint16 Readref = 0;     /* ref of next SDG/NDG to be read? */
#if 0
PRIVATE char Lastfile[DF_MAXFNLEN] = "";    /* last file opened */
#endif
PRIVATE char *Lastfile = NULL;
PRIVATE uint16 Lastref = 0;     /* Last ref to be read/written? */
PRIVATE DFdi lastnsdg;          /* last read nsdg in nsdg_t */

/* Whether we've installed the library termination function yet for this interface */
PRIVATE intn library_terminate = FALSE;

/* Private buffer */
PRIVATE uint8 *ptbuf = NULL;

/* Prototypes */
static intn DFSDIsetnsdg_t(int32 file_id, DFnsdg_t_hdr * l_nsdghdr);
static intn DFSDInextnsdg(DFnsdg_t_hdr * l_nsdghdr, DFdi * nsdg);
static intn DFSDIgetndg(int32 file_id, uint16 tag, uint16 ref, DFSsdg * sdg);
static intn DFSDIputndg(int32 file_id, uint16 ref, DFSsdg * sdg);
static intn DFSDIstart(void);

/*--------------------------------------------------------------------------
 NAME
       DFSDgetdims
 USAGE
       int DFSDgetdims(filename, prank, sizes, maxrank)
       char  *filename;   IN:  Name of file with scientific data set
       int   prank;       OUT: Number of dimensions
       int32 sizes:       OUT: array for holding dimensions of data set in file.
       int   maxrank:     IN:  size of array "dimsizes"

 RETURNS
       Returns SUCCED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Gets the number of dimensions(rank) and the sizes of the dimensions
       (dimsizes) for the next data set in the file. The input argument
       "maxrank" tells the size of the array that is allocated for storing
       the "dimsizses" array. The value of rank cannot exceed the value of
       "maxrank". The allocation of space for reading in the scientific
       data set should correspond to the values read in by "DFSDgetdims".
       The first value in the array "dimsizes" should equal the first
       dimension of the array that is allocated to hold the data set; the
       second value in "dimsizes" should equal the second dimension of the
       data set, and so forth. "DFSDgetdims" opens and closes the file.

--------------------------------------------------------------------------*/
intn
DFSDgetdims(const char *filename, intn *prank, int32 sizes[], intn maxrank)
{
  intn        i;
  int32       file_id;
  CONSTR(FUNC, "DFSDgetdims");
  intn       ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDgetdims);
#endif /* HAVE_PABLO */

  HEclear();  /* Clear error stack */

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (!prank)     /* check if ptr is valid */
    HGOTO_ERROR(DFE_BADPTR, FAIL);

  file_id = DFSDIopen(filename, DFACC_READ);  /* open/reopen file */
  if (file_id == FAIL)
    {
      ret_value = FAIL;
      goto done;
    }

  if (DFSDIsdginfo(file_id) < 0)
    {     /* reads next SDG from file */
      Hclose(file_id);
      ret_value = FAIL;  /* on error, close file and return */
      goto done;
    }

  *prank = Readsdg.rank;  /* copy rank, dimensions */
  if (maxrank < *prank)   /* if not all dimensions copied */
    HGOTO_ERROR(DFE_NOTENOUGH, FAIL);

  for (i = 0; i < *prank; i++)    /* copy dimensions */
    sizes[i] = Readsdg.dimsizes[i];
  Nextsdg = 0;

  ret_value = Hclose(file_id);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDgetdims);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*---------------------------------------------------------------------------
 NAME
       DFSDgetdatastrs
 USAGE
       int DFSDgetdatastrs(label, unit, format, coordsys)
       char *label;    OUT: label string that describes the data
       char *unit;     OUT: unit string that describes the unit used
       char *format;   OUT: format string that describes the format for
                            displaying the data
       char *coordsys; OUT: string describing coordinate system
 RETURN
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Retrieves information about the data from its associated attribute
       strings. Particularly, it reads the label, unit and format strings
       for the data. The parameter "coordsys" gives the coordinate system
       that is to be used for interpreting the dimension information.

----------------------------------------------------------------------------*/
intn
DFSDgetdatastrs(char *label, char *unit, char *format, char *coordsys)
{
  int32       luf;
  char       *lufp;
  CONSTR(FUNC, "DFSDgetdatastrs");
  intn        ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDgetdatastrs);
#endif /* HAVE_PABLO */

  HEclear();  /* Clear error stack */

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Newdata < 0)
    HGOTO_ERROR(DFE_BADCALL, FAIL);
/* NOTE: Once DFSDsetdatastrs is changed to always write all three (label,
   unit and format) whenever it is called, this routine should be
   changed so that it returns all three, if any exist.  This means
   that it also should be changed to return -1 if none exist.
   (Currently it returns FAIL only if the SDS doesn't exist.)
   */

    /* copy label, unit, format */
  for (luf = LABEL; luf <= FORMAT; luf++)
    {
      lufp = (luf == LABEL) ? label : (luf == UNIT) ? unit : format;
      if (lufp)
        {
          if (Readsdg.dataluf[luf])
            HIstrncpy(lufp, Readsdg.dataluf[luf], Maxstrlen[luf]);
        }
    }     /* end for 'luf' */
  /* copy coordsys */
  if (coordsys)
    {
      if (Readsdg.coordsys)
        HIstrncpy(coordsys, Readsdg.coordsys, Maxstrlen[COORDSYS]);
      else
        coordsys[0] = '\0';
    }

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDgetdatastrs);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*----------------------------------------------------------------------------
 NAME
       DFSDgetdimstrs
 USAGE
       int DFSDgetdimstrs(dim, label, unit, format)
       int  dim;      OUT: Dimension this label, unit and format refer to
       char *label;   OUT: Label that describes this dimension
       char *unit;    OUT: Unit to be used with this dimension
       char *format;  OUT: Format to be used in displaying scale for this
                           dimension
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Gets the labe, unit and format strings corresponding to the specified
       dimension. The space allocated for the label, unit and format string
       must be at least 1 byte larger than the length of the string. If the
       length is unknown when the program is written, declare the array size
       as 1+maxlen_label, _unit or _format after they are set by "DFSDsetlengths".
       The maximum default string length is 255.
----------------------------------------------------------------------------*/
intn
DFSDgetdimstrs(int dim, char *label, char *unit, char *format)
{
  intn        luf;
  intn        rdim;
  char       *lufp;
  CONSTR(FUNC, "DFSDgetdimstrs");
  intn        ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDgetdimstrs);
#endif /* HAVE_PABLO */

  HEclear();  /* Clear error stack */

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Newdata < 0)
    HGOTO_ERROR(DFE_BADCALL, FAIL);
/* NOTE: Once DFSDsetdimstrs is changed to always write all three (label,
   unit and format) whenever it is called, this routine should be
   changed so that it returns all three, if any exist.  This means
   that it also should be changed to return -1 if none exist.
   (Currently it returns FAIL only if the SDS doesn't exist.)
   */

  rdim = dim - 1;     /* translate dim to zero origin */
  if ((rdim >= Readsdg.rank) || (rdim < 0))
    HGOTO_ERROR(DFE_BADDIM, FAIL);

    /* copy labels etc */
  for (luf = LABEL; luf <= FORMAT; luf++)
    {
      lufp = (luf == LABEL) ? label : (luf == UNIT) ? unit : format;
      if (lufp)
        {
          if (!Readsdg.dimluf)
            {     /* no labels etc */
              *lufp = '\0';
              continue;
            }
          if (Readsdg.dimluf[luf])
            HIstrncpy(lufp, Readsdg.dimluf[luf][rdim], Maxstrlen[luf]);
        }   /* end if 'lufp' */
    }     /* end for 'luf' */

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDgetdimstrs);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*---------------------------------------------------------------------------
 NAME
       DFSDgetdatalen
 USAGE
       int DFSDgetdatalen(llabel, lunit, lformat, lcoordsys)
       int *llabel;     OUT: length of label string
       int *lunit;      OUT: length of unit string
       int *lformat;    OUT: length of format string
       int *lcoordsys;  OUT: length of coordsys string

 RETURN
       Returns SUCCED(0) if succesful and FAIL(-1) otherwise
 DESCRIPTION
       Gets actual length of label, unit, format and coordinate system
       strings. The space allocated for the label, unit, format and
       coordinate system strings must be at least 1 byte larger than the
       actual length of the string.

----------------------------------------------------------------------------*/
intn
DFSDgetdatalen(intn *llabel, intn *lunit, intn *lformat, intn *lcoordsys)
{
  CONSTR(FUNC, "DFSDgetdatalen");
  intn      ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDgetdatalen);
#endif /* HAVE_PABLO */

  HEclear();  /* Clear error stack */

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Newdata < 0)
    HGOTO_ERROR(DFE_BADCALL, FAIL);

  *llabel = (intn)(Readsdg.dataluf[LABEL] ?  HDstrlen(Readsdg.dataluf[LABEL]) : 0);
  *lunit = (intn)(Readsdg.dataluf[UNIT] ?  HDstrlen(Readsdg.dataluf[UNIT]) : 0);
  *lformat = (intn)(Readsdg.dataluf[FORMAT] ?  HDstrlen(Readsdg.dataluf[FORMAT]) : 0);
  *lcoordsys = (intn)(Readsdg.coordsys ?  HDstrlen(Readsdg.coordsys) : 0);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDgetdatalen);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*---------------------------------------------------------------------------
 NAME
       DFSDgetdimlen
 USAGE
       int DFSDgetdimlen(dim, llabel, lunit, lformat)
       int dim;       OUT: number of dimensions to get info about
       int *llabel;   OUT: length of label string
       int *lunit;    OUT: length of unit string
       int *lformat;  OUT: length of format string
 RETURN
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Get the length of label, unit, and format for the specified
       dimension. The space allocated to hold the label, unit, and format
       strings must be at least 1 byte larger than the actual length of the
       string.
----------------------------------------------------------------------------*/
intn
DFSDgetdimlen(intn dim, intn *llabel, intn *lunit, intn *lformat)
{
  CONSTR(FUNC, "DFSDgetdimlen");
  intn ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDgetdimlen);
#endif /* HAVE_PABLO */

  HEclear();  /* Clear error stack */

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Newdata < 0)
    HGOTO_ERROR(DFE_BADCALL, FAIL);

  if (dim > Readsdg.rank)
    HGOTO_ERROR(DFE_BADDIM, FAIL);

  *llabel = (intn)(Readsdg.dimluf[LABEL][dim - 1] ?  HDstrlen(Readsdg.dimluf[LABEL][dim - 1]) : 0);
  *lunit = (intn)(Readsdg.dimluf[UNIT][dim - 1] ?  HDstrlen(Readsdg.dimluf[UNIT][dim - 1]) : 0);
  *lformat = (intn)(Readsdg.dimluf[FORMAT][dim - 1] ?  HDstrlen(Readsdg.dimluf[FORMAT][dim - 1]) : 0);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDgetdimlen);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*----------------------------------------------------------------------------
 NAME
       DFSDgetdimscale
 USAGE
       int DFSDgetdimscale(dim, maxsize, scale)
       int   dim;      IN: Dimension this scale corresponds to
       int32 size;     IN:  size of scale
       VOIDP scale;    OUT: the scale
 RETURN
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Gets the scale corresponding to the specified demension. The DFSD
       interface in HDFv3.3 requires that the dimension scales are of the
       same number type as the corresponding data. To store dimension scales
       of a different number type than the corresponding data see the
       multi-file SD interface.
---------------------------------------------------------------------------*/
intn
DFSDgetdimscale(intn dim, int32 maxsize, VOIDP scale)
{
  uint32      dimsize;
  int32       numtype;
  int32       localNTsize;
  intn        rdim;
  uint8      *p1, *p2;
  CONSTR(FUNC, "DFSDgetdimscale");
  intn       ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDgetdimscale);
#endif /* HAVE_PABLO */

  HEclear();  /* Clear error stack */

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Newdata < 0)
    HGOTO_ERROR(DFE_BADCALL, FAIL);

  rdim = dim - 1;     /* translate dim to zero origin */
  if ((rdim >= Readsdg.rank) || (rdim < 0))
    HGOTO_ERROR(DFE_BADDIM, FAIL);

  if (maxsize < Readsdg.dimsizes[rdim])
    HGOTO_ERROR(DFE_NOSPACE, FAIL);

  if (!scale)
    HGOTO_ERROR(DFE_BADPTR, FAIL);

  if (!Readsdg.dimscales || !Readsdg.dimscales[rdim])     /* no scale */
    HGOTO_ERROR(DFE_NOVALS, FAIL);

    /* get number type and copy data from Readsdg to scale */
  if (Readsdg.numbertype == DFNT_NONE)
    Readsdg.numbertype = DFNT_FLOAT32;

  numtype = Readsdg.numbertype;
  localNTsize = DFKNTsize((numtype | DFNT_NATIVE) & (~DFNT_LITEND));
  dimsize = (uint32)localNTsize * (uint32)Readsdg.dimsizes[rdim];     /* in bytes  */

  p1 = (uint8 *) scale;
  p2 = (uint8 *) (Readsdg.dimscales[rdim]);
  HDmemcpy(p1, p2, dimsize);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDgetdimscale);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*---------------------------------------------------------------------------
 NAME
       DFSDgetrange
 USAGE
       int DFSDgetrange(max, min)
       VOIDP max;    OUT: High value stored with the scientific data set
       VOIDP min;    OUT: Low value stored with the scientific data set
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Gets the maximum and minimum values stored with the scientific data
       set. The "max" and "min" values are set by "DFSDsetrange". They are
       not automatically stored when a data set is written to a file.
       Since these values are supposed to relate to the data itself, it is
       assumed that they are of the same number type as the data itself.
       One implication of this is that in the C version of "DFSDgetrange"
       the arguments are pointers, rather than simple variables, whereas in
       the FORTRAN version they are simple variables of the same type as the
       data array. Neither "DFSDgetrange" or "DFSDgetdata" compare the "max"
       and "min" values stored with the data set to the actual values in
       the data set; they merely retrieve the data. As a result, the
       maximum and minimum values may not always reflect the actual maximum
       and minimum vlaues in the data set. In some cases the "max" and "min"
       values may actually lie outside the range of values in the data set.
---------------------------------------------------------------------------*/
int
DFSDgetrange(VOIDP pmax, VOIDP pmin)
{
  int32       numtype;
  uint32      localNTsize;
  uint8      *p1, *p2;
  CONSTR(FUNC, "DFSDgetrange");
  int         ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDgetrange);
#endif /* HAVE_PABLO */

  HEclear();  /* Clear error stack */

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Newdata < 0)
    HGOTO_ERROR(DFE_BADCALL, FAIL);

    /* get number type and copy data  */
  if (Readsdg.numbertype == DFNT_NONE)
    Readsdg.numbertype = DFNT_FLOAT32;
  numtype = Readsdg.numbertype;
  localNTsize = (uint32)DFKNTsize((numtype | DFNT_NATIVE) & (~DFNT_LITEND));

  if (Ismaxmin)   /* Ismaxmin is a global */
    {
      p1 = (uint8 *) pmax;
      p2 = (uint8 *) &(Readsdg.max_min[0]);
      HDmemcpy(p1, p2, localNTsize);
      p1 = (uint8 *) pmin;
      p2 = &(Readsdg.max_min[localNTsize]);
      HDmemcpy(p1, p2, localNTsize);
      ret_value = SUCCEED;
      goto done;
    }
  else
    HGOTO_ERROR(DFE_NOVALS, FAIL);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDgetrange);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*---------------------------------------------------------------------------
 NAME
      DFSDgetdata
 USAGE
      intn DFSDgetdata(filename, rank, maxsizes, data)
      char  filename;    IN:  name of HDF file containing scientific data set
      intn  rank;        IN:  number of dimensions of array "data"
      int32 maxsizes;    IN:  Array that holds dimensions of buffer that will
                              hold the data
      VOIDP data;        OUT: Array for holding the data

 RETURN
       Returns SUCCEED(0) if succesful and FAIL(-1) otherwise.
 DESCRIPTION
       Reads the next data set in the file. If you do no know the values of
       "rank" or "dimsizes", you must call "DFSDgetdims" to get them and
       then use them to provide the right amount of space for the array
       "data". If you do not know the number type of the data in the
       scientific dat set, you can call "DFSDgetNT" to find out.
       Subsequent calls to "DFSDgetdata"(or to "DFSDgetdims" and "DFSDgetdata")
       will sequentially read scientific data sets from the file. For example,
       if "DFSDgetdata" is called three times in succession, the third call
       reads data from the third scientific data set in the file. Note that
       if you do no know the values of "rank" or "dimsizes" you must call
       "DFSDgetdims" to get them each time and then provide the necessary
       space to hold the data. If "DFSDgetdims" or "DFSDgetdata" is called
       and there are no more scientific data sets left in the file, an
       error code is returned and nothing is read. "DFSDrestart" can be
       used to override this convention.

---------------------------------------------------------------------------*/
intn
DFSDgetdata(const char *filename, intn rank, int32 maxsizes[], VOIDP data)
{
  intn ret_value;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDgetdata);
#endif /* HAVE_PABLO */

  ret_value = (DFSDIgetdata(filename, rank, maxsizes, data, 0));   /* 0 == C */

#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDgetdata);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDsetlengths
 USAGE
       int DFSDsetlengths(maxlen_label, maxlen_unit, maxlen_format, maxlen_coordsys)
       int maxlen_label;     IN: maximum length of any label
       int maxlen_unit;      IN: maximum length of any unit
       int maxlen_format;    IN: maximum length of any format
       int maxlen_coordsys;  IN: maximum length of any coordsys
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Sets the maximum lengths for the strings that will hold labels, units
       formats and the name of the coordinate system. These lengths are used
       by the routines "DFSDgetdimstrs" and "DFSDgetdatastrs" to determine
       the maximum lengths of strings that they get from the HDF file.
       Normally, "DFSDsetlengths" is not needed. If it is not called,
       default maximum lengths of 255 are used for all strings.
-----------------------------------------------------------------------------*/
intn
DFSDsetlengths(intn maxlen_label, intn maxlen_unit, intn maxlen_format,
               intn maxlen_coordsys)
{
  CONSTR(FUNC, "DFSDsetlengths");
  intn ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDsetlengths);
#endif /* HAVE_PABLO */

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (maxlen_label > 0)
    Maxstrlen[LABEL] = maxlen_label;
  if (maxlen_unit > 0)
    Maxstrlen[UNIT] = maxlen_unit;
  if (maxlen_format > 0)
    Maxstrlen[FORMAT] = maxlen_format;
  if (maxlen_coordsys > 0)
    Maxstrlen[COORDSYS] = maxlen_coordsys;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDsetlengths);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDsetdims
 USAGE
       int DFSDsetdims(rank, dimsizes)
       int   rank;         IN: number of dimensions
       int32 dimsizes[];   IN: array containing dimensions of scientific data sett
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Sets the rank and dimension sizes for all subsequent scientific data
       sets written to the file. This routine must be called before calling
       either "DFSDgetdimstrs" or "DFSDsetdimscale". "DFSDsetdims" need not
       be called if other set routines are not called and the correct
       dimensions are supplied in "DFSDputdata" or "DFSDadddata". If the
       rank or dimension sizes change, all previous set calls are cleared,
       except for the number type which is set by calling "DFSDsetNT".
-----------------------------------------------------------------------------*/
intn
DFSDsetdims(intn rank, int32 dimsizes[])
{
  intn        i;
  CONSTR(FUNC, "DFSDsetdims");
  intn     ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDsetdims);
#endif /* HAVE_PABLO */

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Sfile_id != DF_NOFILE)
    HGOTO_ERROR(DFE_BADCALL, FAIL);

  if (Writesdg.rank == rank)  /* check if dimensions same */
    {
      if (Writesdg.dimsizes)
        {
          for (i = 0; i < rank; i++)
            {
              if (Writesdg.dimsizes[i] != dimsizes[i])
                break;
            }
          if (i == rank)
            {
              ret_value= SUCCEED;     /* Dimensions same as before */
              goto done;
            }
        }
    }

  /* forget all attributes set previously */
  if (DFSDIclear((DFSsdg *) & Writesdg) < 0)
    {
      ret_value = FAIL;
      goto done;
    }

  /* allocate dimensions */
  Writesdg.dimsizes = (int32 *) HDmalloc((size_t)rank * sizeof(int32));
  if (Writesdg.dimsizes == NULL)
    {
      ret_value = FAIL;
      goto done;
    }

  /* copy dimensions */
  Writesdg.rank = rank;
  for (i = 0; i < rank; i++)
    Writesdg.dimsizes[i] = dimsizes[i];

    /* Note dimensions modified */
  Ref.dims = 0;

  /*
   *  Added side effect, allows creation of new "ref" whenever called
   *  before DFSDwriteslab().
   */
  Ref.new_ndg = 0;
  Writeref = 0;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF(PABLO_mask, ID_DFSDsetdims);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDsetdatastrs
 USAGE
       int DFSDsetdatastrs(label, unit, format, coordsys)
       char *label;       IN: label that describes the data
       char *unit;        IN: unit to be used with the data
       char *format;      IN: format to be used in displaying the data
       char *coordsys;    IN: coordinate system
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Sets the label, unit, format and coordsys(coordinate system) strings
       to be assigned to the next data set written to the file.
-----------------------------------------------------------------------------*/
intn
DFSDsetdatastrs(const char *label, const char *unit, const char *format, const char *coordsys)
{
  intn ret_value;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDsetdatastrs);
#endif /* HAVE_PABLO */

  ret_value = (DFSDIsetdatastrs(label, unit, format, coordsys));

#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDsetdatastrs);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 * Name:    DFSDIsetdatastrs()
 * Purpose: Set label, unit and format for displaying subsequent SDGs
 * Inputs:  label: label to be used to describe data
 *          unit: unit corresponding to data values
 *          format: format to be used in displaying data values
 *          coordsys: type of coordinate system
 * Globals: Writesdg, Ref
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   HDF users, utilities, other routines
 * Invokes: none
 * Method:  Stores values in global structure Writesdg
 * Remarks: should we validate coordsys? proposed strings: "cartesian",
 *          "polar" (="spherical") and "cylindrical".  Do "spherical" and
 *          "cylindrical" make sense for 2D?
 *---------------------------------------------------------------------------*/
intn
DFSDIsetdatastrs(const char *label, const char *unit, const char *format, const char *coordsys)
{
  intn        luf;            /* takes values LABEL, UNIT, FORMAT */
                                /* in succession */
  const char *lufp;           /* points to label, unit, format */
                                /* in succession */
  CONSTR(FUNC, "DFSDIsetdatastrs");    /* for HERROR */
  intn      ret_value = SUCCEED;

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

   /* NOTE: The following code should be changed to write all three, even if
      one or more is an empty string.  Then, when DFSDgetdatastrs is called
      values will be returned for all three also, even though some might
      be empty strings.
    */
  for (luf = LABEL; luf <= FORMAT; luf++)
    {
      /* set lufp to point to label etc. as apppropriate */
      lufp = (luf == LABEL) ? label : (luf == UNIT) ? unit : format;

          /* free space if allocated */
      HDfreenclear(Writesdg.dataluf[luf]);

      /* copy string */
      if (lufp)
        {
          Writesdg.dataluf[luf] = (char *) HDstrdup(lufp);
          if (Writesdg.dataluf[luf] == NULL)
            {
              ret_value= FAIL;
              goto done;
            }
        }
    }

  HDfreenclear(Writesdg.coordsys);

  if (coordsys)
    {
      Writesdg.coordsys = (char *) HDstrdup(coordsys);
      if (Writesdg.coordsys == NULL)
        {
          ret_value = FAIL;
          goto done;
        }
    }

  /* indicate that label, unit, format and coordsys info modified */
  Ref.luf[LABEL] = Ref.luf[UNIT] = Ref.luf[FORMAT] = Ref.coordsys = 0;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDsetdimstrs()
 USAGE
       int  DFSDsetdimstrs(dim, label, unit, format)
       int  dim;        IN: dimension this label, unit and format refer to
       char *label;     IN: label that describes this dimension
       char *unit;      IN: unit to be used with this dimension
       char *format;    IN: format to be used to display scale
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Sets the label, unit, and format strings corresponding to the specified
       dimension. In both FORTRAN and C programs, dim=1 for the first
       dimension, and dim=2 for the second and so on. If the user is not
       interested in one or more strings, empty strings can be used as
       parameters for the "DFSDsetdimstrs" call. For example,
       "DFSDsetdimstrs(1, "vertical", "", "") " will set the label for the
       first dimension to "vertical" and set the unit and format strings to
       empty strings.
-----------------------------------------------------------------------------*/
intn
DFSDsetdimstrs(intn dim, const char *label, const char *unit, const char *format)
{
  intn ret_value;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDsetdimstrs);
#endif /* HAVE_PABLO */

  ret_value = (DFSDIsetdimstrs(dim, label, unit, format));

#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDsetdimstrs);
#endif /* HAVE_PABLO */
  
  return ret_value;
}   /* DFSDsetdimstrs */

/*-----------------------------------------------------------------------------
 * Name:    DFSDIsetdimstrs()
 * Purpose: For the given dimension, set label, unit, format
 *          This routine needs to be called once for each dimension whose
 *          values the user wants to set.
 * Inputs:  dim: the dimension that this info applies to
 *          label: label to be used to describe this dimension
 *          unit: units for dimension
 *          format: format to be used in displaying
 * Globals: Writesdg, Ref
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   HDF users, utilities, other routines
 * Invokes: none
 * Method:  Stores values in global structure Writesdg
 *---------------------------------------------------------------------------*/
intn
DFSDIsetdimstrs(intn dim, const char *label, const char *unit, const char *format)
{
  intn        i;
  intn        rdim;
  intn        luf;            /* takes values LABEL, UNIT, FORMAT */
                                /* in succession */
  const char *lufp;           /* points to label, unit, format */
                                /* in succession */
  CONSTR(FUNC, "DFSDsetdimstrs");
  intn        ret_value = SUCCEED;

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  /* translate from 1 to 0 origin */
  rdim = dim - 1;

  if ((rdim >= Writesdg.rank) || (rdim < 0))
    HGOTO_ERROR(DFE_BADDIM, FAIL);

  for (luf = LABEL; luf <= FORMAT; luf++)
    {
      /* set lufp to point to label etc. as apppropriate */
      lufp = (luf == LABEL) ? label : (luf == UNIT) ? unit : format;

      /* allocate space if necessary */
      if (!Writesdg.dimluf[luf])
        {
          Writesdg.dimluf[luf] =
            (char **) HDmalloc((uint32) Writesdg.rank * sizeof(char *));
          if (Writesdg.dimluf[luf] == NULL)
            {
              ret_value = FAIL;
              goto done;
            }
          for (i = 0; i < Writesdg.rank; i++)     /* set allocated pointers to NULL */
            Writesdg.dimluf[luf][i] = NULL;
        }

      /* free string space if allocated */
      HDfreenclear(Writesdg.dimluf[luf][rdim]);

      /* NOTE: The following code should be changed to write all three, even if
             one or more is an empty string.  Then, when DFSDgetdimstrs is called
             values will be returned for all three also, even though some might
             be empty strings.
      */
      /* copy string */
      if (lufp)
        {
          Writesdg.dimluf[luf][rdim] = (char *) HDstrdup(lufp);
          if (Writesdg.dimluf[luf][rdim] == NULL)
            {
              ret_value = FAIL;
              goto done;
            }
        }
    }
  /* Indicate that this info has not been written to file */
  Ref.luf[LABEL] = Ref.luf[UNIT] = Ref.luf[FORMAT] = 0;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDsetdimscale
 USAGE
       int DFSDsetdimscale(dim, dimsize, scale)
       int   dim;        IN: dimension the is scale corresponds to
       int32 dimsize;    IN: size of scale in the dimension
       VOID  *scale;     IN: the scale
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Sets the scale for a dimension. A scale is a 1D array whose value
       describes reference points along one of the dimensions of the
       scientific data set. For example, a 2D scientific data set representing
       points on a mpa could have two scales, one representing points of
       latitude, and the other points of longitude.
-----------------------------------------------------------------------------*/
intn
DFSDsetdimscale(intn dim, int32 dimsize, VOIDP scale)
{
    int32       i;
    intn        rdim;
    int32       numtype;
    uint32      bytesize;
    int32       localNTsize;
    uint8      *p1, *p2;
    intn        ret_value = SUCCEED;
    CONSTR(FUNC, "DFSDsetdimscale");

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDsetdimscale);
#endif /* HAVE_PABLO */

    HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

    rdim = dim - 1;     /* translate from 1 to 0 origin */

    if (!Writesdg.dimsizes)
        HGOTO_ERROR(DFE_BADCALL, FAIL);

    if (Writesdg.numbertype == DFNT_NONE)
      {
          if (DFSDsetNT(DFNT_FLOAT32) < 0)
            {
              ret_value = FAIL;
              goto done;
            }
      }
    numtype = Writesdg.numbertype;
    localNTsize = DFKNTsize((numtype | DFNT_NATIVE) & (~DFNT_LITEND));

    if ((rdim >= Writesdg.rank) || (rdim < 0)   /* check dimensions */
        || (dimsize != Writesdg.dimsizes[rdim]))
      {
          HGOTO_ERROR(DFE_BADDIM, FAIL);
      }

    if (!scale)
      {     /* No scale for this dimension */
          if (Writesdg.dimscales)
              HDfreenclear(Writesdg.dimscales[rdim]);
          Ref.scales = 0;
          ret_value = SUCCEED;
          goto done;
      }

    /* get number type and size of this type in this machine  */
    if (Writesdg.numbertype == DFNT_NONE)
      {
          if (DFSDsetNT(DFNT_FLOAT32) < 0)
            {
              ret_value = FAIL;
              goto done;
            }
      }
    numtype = Writesdg.numbertype;
    localNTsize = DFKNTsize((numtype | DFNT_NATIVE) & (~DFNT_LITEND));
    bytesize = (uint32)(dimsize * localNTsize);

    /* allocate space for dimscales if necessary */
    if (!Writesdg.dimscales)
      {
          Writesdg.dimscales =
              (uint8 **) HDmalloc((uint32) Writesdg.rank * sizeof(int8 *));
          if (Writesdg.dimscales == NULL)
            {
              ret_value = FAIL;
              goto done;
            }
          for (i = 0; i < Writesdg.rank; i++)   /* set allocated pointers to NULL */
              Writesdg.dimscales[i] = NULL;
      }

    if (!Writesdg.dimscales[rdim])
      {
          /* allocate dimension scale space if necessary */
          Writesdg.dimscales[rdim] =
              (uint8 *) HDmalloc((uint32) bytesize);
          if (Writesdg.dimscales[rdim] == NULL)
            {
              ret_value = FAIL;
              goto done;
            }
      }

    /* copy scale */
    p1 = (uint8 *) scale;
    p2 = (uint8 *) Writesdg.dimscales[rdim];
    HDmemcpy(p2, p1, bytesize);

    /* Indicate scales modified */
    Ref.scales = 0;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDsetdimscale);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDsetrange
 USAGE
       int DFSDsetrange(max, min)
       VOIDP max;    IN: High value in the scientific data set
       VOIDP min;    IN: Low value in the scientific data set
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Sets the maximum and minimum values to be stored with the scientific
       data set. Since these values are supposed to relate to the data itself,
       it is assumed that they are of the same number type as the data itself.
       One implication of this is that in the C version of "DFSDgetrange"
       the arguments are pointers, rather than simple variables, whereas in
       the FORTRAN version they are simple variables of the same type as the
       data array. This routine does not compute the maximum and minimum values;
       it merely stores the values it is given. As a result, the
       maximum and minimum values may not always reflect the actual maximum
       and minimum vlaues in the data set. When the maximum and minimum
       values are written to a file, the HDF elemement that hold these value
       is cleared, because it is assumed that subsequent data sets will have
       different values for "max" and "min". These values are automatically
       cleared aftera a call to either "DFSDputdata" or "DFSDaddadata".
-----------------------------------------------------------------------------*/
intn
DFSDsetrange(VOIDP maxi, VOIDP mini)
{
    int32       numtype;
    uint32      localNTsize;
    intn        i;
    uint8      *p1, *p2;
    intn        ret_value = SUCCEED;
    CONSTR(FUNC, "DFSDsetrange");    /* for HERROR */

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDsetrange);
#endif /* HAVE_PABLO */

    HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

    p1 = &(Writesdg.max_min[0]);
    for (i = 0; i < 16; i++)
        *p1++ = 0;  /* clear max_min   */

    /* get number type and copy the values to Writesdg   */
    if (Writesdg.numbertype == DFNT_NONE)
        DFSDsetNT(DFNT_FLOAT32);

    numtype = Writesdg.numbertype;
    localNTsize = (uint32)DFKNTsize((numtype | DFNT_NATIVE) & (~DFNT_LITEND));
    p1 = (uint8 *) maxi;
    p2 = (uint8 *) mini;

    HDmemcpy((uint8 *) &(Writesdg.max_min[0]), p1, localNTsize);
    HDmemcpy((uint8 *) &(Writesdg.max_min[localNTsize]), p2, localNTsize);

    Ref.maxmin = 0;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDsetrange);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDputdata
 USAGE
       intn DFSDputdata(filename, rank, dimsizes, data)
       char  *filename;     IN: name of file to store scientific data set in
       int   rank;          IN: number of dimensions of data array to be stored
       int32 dimsizes[];    IN: array that holds sizes of dimensions
       VOID  *data;         IN: array holding data to be stored
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Writes scientific data and related information to an HDF file.
       If a new filename is used, "DFSDputdata" functions exactly like
       "DFSDadddata".
 WARNING
       "DFSDputdata" will write data to an existing file by destroying the
       contents of the original file. Use with caution.
-----------------------------------------------------------------------------*/

intn
DFSDputdata(const char *filename, intn rank, int32 dimsizes[], VOIDP data)
{
  intn ret_value;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDputdata);
#endif /* HAVE_PABLO */

  /* 0, 0 specify create mode, C style array (row major) */
  ret_value = (DFSDIputdata(filename, rank, dimsizes, data, 0, 0));

#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDputdata);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
NAME
      DFSDadddata
USAGE
      intn DFSDadddata(filename, rank, dimsizes, data)
      char  *filename;    IN: Name of HDF file to store the data set
      intn  rank;         IN: Number of dimensions in the data array to be written
      int32 dimsizes[];   IN: Array holding the size of each dimension
      VOIDP data;         IN: Array holding the data to be stored

RETURNS
      SUCCEED(0) if successful and FAIL(-1) otherwise.

DESCRIPTION
      In addition to appending any multidemensional array of data to an HDF
      file, "DFSDaddata" automatically stores any information pertinent to the
      data set. It will not overwrite existing data in the file. The array
      "data" can be of any valid type. However if no number type has been set
      by "DFSDsetNT", it is assumed that the data type is of type "float32".
      The invocation of "DFSDadddata" triggers the writing of the entire
      scientific data set. That is, when "DFSDadddat" is called, all
      information that has been set by "DFSDset* " calls is written to the
      file, along with the data array itself.
-----------------------------------------------------------------------------*/
intn
DFSDadddata(const char *filename, intn rank, int32 dimsizes[], VOIDP data)
{
  intn  ret_value;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDadddata);
#endif /* HAVE_PABLO */

  /* 1, 0 specifies append mode, C style array (row major) */
  ret_value = (DFSDIputdata(filename, rank, dimsizes, data, 1, 0));

#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDadddata);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDrestart
 USAGE
       int DFSDrestart()
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Forces the next read operation to read the first scientific data set
       in the file.
-----------------------------------------------------------------------------*/
intn
DFSDrestart(void)
{
  intn ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDndatasets");

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDrestart);
#endif /* HAVE_PABLO */

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Lastfile != NULL)
    *Lastfile = '\0';   /* set to 0-length string instead of NULLing ptr */
  Readref = 0;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDrestart);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDndatasets
 USAGE
       int32 DFSDndatasets(filename)
       char *filename;     IN: filename of HDF file
 RETURNS
       Returns the number of data sets if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Returns the number of scientific datasets in the file. In HDFv3.3
       "DFSDndatasets" replaces "DFSDnumber". In order to maintain backward
       compatibility with existing HDF applications, HDF will continue to
       support "DFSDnumber". However, it is recommended that all new applications
       use "DFSDndatasets" instead of "DFSDnumber".
-----------------------------------------------------------------------------*/
int32
DFSDndatasets(char *filename)
{
  int32       file_id;
  int32       nsdgs = 0;
  int32       ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDndatasets");

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDndatasets);
#endif /* HAVE_PABLO */

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  /* should use reopen if same file as last time - more efficient */
  file_id = DFSDIopen(filename, DFACC_READ);
  if (file_id == FAIL)
    HGOTO_ERROR(DFE_BADOPEN, FAIL);

  nsdgs = (int32)nsdghdr->size;
  if (Hclose(file_id) == FAIL)
      HGOTO_ERROR(DFE_CANTCLOSE,FAIL);

  ret_value = nsdgs;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDndatasets);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDclear
 USAGE
       int DFSDclear()
 RETURNS
      Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
      Clears all possible set values. After a call to "DFSDclear", all
      values set by an "DFSDset*" calls will not be written unless they
      are set again.
----------------------------------------------------------------------------*/
intn
DFSDclear(void)
{
  CONSTR(FUNC, "DFSDclear");
  intn   ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDclear);
#endif /* HAVE_PABLO */

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  lastnsdg.tag = DFTAG_NULL;
  lastnsdg.ref = 0;
  if (DFSDIclearNT(&Writesdg) < 0)
        HGOTO_ERROR(DFE_INTERNAL, FAIL);

  ret_value = DFSDIclear(&Writesdg);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDclear);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDlastref
 USAGE
       uint16 DFSDlastref()
 RETURNS
       Returns the reference number of the last accessed scientific data set
       if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Get the most recent reference number used in writing or reading a
       scientific data set.
-----------------------------------------------------------------------------*/
uint16
DFSDlastref(void)
{
  CONSTR(FUNC, "DFSDlastref");
  uint16  ret_value;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDlastref);
#endif /* HAVE_PABLO */

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, 0);

  ret_value = (uint16) Lastref;

done:
  if(ret_value == 0)    /* 0 is invalid ref */
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDlastref);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDreadref
 USAGE
       int DFSDreadref(filename, ref)
       char   *filename;    IN: name of HDF file containing scientific data set
       uint16 ref;          IN: reference number for next "DFSDgetdata" call
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Specifies teh reference number for the data set to be read during
       the next read operation. This routine is most likely to be used in
       conjunction with "DFANgetlablist" which returns a list of labels for
       a given tag together with their reference numbers. It provides a sort
       of random access to scientific data sets. There is no guarantee that
       reference numbers appear in sequence in an HDF file, so ti is not
       generally safe to assume that a reference number is a sequence number
       for a scientific data set.
-----------------------------------------------------------------------------*/

intn
DFSDreadref(char *filename, uint16 ref)
{
  int32       file_id;
  int32       aid;
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDreadref");

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDreadref);
#endif /* HAVE_PABLO */

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  file_id = DFSDIopen(filename, DFACC_READ);
  if (file_id == DF_NOFILE)
      HGOTO_ERROR(DFE_BADOPEN, FAIL);

  if ((aid = Hstartread(file_id, DFTAG_SDG, ref)) == FAIL
        && (aid = Hstartread(file_id, DFTAG_NDG, ref)) == FAIL)
      HCLOSE_GOTO_ERROR(file_id, DFE_NOMATCH, FAIL);

  Hendaccess(aid);
  Readref = ref;
  Newdata = -1;

  ret_value = Hclose(file_id);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDreadref);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDgetslice
 USAGE
       intn DFSDgetslice(filename, winst, windims, data, dims)
       char  *filename;       IN:  name of HDF file
       int32 winst[];         IN:  array containing the coordinates for the start
                                   of the slice
       int32 windims[];       IN:  array containing the dimensions of the slice
       VOID  *data;           OUT: array for returning the slice
       int32 dims[];          OUT: dimensions of array data
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Gets a part of a scientific data set from a file. "DFSDgetslice"
       accesses the data set last accessed by "DFSDgetdims". If "DFSDgetdims"
       has not been called for the named file, "DFSDgetslice" gets a slice
       from the next data set in the file. The array "winst" specifies the
       coordinates for the beginning of the slice. Array "windims" gives the
       size of the slice. The number of elements in both "winst" and "windims"
       must be equal to the rank of the data set. For example, if the file
       contains a 3D data set, "winst" may contain the values {2,4,3}, while
       "windims" contains the values {3,1,4} and the dims shuld be at least
       {3,1,4}, the same size as the slice. This will extract a 3x4,
       two-dimensional slice, containing the elements between(2,4.3) and
       (4,4,6) from the original data set. The array "data" is the array
       into which the slice is read. It must be at least as big as the desired
       slice. The array "dims" is the array containing the actual dimensions
       of the array "data". The user assigns values to "dims" before calling
       "DFSDgetslice". All parameters assume FORTRAN-style 1-based arrays.
       "DFSDgetslice" is obsoleted by "DFSDreadslab". "DFSDreadslab" is the
       recommended fucntion call to use when reading hyperslabs(previously
       known as data slices). HDFv3.3 will continue to support "DFSDgetslice"
       only to maintain backward compatibility with HDF applications built
       on earlier versions of the library.
----------------------------------------------------------------------------*/
intn
DFSDgetslice(const char *filename, int32 winst[], int32 windims[], VOIDP data,
             int32 dims[])
{
  intn ret_value;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDgetslice);
#endif /* HAVE_PABLO */

  ret_value = (DFSDIgetslice(filename, winst, windims, data, dims, 0));

#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDgetslice);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDstartslice
 USAGE
       int DFSDstartslice(filename)
       char *filename;   IN: name of HDF file to write to
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Prepares the DFSD interface to write a slice to a scientific data set.
       Before calling "DFSDstartslice", you must call "DFSDsetdims" to
       specify the dimensions of the data set to be written to the file.
       "DFSDstartslice" always appends a new data set to an existing file.
       Remember, you must call "DFSDstartslice" before calling "DFSDputslice"
       or "DFSDendslice". "DFSDstarslice" is obsolete in favor of "DFSDstartslab"
       "DFSDstartslab" is the recommended function to call to use when
       beginning hyperslab(i.e. data slabs) opertaions. HDFv3.3 will continue
       to support "DFSDstartslice" only to maintain backward compatibility
       with HDF applications built on earlier version of the library.
-----------------------------------------------------------------------------*/
intn
DFSDstartslice(const char *filename)
{
  intn        i;
  int32       size;
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDstartslice");

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDstartslice);
#endif /* HAVE_PABLO */

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (!Writesdg.rank)     /* dimensions not set */
    HGOTO_ERROR(DFE_BADDIM, FAIL);

  Sfile_id = DFSDIopen(filename, DFACC_WRITE);
  if (Sfile_id == DF_NOFILE)
    HGOTO_ERROR(DFE_BADOPEN, FAIL);

  Writesdg.data.tag = DFTAG_SD;

  if (!Writeref)
    Writeref = Hnewref(Sfile_id);
  if (!Writeref)
    HGOTO_ERROR(DFE_BADREF, FAIL);
  Writesdg.data.ref = Writeref;

  if (Writesdg.numbertype == DFNT_NONE)   /* if NT not set,default to float32 */
    DFSDsetNT(DFNT_FLOAT32);

    /* set up to write data */
  size = DFKNTsize(Writesdg.numbertype);
  for (i = 0; i < Writesdg.rank; i++)
    size *= Writesdg.dimsizes[i];

  Writesdg.aid = Hstartwrite(Sfile_id, DFTAG_SD, Writeref, size);
  if (Writesdg.aid == FAIL)
    HCLOSE_GOTO_ERROR(Sfile_id,DFE_BADAID, FAIL);

  /* allocate array for keeping track of dims written */
  Sddims = (int32 *) HDmalloc((uint32) Writesdg.rank * sizeof(int32));
  if (Sddims == NULL)
    HCLOSE_GOTO_ERROR(Sfile_id,DFE_NOSPACE, FAIL);

  for (i = 0; i < Writesdg.rank; i++)
    Sddims[i] = 0;  /* nothing written so far */

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDstartslice);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDputslice
 USAGE
       int DFSDputslice(winend, data, dims)
       int32 winend[];    IN: dimensions that specify the size of slice to be
                              written
       VOID  *data;       IN: array containing slice to be written
       int32 dims[];      IN: dimensions of array data
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Writes part of a scientific data set to a file. "DFSDputslice" takes
       some contiguous part of an array in memory and stores it as part of
       the scientific data set array specified by "DFSDgetdims". Slices must
       be stored contiguously. Array "windims" specifies the size of the
       slice to be written. The number of elements in "windims" is equal to
       the number of dimensions in the scientific data set array. The array
       "data" is the array in memory containg the slice. The array "dims"
       contains the dimensions of the array "data". Note that the two arrays
       "windim" and "dims" need not be the same since the "windims" arguement
       could refer to a sub-array of "data". In this case only a portion
       of the array "data" is written to the scientific data set. All
       parameters assume FORTRAN-style 1-based arrays. "DFSDputslice" is
       obsoleted by "DFSDwriteslab". DFSDwriteslab is the recommended function
       call to use when writing hyperslabs(previously known as data slices).
       HDFv3.3 will continue to support "DFSDputslice" only to maintain
       backward compatibility with HDF applications built on earlier versions
       of the library.
-----------------------------------------------------------------------------*/
intn
DFSDputslice(int32 winend[], VOIDP data, int32 dims[])
{
  intn ret_value;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDputslice);
#endif /* HAVE_PABLO */

  ret_value = (DFSDIputslice(winend, data, dims, 0));

#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDputslice);
#endif /* HAVE_PABLO */
  
  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDendslice
 USAGE
       int DFSDendslice()
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Terminates the write operation after storing a slice of data in a
       scientific data set. "DFSDendslice" must be called after all the
       slices are written. It checks to insure that the entire data set
       has been written, and returns an error code if not. "DFSDendslice"
       is obsolete in favor of "DFSDendslab". "DFSDendslab" is the
       recommended function call to use when terminating hyperslab(previously
       known as data slices) operations. HDFv3.3 will continue to support
       "DFSDendslice" only to maintain backward compatability with HDF
       applications build on earlier versions of the library.

----------------------------------------------------------------------------*/
intn
DFSDendslice(void)
{
  intn ret_value;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDendslice);
#endif /* HAVE_PABLO */

  ret_value = (DFSDIendslice(0));

#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDendslice);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*---------------------------------------------------------------------------
 NAME
       DFSDsetNT
 USAGE
       int DFSDsetNT(numbertype)
       int32 *numbertype;  IN: Number type of the data to be written
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Sets the number type for the data to be written in the next write
       operation. "DFSDsetNT" must be called if a number type other than
       "float32" is to be stored. "DFSDsetNT" and "DFSDsetdims" can be called
       in any order, but they should be called before any other "DFSDset*"
       functions and before "DFSDputdata" or "DFSDadddata". Valid values for
       "DFSDgetNT" are of the general form "DFNT_<numbertype>". If you include
       the headier file "hdf.h" in your program, you can use the following
       symbolic names for the number types:
            32-bit float          DFNT_FLOAT32    5
            64-bit float          DFNT_FLOAT64    6
            8-bit signed int      DFNT_INT8      20
            8-bit unsigned int    DFNT_UINT8     21
            16-bit signed int     DFNT_INT16     22
            16-bit unsigned int   DFNT_UINT16    23
            32-bit signed int     DFNT_INT32     24
            32-bit unsigned int   DFNT_UINT32    25
---------------------------------------------------------------------------*/
intn
DFSDsetNT(int32 numbertype)
{
  uint8       outNT;
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDsetNT");

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDsetNT);
#endif /* HAVE_PABLO */

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  outNT = (uint8) (DFKisnativeNT(numbertype) ? DFKgetPNSC(numbertype, DF_MT) :
                  (DFKislitendNT(numbertype) ? DFNTF_PC : DFNTF_HDFDEFAULT));
  if ((numbertype == Writesdg.numbertype)
        && (outNT == Writesdg.filenumsubclass))
    HGOTO_DONE(SUCCEED);

  /* Forget previous numbertype  */
  if (DFSDIclearNT((DFSsdg *) & Writesdg) < 0)
    HGOTO_ERROR(DFE_INTERNAL, FAIL);

  Writesdg.numbertype = numbertype;
  Writesdg.filenumsubclass = outNT;
  Ref.nt = 0;
  Ref.dims = (Ref.dims >= 0 ? 0 : Ref.dims);
  Ref.new_ndg = 0;

  ret_value = (DFKsetNT(numbertype));

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDsetNT);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-------------------------------------------------------------------
* Name:    DFSDIclearNT
* Purpose: Reset all "set" values related to number types
* Inputs:  sdg: pointer to sdg struct to clear
* Globals: Ref
* Returns: 0 on success, FAIL on error with error set
* Users:   DFSDsetNT, HDF users
* Invokes: none
* Remarks:
*--------------------------------------------------------------------*/
intn
DFSDIclearNT(DFSsdg * sdg)
{
  intn        i;
  intn      ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDIclearNT");

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  sdg->numbertype = DFNT_NONE;
  sdg->filenumsubclass = DFNTF_NONE;

  /* free scale pointers. Note: scale pointer array is not freed   */
  /* sdg->dimscales will be freed only when rank is changed        */
  if (sdg->dimscales)
    {
      for (i = 0; i < sdg->rank; i++)
        HDfreenclear(sdg->dimscales[i]);
    }

  Ref.nt = -1;
  Ref.maxmin = -1;    /* maxmin and scales should be changed to */
  Ref.scales = -1;    /* new number type              */
  Ref.new_ndg = -1;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*---------------------------------------------------------------------------
 NAME
       DFSDgetNT
 USAGE
       int DFSDgetNT(numbertype)
       int32 *numbertype;    OUT: Number type of the data in the scientific
                                  data set.
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Gets the number type of the current scientific data set. This
       information is then used by calls such as "DFSDgetdata" and
       "DFSDgetslice". Since "DFSDgetNT" gets the number type of the
       current data set, "DFSDgetdims" must be called before calling
       "DFSDgetNT". Valid values for "DFSDgetNT" are of the general form
       "DFNT_<numbertype>". The following are valid symbolic names and
       their number types:

            32-bit float          DFNT_FLOAT32    5
            64-bit float          DFNT_FLOAT64    6
            8-bit signed int      DFNT_INT8      20
            8-bit unsigned int    DFNT_UINT8     21
            16-bit signed int     DFNT_INT16     22
            16-bit unsigned int   DFNT_UINT16    23
            32-bit signed int     DFNT_INT32     24
            32-bit unsigned int   DFNT_UINT32    25
--------------------------------------------------------------------------- */
intn
DFSDgetNT(int32 *pnumbertype)
{
  intn    ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDgetNT");

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDgetNT);
#endif /* HAVE_PABLO */

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  *(pnumbertype) = Readsdg.numbertype;
  if (*(pnumbertype) == DFNT_NONE)
    HGOTO_ERROR(DFE_BADNUMTYPE, FAIL);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDgetNT);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*---------------------------------------------------------------------------
 NAME
       DFSDpre32sdg
 USAGE
       int DFSDpre32sdg(filename, ref, ispre32)
       char   *filename;      IN:  The name of the HDF file containing the
                                   scientific data set
       uint16 ref;            IN:  Reference number of scientific data set
       int    ispre32;        OUT: Pointer to the results of the inquiry
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Tests if the scientific data set with the specified reference number
       was created by a HDF library that precedes HDF3.2. This routine is
       for programmers who need to know whether a scientific data set was
       written by a version of the HDF library earlier than HDF3.2. If the
       scientific data set was created with a version of HDF prior to v3.2,
       "ispre32" will be set to 1, otherwise it will be set to 0. Based on
       this information, programmers can decide whether or not to transpose
       the corresponding array.
----------------------------------------------------------------------------*/
intn
DFSDpre32sdg(char *filename, uint16 ref, intn *ispre32)
{
    uint32      num;
    int32       file_id;
    intn        found = 0;
    DFnsdgle   *ptr;
    intn       ret_value = SUCCEED;
    CONSTR(FUNC, "DFSDpre32sdg");

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDpre32sdg);
#endif /* HAVE_PABLO */

    HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

    file_id = DFSDIopen(filename, DFACC_READ);
    if (file_id == FAIL)
        HGOTO_ERROR(DFE_BADOPEN, FAIL);
    ptr = nsdghdr->nsdg_t;
    num = nsdghdr->size;

    while ((num > 0) && (ptr != NULL) && !found)
      {
          if ((ptr->nsdg.tag == DFTAG_SDG)
              && (ptr->nsdg.ref == ref))
            {   /* pure SDG  */
                found = 1;
                *ispre32 = TRUE;
            }
          else if ((ptr->sdg.tag == DFTAG_SDG)
                   && (ptr->sdg.ref == ref))
            {   /* NDGSDG   */
                found = 1;
                *ispre32 = FALSE;
            }
          else
            {
                ptr = ptr->next;
                num--;
            }
      }     /* while  */

    if (((num == 0) && (ptr != NULL)) || ((num != 0) && (ptr == NULL)) || !found)
      HCLOSE_GOTO_ERROR(file_id, DFE_BADTABLE, FAIL);

    if (Hclose(file_id) < 0)
        ret_value = FAIL;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDpre32sdg);
#endif /* HAVE_PABLO */

  return ret_value;
}   /* end of DFSDpre32sdg   */

/******************************************************************************/
/*--------------------- Lower level routines --------------------------------*/
/******************************************************************************/

/* Functions for NDG and SDG stuff                       */

/*--------------------------------------------------------------------------
 * Name:    DFSDIsetnsdg_t
 * Purpose: Set up the NDG/SDG table. Each node has two
            fields: the 1st field is NDG or SDG, the 2nd
            field has value only when it is a special
            NDG, i.e. the data set is float32 and not
            compressed.
 * Inputs:  file_id: pointer to HDF file containing SDG
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   DFSDIopen for READ
 *--------------------------------------------------------------------------*/
static intn
DFSDIsetnsdg_t(int32 file_id, DFnsdg_t_hdr * l_nsdghdr)
{
  uint32      sz_DFnsdgle = (uint32) sizeof(struct DFnsdgle);
  int32       aid;            /* access id */
  int32       ndgs;           /* number of ndg's */
  int32       sdgs;           /* number of sdg's */
  int32       GroupID;
  uint16      intag=DFTAG_NULL;
  uint16      inref=DFTAG_NULL;
  intn        moretags;
  intn        found;
  DFnsdgle   *ntb = NULL;
  DFnsdgle   *stb = NULL;
  DFnsdgle   *new;
  DFnsdgle   *nf;
  DFnsdgle   *nr;
  DFnsdgle   *sf;
  DFnsdgle   *sr;
  DFdi        di;
  DFdi        lnkdd[2];
  uint8      *bufp;
  intn       ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDsetnsdg_t");

    HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (!HDvalidfid(file_id))
    HGOTO_ERROR(DFE_BADCALL, FAIL);

    /* Check if temproray buffer has been allocated */
  if (ptbuf == NULL)
    {
      ptbuf = (uint8 *) HDmalloc(TBUF_SZ * sizeof(uint8));
      if (ptbuf == NULL)
        HGOTO_ERROR(DFE_NOSPACE, FAIL);
    }

  /* MMM:  Talk to Shiming and make sure the change made to the way ndgs
   and sdgs are handled is ok.
   */
  ndgs = Hnumber(file_id, DFTAG_NDG);
  sdgs = Hnumber(file_id, DFTAG_SDG);
  if ((ndgs == FAIL) || (sdgs == FAIL))
    HGOTO_ERROR(DFE_INTERNAL, FAIL);

  if ((ndgs + sdgs) == 0)
    {     /* no sdgs or ndgs in file */
      l_nsdghdr->size = 0;
      l_nsdghdr->nsdg_t = NULL;
      HGOTO_DONE(SUCCEED);
    }
  if ((ntb = (DFnsdgle *) HDmalloc(sz_DFnsdgle)) == NULL)
    HGOTO_ERROR(DFE_NOSPACE, FAIL);

    /* the first node in each table is a dummy node  */
  ntb->nsdg.tag = DFTAG_NULL;     /* set up and init an ndg table  */
  ntb->nsdg.ref = 0;
  ntb->sdg.tag = DFTAG_NULL;
  ntb->sdg.ref = 0;
  ntb->next = NULL;

  if ((stb = (DFnsdgle *) HDmalloc(sz_DFnsdgle)) == NULL)
    HGOTO_ERROR(DFE_NOSPACE, FAIL);

  stb->nsdg.tag = DFTAG_NULL;     /* set up and init an sdg table  */
  stb->nsdg.ref = 0;
  stb->sdg.tag = DFTAG_NULL;  /* this field should be named as */
  stb->sdg.ref = 0;   /* stb->ndg.tag, the ndg to which this */
  stb->next = NULL;   /* sdg belongs.                 */

  aid = Hstartread(file_id, DFTAG_WILDCARD, DFREF_WILDCARD);
  moretags = (aid != FAIL);
  while (moretags)
    {     /* read dd's and put each dd in ntb or stb */
      HQuerytagref(aid, &intag, &inref);
      /* put NDG or SDG on ntb or stb */
      if (intag == DFTAG_NDG)
        {
          nr = ntb;
          nf = ntb;
          while ((inref > nf->nsdg.ref) && (nf->next != NULL))
            {
              nr = nf;
              nf = nf->next;
            }
          /* MMM:  Tlk to Shiming and make sure the way this part was 
             rearranged is ok.
           */
          /* check for duplicate nsdg */
          if (inref == nf->nsdg.ref)
            HGOTO_ERROR(DFE_BADNDG, FAIL);

          /* add a node to the table */
          if ((new = (DFnsdgle *) HDmalloc(sz_DFnsdgle)) == NULL)
            HGOTO_ERROR(DFE_NOSPACE, FAIL);

          new->nsdg.tag = DFTAG_NDG;
          new->nsdg.ref = inref;
          new->sdg.tag = DFTAG_NULL;
          new->sdg.ref = 0;

          if (inref < nf->nsdg.ref)
            {     /* does it go before current node? */
              new->next = nf;
              nr->next = new;
            }
          else
            {     /* or at the end? */
              new->next = nf->next;
              nf->next = new;
            }

          /* Does this NDG have an SDG?       */
          if ((GroupID = DFdiread(file_id, DFTAG_NDG, inref)) < 0)
            HGOTO_ERROR(DFE_BADGROUP, FAIL);

          found = FALSE;
          di.tag = DFTAG_NULL;
          di.ref = 0;
          while ((found == 0) && (DFdiget(GroupID, &di.tag, &di.ref) == 0))
            {
              if (di.tag == DFTAG_SDLNK)
                found = TRUE;
            }

          if (found)
            {     /* read in the tag/refs in the link element */
              if (Hgetelement(file_id, di.tag, di.ref, ptbuf) == (int32) FAIL)
                {
                  DFdifree(GroupID);
                  HGOTO_ERROR(DFE_GETELEM, FAIL);
                }
              bufp = ptbuf;
              UINT16DECODE(bufp, lnkdd[0].tag);
              UINT16DECODE(bufp, lnkdd[0].ref);
              UINT16DECODE(bufp, lnkdd[1].tag);
              UINT16DECODE(bufp, lnkdd[1].ref);
              new->sdg.tag = lnkdd[1].tag;
              new->sdg.ref = lnkdd[1].ref;
              DFdifree(GroupID);
            }
        }   /* end of NDG    */

      if (intag == DFTAG_SDG)
        {
          sr = stb;
          sf = stb;
          while ((inref > sf->nsdg.ref) && (sf->next != NULL))
            {
              sr = sf;
              sf = sf->next;
            }
          if (inref == sf->nsdg.ref)
            HGOTO_ERROR(DFE_BADNDG, FAIL);

          /* insert a new node */
          if ((new = (DFnsdgle *) HDmalloc(sz_DFnsdgle)) == NULL)
            HGOTO_ERROR(DFE_NOSPACE, FAIL);

          new->nsdg.tag = DFTAG_SDG;
          new->nsdg.ref = inref;
          new->sdg.tag = DFTAG_NULL;
          new->sdg.ref = 0;

          if (inref < sf->nsdg.ref)
            {     /* does it go before current node? */
              new->next = sf;
              sr->next = new;
            }
          else
            {     /* or at the end? */
              new->next = sf->next;
              sf->next = new;
            }
          /* Does it belong to  an NDG?    */
          if ((GroupID = DFdiread(file_id, DFTAG_SDG, inref)) < 0)
              HGOTO_ERROR(DFE_BADGROUP, FAIL);
          found = FALSE;
          di.tag = DFTAG_NULL;
          di.ref = 0;
          while ((found == 0) && (DFdiget(GroupID, &di.tag, &di.ref) == 0))
            {
              if (di.tag == DFTAG_SDLNK)
                found = TRUE;
            }
          if (found)
            {     /* read in the tag/refs in the link element */
              if (Hgetelement(file_id, di.tag, di.ref, ptbuf) == (int32) FAIL)
                {
                  DFdifree(GroupID);
                  HGOTO_ERROR(DFE_GETELEM, FAIL);
                }
              bufp = ptbuf;
              UINT16DECODE(bufp, lnkdd[0].tag);
              UINT16DECODE(bufp, lnkdd[0].ref);
              UINT16DECODE(bufp, lnkdd[1].tag);
              UINT16DECODE(bufp, lnkdd[1].ref);
              new->sdg.tag = lnkdd[0].tag;
              new->sdg.ref = lnkdd[0].ref;
              DFdifree(GroupID);
            }
        }   /* end of SDG    */

      /*   get next dd   */
      moretags = (SUCCEED == Hnextread(aid, DFTAG_WILDCARD, DFREF_WILDCARD, DF_CURRENT));
    }     /* gone through the dd blocks   */
  Hendaccess(aid);

  /* merge stb and ntb        */
  /* remove SDGNDG from stb   */
  nf = ntb->next;
  while (nf != NULL)
    {
      inref = nf->sdg.ref;
      if (inref != 0)
        {   /* it has an SDG   */
          sr = stb;
          sf = stb;
          while ((sf->nsdg.ref < inref) && (sf->next != NULL))
            {
              sr = sf;
              sf = sf->next;
            }
          if (sf->nsdg.ref == inref)
            {
              if (sf->sdg.ref != nf->nsdg.ref)
                {
                  HGOTO_ERROR(DFE_BADNDG, FAIL);
                }
              else
                {
                  sr->next = sf->next;
                  HDfreenclear(sf);
                  sdgs--;
                }
            }
        }
      nf = nf->next;
    }

  /* check all SDGNDGs were removed   */
  sf = stb->next;
  while (sf != NULL)
    {
      if (sf->sdg.ref != 0)
        HGOTO_ERROR(DFE_BADSDG, FAIL);
      sf = sf->next;
    }

  /* merge the two tables into one */
  nf = ntb;   /* looking for the end of ntb   */
  while (nf->next != NULL)
    nf = nf->next;
  nf->next = stb->next;   /* the first node in stb is a dummy */
  l_nsdghdr->size = (uint32)(ndgs + sdgs);
  l_nsdghdr->nsdg_t = ntb->next;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
  /* Release the first nodes in stb and ntb  */
  if (stb != NULL)
    HDfree((VOIDP) stb);
  if (ntb != NULL)
    HDfree((VOIDP) ntb);

  return ret_value;
}   /* end of DFSDsdtnsdg_t   */

/*-----------------------------------------------------------------------
* Name  DFSDInextnsdg
* Purpose: Returns next ndg or sdg in the file
* Inputs:  nsdghdr: point to the nsdg table
*      nsdg: the structure holds the di of next sdg or ndg
* Returns: 0 on succeeds, FAIL on failure
* -------------------------------------------------------------------*/
static intn
DFSDInextnsdg(DFnsdg_t_hdr * l_nsdghdr, DFdi * nsdg)
{
  uint32      num;
  intn        found = FALSE;
  DFnsdgle   *ptr;
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDInextnsdg");

    HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  nsdg->tag = DFTAG_NULL;
  nsdg->ref = 0;
  ptr = l_nsdghdr->nsdg_t;
  num = l_nsdghdr->size;

  if ((ptr == NULL) || (num == 0))
      HGOTO_DONE(SUCCEED);

  if ((lastnsdg.tag == DFTAG_NULL) && (lastnsdg.ref == 0))
    {
      found = TRUE;
    }
  else
    {
      while ((num > 0) && (ptr != NULL) && !found)
        {
          if ((ptr->nsdg.tag == lastnsdg.tag)
              && (ptr->nsdg.ref == lastnsdg.ref))
            {
              if ((ptr = ptr->next) != NULL)
                found = TRUE;
            }
          else
            {
              ptr = ptr->next;
              num--;
            }
        }   /* while  */

      if (((num == 0) && (ptr != NULL))
          || ((num != 0) && (ptr == NULL)) || !found)
        {
          HGOTO_ERROR(DFE_BADTABLE, FAIL);
        }
    }     /* else   */

  if (found)
    {
      nsdg->tag = ptr->nsdg.tag;
      nsdg->ref = ptr->nsdg.ref;
    }

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}   /* end of DFSDInextnsdg   */

/*-----------------------------------------------------------------------------
 * Name:    DFSDIgetndg
 * Purpose: Reads in NDG
 * Inputs:  file_id: pointer to HDF file containing NDG
 *          ref: ref of NDG to read
 *          sdg: pointer to DFSsdg struct to read NDG into
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   HDF programmers, DFSDIsdginfo
 * Invokes: DFgetelement, DFdiread, DFdiget, DFaccess, DFread
        DFSDgetsdg
 * Method:  Reads in NDG using DFdiread.  Gets each tag/ref using DFdiget.
 *          Reads in dimensions using DFgetelement.
 *      Call DFSDgetsdg to read in the rest info.
 *          Mallocs space for these, freeing
 *          previously allocated space.
 * Remarks: This accepts non-float32 data
 *---------------------------------------------------------------------------*/
static intn
DFSDIgetndg(int32 file_id, uint16 tag, uint16 ref, DFSsdg * sdg)
{
  int16       int16var;
  int32       i;
  intn        luf;
  DFdi        elmt;
  DFdi        nt;
  int32       length;
  int32       numtype = 0;    /* current number type */
  int32       fileNTsize = 0; /* size of this NT as it is in the file */
  int32       localNTsize = 0;    /* size of this NT as it is in this machine */
  int32       ret;
  int32       aid;
  int32       GroupID;
  int8        fileNT = 0;     /* file number subclass */
  int8        platnumsubclass = 0;    /* platform number subclass */
  uint8       ntstring[4];
  uint8      *isscales;
  uint8      *buf;
  uint8      *p;              /* temporary pointer for moving things to buffer */
  intn       ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDIgetndg");

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (!HDvalidfid(file_id))
    HGOTO_ERROR(DFE_BADCALL, FAIL);

  if (!ref)
    HGOTO_ERROR(DFE_BADREF, FAIL);

  /* Check if temproray buffer has been allocated */
  if (ptbuf == NULL)
    {
      ptbuf = (uint8 *) HDmalloc(TBUF_SZ * sizeof(uint8));
      if (ptbuf == NULL)
        HGOTO_ERROR(DFE_NOSPACE, FAIL);
    }

  /* read NDG into memory */
  if ((GroupID = DFdiread(file_id, tag, ref)) < 0)
        HGOTO_ERROR(DFE_BADGROUP, FAIL);

  DFSDIclear(sdg);
  if (tag == DFTAG_NDG)
    DFSDIclearNT(sdg);
  Ismaxmin = 0;
  IsCal = FALSE;

  /*
   * Loop through all members of the group
   */
  while (!DFdiget(GroupID, &elmt.tag, &elmt.ref))
    {
      luf = -1;     /* flag value for label/unit/ */
      /* format gets process tag/ref */
      switch (elmt.tag)
        {

        case DFTAG_SD:      /* data tag/ref */
          sdg->data.tag = elmt.tag;   /* put tag/ref in struct */
          sdg->data.ref = elmt.ref;
          break;

        case DFTAG_SDD: /* dimension */
          aid = Hstartread(file_id, elmt.tag, elmt.ref);
          if (aid == FAIL)
            {
              DFdifree(GroupID);
              HGOTO_ERROR(DFE_BADAID, FAIL);
            }

          /* read rank */
          if (Hread(aid, (int32) 2, ptbuf) == FAIL)
            {
              DFdifree(GroupID);
              Hendaccess(aid);
              HGOTO_ERROR(DFE_READERROR, FAIL);
            }
          p = ptbuf;
          INT16DECODE(p, int16var);
          sdg->rank=(intn)int16var;

          /* get space for dimensions */
          sdg->dimsizes = (int32 *) HDmalloc((uint32) sdg->rank *
                                             sizeof(int32));
          if (sdg->dimsizes == NULL)
            {
              DFdifree(GroupID);
              Hendaccess(aid);
              HGOTO_ERROR(DFE_NOSPACE, FAIL);
            }

          /* read dimension record */
          if (Hread(aid, (int32) 4 * sdg->rank, ptbuf) == FAIL)
            {
              DFdifree(GroupID);
              Hendaccess(aid);
              HGOTO_ERROR(DFE_READERROR, FAIL);
            }
          p = ptbuf;
          for (i = 0; i < sdg->rank; i++)
            INT32DECODE(p, sdg->dimsizes[i]);

          /* read tag/ref of NT */
          if (Hread(aid, (int32) 4, ptbuf) == FAIL)
            {
              DFdifree(GroupID);
              Hendaccess(aid);
              HGOTO_ERROR(DFE_READERROR, FAIL);
            }
          p = ptbuf;
          UINT16DECODE(p, nt.tag);
          UINT16DECODE(p, nt.ref);

          /* read actual NT */
          if (Hgetelement(file_id, nt.tag, nt.ref, ntstring) == FAIL)
            {
              DFdifree(GroupID);
              Hendaccess(aid);
              HGOTO_ERROR(DFE_GETELEM, FAIL);
            }

          /* check for any valid NT */
          if (ntstring[1] == DFNT_NONE)
            {
              DFdifree(GroupID);
              Hendaccess(aid);
              HGOTO_ERROR(DFE_BADCALL, FAIL);
            }

          /* if looking for an SDG type must be FLOAT32 */
          if (tag == DFTAG_SDG && ntstring[1] != DFNT_FLOAT32)
            {
              DFdifree(GroupID);
              Hendaccess(aid);
              HGOTO_ERROR(DFE_BADCALL, FAIL);
            }

          /* set NT info */
          numtype = (int32)ntstring[1];
          fileNT = (int8)ntstring[3];
          platnumsubclass = DFKgetPNSC(numtype, DF_MT);
          if ((fileNT != DFNTF_HDFDEFAULT)
              && (fileNT != DFNTF_PC)
              && (fileNT != platnumsubclass))
            {
              DFdifree(GroupID);
              Hendaccess(aid);
              HGOTO_ERROR(DFE_BADCALL, FAIL);
            }
          if (fileNT != DFNTF_HDFDEFAULT)
            {     /* if native or little endian */
              if (fileNT != DFNTF_PC)   /* native */
                numtype |= DFNT_NATIVE;
              else  /* little endian */
                numtype |= DFNT_LITEND;
            }     /* end if */

          sdg->filenumsubclass = ntstring[3];
          sdg->numbertype = numtype;

          /* set size of NT    */
          fileNTsize = DFKNTsize(numtype);
          localNTsize = DFKNTsize((numtype | DFNT_NATIVE) & (~DFNT_LITEND));

          /* read and check all scale NTs */
          for (i = 0; i < sdg->rank; i++)
            {
              if (Hread(aid, (int32) 4, ptbuf) == FAIL)
                {
	              DFdifree(GroupID);
                  Hendaccess(aid);
                  HGOTO_ERROR(DFE_READERROR, FAIL);
                }
              p = ptbuf;
              UINT16DECODE(p, nt.tag);
              UINT16DECODE(p, nt.ref);

              /* read NT itself */
              if (Hgetelement(file_id, nt.tag, nt.ref, ntstring) == FAIL)
                {
	              DFdifree(GroupID);
                  Hendaccess(aid);
                  HGOTO_ERROR(DFE_GETELEM, FAIL);
                }

              /* check for any valid NT */
              if (ntstring[1] == DFNT_NONE)
                {
	              DFdifree(GroupID);
                  Hendaccess(aid);
                  HGOTO_ERROR(DFE_BADCALL, FAIL);
                }

              /* if looking for an SDG type must be FLOAT32 */
              if (tag == DFTAG_SDG && ntstring[1] != DFNT_FLOAT32)
                {
	              DFdifree(GroupID);
                  Hendaccess(aid);
                  HGOTO_ERROR(DFE_BADCALL, FAIL);
                }

            }     /* end for loop */
          Hendaccess(aid);
          break;

        case DFTAG_SDLNK:   /* SDG NDG link */
          break;  /* do nothing in 3.2  */

        case DFTAG_SDL: /* labels */
          if (luf == (-1))
            luf = LABEL;

        case DFTAG_SDU: /* units */
          if (luf == (-1))
            luf = UNIT;

        case DFTAG_SDF: /* formats */
          if (luf == (-1))
            luf = FORMAT;

          if (!sdg->dimsizes)
            {
	          DFdifree(GroupID);
              HGOTO_ERROR(DFE_CORRUPT, FAIL);
            }

          /* get needed size of buffer, allocate */
          length = Hlength(file_id, elmt.tag, elmt.ref);
          if (length == FAIL)
            {
	          DFdifree(GroupID);
              HGOTO_ERROR(DFE_BADLEN, FAIL);
            }
          buf = (uint8 *) HDmalloc((uint32) length);
          if (buf == NULL)
            {
	          DFdifree(GroupID);
              HGOTO_ERROR(DFE_NOSPACE, FAIL);
            }

          /* read in luf */
          if (Hgetelement(file_id, elmt.tag, elmt.ref, buf) == FAIL)
            {
	          DFdifree(GroupID);
              HDfree((VOIDP) buf);
              HGOTO_ERROR(DFE_GETELEM, FAIL);
            }
          p = buf;

          /* allocate data luf space */
          sdg->dataluf[luf] = (char *) HDmalloc((uint32) HDstrlen((char *) p) + 1);

          if (sdg->dataluf[luf] == NULL)
            {
	          DFdifree(GroupID);
              HDfree((VOIDP) buf);
              HGOTO_ERROR(DFE_NOSPACE, FAIL);
            }

          /* extract data luf */
          HDstrcpy(sdg->dataluf[luf], (char *) p);
          p += HDstrlen(sdg->dataluf[luf]) + 1;

          /* get space for dimluf array */
          sdg->dimluf[luf] =
            (char **) HDmalloc((uint32) sdg->rank * sizeof(char *));
          if (sdg->dimluf[luf] == NULL)
            {
	          DFdifree(GroupID);
              HDfree((VOIDP) buf);
              HGOTO_ERROR(DFE_NOSPACE, FAIL);
            }

          /* extract dimension lufs */
          for (i = 0; i < sdg->rank; i++)
            {
              sdg->dimluf[luf][i] = (char *)
                HDmalloc((uint32) HDstrlen((char *) p) + 1);
              if (sdg->dimluf[luf][i] == NULL)
                {
		          DFdifree(GroupID);
                  HDfree((VOIDP) buf);
                  HGOTO_ERROR(DFE_NOSPACE, FAIL);
                }
              HDstrcpy(sdg->dimluf[luf][i], (char *) p);
              p += HDstrlen(sdg->dimluf[luf][i]) + 1;
            }
          HDfree((VOIDP) buf);
          break;

        case DFTAG_SDS: /* scales */
          if (!sdg->dimsizes)
	        {
	          DFdifree(GroupID);
              HGOTO_ERROR(DFE_CORRUPT, FAIL);
            }

          /* set up to read scale */
          aid = Hstartread(file_id, elmt.tag, elmt.ref);
          if (aid == FAIL)
	        {
	          DFdifree(GroupID);
              HGOTO_ERROR(DFE_BADAID, FAIL);
            }

          /* read isscales */
          isscales = (uint8 *) HDmalloc((uint32) sdg->rank);
          if (isscales == NULL)
            {
	          DFdifree(GroupID);
              Hendaccess(aid);
              HGOTO_ERROR(DFE_NOSPACE, FAIL);
            }
          if (Hread(aid, (int32) sdg->rank, isscales) == FAIL)
            {
	          DFdifree(GroupID);
              Hendaccess(aid);
              HGOTO_ERROR(DFE_READERROR, FAIL);
            }

          /* allocate scale pointers */
          sdg->dimscales =
            (uint8 **) HDmalloc((uint32) sdg->rank * sizeof(int8 *));
          if (sdg->dimscales == NULL)
            {
	          DFdifree(GroupID);
              HDfree((VOIDP) isscales);
              Hendaccess(aid);
              HGOTO_ERROR(DFE_NOSPACE, FAIL);
            }

          /* read scales */
          for (i = 0; i < sdg->rank; i++)
            {
              sdg->dimscales[i] = NULL;     /* default */
              if (!isscales[i])
                continue;

              /* space for scale */
              sdg->dimscales[i] = (uint8 *)
                HDmalloc((size_t) (sdg->dimsizes[i] * localNTsize));
              if (sdg->dimscales[i] == NULL)
                {
		          DFdifree(GroupID);
                  HDfree((VOIDP) isscales);
                  Hendaccess(aid);
                  HGOTO_ERROR(DFE_NOSPACE, FAIL);
                }

              if (platnumsubclass == fileNT)
                {   /* no conversion needed */
                  ret = Hread(aid, (int32) sdg->dimsizes[i] * fileNTsize,
                              (uint8 *) sdg->dimscales[i]);
                  if (ret == FAIL)
                    {
			          DFdifree(GroupID);
                      HDfree((VOIDP) isscales);
                      Hendaccess(aid);
                      HGOTO_ERROR(DFE_READERROR, FAIL);
                    }
                }
              else
                {   /* conversion necessary */
                  /* allocate conversion buffer */
                  buf = (uint8 *) HDmalloc((size_t) (sdg->dimsizes[i] * fileNTsize));
                  if (buf == NULL)
                    {
			          DFdifree(GroupID);
                      HDfree((VOIDP) isscales);
                      Hendaccess(aid);
                      HGOTO_ERROR(DFE_NOSPACE, FAIL);
                    }

                  /* read scale from file */
                  ret = Hread(aid,(int32) (sdg->dimsizes[i] * fileNTsize), buf);
                  if (ret == FAIL)
                    {
			          DFdifree(GroupID);
                      HDfree((VOIDP) buf);
                      HDfree((VOIDP) isscales);
                      Hendaccess(aid);
                      HGOTO_ERROR(DFE_READERROR, FAIL);
                    }

                  p = buf;

                  /* convert, all at once */
                  DFKconvert((VOIDP) p, (VOIDP) sdg->dimscales[i], numtype,
                             sdg->dimsizes[i], DFACC_READ, 0, 0);

                  HDfree((VOIDP) buf);
                }
            }
          HDfree((VOIDP) isscales);
          Hendaccess(aid);
          break;

        case DFTAG_SDC: /* coordsys */
          /* find and allocate necessary space */
          length = Hlength(file_id, elmt.tag, elmt.ref);
          if (length == FAIL)
            {
	          DFdifree(GroupID);
              HGOTO_ERROR(DFE_BADLEN, FAIL);
            }

          sdg->coordsys = (char *) HDmalloc((uint32) length);
          if (sdg->coordsys == NULL)
            {
	          DFdifree(GroupID);
              HGOTO_ERROR(DFE_NOSPACE, FAIL);
            }

          /* read coordsys */
          if (Hgetelement(file_id, elmt.tag, elmt.ref,
                          (uint8 *) sdg->coordsys) == FAIL)
	        {
	          DFdifree(GroupID);
              HGOTO_ERROR(DFE_GETELEM, FAIL);
            }
          break;

        case DFTAG_SDM: /* max/min */
          if (fileNT == platnumsubclass)
            {     /* no conversion */
              if (Hgetelement(file_id, elmt.tag, elmt.ref,
                              (uint8 *) &(sdg->max_min[0])) == FAIL)
                {
		          DFdifree(GroupID);
                  HGOTO_ERROR(DFE_GETELEM, FAIL);
                }
            }
          else
            {
              /* conversion needed */
              /* allocate buffer */
              buf = (uint8 *) HDmalloc((size_t) (2 * fileNTsize));
              if (buf == NULL)
                {
 		          DFdifree(GroupID);
                 HGOTO_ERROR(DFE_NOSPACE, FAIL);
                }

              /* read and convert max/min */
              if (Hgetelement(file_id, elmt.tag, elmt.ref, buf) == FAIL)
                {
		          DFdifree(GroupID);
                  HGOTO_ERROR(DFE_GETELEM, FAIL);
                }

              DFKconvert((VOIDP) buf, (VOIDP) &(sdg->max_min[0]), numtype, 2,
                         DFACC_READ, 0, 0);

              HDfree((VOIDP) buf);
            }
          Ismaxmin = 1;
          break;

        case DFTAG_CAL:
          if (fileNT == platnumsubclass)
            {     /* no conversion */
              /* get size of element */
              intn        eltSize = (intn) Hlength(file_id, elmt.tag, elmt.ref);
              if (eltSize == FAIL)
                {
		          DFdifree(GroupID);
                  HGOTO_ERROR(DFE_BADLEN, FAIL);
                }

              if (eltSize == 36)
                {
                   /* element is new, double based type */
                  if (Hgetelement(file_id, elmt.tag, elmt.ref,
                                  (unsigned char *) &sdg->cal) < 0)
	                {
			          DFdifree(GroupID);
                      HGOTO_ERROR(DFE_GETELEM, FAIL);
                    }
                }
              else
                {
                  /* element is old float based type */
                  float32     buf2[4];

                   /* allocate input buffer */
                  if (Hgetelement(file_id, elmt.tag, elmt.ref,
                                  (unsigned char *) buf2) < 0)
	                {
			          DFdifree(GroupID);
                      HGOTO_ERROR(DFE_GETELEM, FAIL);
                    }

                  /* move 'em over */
                  sdg->ioff = (float64) buf2[0];
                  sdg->ioff_err = (float64) buf2[1];
                  sdg->cal = (float64) buf2[2];
                  sdg->cal_err = (float64) buf2[3];
                  sdg->cal_type = DFNT_INT16;

                }
            }
          else
            {
              intn        eltSize;

              /* get size of element */
              eltSize = (intn) Hlength(file_id, elmt.tag, elmt.ref);
              if (eltSize == FAIL)
                {
                  DFdifree(GroupID);
                  HGOTO_ERROR(DFE_BADLEN, FAIL);
                }

              /* allocate buffer */
              buf = (uint8 *) HDmalloc((uint32) eltSize);
              if (buf == NULL)
                {
                  DFdifree(GroupID);
                  HGOTO_ERROR(DFE_NOSPACE, FAIL);
                }

              /* read and convert calibration */
              if (Hgetelement(file_id, elmt.tag, elmt.ref, buf) == FAIL)
                {
                  DFdifree(GroupID);
                  HGOTO_ERROR(DFE_GETELEM, FAIL);
                }

              if (eltSize == 36)
                {
                  /* element is new, double based type */
                 /* read in the 64bit float factors */
                  DFKconvert((VOIDP) buf,
                             (VOIDP) &sdg->cal,
                             DFNT_FLOAT64, 4, DFACC_READ, 0, 0);

                  /* read in the 32bit integer number type */
                  DFKconvert((VOIDP) (buf + 32),
                             (VOIDP) &sdg->cal_type,
                             DFNT_INT32, 1, DFACC_READ, 0, 0);
                }
              else
                {
                  /* element is old float based type */
                  float32     buf2[4];

                  /* convert calibration factors */
                  DFKconvert((VOIDP) buf, (VOIDP) buf2, DFNT_FLOAT32, 4,
                             DFACC_READ, 0, 0);

                  /* move 'em over */
                  sdg->ioff = (float64) buf2[0];
                  sdg->ioff_err = (float64) buf2[1];
                  sdg->cal = (float64) buf2[2];
                  sdg->cal_err = (float64) buf2[3];
                  sdg->cal_type = DFNT_INT16;

                }
              HDfree((VOIDP) buf);
            }
          IsCal = TRUE;
          break;

        case DFTAG_FV:
          if (fileNT == platnumsubclass)
            {     /* no conversion */
              /* get size of element */
              intn        eltSize = (intn) Hlength(file_id, elmt.tag, elmt.ref);
              if (eltSize == FAIL)
                {
                  DFdifree(GroupID);
                  HGOTO_ERROR(DFE_BADLEN, FAIL);
                }

              /* get element */
              if (Hgetelement(file_id, elmt.tag, elmt.ref,
                              (unsigned char *) sdg->fill_value) == FAIL)
                {
                  DFdifree(GroupID);
                  HGOTO_ERROR(DFE_GETELEM, FAIL);
                }
            }
          else
            {
              intn        eltSize;

              /* get size of element  */
              eltSize = (intn) Hlength(file_id, elmt.tag, elmt.ref);
              if (eltSize == FAIL)
                {
                  DFdifree(GroupID);
                  HGOTO_ERROR(DFE_BADLEN, FAIL);
                }

              /* allocate buffer for conversion  */
              buf = (uint8 *) HDmalloc((uint32) eltSize);
              if (buf == NULL)
                {
                  DFdifree(GroupID);
                  HGOTO_ERROR(DFE_NOSPACE, FAIL);
                }

              /* read fill value into buffer */
              if (Hgetelement(file_id, elmt.tag, elmt.ref, buf) == FAIL)
                {
                  DFdifree(GroupID);
                  HGOTO_ERROR(DFE_GETELEM, FAIL);
                }

              /* convert the fill value  */
              DFKconvert((VOIDP) buf, (VOIDP) sdg->fill_value,
                         numtype, 1, DFACC_READ, 0, 0);

              HDfree((VOIDP) buf);
            }
          break;

        case DFTAG_SDT:
          FileTranspose = 1;
          break;
        default:
          if ((elmt.tag <= DFTAG_BREQ) && (elmt.tag >= DFTAG_EREQ))
	        {
	          DFdifree(GroupID);
              HGOTO_ERROR(DFE_BADNDG, FAIL);
            }
          break;
        }
    }

  /* since the dataset exists, the fill value cannot be changed */
  sdg->fill_fixed = TRUE;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

 /*---------------------------------------------------------------------------*
 * Name:    DFSDIputndg
 * Purpose: Write NDG out to HDF file
 * Inputs:  file_id: HDF file pointer
 *          ref: ref to put NDG with
 *          sdg: struct containing NDG info to put
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   HDF programmers, utilities, DFSDputdata, other routines
 * Invokes: DFIcheck, DFdistart, DFdiadd, DFdiend, DFputelement, DFaccess,
 *          DFwrite
 * Remarks: Writes out NTs
 *---------------------------------------------------------------------------*/
static intn
DFSDIputndg(int32 file_id, uint16 ref, DFSsdg * sdg)
{
  int32       i;
  intn        j;
  intn        luf;
  intn        issdg = 0;      /* issdg=1 if it is NDG SDG  */
  intn        len;
  uint16      luftag;
  uint8      *buf;
  uint8      *Isscales = NULL;
  uint8      *bufp;
  uint8       ntstring[4];
  uint8       platnumsubclass;
  uint8       outNT;          /* file number type subclass */
  int32       GroupID;
  int32       numtype;        /* current number type  */
  int32       fileNTsize;     /* size of this NT as it will be in the file */
  int32       scaleNTsize;    /* size of scale NT as it will be in the file */
  int32       aid;
  DFdi        nt;
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDIputndg");

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (!HDvalidfid(file_id))
    HGOTO_ERROR(DFE_BADCALL, FAIL);
  if (!ref)
    HGOTO_ERROR(DFE_BADREF, FAIL);

  /* Check if temproray buffer has been allocated */
  if (ptbuf == NULL)
    {
      ptbuf = (uint8 *) HDmalloc(TBUF_SZ * sizeof(uint8));
      if (ptbuf == NULL)
        HGOTO_ERROR(DFE_NOSPACE, FAIL);
    }

  /* set number type and subclass     */
  if (sdg->numbertype == DFNT_NONE)
    DFSDsetNT(DFNT_FLOAT32);    /* default is float32  */
  numtype = sdg->numbertype;
  fileNTsize = DFKNTsize(numtype);
  scaleNTsize = fileNTsize;   /* for now, assume same. MAY CHANGE */
  outNT = sdg->filenumsubclass;
  platnumsubclass = (uint8)DFKgetPNSC(numtype, (int32)DF_MT);

  /* prepare to start writing ndg   */
  if ((GroupID = DFdisetup(10)) < 0)
    HGOTO_ERROR(DFE_GROUPSETUP, FAIL);

  /* put ND and ref       */
  if (DFdiput(GroupID, sdg->data.tag, sdg->data.ref) < 0)
    HGOTO_ERROR(DFE_PUTGROUP, FAIL);

  if (Ref.nt <= 0)
    {     /* will not execute if has been written in putsdg  */
      /* construct and write out NT */
      ntstring[0] = DFNT_VERSION;   /* version */
      ntstring[1] = (uint8) (numtype & 0xff);   /* type */
      ntstring[2] = (uint8) (fileNTsize * 8);   /* width of number type in bits */
      ntstring[3] = outNT;  /* class: IEEE or machine class */
      if (Hputelement(file_id, DFTAG_NT, ref, ntstring, (int32) 4) == FAIL)
        HGOTO_ERROR(DFE_PUTELEM, FAIL);
      Ref.nt = (intn)ref;
    }

  /* write out NDD (dimension record) */
  if (Ref.dims <= 0)
    { /* new NDD; write rank, dims, data NT and scale NTs */
      /* put rank & dimensions in buffer */
      bufp = ptbuf;
      UINT16ENCODE(bufp, sdg->rank);
      for (i = 0; i < sdg->rank; i++)
        INT32ENCODE(bufp, sdg->dimsizes[i]);

      /* put data NT and scale NTs  in buffer */
      nt.tag = DFTAG_NT;
      nt.ref = (uint16) Ref.nt;     /* same NT for scales too */

      /* "<=" used to put 1 data NT + rank scale NTs in buffer */
      for (i = 0; i <= sdg->rank; i++)
        {   /* scale NTs written even if no scale! */
          UINT16ENCODE(bufp, nt.tag);
          UINT16ENCODE(bufp, nt.ref);
        }
      /* write out NDD record */
      if ( Hputelement(file_id, DFTAG_SDD, ref, ptbuf, (int32) (bufp - ptbuf)) == FAIL)
        HGOTO_ERROR(DFE_PUTELEM, FAIL);
      Ref.dims = (intn)ref;
    }
  /* write dimension record tag/ref */
  if (DFdiput(GroupID, DFTAG_SDD, (uint16) Ref.dims) < 0)
    HGOTO_ERROR(DFE_PUTGROUP, FAIL);

  /* write out label/unit/format */
  for (luf = LABEL; luf <= FORMAT; luf++)
    {
      luftag = (uint16) ((luf == LABEL) ? DFTAG_SDL :
                         (luf == UNIT) ? DFTAG_SDU : DFTAG_SDF);
      bufp = ptbuf;
      /* this block of code checks if luf is NULL, else writes it */
      if (!Ref.luf[luf])
        {   /* if luf was set */
          Ref.luf[luf] = -1;  /* assume it is NULL */

          /* if dataluf non-NULL, set up to write */
          if (sdg->dataluf[luf] && sdg->dataluf[luf][0])
            {
              HDstrcpy((char *) bufp, sdg->dataluf[luf]);
              bufp += HDstrlen(bufp) + 1;
            }
          else
            {     /* dataluf NULL */
              *bufp++ = '\0';
            }

          /* for each dimluf, if non-NULL, set up to write */
          for (i = 0; i < sdg->rank; i++)
            {
              if (sdg->dimluf[luf] && sdg->dimluf[luf][i]
                  && sdg->dimluf[luf][i][0])
                {   /* dimluf not NULL */
                  HDstrcpy((char *) bufp, sdg->dimluf[luf][i]);
                  bufp += HDstrlen(bufp) + 1;
                }
              else
                {   /* dimluf NULL */
                  *bufp++ = '\0';
                }
            }     /* i loop   */
          Ref.luf[luf] = (intn)ref;     /* remember ref */
          if ( Hputelement(file_id, luftag, (uint16) Ref.luf[luf], ptbuf, (int32) (bufp - ptbuf)) == FAIL)
            HGOTO_ERROR(DFE_PUTELEM, FAIL);
        }   /* luf was set */

      /* write luf tag/ref */
      if (Ref.luf[luf] > 0)
        {
          if (DFdiput(GroupID, luftag, (uint16) Ref.luf[luf]) < 0)
            HGOTO_ERROR(DFE_PUTGROUP, FAIL);
        }
    }     /* luf loop     */

  /* check if there is a scale and write it out */
  if (!Ref.scales)
    {     /* if scale set */
      Isscales = (uint8 *) HDmalloc((uint32) sdg->rank);
      if (Isscales == NULL)
        HGOTO_ERROR(DFE_NOSPACE, FAIL);
      Ref.scales = (-1);    /* assume there is no scale */

      /* set up Isscales array */
      for (i = 0; i < sdg->rank; i++)
        {
          if (sdg->dimscales && sdg->dimscales[i])
            {     /* a scale exists */
              Isscales[i] = 1;
              Ref.scales = 0;   /* flag: write out scales */
            }
          else
            Isscales[i] = 0;
        }
    }

  if (!Ref.scales)
    { /* write out scales */
      /* compute space needed for scales */
      len = 0;
      for (i = 0; i < sdg->rank; i++)
        {
          if (Isscales[i] == 1)
            len += (intn) (sdg->dimsizes[i] * scaleNTsize);
        }
      len += sdg->rank;

      aid = Hstartwrite(file_id, DFTAG_SDS, ref, len);
      if (aid == FAIL)
        {
          HDfree((VOIDP) Isscales);
          HGOTO_ERROR(DFE_BADAID, FAIL);
        }

      /* write Isscales */
      if (Hwrite(aid, (int32) sdg->rank, Isscales) == FAIL)
        {
          HDfree((VOIDP) Isscales);
          HGOTO_ERROR(DFE_WRITEERROR, FAIL);
        }

      /* Write scales */
      for (j = 0; j < sdg->rank; j++)
        {
          if (!Isscales[j])
            continue;
          if (platnumsubclass == outNT)
            {     /* no conversion needed */
              if (Hwrite(aid, (int32) (fileNTsize * sdg->dimsizes[j]),
                         (uint8 *) sdg->dimscales[j]) == FAIL)
                {
                  HDfree((VOIDP) Isscales);
                  HGOTO_ERROR(DFE_WRITEERROR, FAIL);
                }
            }
          else
            {     /* convert and write */
              /* allocate buffer */
              buf = (uint8 *) HDmalloc((uint32) (fileNTsize * sdg->dimsizes[j]));
              if (buf == NULL)
                {
                  HDfree((VOIDP) Isscales);
                  HGOTO_ERROR(DFE_NOSPACE, FAIL);
                }
              /* convert, all at once */
              DFKconvert((VOIDP) sdg->dimscales[j], (VOIDP) buf, numtype,
                         sdg->dimsizes[j], DFACC_WRITE, 0, 0);
              /* write it all out */
              if (Hwrite(aid, (int32) (fileNTsize * sdg->dimsizes[j]), buf)
                  == FAIL)
                {
                  HDfree((VOIDP) Isscales);
                  HDfree((VOIDP) buf);
                  HGOTO_ERROR(DFE_WRITEERROR, FAIL);
                }
              HDfree((VOIDP) buf);
            }
        }

      Ref.scales = (intn)ref;
      Hendaccess(aid);
    }
  if (Isscales != NULL)
     HDfree((VOIDP) Isscales);
  Isscales = NULL;
  if (Ref.scales > 0)
    if (DFdiput(GroupID, DFTAG_SDS, (uint16) Ref.scales) < 0)
      HGOTO_ERROR(DFE_PUTGROUP, FAIL);

  /* write coordsys */
  if (!sdg->coordsys || !sdg->coordsys[0])
    Ref.coordsys = (-1);
  if (!Ref.coordsys)
    {
      if ( Hputelement(file_id, DFTAG_SDC, ref, (uint8 *) sdg->coordsys, (int32) (HDstrlen(sdg->coordsys) + 1)) == FAIL)
          HGOTO_ERROR(DFE_PUTELEM, FAIL);
      Ref.coordsys = (intn)ref;
    }
  if (Ref.coordsys > 0)
    {
      if (DFdiput(GroupID, DFTAG_SDC, (uint16) Ref.coordsys) < 0)
          HGOTO_ERROR(DFE_PUTGROUP, FAIL);
    }

  /* write max/min */
  if (!Ref.maxmin)
    {
      if (platnumsubclass == outNT)
        {   /* no conversion */
          if ( Hputelement(file_id, DFTAG_SDM, ref, (uint8 *) &(sdg->max_min[0]), (int32) (2 * fileNTsize)) == FAIL)
              HGOTO_ERROR(DFE_PUTELEM, FAIL);
          Ref.maxmin = (intn)ref;
        }
      else
        {
          /* allocate buffer */
          buf = (uint8 *) HDmalloc((size_t) (2 * fileNTsize));    /* max/min is 8 bytes */
          if (buf == NULL)
              HGOTO_ERROR(DFE_NOSPACE, FAIL);

          /* convert */
          DFKconvert((VOIDP) &(sdg->max_min[0]), (VOIDP) buf,
                     numtype, 2, DFACC_WRITE, 0, 0);

          /* write */
          if ( Hputelement(file_id, DFTAG_SDM, ref, buf, (int32) (2 * fileNTsize)) == FAIL)
            {
              HDfree((VOIDP) buf);
              HGOTO_ERROR(DFE_PUTELEM, FAIL);
            }

          Ref.maxmin = (intn)ref;
          HDfree((VOIDP) buf);
        }
    }
  if (Ref.maxmin > 0)
    {
      if (DFdiput(GroupID, DFTAG_SDM, (uint16) Ref.maxmin) < 0)
          HGOTO_ERROR(DFE_PUTGROUP, FAIL);
    }
  Ref.maxmin = (-1);  /* max/min should be reset for each data set */

    /* Write calibration. */
  if (!Ref.cal)
    {
      if (platnumsubclass == outNT)
        {   /* no conversion */
          if (Hputelement(file_id, DFTAG_CAL, ref,
                          (unsigned char *) &sdg->cal,
                          (int32) 36) < 0)
              HGOTO_ERROR(DFE_PUTELEM, FAIL);
          Ref.cal = (intn)ref;
        }
      else
        {
          /* allocate buffer */
          uint8       buf2[4 * sizeof(float64) + sizeof(int32)];

          /* convert doubles */
          DFKconvert((VOIDP) &sdg->cal, (VOIDP) buf2,
                     DFNT_FLOAT64, 4, DFACC_WRITE, 0, 0);

          /* convert int */
          DFKconvert((VOIDP) &sdg->cal_type, (VOIDP) (buf2 + 32),
                     DFNT_INT32, 1, DFACC_WRITE, 0, 0);

          /* write it into the file */
          if (Hputelement(file_id, DFTAG_CAL, ref,
                          (unsigned char *) buf2, (int32) 36) < 0)
              HGOTO_ERROR(DFE_PUTELEM, FAIL);
          Ref.cal = (intn)ref;

        }
    }

  if (Ref.cal > 0)
    {
      if (DFdiput(GroupID, DFTAG_CAL, (uint16) Ref.cal) < 0)
          HGOTO_ERROR(DFE_PUTGROUP, FAIL);
    }
  Ref.cal = (-1);     /* Calibration should be reset for each data set */

    /* Write fill value.  */
  if (!Ref.fill_value)
    {
      if (platnumsubclass == outNT)
        {   /* No conversion  */
          if (Hputelement(file_id, DFTAG_FV, ref,
                          (unsigned char *) sdg->fill_value,
                          (int32) fileNTsize) == FAIL)
              HGOTO_ERROR(DFE_PUTELEM, FAIL);
          Ref.fill_value = (intn)ref;
        }
      else
        {
          /* Allocate buffer  */
          uint8       buf2[DFSD_MAXFILL_LEN];

          /* Convert from native to IEEE  */
          DFKconvert((VOIDP) sdg->fill_value, (VOIDP) buf2,
                     numtype, 1, DFACC_WRITE, 0, 0);

          /* Write it into the file  */
          if (Hputelement(file_id, DFTAG_FV, ref,
                          (unsigned char *) buf2,
                          (int32) fileNTsize) == FAIL)
              HGOTO_ERROR(DFE_PUTELEM, FAIL);

          Ref.fill_value = (intn)ref;
        }
    }

  /* Check to add to DFgroup  */
  if (Ref.fill_value > 0)
    {
      if (DFdiput(GroupID, DFTAG_FV, (uint16) Ref.fill_value) == FAIL)
          HGOTO_ERROR(DFE_PUTGROUP, FAIL);
    }
  Ref.fill_value = (-1);  /* Fill value should be reset for each data set  */

  if (!Ref.transpose)
    {     /* if transposed, add transpose tag */
      if (Hdupdd(file_id, DFTAG_SDT, ref, DFTAG_SDD, ref) == FAIL)
          HGOTO_ERROR(DFE_DUPDD, FAIL);
      Ref.transpose = (intn)ref;
    }
  if (Ref.transpose > 0)
    {
      if (DFdiput(GroupID, DFTAG_SDT, (uint16) Ref.transpose) < 0)
          HGOTO_ERROR(DFE_PUTGROUP, FAIL);
    }

  if (numtype == DFNT_FLOAT32)
    {     /* if float32, add a DFTAG_SDLNK   */
      DFdi        lnkdd[2];

      issdg = 1;
      lnkdd[0].tag = DFTAG_NDG;
      lnkdd[0].ref = ref;
      lnkdd[1].tag = DFTAG_SDG;
      lnkdd[1].ref = ref;
      bufp = ptbuf;

      for (i = 0; i < 2; i++)
        {
          UINT16ENCODE(bufp, lnkdd[i].tag);
          UINT16ENCODE(bufp, lnkdd[i].ref);
        }
      if ( Hputelement(file_id, DFTAG_SDLNK, ref, ptbuf, (int32) (bufp - ptbuf)) == FAIL)
          HGOTO_ERROR(DFE_PUTELEM, FAIL);

      /* write DFTAG_SDLNK  */
      if (DFdiput(GroupID, DFTAG_SDLNK, ref) < 0)
          HGOTO_ERROR(DFE_PUTGROUP, FAIL);
    }

  /* write out NDG */
  if (DFdiwrite(file_id, GroupID, DFTAG_NDG, ref) < 0)
      HGOTO_ERROR(DFE_GROUPWRITE, FAIL);

  /* write an SDG point to the dataset if it is an NDG SDG  */
  if (issdg)
    {
      if (Hdupdd(file_id, DFTAG_SDG, ref, DFTAG_NDG, ref) == FAIL)
          HCLOSE_GOTO_ERROR(file_id,DFE_DUPDD, FAIL);
    }

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 * Name:    DFSDIendslice
 * Purpose: Write of data to SDG completed, write SDG and close file
 * Inputs:  isfortran: true if called from Fortran
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   DFSDIputdata
 * Invokes: DFSDputsdg, Hclose, HERROR
 * Method:  call DFSDputsdg, close Sfile_id
 * Remarks: checks that slice writes were completed.
 *---------------------------------------------------------------------------*/
intn
DFSDIendslice(intn isfortran)
{
  intn        i;
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDIendslice");

  HEclear();

  if (Sfile_id == DF_NOFILE)
    HGOTO_ERROR(DFE_BADCALL, FAIL);

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  /* check if slice writes complete */
  for (i = 0; i < Writesdg.rank; i++)
    {
      if (!Fortorder && (i == 0) && (Sddims[i] == Writesdg.dimsizes[i]))
        continue;
      if ((isfortran || Fortorder) && (i == Writesdg.rank - 1)
          && (Sddims[i] == Writesdg.dimsizes[i]))
        continue;
      if ((isfortran || Fortorder || i > 0)
          && (!Fortorder || i < Writesdg.rank - 1) && (Sddims[i] == 0))
        continue;

      HGOTO_ERROR(DFE_BADCALL, FAIL);
    }

  if (DFSDIputndg(Sfile_id, Writeref, &Writesdg) < 0)
      HCLOSE_GOTO_ERROR(Sfile_id,DFE_INTERNAL,FAIL);

  /* old nsdg table should be reset next time  */
  if (nsdghdr != NULL)
    {
      if (nsdghdr->nsdg_t != NULL)
        {
          DFnsdgle   *rear, *front;

          rear = nsdghdr->nsdg_t;
          front = rear->next;
          while (rear != NULL)
            {
              HDfreenclear(rear);
              rear = front;
              if (rear != NULL)
                front = rear->next;
            }
          nsdghdr->size = 0;
          nsdghdr->nsdg_t = NULL;
          lastnsdg.tag = DFTAG_NULL;
          lastnsdg.ref = 0;
        }
      HDfreenclear(nsdghdr);
    }

  Lastref = Writeref;     /* remember ref written */
  Writeref = 0;   /* don't know ref to write next */

  Hendaccess(Writesdg.aid);
  ret_value = Hclose(Sfile_id);
  Sfile_id = 0;   /* partial write complete */
  HDfreenclear(Sddims);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/******************************************************************************/
/*----------------------- Internal routines ---------------------------------*/
/******************************************************************************/

/*-----------------------------------------------------------------------------
 * Name:    DFSDIopen
 * Purpose: open or reopen a file
 * Inputs:  filename: name of file to open
 *          acc_mode : access mode
 * Returns: file id on success, -1 (FAIL) on failure with error set
 * Users:   HDF systems programmers, many SD routines
 * Invokes: DFopen
 * Remarks: This is a hook for someday providing more efficient ways to
 *          reopen a file, to avoid re-reading all the headers
 *---------------------------------------------------------------------------*/
int32
DFSDIopen(const char *filename, intn acc_mode)
{
  int32       file_id;
  int32       ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDIopen");

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Sfile_id != DF_NOFILE)  /* in the middle of a partial write */
    HGOTO_ERROR(DFE_ALROPEN, FAIL);

    /* if last filename buffer has not been allocated
     *    allocate buffer for "Lastfile" and open file
     * else if same file as last time
     *    use reopen - more efficient
     *  else
     *    open file for first time
     */
  if (Lastfile == NULL)
    {
      Lastfile = (char *) HDmalloc((DF_MAXFNLEN + 1) * sizeof(char));
      if (Lastfile == NULL)
        HGOTO_ERROR(DFE_NOSPACE, FAIL);
      /* open file */
      if (( file_id = Hopen(filename, acc_mode, (int16) 0)) == FAIL)
        HGOTO_ERROR(DFE_BADOPEN, FAIL);
    }
  else if ((HDstrcmp(Lastfile, filename)) || (acc_mode == DFACC_CREATE))
    {     /* open a new file, delete nsdg table and reset lastnsdg  */
      if (nsdghdr != NULL)
        {
          if (nsdghdr->nsdg_t != NULL)
            {
              DFnsdgle   *rear, *front;

              rear = nsdghdr->nsdg_t;
              while (rear != NULL)
                {
                  front = rear->next;
                  HDfreenclear(rear);
                  rear = front;
                }
              nsdghdr->size = 0;
              nsdghdr->nsdg_t = NULL;
              lastnsdg.tag = DFTAG_NULL;
              lastnsdg.ref = 0;
            }
          HDfreenclear(nsdghdr);
        }

      /* treat create as different file */
      if (( file_id = Hopen(filename, acc_mode, (int16) 0))== FAIL)
        HGOTO_ERROR(DFE_BADOPEN, FAIL);
      Newdata = (-1);   /* data in Readsdg is not fresh */
      Readsdg.data.ref = 0;     /* No SDG read yet */

      /* remember no info written to file */
      Ref.scales = (Ref.scales >= 0) ? 0 : Ref.scales;
      Ref.luf[LABEL] = (Ref.luf[LABEL] >= 0) ? 0 : Ref.luf[LABEL];
      Ref.luf[UNIT] = (Ref.luf[UNIT] >= 0) ? 0 : Ref.luf[UNIT];
      Ref.luf[FORMAT] = (Ref.luf[FORMAT] >= 0) ? 0 : Ref.luf[FORMAT];
      Ref.dims = (Ref.dims >= 0) ? 0 : Ref.dims;
      Ref.coordsys = (Ref.coordsys >= 0) ? 0 : Ref.coordsys;
      Ref.maxmin = (Ref.maxmin >= 0) ? 0 : Ref.maxmin;
      Ref.nt = (Ref.nt >= 0) ? 0 : Ref.nt;
      Ref.transpose = (Ref.transpose >= 0) ? 0 : Ref.transpose;
    }
  else
    {
      if (( file_id = Hopen(filename, acc_mode, (int16) 0))== FAIL)
        HGOTO_ERROR(DFE_BADOPEN, FAIL);
    }

  /* if read, set up nsdg table */
  if (nsdghdr == NULL)
    {
      nsdghdr = (DFnsdg_t_hdr *) HDmalloc((uint32) sizeof(DFnsdg_t_hdr));
      if (nsdghdr == NULL)
        HGOTO_ERROR(DFE_NOSPACE, FAIL);
      nsdghdr->size = 0;
      nsdghdr->nsdg_t = NULL;
    }
  if ((nsdghdr->nsdg_t == NULL) && (acc_mode == DFACC_READ))
    {
      if (DFSDIsetnsdg_t(file_id, nsdghdr) < 0)
        HGOTO_ERROR(DFE_INTERNAL, FAIL);
      lastnsdg.tag = DFTAG_NULL;
      lastnsdg.ref = 0;
    }

  HIstrncpy(Lastfile, filename, DF_MAXFNLEN);
  /* remember filename, so reopen may be used next time if same file */

  ret_value = file_id;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 * Name:    DFSDIsdginfo
 * Purpose: Locates next sdg in file
 * Inputs:  file_id: pointer to DF file
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   HDF systems programmers, DFSDgetdims, DFSDgetdata
 * Invokes: DFIfind, DFSDIgetndg
 * Method:  Call DFIfind to find SDG, then DFSDIgetndg to read it in to Readsdg
 * Remarks: none
 *---------------------------------------------------------------------------*/
intn
DFSDIsdginfo(int32 file_id)
{
  DFdi        ptr;
  CONSTR(FUNC, "DFSDIsdginfo");
  int32       aid;
  intn        ret_value = SUCCEED;

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (!HDvalidfid(file_id))
    HGOTO_ERROR(DFE_BADCALL, FAIL);

  if (Readref != 0)
    {
      aid = Hstartread(file_id, DFTAG_NDG, Readref);
      if (aid != FAIL)
        {
          ptr.ref = Readref;
          ptr.tag = DFTAG_NDG;
          Hendaccess(aid);
        }
      else
        {
          aid = Hstartread(file_id, DFTAG_SDG, Readref);
          if (aid != FAIL)
            {
              ptr.ref = Readref;
              ptr.tag = DFTAG_SDG;
              Hendaccess(aid);
            }
          else
            HGOTO_ERROR(DFE_BADAID, FAIL);
        }
    }
  else
    {
      if (DFSDInextnsdg(nsdghdr, &ptr) < 0)
        HGOTO_ERROR(DFE_INTERNAL, FAIL);
      if ((ptr.tag != DFTAG_NDG) && (ptr.tag != DFTAG_SDG))
        HGOTO_ERROR(DFE_BADTAG, FAIL);
      if (ptr.ref == DFREF_WILDCARD)
        HGOTO_ERROR(DFE_BADREF, FAIL);
      Readref = ptr.ref;
    }

  /* find next sd object */
  if (DFSDIgetndg(file_id, ptr.tag, ptr.ref, &Readsdg) < 0)
        HGOTO_ERROR(DFE_INTERNAL, FAIL);

  /* remember what type of thing we just read */
  Readsdg.isndg = (ptr.tag == DFTAG_NDG) ? 1 : 0;

  Lastref = ptr.ref;  /* remember ref read */
  lastnsdg.tag = ptr.tag;
  lastnsdg.ref = ptr.ref;

  Newdata = 1;    /* now Readsdg is fresh */
  Readref = 0;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 * Name:    DFSDIrefresh
 * Purpose: get next sdg if Readsdg is not fresh
 * Inputs:  filename
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   HDF systems programmers, functions in dfsdF.c
 * Invokes: DFSDIopen, DFSDIsdginfo
 * Method:  test Newdata and Nextsdg, call DFSDIsdginfo if necessary
 * Remarks: none
 *---------------------------------------------------------------------------*/
intn
DFSDIrefresh(char *filename)
{
  int32       file_id;
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDIrefresh");

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Newdata != 1 || Nextsdg)
    {     /* if Readsdg not fresh  */
      if (( file_id = DFSDIopen(filename, DFACC_READ))== FAIL)
            HGOTO_ERROR(DFE_BADOPEN, FAIL);
      if (DFSDIsdginfo(file_id) < 0)
        HCLOSE_GOTO_ERROR(file_id,DFE_INTERNAL, FAIL);
      if (Hclose(file_id) < 0)
        HGOTO_ERROR(DFE_CANTCLOSE, FAIL);
      Nextsdg = 0;
    }

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 * Name:    DFSDIisndg
 * Purpose: is the current read sds an sdg or nsdg/ndg
 * Inputs:  isndg: 0 -- pure sdg( written by 3.1); 1 -- nsdg/ndg
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   HDF systems programmers, functions in dfsdF.c
 * Invokes: none
 * Method:  Assigns Readsdg.isndg to isndg.
 * Remarks: none
 *---------------------------------------------------------------------------*/
intn
DFSDIisndg(intn *isndg)
{
  intn ret_value = SUCCEED;

  *isndg = (intn) Readsdg.isndg;

  return ret_value;
}

/*-----------------------------------------------------------------------------
 * Name:    DFSDIgetrrank
 * Purpose: get rank of the current sdg, to transpose dims for Fortran
 * Inputs:  &rank: address to return the rank
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   HDF systems programmers, functions in dfsdF.c
 * Invokes: none
 * Method:  Assigns Readsdg.rank to rank.
 * Remarks: none
 *---------------------------------------------------------------------------*/
intn
DFSDIgetrrank(intn *rank)
{
  intn  ret_value = SUCCEED;

  *rank = (intn) Readsdg.rank;

  return ret_value;
}

/*-----------------------------------------------------------------------------
 * Name:    DFSDIgetwrank
 * Purpose: get rank of the current sdg, to transpose dims for Fortran
 * Inputs:  &rank: address to return the rank
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   HDF systems programmers, functions in dfsdF.c
 * Invokes: none
 * Method:  Assigns Readsdg.rank to rank.
 * Remarks: none
 *---------------------------------------------------------------------------*/
intn
DFSDIgetwrank(intn *rank)
{
  intn ret_value = SUCCEED;

  *rank = (intn) Writesdg.rank;

  return ret_value;
}

/*-----------------------------------------------------------------------------
 * Name:    DFSDIclear
 * Purpose: Reset all "set" values, free allocated space
 * Inputs:  sdg: pointer to sdg struct to clear
 * Globals: Ref
 * Returns: 0 on success, FAIL on error with error set
 * Users:   HDF users, utilities, other routines
 * Invokes: none
 * Method:  Release space in sdg
 * Remarks: none
 *---------------------------------------------------------------------------*/
intn
DFSDIclear(DFSsdg * sdg)
{
  intn        i;
  intn        luf;
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDIclear");

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Sfile_id != DF_NOFILE)  /* cannot clear during slice writes */
    HGOTO_ERROR(DFE_BADCALL, FAIL);

  HDfreenclear(sdg->dimsizes);
  HDfreenclear(sdg->coordsys);

    /* free label/unit/format pointers */
  for (luf = LABEL; luf <= FORMAT; luf++)
    {
      if (sdg->dimluf[luf])
        {   /* free strings */
          for (i = 0; i < sdg->rank; i++)
            HDfreenclear(sdg->dimluf[luf][i]);
        }

      /* free string pointers */
      HDfreenclear(sdg->dimluf[luf]);

      /* free data string */
      HDfreenclear(sdg->dataluf[luf]);
    }

  /* free scale pointers */
  if (sdg->dimscales)
    {
      for (i = 0; i < sdg->rank; i++)
        HDfreenclear(sdg->dimscales[i]);
    }

  /* free array of scale pointers */
  HDfreenclear(sdg->dimscales);
  sdg->rank = 0;

    /* number type is independant to dimsizes   4/7/92  sxu
       sdg->numbertype = DFNT_NONE;
       sdg->filenumsubclass = DFNTF_NONE;
     */
  sdg->aid = (int32) -1;
  sdg->compression = (int32) 0;
  FileTranspose = 0;
  sdg->fill_fixed = FALSE;    /* allow fill_value to be changed */

  Ref.dims = -1;
  Ref.scales = Ref.luf[LABEL] = Ref.luf[UNIT] = Ref.luf[FORMAT] = (-1);
  Ref.coordsys = Ref.maxmin = (-1);
  Ref.new_ndg = -1;
  Ref.fill_value = -1;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 * Name:    DFSDIgetdata
 * Purpose: Get data from SDG.  Will sequence to next SDG if DFSDgetdims not
 *          called.
 * Inputs:  filename: name of HDF file to use
 *          rank: no of dimensions of array "data"
 *          maxsizes: actual dimensions of array "data"
 *          data: data for returning scientific data
 *          isfortran : 0 if called from C, 1 when called from FORTRAN
 * Returns: 0 on success, FAIL on failure with error set
 * Outputs: actual scientific data in array
 * Users:   DFSDgetdata
 * Invokes: DFSDIgetslice, HDmalloc, HDfree, DFSDIopen, Hclose,
 *          HERROR, DFSDIsdginfo
 * Method:  Open file, call DFSDIsdginfo to read sdg if necessary, set up
 *          window start and end arrays, call DFSDIgetslice.
 * Remarks: maxsizes may be larger than actual size.  In that event, the actual
 *          data may not be contiguous in the array "data"
 *          User sets maxsizes before call.
 *---------------------------------------------------------------------------*/
intn
DFSDIgetdata(const char *filename, intn rank, int32 maxsizes[], VOIDP data,
             intn isfortran)
{
  intn        i;
  int32      *winst;
  int32      *windims;
  int32       file_id;
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDIgetdata");

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Newdata != 1 || Nextsdg)
    {     /* if Readsdg not fresh */
      if (( file_id = DFSDIopen(filename, DFACC_READ))== DF_NOFILE)
        HGOTO_ERROR(DFE_BADOPEN, FAIL);
      if (DFSDIsdginfo(file_id) < 0)
        HCLOSE_GOTO_ERROR(file_id,DFE_INTERNAL, FAIL);
      if (Hclose(file_id) == FAIL)
        HGOTO_ERROR(DFE_CANTCLOSE, FAIL);
    }

  winst = (int32 *) HDmalloc((uint32) Readsdg.rank * sizeof(int32));
  if (winst == NULL)
    HGOTO_ERROR(DFE_NOSPACE,FAIL);

  windims = (int32 *) HDmalloc((uint32) Readsdg.rank * sizeof(int32));
  if (windims == NULL)
    {
      HDfree((VOIDP) winst);
      HGOTO_ERROR(DFE_NOSPACE,FAIL);
    }

  for (i = 0; i < rank; i++)
    {
      winst[i] = 1;
      windims[i] = Readsdg.dimsizes[i];
    }

  ret_value = DFSDIgetslice(filename, winst, windims, data, maxsizes, isfortran);
  Nextsdg = 1;
  HDfree((VOIDP) winst);
  HDfree((VOIDP) windims);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 * Name:    DFSDIputdata
 * Purpose: Writes entire SDG to file
 * Inputs:  filename: name of HDF file to use
 *          rank: rank of data array
 *          dimsizes: sizes of the dimensions of data array
 *          data: array that holds data
 *          accmode: 0 if write to new file, 1 if append to file
 *          isfortran: 0 if C, 1 if FORTRAN
 * Globals: Writeref
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   HDF users, utilities, other routines
 * Invokes: DFSDIopen, Hclose, HDmalloc, HDfree, DFSDIputslice,
 *          DFSDstartslice, DFSDIendslice
 * Method:  Create file if necessary, allocate arrays, call slice routines
 *---------------------------------------------------------------------------*/
intn
DFSDIputdata(const char *filename, intn rank, int32 *dimsizes, VOIDP data,
             intn accmode, intn isfortran)
{
  int32       file_id;
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDIputdata");

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (!accmode)
    {     /* new file */
      if (( file_id = DFSDIopen(filename, DFACC_CREATE))== DF_NOFILE)
        HGOTO_ERROR(DFE_BADOPEN, FAIL);
      if (Hclose(file_id) == FAIL)
        HGOTO_ERROR(DFE_CANTCLOSE, FAIL);
    }

  if (Ref.dims)
    {     /* don't call setdims if already called */
      if (DFSDsetdims(rank, dimsizes) < 0)
        HGOTO_ERROR(DFE_INTERNAL, FAIL);
    }

  if (DFSDstartslice(filename) < 0)
    HGOTO_ERROR(DFE_INTERNAL, FAIL);

  if (DFSDIputslice(Writesdg.dimsizes, data, dimsizes, isfortran) < 0)
    HGOTO_ERROR(DFE_INTERNAL, FAIL);

  ret_value = DFSDIendslice(isfortran);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*----------------------------------------------------------------------------
 * Name:    DFSDIgetslice
 * Purpose: Get slice of data from SDG.  Will sequence to next SDG if
 *          DFSDgetdims, DFSDgetdata or DFSDgetslice not called earlier.
 * Inputs:  filename: name of HDF file to use
 *          winst: array of size = rank of data, containing start of slice
 *          windims: array of size rank, containing size of slice
 *          data: array for returning slice
 *          dims: dimensions of array data
 *          isfortran : 0 if called from C, 1 when called from FORTRAN
 * Returns: 0 on success, FAIL on failure with error set
 * Outputs: slice of data in data
 * Users:   DFSDIgetdata
 * Invokes: DFSDIopen, Hclose, HERROR, DFSDIsdginfo, DFaccess, DFread
 * Method:  Open file, call DFSDIsdginfo to read sdg if necessary, read the
 *          data, convert types if necessary, place in data as appropriate
 *          data is assumed column major for FORTRAN, row major for C
 * Remarks: dims may be larger than size of slice.  In that event, the actual
 *          data may not be contiguous in the array "data".
 *          User sets dims before call.
 *--------------------------------------------------------------------------*/

/*****************************************************************************/
/* DESIGN DECISIONS                                                          */
/*****************************************************************************/
/*
   A. All stride/index/offset value will, when this is done -- refer to
   element counts rather than byte counts in the name of consistency.

   B. The conversion buffers/allcated areas... will all be char buffers --
   providing that the Cray-2 is cooperative.
 */
/*****************************************************************************/

/*****************************************************************************/
/* CHANGE LOG                                                                */
/*****************************************************************************/
/*
   A.
 */
/*****************************************************************************/
intn
DFSDIgetslice(const char *filename, int32 winst[], int32 windims[],
              VOIDP data, int32 dims[], intn isfortran)
{
  intn        rank;           /* number of dimensions in data[] */
  int32       leastsig;       /* fastest varying subscript in the array */
  int32       error;          /* flag if an error occurred, */
                              /*  used by DFconvert macro */
  int32       convert;        /* true if machine NT != NT to be read */
  int32       transposed;     /* true if we must transpose the data before writing */
  int32       done;           /* true if we are at the end of the slice */
  int32       aid;
  int32       i, j;           /* temporary loop index */
  int32       issdg;          /* 1 -- pure sdg. do what HDF3.1 does   */
  int32      *wstart;         /* tmp array containing starting slice dims */
  int32      *wdims;          /* tmp array containing the slice size */
  int32      *adims;          /* tmp array containing the dimensions of data[] */
  int32      *fdims;          /* tmp array containing the dimensions */
                              /*  of the dataset in the file */
  int32       numtype;        /* current number type  */
  int32       fileNTsize;     /* size of this NT in the file  */
  int32       localNTsize;    /* size of this NT as it occurs in this machine */
  int32       numelements;    /* number of floats to read at once */
  int32       readsize;       /* number of bytes to read at once */
  int32       datastride;     /* number of floats in one row of data[] */
  int32      *offset;         /* array for accessing the next element in data[] */
  int32      *foffset;        /* array for accessing the next element in the file */
  int32      *dimsleft;       /* array for tracking the current position in data[] */
  int32       isnative;
  int32       fileoffset;     /* offset into the current dataset in the file */
  uint8       platnumsubclass;    /* class of this NT for this platform */
  uint8       fileNT;         /* file number subclass */
  uint8      *scatterbuf;     /* buffer to hold the current row contiguously */
  uint8      *sp;             /* ptr into scatterbuf      */
  uint8      *datap;          /* ptr into data[] at starting offset */
                              /* of current block */
  uint8      *dp;             /* ptr into data[] at an element of the current row */
  uint8      *buf;            /* buffer containing the converted current row */
  int32       file_id;        /* HDF file pointer */
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDIgetslice");

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (data == NULL)
    HGOTO_ERROR(DFE_BADPTR, FAIL);

  if (( file_id = DFSDIopen(filename, DFACC_READ))== DF_NOFILE)
    HGOTO_ERROR(DFE_BADOPEN, FAIL);

  if (Newdata != 1)
    {     /* if Readsdg not fresh */
      if (DFSDIsdginfo(file_id) < 0) /* reads next SDG from file */
        HCLOSE_GOTO_ERROR(file_id,DFE_INTERNAL, FAIL);
    }
  rank = Readsdg.rank;
  numtype = Readsdg.numbertype;
  fileNT = Readsdg.filenumsubclass;
  issdg = Readsdg.isndg ? 0 : 1;
  isnative = DFNT_NATIVE;
  localNTsize = DFKNTsize((numtype | isnative) & (~DFNT_LITEND));
  fileNTsize = DFKNTsize(numtype);
  platnumsubclass = (uint8)DFKgetPNSC(numtype & (~DFNT_LITEND), (int32)DF_MT);

  /* get dimensions of slice to extract, set nwindims. also err check */
  for (i = 0; i < (int32) rank; i++)
    {
      /* check validity for the dimension ranges */
      if ((windims[i] < 1) || (winst[i] < 1)
          || (winst[i] + windims[i] - 1 > Readsdg.dimsizes[i]))
        {
          HCLOSE_GOTO_ERROR(file_id, DFE_BADDIM, FAIL);
        }
      /* check if space allocated is sufficient */
      if (dims[i] < windims[i])
        {
          HCLOSE_GOTO_ERROR(file_id, DFE_NOTENOUGH, FAIL);
        }
    }
  /* allocate buffers */
  wstart = (int32 *) HDmalloc((size_t) (4 * rank) * sizeof(int32));
  if (wstart == NULL)
    {
      HCLOSE_GOTO_ERROR(file_id, DFE_NOSPACE, FAIL);
    }
  wdims = wstart + rank;
  adims = wdims + rank;
  fdims = adims + rank;

  /* copy arrays to private workspace (so that they are in row major order) */
  for (i = 0; i < (int32) rank; i++)
    {
      int32       ii = (issdg && isfortran) ? rank - i - 1 : i;

      adims[i] = dims[ii];
      ii = (issdg && FileTranspose) ? rank - i - 1 : i;
      wstart[i] = winst[ii] - 1;    /* translate to 0 origin */
      wdims[i] = windims[ii];
      fdims[i] = Readsdg.dimsizes[ii];
    }

  convert = (fileNT != platnumsubclass);  /* is conversion necessary */
  transposed = issdg && (isfortran ^ FileTranspose);  /* is transposition needed */

  /*
   * Note that if the data is transposed we must work on a row by row
   * basis and cannot collapse dimensions.
   */
  if (!transposed)
    {
      /* collapse dimensions if contiguous both in the file and in memory */
      for (i = (int32) rank - 1; i > 0; i--)
        {   /* stop before most sig dim */
          if (adims[i] > wdims[i]     /* not all of data[] will be filled */
              || wstart[i] != 0   /* reading only part of the dataset */
              || wdims[i] < fdims[i])
            {
              break;
            }
          wstart[i - 1] *= fdims[i];
          wdims[i - 1] *= wdims[i];
          adims[i - 1] *= adims[i];
          fdims[i - 1] *= fdims[i];
          rank--;
        }
    }
  leastsig = (int32) rank - 1;    /* which is least sig dim */

  /* position at start of data set */
  aid = Hstartread(file_id, Readsdg.data.tag, Readsdg.data.ref);
  if (aid == FAIL)
    {
      HDfree((VOIDP) wstart);
      HCLOSE_GOTO_ERROR(file_id,DFE_BADAID, FAIL);
    }

  error = 0;
  if (rank == 1 && !convert)
    {
      /* all data is contiguous with no conversions */
      readsize = adims[0] * fileNTsize;
      if ((Hseek(aid, wstart[0] * fileNTsize, 0) == FAIL)
          || (readsize != Hread(aid, readsize, (uint8 *) data)))
        {
          error = 1;
        }
    }
  else
    {
      /*
       * The data must be further manipulated.
       * It may be transposed, may need conversion, may not be contiguous, or
       * any combination of these.
       */
      numelements = wdims[leastsig];
      readsize = numelements * fileNTsize;

      /* allocate 1 row buffers */
      if (convert)
        {
          if ((buf = (uint8 *) HDmalloc((uint32) readsize)) == NULL)
            {
              HDfree((VOIDP) wstart);
              Hendaccess(aid);
              HCLOSE_GOTO_ERROR(file_id, DFE_NOSPACE, FAIL);
            }
        }
      else
        buf = NULL;

      if (transposed)
        {
          scatterbuf =
            (uint8 *) HDmalloc((size_t) (numelements * localNTsize));

          if (scatterbuf == NULL)
            {
              HDfree((VOIDP) wstart);
              HDfree((VOIDP) buf);
              Hendaccess(aid);
              HCLOSE_GOTO_ERROR(file_id, DFE_NOSPACE, FAIL);
            }
        }
      else
        scatterbuf = NULL;

      offset = (int32 *) HDmalloc((size_t) (3 * rank) * sizeof(int32));
      if (offset == NULL)
        {
          HDfree((VOIDP) wstart);
          HDfree((VOIDP) buf);
          HDfree((VOIDP) scatterbuf);
          Hendaccess(aid);
          HCLOSE_GOTO_ERROR(file_id, DFE_NOSPACE, FAIL);
        }
      foffset = offset + rank;
      dimsleft = foffset + rank;

      /* compute initial position in the data */
      for (i = leastsig; i >= 0; i--)
        dimsleft[i] = wdims[i];

      /* compute offsets in the source array */
      if (transposed)
        {
          offset[0] = 1 * localNTsize;
          for (i = 0; i < leastsig; i++)
            offset[i + 1] = offset[i] * adims[leastsig - i];
        }
      else
        {
          offset[leastsig] = 1 * localNTsize;
          for (i = leastsig; i > 0; i--)
            offset[i - 1] = offset[i] * adims[i];
        }
      datastride = offset[leastsig];

      /* compute offsets in the file */
      for (i = leastsig, foffset[i] = 1 * fileNTsize; i > 0; i--)
        foffset[i - 1] = foffset[i] * fdims[i];

       /*
        * Compute starting position in file
        * All file reads are done relative to this starting offset.
        * Cumulative offset is from most sig to next to least sig dim.
        */
      for (i = 0, fileoffset = 0; i < leastsig; i++)
        fileoffset = (fileoffset + wstart[i]) * fdims[i + 1];
      fileoffset += wstart[leastsig];   /* adjust for last dim */
      fileoffset *= fileNTsize;     /* convert to bytes */

      datap = (uint8 *) data;
      done = 0;
      /* -- now read in the data */
      do
        {
          /* move to the next data element in the file */
          if (Hseek(aid, fileoffset, 0) == FAIL)
            {
              error = 1;
              break;
            }

          /* read and convert one contiguous block of data */
          if (convert)
            {
              if (readsize != Hread(aid, readsize, buf))
                {
                  error = 1;
                  break;
                }
              DFKconvert((VOIDP) buf, transposed ? (VOIDP) scatterbuf :
                         (VOIDP) datap, numtype, numelements, DFACC_READ, 0, 0);
            }
          else
            {
              if (readsize != Hread(aid, readsize,
                                    transposed ? scatterbuf : datap))
                {
                  error = 1;
                  break;
                }
            }
          if (transposed)
            {
              /* scatter out the elements of one row */
#ifdef UNICOS
#pragma ivdep
#endif
              for (dp = datap, sp = scatterbuf, i = 0; i < numelements; i++)
                {
                  for (j = 0; j < localNTsize; j++)
                    *(dp + j) = *(sp + j);
                  sp += localNTsize;
                  dp += datastride;
                }
            }

          /*
           * Find starting place of the next row/block.
           * Note that all moves are relative:
           *   this preserves the starting offsets of each dimension
           */
          for (i = leastsig - 1; i >= 0; i--)
            {
              if (--dimsleft[i] > 0)
                {
                  /* move to next element in the current dimension */
                  datap += offset[i];
                  fileoffset += foffset[i];
                  break;
                }
              else
                {
                  dimsleft[i] = wdims[i];
                  /*
                   * Note that we are still positioned at the beginning of
                   * the last element in the current dimension
                   */
                  /* move back to the beginning of dimension i */
                  datap -= offset[i] * (wdims[i] - 1);
                  /* move back to beginning read position of dimension i */
                  fileoffset -= foffset[i] * (wdims[i] - 1);
                  if (i == 0)
                    done = 1;
                }
            }
        }
      while (!done && leastsig > 0);

      if (buf != NULL)
         HDfree((VOIDP) buf);
      if (scatterbuf != NULL)
         HDfree((VOIDP) scatterbuf);
      if (offset != NULL)
         HDfree((VOIDP) offset);
    }

  Hendaccess(aid);
  HDfree((VOIDP) wstart);
  if (error)
    {
      Hclose(file_id);
      ret_value = FAIL;
    }
  else
    ret_value = Hclose(file_id);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*----------------------------------------------------------------------------
 * Name:    DFSDIputslice
 * Purpose: Put slice of data to SDG.
 * Inputs:  windims: array of size rank, containing size of slice
 *          data: array containing slice
 *          dims: dimensions of array data
 *          isfortran: 0 for C, 1 for Fortran
 * Returns: 0 on success, FAIL on failure with error set
 * Users:   DFSDputslice
 * Invokes: DFwrite, HDmalloc, HDfree,DFKnumout(if conversion
        required)
 * Method:  check dimensions for contiguity, convert types if necessary
 *          write to file
 * Remarks: dims may be larger than size of slice.  In that event, the actual
 *          data may not be contiguous in the array "data".
 *          DFSDstartslice must have been called first
 *          If DFKnumout is called, DFSDsetNT may need to have been
 *      called
 *      Note, writes must be contiguous - successive calls to putslice
 *          must write out array consecutively, according to the setting
 *          of the Fortorder variable - row major if 0, column major if 1
 *--------------------------------------------------------------------------*/
intn
DFSDIputslice(int32 windims[], VOIDP data, int32 dims[], intn isfortran)
{
  intn        rank;           /* number of dimensions in data[] */
  int32       leastsig;       /* fastest varying subscript in the array */
  int32       convert;        /* true if machine NT = NT to be written */
  int32       contiguous;     /* true if there are no gaps in the data to be written */
  int32       numtype;        /* current number type */
  int32       platnumsubclass;    /* class of this NT for this platform */
  int32       fileNTsize;     /* size of this NT as it will be in the file */
  int32       fileNT;         /* class of NT for the data to write */
  int32       isnative;
  int32       localNTsize;    /* size of this NT as it occurs in theis machine */
  int32       ret = SUCCEED;  /* return code from DFwrite */
  int32       i, j;           /* temporaries */
  int32       numelements;    /* number of elements to write out per row */
  int32       writesize;      /* number of bytes to write out per row */
  int32       datastride;     /* number of bytes in one row of data[] */
  uint8      *datap;          /* pointer into data[] at */
                                /*  the start of the current row */
  uint8      *buf;            /* buffer containing converted current row */
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDIputslice");

  /* shut compiler up */
  isfortran = isfortran;

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (!data)
    HGOTO_ERROR(DFE_BADPTR, FAIL);

  if (Sfile_id == DF_NOFILE)
    HGOTO_ERROR(DFE_BADCALL, FAIL);

  rank = Writesdg.rank;

  for (i = 0; i < (int32) rank; i++)
    {
      /* check validity for the dimension ranges */
      if ((windims[i] <= 0) || (windims[i] > Writesdg.dimsizes[i]))
        HGOTO_ERROR(DFE_BADDIM, FAIL);

          /* check if space allocated is sufficient */
      if (dims[i] < windims[i])
        HGOTO_ERROR(DFE_NOTENOUGH, FAIL);
    }

  /* check to see if the slices fit together */
  /* Same for Fortran or C    */
  /* find the first significant dimension */
  for (i = 0; windims[i] == 1 && i < (int32) rank - 1; i++)
    /* empty */ ;
  /* check that all 'lesser' dims match */
  for (j = i + 1; j < (int32) rank; j++)
    {
      if (Writesdg.dimsizes[j] != windims[j])
        HGOTO_ERROR(DFE_BADDIM, FAIL);
    }

  /* update Sddims to reflect new write */
  Sddims[i] += windims[i];
  for (; i > 0 && Sddims[i] >= Writesdg.dimsizes[i]; i--)
    {
      Sddims[i - 1] += Sddims[i] / Writesdg.dimsizes[i];
      /* promote the unit */
      Sddims[i] %= Writesdg.dimsizes[i];
    }

  leastsig = (int32) rank - 1;    /* which is least sig dim */
  numtype = Writesdg.numbertype;

  /* get class of this num type for this platform */
  fileNT = (int32)Writesdg.filenumsubclass;
  isnative = DFNT_NATIVE;
  fileNTsize = DFKNTsize(numtype);
  localNTsize = DFKNTsize((numtype | isnative) & (~DFNT_LITEND));
  platnumsubclass = (int32)DFKgetPNSC(numtype & (~DFNT_LITEND), (int32)DF_MT);
  convert = (platnumsubclass != fileNT);

  contiguous = 1;
  for (i = 0; contiguous && i < (int32) rank; i++)
    {
      /* check if data at the end of the users array will be contiguous */
      if (dims[i] > Writesdg.dimsizes[i])
        contiguous = 0;
      /* Note: if a winstart[] array is ever added, will also need */
      /*      to check if data at start of users array will be */
      /*      contig                  */
    }

  /*
     *  2 Factors that determine how we write (in order of importance)
     *  conversion and contiguous
     */
  datap = (uint8 *) data;
  if (!convert && contiguous)
    {
      /* compute total number of elements to write */
      for (i = 0, numelements = 1; i < (int32) rank; i++)
        numelements *= windims[i];
      writesize = numelements * fileNTsize;

      if ( Hwrite(Writesdg.aid, writesize, (uint8 *) data) == FAIL)
            HCLOSE_GOTO_ERROR(Sfile_id,DFE_WRITEERROR, FAIL);
    }
  else
    {     /* must step through the data */
      /* compute number of occurrences of the least sig dim */
      if (Fortorder)
        {
          for (i = (int32) rank - 1, j = 1; i > 0; i--)
            j *= windims[i];
        }
      else
        {
          for (i = 0, j = 1; i < (int32) rank - 1; i++)
            j *= windims[i];
        }

      numelements = windims[leastsig];
      writesize = numelements * fileNTsize;
      datastride = dims[leastsig] * localNTsize;
      if (convert)
        {
          buf = (uint8 *) HDmalloc((uint32) writesize);
          if (buf == NULL)
            HCLOSE_GOTO_ERROR(Sfile_id,DFE_NOSPACE, FAIL);
          for (i = 0; i < j; i++, datap += datastride)
            {
              DFKconvert((VOIDP) datap, (VOIDP) buf, numtype,
                         numelements, DFACC_WRITE, 0, 0);
              ret = Hwrite(Writesdg.aid, writesize, buf);   /* done */
              if (ret == FAIL)
                {
                  HDfree((VOIDP) buf);
                  HCLOSE_GOTO_ERROR(Sfile_id,DFE_WRITEERROR, FAIL);
                }
            }
          HDfree((VOIDP) buf);
        }
      else
        {   /* !contiguous      */
          for (i = 0; i < j; i++, datap += datastride)
              if ( Hwrite(Writesdg.aid, writesize, datap) == FAIL)
                  HCLOSE_GOTO_ERROR(Sfile_id,DFE_WRITEERROR, FAIL);
        }
    }

  ret_value = (ret >= 0 ? 0 : -1);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDgetcal
 USAGE
       int DFSDgetcal(pcal, pcal_err, pioff, pioff_err, cal_nt)
       float64 *pcal;       OUT: calibration factor
       float64 *pcal_err;   OUT: calibration error value
       float64 *pioff;      OUT: uncalibrated offset value
       float64 *pioff_err;  OUT: uncalibrated offset error value
       int32   *cal_nt;     OUT: Nunber type of uncalibrated data

 RETURNS
       Returns SUCCED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       This routine reads the calibration record, if there is one, attached
       to the scientific data set. A calibration record contains four 64-bit
       floating point values followed by a 32-bit integer, to be interpreted
       as follows:

            cal        - calibration factor
            cal_err    - calibration error
            offset     - uncalibrated offset
            offset_err - uncalibrated offset error
            num_type   - number type of uncalibrated data

       The relationship between a value 'iy' stored in a data set and the
       actual value 'y' is defined as:

            y = cal * (iy - offset)

       The variable "offset_err" contains a potential error of offset,
       and "cal_err" contains a potential error of "cal". Currently the
       calibration record is provided for information only. The DFSD
       interface performs no operations on the data based on the calibration
       tag.

----------------------------------------------------------------------------*/
intn
DFSDgetcal(float64 *pcal, float64 *pcal_err, float64 *pioff,
           float64 *pioff_err, int32 *cal_nt)
{
  intn      ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDgetcal");

#ifdef HAVE_PABLO
  TRACE_ON(PABLO_mask,ID_DFSDgetcal);
#endif /* HAVE_PABLO */

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  if (Newdata < 0)
    HGOTO_ERROR(DFE_BADCALL, FAIL);

  if (IsCal)
    {
      *pcal = Readsdg.cal;
      *pcal_err = Readsdg.cal_err;
      *pioff = Readsdg.ioff;
      *pioff_err = Readsdg.ioff_err;
      *cal_nt = Readsdg.cal_type;
      ret_value = SUCCEED;
    }
  else
    HGOTO_ERROR(DFE_NOVALS, FAIL);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF(PABLO_mask, ID_DFSDgetcal);
#endif /* HAVE_PABLO */

  return ret_value;
}   /* DFSDgetcal */

/*-----------------------------------------------------------------------------
 NAME
       DFSDsetcal
 USAGE
       intn DFSDsetcal(cal, cal_err, ioff, ioff_err, cal_nt)
       float64 cal;         IN: calibration factor
       float64 cal_err;     IN: calibration error
       float64 ioff;        IN: uncalibrated offset
       float64 ioff_err;    IN: uncalibrated offset error
       int32   cal_nt;      IN: number type of uncalibrated data
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Sets the calibration information associated with data. This routine
       sets the calibration record attached to a data set. A calibration
       record contains four 64-bit floating point values followed by a
       32-bit integer, to be interpreted as follows:

            cal        - calibration factor
            cal_err    - calibration error
            offset     - uncalibrated offset
            offset_err - uncalibrated offset error
            num_type   - number type of uncalibrated data

       The relationship between a value 'iy' stored in a data set and the
       actual value 'y' is defined as:

            y = cal * (iy - offset)

       The variable "offset_err" contains a potential error of offset,
       and "cal_err" contains a potential error of "cal". Currently the
       calibration record is provided for information only. The DFSD
       interface performs no operations on the data based on the calibration
       tag. "DFSDsetcal" works like other "DFSDset*" routines, with one
       exception: the calibration information is automatically cleared
       after a call to "DFSDputdata" or "DFSDadddata". Hence, "DFSDsetcal"
       must be called anew for each data set that is to be written.
----------------------------------------------------------------------------*/
intn
DFSDsetcal(float64 cal, float64 cal_err, float64 ioff, float64 ioff_err,
           int32 cal_nt)
{
    CONSTR(FUNC, "DFSDsetcal");
  intn    ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDsetcal);
#endif /* HAVE_PABLO */

  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  Writesdg.cal = (float64) cal;
  Writesdg.cal_err = (float64) cal_err;
  Writesdg.ioff = (float64) ioff;
  Writesdg.ioff_err = (float64) ioff_err;
  Writesdg.cal_type = (int32) cal_nt;

  Ref.cal = 0;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF(PABLO_mask, ID_DFSDsetcal);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDwriteref
 USAGE
       int DFSDwriteref(filename, ref)
       char   *filename;  IN: HDF file to write to
       uint16 ref;        IN: reference number for the next writing of data
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Specifies the data set to be written to next by using the reference
       number. Note that there is no gaurantee that reference numbers appear
       in sequence in and HDF file; therefore, it is not safe to assume
       that a reference number is the sequence number for a data set.
-----------------------------------------------------------------------------*/
intn
DFSDwriteref(const char *filename, uint16 ref)
{
  int32       file_id;
  int32       aid;
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDwriteref");

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDwriteref);
#endif /* HAVE_PABLO */

  /* Clear error stack */
  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  /* Open file for read access */
  if (( file_id = DFSDIopen(filename, DFACC_READ))== DF_NOFILE)
    HGOTO_ERROR(DFE_BADOPEN, FAIL);

  /* Check for existence of SDG */
  if ((aid = Hstartread(file_id, DFTAG_SDG, ref)) == FAIL
      && (aid = Hstartread(file_id, DFTAG_NDG, ref)) == FAIL)
    {
      HCLOSE_GOTO_ERROR(file_id, DFE_NOMATCH, FAIL);
    }

  /*
   ** Probably need to call DFSDgetndg to intialize Writesdg struct
   ** This is so that we use the information of an SDG that has
   ** already been written out. Once a SDG has been written out,
   ** a user should not be able to change attributes such as
   ** numbertype, dimensions or fill value.
  */
  if ((DFSDIgetndg(file_id, DFTAG_SDG, ref, &Writesdg) < 0)
      && (DFSDIgetndg(file_id, DFTAG_NDG, ref, &Writesdg) < 0))
    {
      Hendaccess(aid);
      HCLOSE_GOTO_ERROR(file_id, DFE_INTERNAL, FAIL);
    }

  /* Close access to file, set Writeref */
  Hendaccess(aid);
  Writeref = ref;
  Lastref = ref;

  ret_value = Hclose(file_id);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
        TRACE_OFF(PABLO_mask, ID_DFSDwriteref);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDsetfillvalue
 USAGE
       int DFSDsetfillvalue(fill_value)
       VOID *fill_value;     IN: number to be stored in any unwritten locations
                                 of the scientific data set
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Set the value used to fill in any unwritten location in a scientific
       data set. Since the data is written in hyperslabs, it is possible
       that not alof the possible locations in a given data set are written
       to. The fill value is placed into all unwritten locations. The value
       gets written if the following "DFSDwriteslab" call is the first
       call to the data set i.e. the data set is created. It is assumed that
       the fille value has the same number type as the data set.
 WARNING
       Memory bug on SGI's if you try to free allocated space for fill values.
-----------------------------------------------------------------------------*/
intn
DFSDsetfillvalue(VOIDP fill_value)
{
  int32       numtype;        /* current number type  */
  uint32      localNTsize;    /* size of this NT on as it is on this machine  */
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDsetfillvalue");

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_DFSDsetfillvalue);
#endif /* HAVE_PABLO */

  /* Clear error stack  */
  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  /* Check to see if fill value written out already */
  if (Ref.fill_value == -1 && Writesdg.fill_fixed == TRUE)
    {
      HGOTO_ERROR(DFE_INTERNAL, FAIL);
    }
  else
    {
      /* Get local and file number type sizes  */
      numtype = Writesdg.numbertype;
      localNTsize = (uint32)DFKNTsize((numtype | DFNT_NATIVE) & (~DFNT_LITEND));

      /* Set fill value in Writesdg struct, and set fill value flag  */
      Ref.fill_value = 0;
      if (HDmemcpy(Writesdg.fill_value, fill_value, localNTsize) != NULL)
        ret_value = SUCCEED;
      else
        ret_value = FAIL;
    }

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF(PABLO_mask, ID_DFSDsetfillvalue);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDgetfillvalue
 USAGE
       int DFSDgetfillvalue(fill_value)
       VOID *fill_value;    OUT: Number to be stored in any unwritten
                                 locations of the scientific data set
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
      Gets the value used to fill any unwritten location in a scientific
      data set. The value is set by "DFSDsetfillvalue". Note that a call
      that initializes file information structures such as "DFSDgetdims"
      is required in order for "DFSDgetfillvalue" to succeed. This is because
      "DFSDgetfillvalue" does not take a file name as an arguement.
-----------------------------------------------------------------------------*/
intn
DFSDgetfillvalue(VOIDP fill_value)
{
  int32       numtype;        /* current number type  */
  uint32      localNTsize;    /* size of this NT on as it is on this machine  */
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDgetfillvalue");

#ifdef HAVE_PABLO
  TRACE_ON(PABLO_mask,ID_DFSDgetfillvalue);
#endif /* HAVE_PABLO */

  /* Clear error stack  */
  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  /* Check if Readsdg is fresh  */
  if (Newdata < 0)
    HGOTO_ERROR(DFE_BADCALL, FAIL);

    /* Get local number type size  */
  numtype = Readsdg.numbertype;
  localNTsize = (uint32)DFKNTsize((numtype | DFNT_NATIVE) & (~DFNT_LITEND));

    /* Set return fill value  */
  if (HDmemcpy(fill_value, Readsdg.fill_value, localNTsize) != NULL)
    ret_value = SUCCEED;
  else
    ret_value = FAIL;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF(PABLO_mask, ID_DFSDgetfillvalue);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDreadslab
 USAGE
       int DFSDreadslab(filename, start, slab_size, stride, buffer, buffer_size)
       char *filename;       IN: name of HDF file to write to
       int32 start[];        IN: array of size rank containing the coordinates
                                 for the start of the slab of data
       int32 slab_size[];    IN: array of size rank containing the size of
                                 each dimension in the slab of data
       int32 stride[];       IN: sub-sampling stride(not implemented)
       VOIDP buffer;         OUT: buffer to hold the extracted slab of data
       int32 buffer_size[];  OUT: array containing the dimensions of the buffer
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Reads a sub-array of data from a scientific data set. "DFSDreadslab"
       will sequence to the next scientific data set if "DFSDgetdims" or
       "DFSDgetdata" is not called earlier. The "start[]" indices are relative
       to 1. The rank(size) of "start[]" must be the same as the number of
       dimensions of the specified variable. The elements of "slab_size[]"
       must be no larger than the dimensions of the scientific data set.
       The stride feature is not currently implemented. For now just pass
       the "start[]" array as the arguement for "stride[]" where it will be
       ignored. To extract a slab of lower dimension than that of the data
       set, enter 1 in the "slab_size[] array for each ommitted dimension.
       For example, to extract a 2D slab from a 3D data set, specify the
       beginning coordiantes in 3 dimensions in "start[]" and enter a 1 for
       the for the missing dimension in the array "slab_size[]". More
       specifically, to extract a 3x4 slab containing the elements (6,7,8)
       through (8.7,11) specify the begining coordiantes as {6,7,8} and
       the slab size as {3,1,4}.
-----------------------------------------------------------------------------*/
intn
DFSDreadslab(const char *filename, int32 start[], int32 slab_size[],
             int32 stride[], VOIDP buffer, int32 buffer_size[])
{
  intn  ret_value = SUCCEED;
  /* shut compiler up */
  stride = stride;

#ifdef HAVE_PABLO
  TRACE_ON(PABLO_mask,ID_DFSDreadslab);
#endif /* HAVE_PABLO */

  ret_value = (DFSDgetslice(filename, start, slab_size, buffer, buffer_size));

#ifdef HAVE_PABLO
    TRACE_OFF(PABLO_mask, ID_DFSDreadslab);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDstartslab
 USAGE
       int DFSDstartslab(filename)
       char *filename;    IN: name of HDF file to write to
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Prepares the DFSD interface to write a slab(s) od data to a scientific
       data set. "DFSDsetdims" must be called before calling "DFSDstartslab".
       No call which needs to open a file may be made after a "DFSDstartslab"
       call until "DFSDendslab" is called. This routine will write out the
       fill values if "DFSDsetfillvalue" is called before this routine and
       this is the first write(i.e. creation) to the data set.
-----------------------------------------------------------------------------*/
intn
DFSDstartslab(const char *filename)
{
  int32       i;
  int32       sdg_size;
  int32       localNTsize;
  int32       fileNTsize;
  int32       fill_bufsize = 16384;   /* Chosen for the PC */
  int32       odd_size;
  uint8      *fill_buf;
  uint8       platnumsubclass;
  uint8       outNT;          /* file number type subclass */
  intn       ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDstartslab");

#ifdef HAVE_PABLO
  TRACE_ON(PABLO_mask,ID_DFSDstartslab);
#endif /* HAVE_PABLO */

  /* Clear errors */
  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  /* Check rank set i.e. DFSDsetdims()  */
  if (!Writesdg.rank)
    HGOTO_ERROR(DFE_BADDIM, FAIL);

  /* If NT not set(i.e. DFSDsetNT() not called), default to float32  */
  if (Writesdg.numbertype == DFNT_NONE)
    DFSDsetNT(DFNT_FLOAT32);

  /* Open file */
  if (( Sfile_id = DFSDIopen(filename, DFACC_WRITE))== DF_NOFILE)
    HGOTO_ERROR(DFE_BADOPEN, FAIL);

  /*
  ** Check for Writeref set i.e. DFSDwriteref() called?
  ** If not Writeref then we create a new Writeref i.e new SDG
  ** Else use existing one.
  */
  /* Set tag, ref of SDG to write */
  Writesdg.data.tag = DFTAG_SD;
  if (!Writeref)
    Writeref = Hnewref(Sfile_id);
  if (!Writeref)
    HGOTO_ERROR(DFE_BADREF, FAIL);
  Writesdg.data.ref = Writeref;

  /* Intialize a few local variables */
  localNTsize = DFKNTsize((Writesdg.numbertype | DFNT_NATIVE) & (~DFNT_LITEND));
  fileNTsize = DFKNTsize(Writesdg.numbertype);

  /* Calculate size of of dataset */
  sdg_size = fileNTsize;
  for (i = 0; i < Writesdg.rank; i++)
    sdg_size *= Writesdg.dimsizes[i];

  /* set up to write data */
  Writesdg.aid = Hstartwrite(Sfile_id, DFTAG_SD, Writeref, sdg_size);
  if (Writesdg.aid == FAIL)
    HCLOSE_GOTO_ERROR(Sfile_id,DFE_BADAID, FAIL);

  /*
   ** Check if fill value is set
  */
  if (!Ref.fill_value)
    {
      /* make the fill buffer smaller if possible */
      if (fill_bufsize > sdg_size && localNTsize == fileNTsize)
        fill_bufsize = sdg_size;

          /* Allocate space for fill buffer */
      if ((fill_buf = (uint8 *) HDmalloc((uint32) fill_bufsize)) == NULL)
        {
          Hendaccess(Writesdg.aid);
          HCLOSE_GOTO_ERROR(Sfile_id,DFE_NOSPACE, FAIL);
        }

      /* Get number types and compare */
      outNT = Writesdg.filenumsubclass;
      platnumsubclass = (uint8)DFKgetPNSC(Writesdg.numbertype, (int32)DF_MT);

      if (platnumsubclass != outNT)
        {   /* conversion  */
          /* Allocate buffer  */
          uint8       buf2[DFSD_MAXFILL_LEN];

          /* Convert from native to IEEE  */
          DFKconvert((VOIDP)&Writesdg.fill_value, (VOIDP) buf2,
                     Writesdg.numbertype, 1, DFACC_WRITE, 0, 0);

          /* Intialize buffer to fill value */
          for (i = 0; i < fill_bufsize; i = i + localNTsize)
              HDmemcpy(&(fill_buf[i]), buf2, localNTsize);

        }
      else /* no conversion */
        {
            /* Intialize buffer to fill value */
            for (i = 0; i < fill_bufsize; i = i + localNTsize)
                HDmemcpy(&(fill_buf[i]), Writesdg.fill_value, localNTsize);
        }


      /* Write fill values */
      if (sdg_size <= fill_bufsize)
        odd_size = sdg_size;
      else
        {
          odd_size = sdg_size % fill_bufsize;
          for (i = 0; i < (sdg_size / fill_bufsize); i++)
            {     /* Write out fill buffer X times */
              if (Hwrite(Writesdg.aid, fill_bufsize, fill_buf) == FAIL)
                {
                  Hendaccess(Writesdg.aid);
                  HDfree((VOIDP) fill_buf);
                  HCLOSE_GOTO_ERROR(Sfile_id,DFE_WRITEERROR, FAIL);
                }
            }
        }
      /* Write fill values for odd size piece */
      if (Hwrite(Writesdg.aid, odd_size, fill_buf) == FAIL)
        {
          Hendaccess(Writesdg.aid);
          HDfree((VOIDP) fill_buf);
          HCLOSE_GOTO_ERROR(Sfile_id,DFE_WRITEERROR, FAIL);
        }

      Writesdg.fill_fixed = TRUE;   /* fill value cannot be changed */
      /* Free up space */
      HDfree((VOIDP) fill_buf);
    }

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF(PABLO_mask, ID_DFSDstartslab);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*----------------------------------------------------------------------------
 NAME
       DFSDwriteslab
 USAGE
       int  DFSDwriteslab(start, stride, count, data)
       int32 start[];     IN: array containing the coordinates of the start
                              of the slab in the HDF file
       int32 stride[];    IN: array containing the dimensions of data[]
       int32 count[];     IN: array containing the size of the slab
       VOID  *data;       IN: array to hold the floating point data to write
 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Writes a slab of data to a scientific data set. The "start" indices
       are relative to 1. The rank of "start" must be the same as the
       number of dimensions of the specified variable. The elements of "start"
       must be no larger than the scientific data set's dimensions in order.
       The stride feature is not currently implemented. For now just pass the
       "start" array as the argument for "stride" where it will be ignored.
       The rank of "count" must be the same as the number of dimensions of the
       specified variable. The elements of "count" must be no larger that
       the scientific data sets's dimensions in order. The order in which
       the data will be written into the specified hyperslab is with the
       last dimension varying the fastest. The array "data" should be of
       appropriate number type for the data set. Note that neither the
       compilier nor the HDF sotfware can detect if the wrong type of data
       is written.
----------------------------------------------------------------------------*/
intn
DFSDwriteslab(int32 start[], int32 stride[],
              int32 count[], VOIDP data)
{
  intn        rank;           /* number of dimensions in data[] */
  int32       i;              /* temporary loop index */

  int32       leastsig;       /* fastest varying subscript in the array */
  int32       convert;        /* true if machine NT != NT to be read */
  int32       done;           /* true if we are at the end of the slab */
  int32       numtype;        /* current number type  */
  int32       fileNTsize;     /* size of this NT in the file  */
  int32       localNTsize;    /* size of this NT as it occurs in this machine */
  int32       numelements;    /* number of floats to read at once */
  int32       sdgsize;        /* number of bytes to be written in the SDG */
  int32       rowsize;        /* number of bytes to be written at once */
                                /*   in the hyperslab */
  int32       fileoffset;     /* offset into the current dataset in the file */
  int32      *doffset;        /* array for accessing the next element in data[] */
  int32      *foffset;        /* array for accessing  next element in the file */
  int32      *dimsleft;       /* array for tracking current position in data[] */
  int32      *startdims;      /* tmp array containing starting slab dims */
  int32      *sizedims;       /* tmp array containing the slab size */
  int32      *filedims;       /* tmp array containing the dimensions */
                                /*   of the dataset in the file */
  int32       r_error;        /* flag if an error occurred, */
                                /*   used by DFconvert macro */
  int32       aid;

  uint8       platnumsubclass;    /* class of this NT for this platform */
  uint8       fileNT;         /* file number subclass  */
  uint8      *buf;            /* buffer containing the converted current row */
  uint8      *datap;          /* ptr into data[] at starting offset */
                                /*   of current block */
  intn       ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDwriteslab");

#ifdef HAVE_PABLO
  TRACE_ON(PABLO_mask,ID_DFSDwriteslab);
#endif /* HAVE_PABLO */

  /* shut compiler up */
  stride = stride;

  /* Clear error stack  */
  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  /* Sanity checking of input data  */
  if (!data)
    HGOTO_ERROR(DFE_BADPTR, FAIL);

    /* Set rank */
  rank = Writesdg.rank;

  /* Do sanity checking of starting and size dimension arrays  */
  for (i = 0; i < (int32) rank; i++)
    {
      /*
      ** Check validity for the dimension ranges by
      **  checking lower bound of slab sizes
      **  checking lower bound of starting dimensions
      **  checking upper bound on writing dimensions
      */
      if ((count[i] < 1) || (start[i] < 1)
          || (start[i] + count[i] - 1 > Writesdg.dimsizes[i]))
          HCLOSE_GOTO_ERROR(Sfile_id,DFE_BADDIM, FAIL);
    }

  /* Intialize a few local variables */
  numtype = Writesdg.numbertype;
  platnumsubclass = (uint8)DFKgetPNSC(numtype & (~DFNT_LITEND), DF_MT);
  localNTsize = DFKNTsize((numtype | DFNT_NATIVE) & (~DFNT_LITEND));
  fileNTsize = DFKNTsize(numtype);
  fileNT = Writesdg.filenumsubclass;

  /* Calculate total bytes in SDS that can be written */
  sdgsize = fileNTsize;
  for (i = 0; i < Writesdg.rank; i++)
    sdgsize *= Writesdg.dimsizes[i];

  /* Set Access Id */
  aid = Writesdg.aid;

  /*
   ** Get dimensions of hyperslab to write out
   ** Allocate temporary buffers(3) to hold starting, size,
   **  and file(SDG in file) dimensions
   */
  startdims = (int32 *) HDmalloc((size_t) (3 * rank) * sizeof(int32));
  if (startdims == NULL)
      HCLOSE_GOTO_ERROR(Sfile_id,DFE_NOSPACE, FAIL);
  sizedims = startdims + rank;
  filedims = sizedims + rank;

  /* Copy arrays to private workspace (row major order) */
  for (i = 0; i < (int32) rank; i++)
    {
      startdims[i] = start[i] - 1;
      sizedims[i] = count[i];   /* dimensions of just slab */
      filedims[i] = Writesdg.dimsizes[i];   /* dimensions of whole SDG */
    }

  /* Is conversion necessary */
  convert = (fileNT != platnumsubclass);

  /* Collapse dimensions if contiguous both in the file and in memory */
  for (i = (int32) rank - 1; i > 0; i--)  /* stop before most sig dim */
    {
      /* read only part of dataset */
      if (startdims[i] != 0 || sizedims[i] < filedims[i])
        break;
      startdims[i - 1] *= filedims[i];
      sizedims[i - 1] *= sizedims[i];
      filedims[i - 1] *= filedims[i];
      rank--;
    }

  /*
   ** Which is least sig dim i.e fastest varying.
   ** In C usually the last.
  */
  leastsig = (int32) rank - 1;

  r_error = 0;
  if (rank == 1 && !convert)
    {
      /* all data is contiguous with no conversions */
      rowsize = sizedims[0] * fileNTsize;
      if ((Hseek(aid, startdims[0] * fileNTsize, 0) == FAIL)
          || (rowsize != Hwrite(aid, rowsize, (uint8 *) data)))
        {
          r_error = 1;
        }
    }
  else
    {
      /*
       * The data must be further manipulated.
       * It may need conversion, may not be contiguous, or
       * any combination of these.
       */
      numelements = sizedims[leastsig];     /* # of elmenents in a row */
      rowsize = numelements * fileNTsize;   /* # of bytes in a row */

      /* If conversion, allocate 1 row buffers */
      if (convert)
        {
          if ((buf = (uint8 *) HDmalloc((uint32) rowsize)) == NULL)
            {
              HDfree((VOIDP) startdims);
              Hendaccess(aid);
              HCLOSE_GOTO_ERROR(Sfile_id,DFE_NOSPACE, FAIL);
            }
        }
      else
        buf = NULL;

      /* Allocate space for file and data offsets and dimsleft */
      foffset = (int32 *) HDmalloc((size_t) (3 * rank) * sizeof(int32));
      if (foffset == NULL)
        {
          HDfree((VOIDP) startdims);
          HDfree((VOIDP) buf);
          Hendaccess(aid);
          HCLOSE_GOTO_ERROR(Sfile_id,DFE_NOSPACE, FAIL);
        }
      dimsleft = foffset + rank;
      doffset = dimsleft + rank;

      /* Set number of dimensions left */
      for (i = leastsig; i >= 0; i--)
        dimsleft[i] = sizedims[i];

      /* compute offsets in the source array */
      doffset[leastsig] = 1 * localNTsize;
      for (i = leastsig; i > 0; i--)
        doffset[i - 1] = doffset[i] * sizedims[i];

      /*
       ** Compute offsets in the file for dimension, according to the
       ** possible length for each dimension. Depends on numbertype.
       */
      for (i = leastsig, foffset[i] = 1 * fileNTsize; i > 0; i--)
        foffset[i - 1] = foffset[i] * filedims[i];

      /*
       ** Compute starting position in file
       ** All file writes are done relative to this starting offset.
       ** Cumulative offset is from most sig to next to least sig dim.
       */
      for (i = 0, fileoffset = 0; i < leastsig; i++)
        fileoffset = fileoffset + (startdims[i] * foffset[i]);

      /* Dont forget about last dimension */
      fileoffset = fileoffset + startdims[leastsig] * fileNTsize;

      datap = (uint8 *) data;
      done = 0;

      /* -- now write the data */
      do
        {
          /* move to the next data element in the file */
          if (Hseek(aid, fileoffset, 0) == FAIL)
            {
              r_error = 1;
              break;
            }

          /*  If convert and write one contiguous block of data */
          /*  Else write one contiguous block of data */
          if (convert)
            {
              DFKconvert((VOIDP) datap, (VOIDP) buf, numtype,
                         numelements, DFACC_WRITE, 0, 0);
              if (rowsize != Hwrite(aid, rowsize, buf))
                {
                  r_error = 1;
                  break;
                }
            }
          else
            {
              if (rowsize != Hwrite(aid, rowsize, datap))
                {
                  r_error = 1;
                  break;
                }
            }

          /*
           * Find starting place of the next row/block.
           * Note that all moves are relative:
           *   this preserves the starting offsets of each dimension
           */
          for (i = leastsig - 1; i >= 0; i--)
            {
              if (--dimsleft[i] > 0)
                {
                  /* Move to next element in the current dimension */
                  datap += doffset[i];
                  fileoffset += foffset[i];
                  break;
                }
              else
                {
                  dimsleft[i] = sizedims[i];
                  /*
                   * Note that we are still positioned at the beginning of
                   * the last element in the current dimension
                   */
                  /* move back to the beginning of dimension i */
                  datap -= doffset[i] * (sizedims[i] - 1);

                  /* move back to beginning read position of dimension i */
                  fileoffset -= foffset[i] * (sizedims[i] - 1);
                  if (i == 0)
                    done = 1;
                }
            }
        }
      while (!done && leastsig > 0);

      if (buf != NULL)
         HDfree((VOIDP) buf);
      if (foffset != NULL)
         HDfree((VOIDP) foffset);
    }

  /* Clean up time.... */
  HDfree((VOIDP) startdims);

  if (r_error)
    ret_value = FAIL;
  else
    ret_value = SUCCEED;

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF(PABLO_mask, ID_DFSDwriteslab);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*-----------------------------------------------------------------------------
 NAME
       DFSDendslab
 USAGE
       int DFSDendslab()

 RETURNS
       Returns SUCCEED(0) if successful and FAIL(-1) otherwise.
 DESCRIPTION
       Ends a sequence of slab calls started by "DFSDstartslab" by closing
       the file. Writes the NDG information to the file if this call follows
       a "DFSDstartslab" that created a new SDG.

----------------------------------------------------------------------------*/
intn
DFSDendslab(void)
{
  intn        ret_value = SUCCEED;
  CONSTR(FUNC, "DFSDendslab");

#ifdef HAVE_PABLO
  TRACE_ON(PABLO_mask,ID_DFSDendslab);
#endif /* HAVE_PABLO */

  /* Clear error stack */
  HEclear();

    /* Perform global, one-time initialization */
    if (library_terminate == FALSE)
        if(DFSDIstart()==FAIL)
            HGOTO_ERROR(DFE_CANTINIT, FAIL);

  /* Valid file id */
  if (Sfile_id == DF_NOFILE)
    HGOTO_ERROR(DFE_BADCALL, FAIL);

  /* Check to see if we have written out the SDG info */
  if (!Ref.new_ndg)
    {
      if (DFSDIputndg(Sfile_id, Writeref, &Writesdg) < 0)
        HCLOSE_GOTO_ERROR(Sfile_id,DFE_INTERNAL, FAIL);

      /* old nsdg table should be reset next time  */
      if (nsdghdr != NULL)
        {
          if (nsdghdr->nsdg_t != NULL)
            {
              DFnsdgle   *rear, *front;

              rear = nsdghdr->nsdg_t;
              front = rear->next;
              while (rear != NULL)
                {
                  HDfreenclear(rear);
                  rear = front;
                  if (rear != NULL)
                    front = rear->next;
                }
              nsdghdr->size = 0;
              nsdghdr->nsdg_t = NULL;
              lastnsdg.tag = DFTAG_NULL;
              lastnsdg.ref = 0;
            }

          HDfreenclear(nsdghdr);
        }

      Ref.new_ndg = -1;
    }

  /* Slab clean up */
  Hendaccess(Writesdg.aid);
  ret_value = Hclose(Sfile_id);
  Sfile_id = 0;
  Lastref = (uint16) Writeref;    /* remember ref written */
  Writeref = 0;   /* Reset Write ref */

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF(PABLO_mask, ID_DFSDendslab);
#endif /* HAVE_PABLO */

  return ret_value;
}

/*--------------------------------------------------------------------------
 NAME
    DFSDIstart
 PURPOSE
    DFSD-level initialization routine
 USAGE
    intn DFSDIstart()
 RETURNS
    Returns SUCCEED/FAIL
 DESCRIPTION
    Register the shut-down routine (DFSDPshutdown) for call with atexit
 GLOBAL VARIABLES
 COMMENTS, BUGS, ASSUMPTIONS
 EXAMPLES
 REVISION LOG
--------------------------------------------------------------------------*/
PRIVATE intn DFSDIstart(void)
{
    CONSTR(FUNC, "DFSDIstart");    /* for HERROR */
    intn        ret_value = SUCCEED;

    /* Don't call this routine again... */
    library_terminate = TRUE;

    /* Install atexit() library cleanup routine */
    if (HPregister_term_func(&DFSDPshutdown) != 0)
      HGOTO_ERROR(DFE_CANTINIT, FAIL);

done:
  if(ret_value == FAIL)   
    { /* Error condition cleanup */

    } /* end if */

  /* Normal function cleanup */

    return(ret_value);
} /* end DFSDIstart() */

/*--------------------------------------------------------------------------
 NAME
    DFSDPshutdown
 PURPOSE
    Terminate various static buffers.
 USAGE
    intn DFSDshutdown()
 RETURNS
    Returns SUCCEED/FAIL
 DESCRIPTION
    Free various buffers allocated in the DFSD routines.
 GLOBAL VARIABLES
 COMMENTS, BUGS, ASSUMPTIONS
    Should only ever be called by the "atexit" function HDFend
 EXAMPLES
 REVISION LOG
--------------------------------------------------------------------------*/
intn DFSDPshutdown(void)
{
    DFSDIclear(&Readsdg);
    DFSDIclear(&Writesdg);

    /* old nsdg table should be reset next time  */
    if (nsdghdr != NULL)
      {
          if (nsdghdr->nsdg_t != NULL)
            {
                DFnsdgle   *rear, *front;

                rear = nsdghdr->nsdg_t;
                front = rear->next;
                while (rear != NULL)
                  {
                      HDfree(rear);
                      rear = front;
                      if (rear != NULL)
                          front = rear->next;
                  }
                lastnsdg.tag = DFTAG_NULL;
                lastnsdg.ref = 0;
            }
          HDfreenclear(nsdghdr);
      }

    if(ptbuf!=NULL)
      {
          HDfree(ptbuf);
          ptbuf=NULL;
      } /* end if */

    if(Lastfile!=NULL)
      {
          HDfree(Lastfile);
          Lastfile=NULL;
      } /* end if */
    return(SUCCEED);
} /* end DFSDPshutdown() */

