/****************************************************************************
 * 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.135.2.3 $";
#endif

/* $Id: mfsd.c,v 1.135.2.3 2003/09/18 23:07:10 bmribler Exp $ */
#ifdef HAVE_PABLO
#define PABLO_mask ID_mfsd_c
#include "ProcIDs.h"
#endif

/******************************************************************************
file - mfsd.c

  This file contains the HDF/netCDF based multi-file interface for SDSs

  All of the routines that make up this interface have names beginning
  with SD.  Routines beginning with SDI are internal routines and
  should not be used outside of this module.

  Defining SDDEBUG will print status messages to stderr

Reading interface:
------------------

fid    = SDstart(file name, access);

        --- get the number of data-sets in the file ---
num    = SDnumber(fid);
        
sdsid  = SDselect(fid, i, ...);   0 <= i < num

        --- return the name, rank, dimsizes, #of attr, datatype ---
status = SDgetinfo(sdsid, ...); 

status = SDreaddata(sdsid, ...);

status = SDgetrange(sdsid, ...);

status = SDend(fid);

status = SDisdimval_bwcomp(dimid);

status = SDcheckempty(sdsid, emptySDS);

 NOTE: This file needs to have the comments cleaned up for most of the
       functions here. -GV 9/10/97

******************************************************************************/

#include "local_nc.h"
#ifdef VMS
#include <stat.h>
#endif

#ifdef HDF
#include "mfhdf.h"
#include "hfile.h"
#include "hchunks.h" /* include here for now */

/* for Chunk debugging */
/*
#define CHK_DEBUG
*/

/* Local function prototypes */

PRIVATE NC_dim * SDIget_dim
    (NC *handle, int32 id);

PRIVATE NC * SDIhandle_from_id 
    (int32 id, intn typ);

PRIVATE NC_var *SDIget_var
    (NC *handle, int32 sdsid);

PRIVATE intn SDIputattr 
    (NC_array **ap, const char *name, int32 nt, intn count, const void * data);

PRIVATE int32 SDIgetcoordvar 
    (NC *handle, NC_dim *dim, int32 id, int32 nt);

PRIVATE int32 SDIfreevarAID 
    (NC * handle, int32 index);

PRIVATE intn SDIapfromid
    (int32 id, NC ** handlep, NC_array *** app);

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

/******************************************************************************
 NAME
	SDIhandle_from_id -- get the handle from this object

 DESCRIPTION
    Map an ID to the handle for this file

 RETURNS
    NULL or the handle

******************************************************************************/
PRIVATE NC *
SDIhandle_from_id(int32 id, /* IN: an object (file, dim, dataset) ID */
                  intn  typ /* IN: IN: the type of ID this is */)
{
    int32 tmp;
    NC   *ret_value = NULL;

    /* check that it is the proper type of id */
    tmp = (id >> 16) & 0xff;
    if(tmp != typ)
      {
        ret_value = NULL;
        goto done;
      }

    /* get the file from top 8 bits*/
    tmp = (id >> 24) & 0xff;
    ret_value = NC_check_id((int)tmp);

done:
    if (ret_value == NULL)
      { /* Failure cleanup */

      }
    /* Normal cleanup */

    return ret_value;
} /* SDIhandle_from_id */


/******************************************************************************
 NAME
	SDIget_var -- get the variable record

 DESCRIPTION
    Map an ID and a handle to the NC_var object for this dataset

 RETURNS
    NULL or the variable object

******************************************************************************/
PRIVATE NC_var *
SDIget_var(NC   *handle, /* IN: the handle for this file */
           int32 sdsid   /* IN: a dataset ID */)
{
    int32      varid;
    NC_array **ap = NULL;
    NC_var    *ret_value = NULL;

    /* varid is low 16bits of sdsid */
    varid = sdsid & 0xffff;

    if(handle->vars != NULL 
       && varid >= 0 
       && varid < handle->vars->count) 
      {
        ap = (NC_array **)handle->vars->values;
        ap += varid;
      } 
    else 
      {
        ret_value = NULL;
        goto done;
      }
    
    ret_value = ((NC_var *)*ap);

done:
    if (ret_value == NULL)
      { /* Failure cleanup */

      }
    /* Normal cleanup */

    return ret_value;    
} /* SDIget_var */


/******************************************************************************
 NAME
	SDIget_dim -- get the dimension record

 DESCRIPTION
    Map an ID and a handle to the NC_dim object for this dimension

 RETURNS
    NULL or the variable object

******************************************************************************/
PRIVATE NC_dim *
SDIget_dim(NC   *handle,/* IN: the handle for this file */
           int32 id     /* IN: a dimension ID */)
{
    int32      dimid;
    NC_array **ap = NULL;
    NC_dim    *ret_value = NULL;

    /* dimid is low 16bits of id */
    dimid = id & 0xffff;

    if(handle->dims != NULL 
       && dimid >= 0 
       && dimid < handle->dims->count) 
      {
        ap = (NC_array **)handle->dims->values;
        ap += dimid;
      } 
    else 
      {
        ret_value = NULL;
        goto done;
      }
    
    ret_value = ((NC_dim *)*ap);

done:
    if (ret_value == NULL)
      { /* Failure cleanup */

      }
    /* Normal cleanup */

    return ret_value;    
} /* SDIget_dim */


/******************************************************************************
 NAME
	SDIstart -- initialize the SD interface

 DESCRIPTION
    Register the atexit callback function, etc.

 RETURNS
    SUCCEED/FAIL
******************************************************************************/
static intn
SDIstart(void)
{
    CONSTR(FUNC, "SDIstart");    /* for HGOTO_ERROR */
    intn        ret_value = SUCCEED;

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

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

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

    } /* end if */

    /* Normal function cleanup */
    return(ret_value);
} /* end SDIstart() */

/******************************************************************************
 NAME
	SDstart -- open a file

 DESCRIPTION
    Open a file by calling ncopen() or nccreate() and return a
    file ID to the file.

 RETURNS
    A file ID or FAIL

******************************************************************************/
int32
SDstart(const char *name,   /* IN: file name to open */
        int32       HDFmode /* IN: access mode to open file with */)
{
    CONSTR(FUNC, "SDstart");    /* for HGOTO_ERROR */
    intn    cdfid;
    int32   fid;
    intn    NCmode;
    NC     *handle = NULL;
    int32   ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDstart: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON (PABLO_mask,ID_SDstart );
#endif
    /* turn off annoying crash on error stuff */
    ncopts = 0;

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

    /* check access mode */
    if(HDFmode & DFACC_WRITE)
        NCmode = NC_RDWR;
    else
        NCmode = NC_NOWRITE;

    if(HDFmode & DFACC_CREATE) 
      { /* create file */
        cdfid = nccreate(name, NC_CLOBBER);
      } 
    else 
      { /* open the file */
        cdfid = ncopen(name, NCmode);
      }

    /* check if bad create/open */
    if(cdfid == -1) 
        HGOTO_ERROR(DFE_BADOPEN, FAIL);

    /* hmm.....*/
    handle = NC_check_id(cdfid);
    if(handle == NULL) 
        HGOTO_ERROR(DFE_ARGS, FAIL);

    /* set in 'define' mode? */
    handle->flags &= ~(NC_INDEF);

    /* create file id to return */
    fid = (((int32) cdfid) << 24) + (((int32) CDFTYPE) << 16) + cdfid;

    ret_value = fid;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDstart );
#endif

    return ret_value;
} /* SDstart */


/******************************************************************************
 NAME
	SDend -- close a file

 DESCRIPTION
    Close the file

 RETURNS
    SUCCEED / FAIL

******************************************************************************/
intn
SDend(int32 id /* IN: file ID of file to close */)
{
    intn  cdfid;
    NC   *handle = NULL;
    intn  ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDend: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDend );
#endif

    /* get id? */
    cdfid = (intn)id & 0xffff;

#ifndef SYNC_ON_EACC

    /* get the handle */
    handle = SDIhandle_from_id(id, CDFTYPE);
    if(handle == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* make sure we can write to the file */
    if(handle->flags & NC_RDWR) 
      {

        handle->xdrs->x_op = XDR_ENCODE;

        /* see if the meta-data needs to be updated */
        if(handle->flags & NC_HDIRTY) 
          {
            if(!xdr_cdf(handle->xdrs, &handle))
              {
                ret_value = FAIL;
                goto done;
              }

            handle->flags &= ~(NC_NDIRTY | NC_HDIRTY);
          } 
        else 
          {

            /* see if the numrecs info needs updating */
              if(handle->flags & NC_NDIRTY) 
                {
                    if(!xdr_numrecs(handle->xdrs, handle))
                      {
                          ret_value = FAIL;
                          goto done;
                      }

                    if (handle->file_type != HDF_FILE)
                        handle->flags &= ~(NC_NDIRTY);
                }
          }
      }

#endif /* SYNC_ON_EACC */

    /* call netCDF close */
    ret_value = ncclose(cdfid);

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDend );
#endif

    return ret_value;
} /* SDend */


/******************************************************************************
 NAME
	SDfileinfo -- get info about an open file

 DESCRIPTION
    Return the number of datasets and global attributes in the file.
    NOTE:  the number of datasets includes coordinate variable
    datasets.  The routine SDiscoordvar() should be used if the
    distinction is important.

 RETURNS
    SUCCEED / FAIL

******************************************************************************/
intn
SDfileinfo(int32  fid,     /* IN:  file ID */
           int32 *datasets,/* OUT: number of datasets in the file */
           int32 *attrs    /* OUT: number of global attributes */)
{
    NC   *handle = NULL;
    intn  ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDnumber: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDfileinfo );
#endif

    /* check that fid is valid and get file structure */
    handle = SDIhandle_from_id(fid, CDFTYPE);
    if(handle == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

#ifdef SDDEBUG
    fprintf(stderr, "SDnumber: looked up handle as %d\n", handle);
#endif

    /* get number of data sets and global attributes */
    *(int32 *)datasets = ((handle->vars != NULL) ? handle->vars->count : 0);
    *(int32 *)attrs    = ((handle->attrs != NULL) ? handle->attrs->count : 0);


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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDfileinfo );
#endif

    return ret_value;
} /* SDfileinfo */


/******************************************************************************
 NAME
	SDselect -- get a dataset ID

 DESCRIPTION
    Return an id for the index-th data set in the file

    We've got 32bits, the current ID looks like:

    sdsID:
        
        32       24       16               0
        ------------------------------------
        |  fid   | id-type| position index |
        ------------------------------------
        
    fid is the netCDF based file ID (i.e. from ncopen).  ID type
    is SDSTYPE defined in mfhdf.h and position index is the 
    position in the file of this dataset.

    The position index is zero based

 RETURNS
   An ID to a dataset else FAIL
        
******************************************************************************/
int32
SDselect(int32 fid,  /* IN: file ID */
         int32 index /* IN: index of dataset to get ID for */)
{
    NC    *handle = NULL;
    int32  sdsid;         /* the id we're gonna build */
    int32  ret_value = FAIL;

#ifdef SDDEBUG
    fprintf(stderr, "SDselect: I've been called (index: %d) \n", index);
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDselect );
#endif

    /* check that fid is valid */
    handle = SDIhandle_from_id(fid, CDFTYPE);
    if(handle == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* check that a data set with this index exists */
    if(handle->vars == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    if(handle->vars->count < index)
      {
        ret_value = FAIL;
        goto done;
      }

    /* create SDS id to return */
    sdsid  = (((int32) fid & 0xffff) << 24) + (((int32) SDSTYPE) << 16) + index;

    ret_value = sdsid;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDselect );
#endif

    return ret_value;
} /* SDselect */

/******************************************************************************
 NAME
	SDgetinfo -- get info about a dataset

 DESCRIPTION
    The user is repsonsible for allocating space to hold
    the dataset name.  It can be at most MAX_NC_NAME 
    characters in length.  NULL can be passed for the name
    if it is not required.

    dimsizes should be an array to hold the dimension sizes
    a dataset can have at most MAX_VAR_DIMS dimensions.

 RETURNS
        SUCCEED / FAIL

******************************************************************************/ 
intn
SDgetinfo(int32  sdsid,   /* IN:  dataset ID */
          char  *name,    /* OUT: name of the dataset */
          int32 *rank,    /* OUT: rank of the dataset */
          int32 *dimsizes,/* OUT: array of dimension siszes */ 
          int32 *nt,      /* OUT: number type of data */
          int32 *nattr    /* OUT: the number of local attributes */)
{
    CONSTR(FUNC, "SDgetinfo");    /* for HGOTO_ERROR */
    intn    i;
    NC     *handle = NULL;
    NC_var *var = NULL;
    intn    ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDgetinfo: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDgetinfo );
#endif

    if( rank == NULL || dimsizes == NULL || nt == NULL || nattr == NULL)
	HGOTO_ERROR(DFE_ARGS, FAIL);

    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
        ret_value = FAIL;
        goto done;
      }

    if(handle->vars == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    if(name != NULL) 
      {
          HDmemcpy(name, var->name->values, var->name->len);
#if 0
          HDstrncpy(name, var->name->values, var->name->len);
#endif
          name[var->name->len] = '\0';
      }

    *rank  = var->assoc->count;
    if(!var->HDFtype)
        *nt    = hdf_map_type(var->type);
    else
        *nt    = var->HDFtype;

    *nattr = (var->attrs ? var->attrs->count : 0);

    for(i = 0; i < *rank; i++)
        dimsizes[i] = (int32) var->shape[i];

    if(dimsizes[0] == NC_UNLIMITED) 
      {
          if(handle->file_type == HDF_FILE)
              dimsizes[0] = var->numrecs;
          else
              dimsizes[0] = handle->numrecs;
      }

#ifdef SDDEBUG
    fprintf(stderr, "SDgetinfo: looked up handle as %d\n", handle);
#endif

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDgetinfo );
#endif

    return ret_value;
} /* SDgetinfo */


/******************************************************************************
 NAME
	SDreaddata -- read a hyperslab of data

 DESCRIPTION
    Read a hyperslab of data from the given variable.

 RETURNS
     SUCCEED / FAIL
 
******************************************************************************/
intn
SDreaddata(int32  sdsid,  /* IN:  dataset ID */
           int32 *start,  /* IN:  coords of starting point */
           int32 *stride, /* IN:  stride along each dimension */
           int32 *end,    /* IN:  number of values to read per dimension */
           void *  data    /* OUT: data buffer */)
{
    NC     *handle = NULL;
    NC_dim *dim = NULL;
    intn    varid;
    int32   status;
#ifdef BIG_LONGS
    long    Start[MAX_VAR_DIMS];
    long    End[MAX_VAR_DIMS];
    long    Stride[MAX_VAR_DIMS];
#else
    long    *Start = NULL;
    long    *End   = NULL;
    long    *Stride = NULL;
#endif
    intn    ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDreaddata: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDreaddata );
#endif
    
    if((start == NULL) || (end == NULL) || (data == NULL))
      {
        ret_value = FAIL;
        goto done;
      }

    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
          handle = SDIhandle_from_id(sdsid, DIMTYPE);
          if(handle == NULL) 
            {
              ret_value = FAIL;
              goto done;
            }
          dim = SDIget_dim(handle, sdsid);
      }

    if(handle->vars == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* get ready to read */
    handle->xdrs->x_op = XDR_DECODE ;
   
    /* 
     * figure out the index of the variable to read from
     * the user might have passed us a dimension, in which
     * case we want to reade from its coordinate variable
     */
    if(dim) 
      {
          varid = SDIgetcoordvar(handle, dim, (int32)(sdsid & 0xffff), (int32) 0);
      } 
    else 
      {
          /* oops, how do we know this ? */
          varid = (intn)sdsid & 0xffff;
      }

    /*
     * In general, (long) == int32 
     * In cases where it doesn't we need to convert
     */
#ifdef BIG_LONGS
    {
        int i;
        NC_var * var = SDIget_var(handle, sdsid);
        if(var == NULL)
          {
            ret_value = FAIL;
            goto done;
          }
        
        for(i = 0; i < var->assoc->count; i++) 
          {
            Start[i]  = (long) start[i];
            End[i]    = (long) end[i];
            if(stride) 
                Stride[i] = (long) stride[i];
          }
    }

#else

    Start  = (long *)start;
    End    = (long *)end;
    Stride = (long *)stride;

#endif

    /* call the readg routines if a stride is given */
    if(stride == NULL)
        status = NCvario(handle, varid, Start, End, (Void *)data);
    else
        status = NCgenio(handle, varid, Start, End, Stride, NULL, (Void *)data);

    if(status == -1)
        ret_value = FAIL;
    else
        ret_value = SUCCEED;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask,  ID_SDreaddata );
#endif

    return ret_value;
} /* SDreaddata */


/******************************************************************************
 NAME
	SDnametoindex -- map a dataset name to an index

 DESCRIPTION
    Given a data set name return the index (not ID) of the 
    first data set whose name matches.

    There can be multiple data sets with the same name.  In 
    such a case we only ever return the index of the first
    such dataset.
    Wildcards are not supported

 RETURNS
        Index of a dataset or FAIL

******************************************************************************/
int32
SDnametoindex(int32 fid,  /* IN: file ID */
              const char *name  /* IN: name of dataset to search for */)
{
    intn     ii;
    intn     len;
    NC      *handle = NULL;
    NC_var **dp = NULL;
    int32    ret_value = FAIL;

#ifdef SDDEBUG
    fprintf(stderr, "SDnametoindex: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDnametoindex );
#endif

    /* check that fid is valid */
    handle = SDIhandle_from_id(fid, CDFTYPE);
    if(handle == NULL) 
      {
        ret_value = FAIL;
        goto done;
      }

    if(handle->vars == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    len = HDstrlen(name) ;
    dp = (NC_var**)handle->vars->values ;
    for(ii = 0 ; ii < handle->vars->count ; ii++, dp++) 
      {
        if( len == (*dp)->name->len 
            && HDstrncmp(name, (*dp)->name->values, len) == 0) 
          {
            ret_value = ii;
            goto done;
          }
      }

    ret_value = FAIL;

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

      }
    /* Normal cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDnametoindex);
#endif

    return ret_value;    
} /* SDnametoindex */



/******************************************************************************
 NAME
	SDgetrange -- simulate a call to DFSDgetrange

 DESCRIPTION
    If a "valid_range" attribute is provided return its 
    values in pmax and pmin.  Else if both a "valid max" 
    AND a "vaild min" exist return their values in pmax and pmin.

    Arrgghh, in HDF it was assumed that the max and min values 
    were of the same data type as the rest of the data.  So 
    the user would know the amount of storage to send to get 
    back the max and min values.  This is not the case with 
    netCDF.  This routine will only work if they are already 
    the same number types.

 RETURNS
    On error or missing attributes return FAIL else SUCCEED.

******************************************************************************/
intn
SDgetrange(int32 sdsid, /* IN:  dataset ID */
           void * pmax,  /* OUT: valid max */
           void * pmin   /* OUT: valid min */)
{
    NC       *handle = NULL;
    NC_var   *var = NULL;
    NC_attr **attr = NULL;
    NC_attr **attr1 = NULL;
    NC_attr **attr2 = NULL;
    NC_array *array = NULL;
    intn      ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDgetrange: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDgetrange );
#endif

    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
        ret_value = FAIL;
        goto done;
      }

    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_ValidRange);
    if((attr != NULL) && ((*attr)->data->type == var->type)) 
      {
        /* BUG: this may be a pointer to a pointer */
        array = (NC_array *) (*attr)->data;
        HDmemcpy(pmin, array->values, array->szof);
        HDmemcpy(pmax, array->values + array->szof, array->szof);
      } 
    else 
      {
        attr1 = (NC_attr **) NC_findattr(&(var->attrs), "valid_max");
        attr2 = (NC_attr **) NC_findattr(&(var->attrs), "valid_min");
        
        if((attr1 == NULL) || (attr2 == NULL)) 
          {
#ifdef SDDEBUG
            fprintf(stderr, "No dice on range info (missing at least one)\n");
#endif   
            ret_value = FAIL;
            goto done;
          }

        if(((*attr1)->HDFtype != var->HDFtype) 
           || ((*attr2)->HDFtype != var->HDFtype)) 
          {
#ifdef SDDEBUG
            fprintf(stderr, "No dice on range info (wrong types)\n");
#endif   
            ret_value = FAIL;
            goto done;
          }

        NC_copy_arrayvals((char *)pmax, (*attr1)->data) ;
        NC_copy_arrayvals((char *)pmin, (*attr2)->data) ;
      }

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDgetrange);
#endif

    return ret_value;
} /* SDgetrange */


/* -------------------------- CREATION INTERFACE -------------------------- */
/*

        --- create a new data set ---
sdsid   = SDcreate(fid, name, numbertype, rank, dimsizes);

        --- associate a name with a dimension.  If a prev sdsid is ---
        --- provided then it is assumed that the current dimension is the ---
        --- same as the dimension with the same name of the previous sds ---
status  = SDsetdim(sdsid, dimnumber, dimname, [prev sdsid] );

        --- note that it will be possible to store values for a ---
        --- dimension without having to name it ---
status  = SDsetdimvalues(sdsid, dimnumber, numbertype, count, data);

        --- set the units and format strings ---
status  = SDsetdimstrs(sdsid, dimnumber, unitstr, formatstr);

        --- we will need an SDendaccess() so that we know when it ---
        --- is safe to write the information out in this case ---
status  = SDendaccess(sdsid);

        --- set fill mode for a file open for write
cur_mode = SDsetfillmode(fid, fillmode);

        --- set dimval backward compatible  for write
status  = SDsetdimval_comp(dimid, compt_mode);
 
*/

/******************************************************************************
 NAME
	SDcreate -- create a new dataset

 DESCRIPTION
    Simulate a call to ncvardef without having to be in 
    define mode.  name can be at most MAX_NC_NAME
    characters.  Rank can be at most MAX_VAR_DIMS

    It looks like for the call to NC_new_var() we need to 
    have dimension IDs already.  So I guess we should just 
    create the fake dimensions now and when optional 
    information comes in (i.e.  name, tying to other 
    dimensions) we'll go in and modify the structure in place.
    This is gonna be heinous.  Please do not attempt this at home
    See SDselect() for a discussion on how SDS IDs are set up.

 RETURNS
    An ID to the new dataset else FAIL

******************************************************************************/
int32
SDcreate(int32  fid,      /* IN: file ID */
         const char  *name, /* IN: dataset name */
         int32  nt,       /* IN: dataset number type */
         int32  rank,     /* IN: rank of dataset */
         int32 *dimsizes  /* IN: array of dimension sizes */)
{
    intn     i;
    NC      *handle = NULL;
    NC_var  *var = NULL;
    NC_dim  *newdim = NULL;
    int32    sdsid;
    nc_type  nctype;
    char     dimname[MAX_NC_NAME];
    intn     num;
    intn    *dims = NULL;
    intn     is_ragged;
    int32    ret_value = FAIL;

#ifdef SDDEBUG
    fprintf(stderr, "SDcreate: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDcreate );
#endif

    /* check that fid is valid */
    handle = SDIhandle_from_id(fid, CDFTYPE);
    if(handle == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* fudge the name since its optional */
    if((name == NULL) || (name[0] == ' ') || (name[0] == '\0'))
        name = "DataSet";

    /* check if its a ragged array.
       Why is this code still here? -GV */
    if((rank > 1) && dimsizes[rank - 1] == SD_RAGGED) 
      {
#ifdef DEBUG
        printf("YOW!  We have a ragged array kids: %s\n", name);
#endif
        rank--;
        is_ragged = TRUE;
      } 
    else 
      {
        is_ragged = FALSE;        
      }

    /* make fake dimensions which may or may not be over-ridden later */
    dims = (intn *) HDmalloc(rank * sizeof(intn));
    if(dims == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    if(rank > MAX_VAR_DIMS)
      {
        ret_value = FAIL;
        goto done;
      }

    for(i = 0; i < rank; i++) 
      {

          num = (handle->dims ? handle->dims->count : 0);
          sprintf(dimname, "fakeDim%d", num);

          newdim = (NC_dim *) NC_new_dim(dimname, dimsizes[i]);
          if(newdim == NULL) 
            {
                ret_value = FAIL;
                goto done;
            }

          if(handle->dims == NULL) 
            { /* first time */
                handle->dims = NC_new_array(NC_DIMENSION,(unsigned)1, (Void *)&newdim);
                if(handle->dims == NULL)
                  {
                      ret_value = FAIL;
                      goto done;
                  }
            } 
          else 
            {
                if( NC_incr_array(handle->dims, (Void *)&newdim) == NULL)
                  {
                      ret_value = FAIL;
                      goto done;
                  }
            }

          dims[i] = (intn) handle->dims->count -1;

      } /* end for 'i < rank' */

    /* create the actual variable */
    if ((nctype = hdf_unmap_type((int)nt)) == FAIL)
      {
#ifdef SDDEBUG
          /* replace it with NCAdvice or HERROR? */
          fprintf(stderr "SDcreate: hdf_unmap_type failed for %d\n", nt);
#endif
          ret_value = FAIL;
          goto done;
      }

    var = (NC_var *) NC_new_var(name, nctype, (int)rank, dims);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }
    
    /* Set the "newly created" & "set length" flags for use in SDwritedata */
    var->created=TRUE;
    var->set_length=FALSE;

    /* NC_new_var strips off "nativeness" add it back in if appropriate */
    var->HDFtype = nt;
    if (FAIL == (var->HDFsize = DFKNTsize(nt)))
      {
          ret_value    = FAIL;
          goto done;
      } 

    var->cdf     = handle; /* set cdf before calling NC_var_shape */
    /* get a new NDG ref for this sucker */
#ifdef NOT_YET
    var->ndg_ref = Htagnewref(handle->hdf_file,DFTAG_NDG);
#else /* NOT_YET */
    var->ndg_ref = Hnewref(handle->hdf_file);
#endif /* NOT_YET */

    /* set ragged status. Why is this still here -GV */
    var->is_ragged = is_ragged;
    
    /* no ragged array info stored yet */
    if(var->is_ragged) 
      {
        var->rag_list = NULL;
        var->rag_fill = 0;
      }

    /* add it to the handle */
    if(handle->vars == NULL) 
      { /* first time */
          handle->vars = NC_new_array(NC_VARIABLE,(unsigned)1, (Void *)&var);
          if(handle->vars == NULL)
            {
                ret_value = FAIL;
                goto done;
            }
      } 
    else 
      {
          if(handle->vars->count >= MAX_NC_VARS) 
            {
                ret_value = FAIL;
                goto done;
            } 
          else 
            {
                if( NC_incr_array(handle->vars, (Void *)&var) == NULL)
                  {
                      ret_value = FAIL;
                      goto done;
                  } 
            }
      }

    /* compute all of the shape information */
    if(NC_var_shape(var, handle->dims) == -1)
      {
          ret_value = FAIL;
          goto done;
      } 

    /* create a handle we can give back to the user */
    sdsid  = (((int32) fid) << 24) + (((int32) SDSTYPE) << 16);
    sdsid += handle->vars->count -1;

    /* make sure it gets reflected in the file */
    handle->flags |= NC_HDIRTY;

    /* free dims */
    HDfree(dims);

    ret_value = sdsid;

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

      }
    /* Normal cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDcreate);
#endif

    return ret_value;
} /* SDcreate */


/******************************************************************************
 NAME
	SDgetdimid -- get a dimension ID

 DESCRIPTION
    Given an sdsid and a dimension number return a 
    dimid.  Index is a ZERO based quantity

    The dimID looks similar to the sdsID except DIMTYPE 
    is substituted for SDSTYPE as the id-type:

    dimID:
        
        32       24       16               0
        ------------------------------------
        |  fid   | id-type| position index |
        ------------------------------------

 RETURNS
        An ID to the dimension else FAIL
        
******************************************************************************/
int32
SDgetdimid(int32 sdsid,  /* IN: dataset ID */
           intn  number  /* IN: index of dimension */)
{
    NC     *handle = NULL;
    NC_var *var = NULL;
    int32   id;
    int32   dimindex;
    int32   ret_value = FAIL;

#ifdef SDDEBUG
    fprintf(stderr, "SDgetdimid: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDgetdimid );
#endif

    /* get the handle */
    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
        ret_value = FAIL;
        goto done;
      }

    /* get the variable */
    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* check if enough / too many dims */
    if((var->assoc == NULL) || (var->assoc->count < number))
      {
        ret_value = FAIL;
        goto done;
      }

    /* get the dim number out of the assoc array */
    dimindex = var->assoc->values[number];

    /* build the dim id */
    id  = (sdsid & 0xff000000) + (((int32) DIMTYPE) << 16) + dimindex;

    ret_value = id;

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

      }
    /* Normal cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDgetdimid);
#endif

    return ret_value;
} /* SDgetdimid */


/******************************************************************************
 NAME
	SDsetdimname -- give a name to a dimension

 DESCRIPTION
    Set the name of a dimension -- at most MAX_NC_NAME characters.  
    If this name is already in use we should point to the 
    existing dimension with that name.  If the sizes are 
    different return an error.  If this dimension already has
    a name throw it out and use the new one.

 RETURNS
    SUCCEED / FAIL

******************************************************************************/
intn
SDsetdimname(int32  id,   /* IN: dataset ID */
             const char  *name  /* IN: dimension name */)
{
    NC         *handle = NULL;
    NC_dim     *dim = NULL;
    NC_dim    **dp = NULL;
    NC_string  *old = NULL;
    NC_string  *new = NULL;
    NC_array  **ap = NULL;
    int32       len;
    int32       ii;
    intn        ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetdimname: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetdimname );
#endif

    /* get the handle */
    handle = SDIhandle_from_id(id, DIMTYPE);
    if(handle == NULL) 
      {
        ret_value = FAIL;
        goto done;
      }

    /* get the dimension structure */
    dim = SDIget_dim(handle, id);
    if(dim == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* check for name in use */
    len = HDstrlen(name) ;
    dp = (NC_dim**)handle->dims->values ;
    for(ii = 0 ; ii < handle->dims->count ; ii++, dp++) 
      {
          if( len == (*dp)->name->len 
              && HDstrncmp(name, (*dp)->name->values, (size_t)len) == 0) 
            {
                if(dim != (*dp)) 
                  {
                      /* a dimension with this name already exists */
                      /* so change to point to it */
                      if(dim->size != (*dp)->size)
                        {
                            ret_value = FAIL;
                            goto done;
                        }

                      ap = (NC_array **) handle->dims->values;
                      ap += id & 0xffff;
                      NC_free_dim(dim);
                      (*dp)->count += 1;
                      (*ap) = (NC_array *) (*dp);
                      ret_value = SUCCEED;
                      goto done;
                  }
            }
      }
    
    /* throw out the old name if it exists and create a new one */
    old = dim->name;
    new = NC_new_string((unsigned)HDstrlen(name),name);
    if(new == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    dim->name = new;
    NC_free_string(old);
    
    /* make sure it gets reflected in the file */
    handle->flags |= NC_HDIRTY;

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

      }
    /* Normal cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetdimname);
#endif

    return ret_value;
} /* SDsetdimname */


/******************************************************************************
 NAME
	SDendaccess -- close a sds ID

 DESCRIPTION
    Close down this access ID to a data object

    Usually, this will do nothing.  However, if the meta-data 
    has changed and SYNC_ON_EACC is defiend flush it all out 
    to disk.

 RETURNS
        SUCCEED / FAIL          

******************************************************************************/
intn
SDendaccess(int32 id /* IN: dataset ID */)
{
    NC     *handle;
    int32   ret_value = SUCCEED;
	
#ifdef SDDEBUG
    fprintf(stderr, "SDendaccess: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDendaccess );
#endif

    /* get the handle */
    handle = SDIhandle_from_id(id, SDSTYPE);
    if(handle == NULL) 
      {
        ret_value = FAIL;
        goto done;
      }

#ifdef SYNC_ON_EACC

    /* make sure we can write to the file */
    if(handle->flags & NC_RDWR) 
      {
          handle->xdrs->x_op = XDR_ENCODE;

          /* see if the meta-data needs to be updated */
          if(handle->flags & NC_HDIRTY) 
            {
                if(!xdr_cdf(handle->xdrs, &handle) )
                  {
                      ret_value = FAIL;
                      goto done;
                  }

                handle->flags &= ~(NC_NDIRTY | NC_HDIRTY);
            } 
          else 
            {
                /* see if the numrecs info needs updating */
                if(handle->flags & NC_NDIRTY) 
                  {
                      if(!xdr_numrecs(handle->xdrs, handle) )
                        {
                            ret_value = FAIL;
                            goto done;
                        }

                      handle->flags &= ~(NC_NDIRTY);
                  }
            }
      }

#else 

    /* free the AID */
    ret_value = SDIfreevarAID(handle, id & 0xffff);
     
#endif /* SYNC_ON_EACC */
  
done:
    if (ret_value == FAIL)
      { /* Failure cleanup */

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDendaccess);
#endif

    return ret_value;    
} /* SDendaccess */

/******************************************************************************
 NAME
	SDIputattr -- put an attribute in an attribute list

 DESCRIPTION
    Common code for adding an attribute to an attribute list.
    The list gets created if it had previously been empty

 RETURNS
     SUCCEED / FAIL

******************************************************************************/
PRIVATE intn
SDIputattr(NC_array **ap,   /* IN/OUT: attribute list */
           const char *name, /* IN:     attribute name */
           int32      nt,   /* IN:     attribute number type */
           intn       count,/* IN:     number of attribute values */
           const void *      data  /* IN:     attribute values */)
{
    NC_attr *attr = NULL;
    NC_attr **atp = NULL;
    NC_attr *old = NULL;
    nc_type  type;   /* unmap -- HDF type to NC type */
    intn     ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDIputattr: I've been called\n");
#endif
    
    if ((type = hdf_unmap_type((int)nt)) == FAIL)
      {
#ifdef SDDEBUG
          /* replace it with NCAdvice or HERROR? */
          fprintf(stderr "SDIputattr: hdf_unmap_type failed for %d\n", nt);
#endif
          ret_value = FAIL;
          goto done;
      }

    if(*ap == NULL) 
      { /* first time */
          attr = (NC_attr *) NC_new_attr(name,type,(unsigned)count,data) ;
          if(attr == NULL)
            {
                ret_value = FAIL;
                goto done;
            }

          attr->HDFtype = nt; /* Add HDFtype  */
          *ap = NC_new_array(NC_ATTRIBUTE,(unsigned)1, (Void*)&attr) ;
          if(*ap == NULL)
            {
                ret_value = FAIL;
                goto done;
            }
      }
    else
      {
          if((atp = NC_findattr(ap, name)) != NULL) 
            { /* name in use */
                old = *atp ;
                *atp = (NC_attr *) NC_new_attr(name,type,(unsigned)count,data);
                if(*atp == NULL) 
                  {
                      *atp = old;
                      ret_value = FAIL;
                      goto done;
                  }
                (*atp)->HDFtype = nt; /* Add HDFtype  */
                NC_free_attr(old);
            }
          else   
            {
                if((*ap)->count >= MAX_NC_ATTRS) 
                  {  /* Too many */
                      ret_value = FAIL;
                      goto done;
                  }

                /* just add it */
                attr = (NC_attr *) NC_new_attr(name,type,(unsigned)count,data);
                attr->HDFtype = nt; /* Add HDFtype  */
                if(attr == NULL)
                  {
                      ret_value = FAIL;
                      goto done;
                  }

                if(NC_incr_array((*ap), (Void *)&attr) == NULL)
                  {
                      ret_value = FAIL;
                      goto done;
                  }
            }
      }
        
done:
    if (ret_value == FAIL)
      { /* Failure cleanup */

      }
    /* Normal cleanup */

    return ret_value;    
} /* SDIputattr */


/******************************************************************************
 NAME
	SDsetrange -- simulate a call to DFSDsetrange

 DESCRIPTION
    Store range info for this variable in the valid_range 
    attribute.  If that attribute already exists overwrite 
    the current values.  It is assumed that the values are 
    the same type as the data set.

    It is up to the user to decide what is meant by the
    "valid" max and min.

 RETURNS
    On error FAIL else SUCCEED.

******************************************************************************/
intn
SDsetrange(int32 sdsid, /* IN: dataset ID */
           void * pmax,  /* IN: valid max */
           void * pmin   /* IN: valid min */)
{
    NC      *handle = NULL;
    NC_var  *var = NULL;
    uint8    data[80];
    intn     sz;
    intn     ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetrange: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetrange );
#endif

    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    if((pmax == NULL) || (pmin == NULL))
      {
          ret_value = FAIL;
          goto done;
      }

    /* move data values over */
    if (FAIL == (sz = DFKNTsize(var->HDFtype | DFNT_NATIVE)))
      {
          ret_value    = FAIL;
          goto done;
      } 

    HDmemcpy(data, pmin, sz);
    HDmemcpy(data + sz, pmax, sz);

    /* call common code */
    if(SDIputattr(&var->attrs, _HDF_ValidRange, var->HDFtype, (intn) 2, data) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }
    
    /* make sure it gets reflected in the file */
    handle->flags |= NC_HDIRTY;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetrange);
#endif

    return ret_value;    
} /* SDsetrange */


/******************************************************************************
 NAME
	SDIapfromid -- get the attribute list

 DESCRIPTION
    Given a ID figure out what the handle and attribute 
    list pointer are for that object.

 RETURNS
     On error FAIL else SUCCEED.

******************************************************************************/
PRIVATE intn
SDIapfromid(int32       id,      /* IN:  object ID */
            NC        **handlep, /* IN:  handle for this file */
            NC_array ***app      /* OUT: attribute list */)
{
    NC     *handle = NULL;
    NC_var *var = NULL;
    NC_dim *dim = NULL;
    int32   varid;
    intn    ret_value = SUCCEED;

    /* see if its a variable ID */
    handle = SDIhandle_from_id(id, SDSTYPE);
    if(handle != NULL) 
      { 
          /* find the variable */
          var = SDIget_var(handle, id);
          if(var == NULL)
            {
                ret_value = FAIL;
                goto done;
            }

          (*app) = &(var->attrs);
          (*handlep) = handle;
          ret_value = SUCCEED;
          goto done;
      } 

    /* see if its a file ID */
    handle = SDIhandle_from_id(id, CDFTYPE);
    if(handle != NULL) 
      {
          (*app) = &(handle->attrs);
          (*handlep) = handle;
          ret_value = SUCCEED;
          goto done;
      }

    /* see if its a dimension ID */
    handle = SDIhandle_from_id(id, DIMTYPE);
    if(handle != NULL) 
      {
          /* find the dimension */
          dim = SDIget_dim(handle, id);
          if(dim == NULL)
            {
                ret_value = FAIL;
                goto done;
            }

          /* get index of coordinate variable */
          varid = SDIgetcoordvar(handle, dim, (int32)(id & 0xffff), (int32) 0);

          /* get the variable object */
          var = NC_hlookupvar(handle, varid);
          if(var == NULL)
            {
                ret_value = FAIL;
                goto done;
            }


          (*app) = &(var->attrs);
          (*handlep) = handle;
          ret_value = SUCCEED;
          goto done;
      }

    ret_value = FAIL;

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

      }
    /* Normal cleanup */

    return ret_value;    
} /* SDIapfromid */


/******************************************************************************
 NAME
   SDsetattr -- user level function to create and set an attribute

 DESCRIPTION
   Given an ID and an attribute defintion attach the atrribute 
   to the thing represented by the ID.  For starters, the valid 
   IDs could be variable, file or dimesnion IDs

 RETURNS
   On error FAIL else SUCCEED.

******************************************************************************/
intn
SDsetattr(int32 id,    /* IN: object ID */
          const char *name,  /* IN: attribute name */
          int32 nt,    /* IN: attribute number type */
          int32 count, /* IN: number of attribute values */
          const void * data   /* IN: attribute values */)
{
    NC_array **ap = NULL;
    NC        *handle = NULL;
    intn       sz;
    intn       ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetattr: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetattr );
#endif

    /* sanity check args */
    if(name == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    /* This release doesn't support native number types for attr  */
    if (nt & DFNT_NATIVE) 
      {
          ret_value = FAIL;
          goto done;
      }

    /* Make sure that count is less than MAX_ORDER(Vdata)
           and total size is less than MAX_FIELD_SIZE(Vdata) */
    if (FAIL == (sz = DFKNTsize(nt)))
      {
          ret_value = FAIL;
          goto done;
      }

    if ((count > MAX_ORDER) ||
        ((count * sz) > MAX_FIELD_SIZE))
      {
          ret_value = FAIL;
          goto done;
      }

    /* determine what type of ID we've been given */
    if(SDIapfromid(id, &handle, &ap) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* still no handle ? */
    if(handle == NULL)
      {
          ret_value = FAIL;
          goto done;
      }
    
    /* hand over to SDIputattr */
         
    if(SDIputattr(ap, name, nt, count, data) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }
    
    /* make sure it gets reflected in the file */
    handle->flags |= NC_HDIRTY;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetattr);
#endif

    return ret_value;        
} /* SDsetattr */


/******************************************************************************
 NAME
	SDattrinfo -- get info about an attribute

 DESCRIPTION
    Inquire about an attribute.  Attribute indexes are zero based.

    Given the ID of the attribute's parent and the attribute's 
    index return the number type, name and count of the attribute 
    so the user knows how much space to provide to read it

 RETURNS
        On error FAIL else SUCCEED.
******************************************************************************/
intn
SDattrinfo(int32  id,    /* IN:  object ID */
           int32  index, /* IN:  attribute index */
           char  *name,  /* OUT: attribute name */
           int32 *nt,    /* OUT: attribute number type */
           int32 *count  /* OUT: number of attribute values */)
{
    CONSTR(FUNC, "SDattrinfo");    /* for HGOTO_ERROR */
    NC_array  *ap = NULL;
    NC_array **app = NULL;
    NC_attr  **atp = NULL;
    NC        *handle = NULL;
    intn       ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDattrinfo: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDattrinfo );
#endif

    /* sanity check args */
    if((name == NULL) || (nt == NULL) || (count == NULL))
        HGOTO_ERROR(DFE_ARGS, FAIL);

    /* determine what type of ID we've been given */
    if(SDIapfromid(id, &handle, &app) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    ap = (*app);
    if((ap == NULL) || (index >= ap->count))
      {
          ret_value = FAIL;
          goto done;
      }

    /* 
     * ap is the proper attribute list now look up something with this
     *  name
     */
    atp = (NC_attr **) ((char *)ap->values + index * ap->szof);
    if(*atp == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    /* move the information over */
    if(name != NULL) 
      {
#if 0
          HDstrncpy(name, (*atp)->name->values, (*atp)->name->len);
#endif
          HDmemcpy(name, (*atp)->name->values, (*atp)->name->len);
          name[(*atp)->name->len] = '\0';
      }

    *count = (*atp)->data->count;
    *nt = (*atp)->HDFtype;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDattrinfo);
#endif

    return ret_value;    
} /* SDattrinfo */


/******************************************************************************
 NAME
	SDreadattr -- read an attribute's values

 DESCRIPTION
    Read the actual contents of the given attribute
    Assume that the user has called SDinqattr() and so 
    has allocated sufficient space

 RETURNS
    On error FAIL else SUCCEED.

******************************************************************************/
intn
SDreadattr(int32 id,    /* IN:  object ID */
           int32 index, /* IN:  attribute index */
           void * buf    /* OUT: data buffer  */)
{
    CONSTR(FUNC, "SDreadattr");    /* for HGOTO_ERROR */
    NC_array  *ap = NULL;
    NC_array **app = NULL;
    NC_attr  **atp = NULL;
    NC        *handle = NULL;
    intn       ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDreadattr: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDreadattr );
#endif

    /* sanity check args */
    if(buf == NULL)
        HGOTO_ERROR(DFE_ARGS, FAIL);

    /* determine what type of ID we've been given */
    if(SDIapfromid(id, &handle, &app) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    ap = (*app);
    if((ap == NULL) || (index >= ap->count))
      {
          ret_value = FAIL;
          goto done;
      }

    /* 
     * ap is the proper attribute list now look up something with this
     *  index
     */
    atp = (NC_attr **) ((char *)ap->values + index * ap->szof);
    if(*atp == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    /* move the information over */
    HDmemcpy(buf, (*atp)->data->values, (*atp)->data->count * (*atp)->data->szof);

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDreadattr);
#endif

    return ret_value;    
} /* SDreadattr */


/******************************************************************************
 NAME
	SDwritedata -- write a hyperslab of data

 DESCRIPTION
    Write out a chunk o data.  Except for the line setting the 
    XDR op-code this is exactly the same as SDreaddata().  The 
    two routines should really be combined at some point

 RETURNS
    SUCCEED / FAIL
 
******************************************************************************/
intn
SDwritedata(int32  sdsid,  /* IN: dataset ID */
            int32 *start,  /* IN: coords of starting point */
            int32 *stride, /* IN: stride along each dimension */
            int32 *end,    /* IN: number of values to write per dimension */
            void *  data    /* IN: data buffer */)
{
    CONSTR(FUNC, "SDwritedata");    /* for HGOTO_ERROR */
    intn    varid;
    int32   status;
    NC     *handle = NULL;
    NC_dim *dim = NULL;
#ifdef BIG_LONGS
    long    Start[MAX_VAR_DIMS];
    long    End[MAX_VAR_DIMS];
    long    Stride[MAX_VAR_DIMS];
#else
    long   *Start = NULL;
    long   *End = NULL;
    long   *Stride = NULL;
#endif
    intn    no_strides = 0;
    intn    ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDwritedata: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDwritedata );
#endif

    if((start == NULL) || (end == NULL) || (data == NULL))
        HGOTO_ERROR(DFE_ARGS, FAIL);

    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
        handle = SDIhandle_from_id(sdsid, DIMTYPE);
        if(handle == NULL) 
          {
              ret_value = FAIL;
              goto done;
          }

        dim = SDIget_dim(handle, sdsid);
      }

#ifdef QAK
    fprintf(stderr, "SDwritedata: check 1.0\n");
#endif
    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* get ready to write */
    handle->xdrs->x_op = XDR_ENCODE;
    
    /* 
     * figure out the index of the variable to write to
     * the user might have passed us a dimension, in which
     * case we want to write to its coordinate variable
     */
    if(dim) 
      {
        varid = SDIgetcoordvar(handle, dim, (int32)(sdsid & 0xffff), (int32) 0);
      } 
    else 
      {
        /* oops, how do we know this ? */
        varid = (intn)sdsid & 0xffff;
      }

#ifdef QAK
    fprintf(stderr, "SDwritedata: check 2.0\n");
#endif
    /* Check for strides all set to '1', so it acts like NULL was passed */
    if(stride!=NULL)
      {
        int i;
        NC_var *var = SDIget_var(handle, sdsid);

        if(var == NULL)
          {
              ret_value = FAIL;
              goto done;
          }

        no_strides=1;
        /* if the stride for any dim. is not '1', real stride processing has to occur */
        for(i = 0; i < var->assoc->count; i++) 
          {
              if(stride[i]!=1)    
                  no_strides=0;
          }
      } /* end if */

    /*
     * In general, (long) == int32 
     * In cases where it doesn't we need to convert
     */
#ifdef BIG_LONGS

    {
        int i;
        NC_var *var = SDIget_var(handle, sdsid);

        if(var == NULL)
          {
              ret_value = FAIL;
              goto done;
          }
        
        for(i = 0; i < var->assoc->count; i++) 
          {
            Start[i]  = (long) start[i];
            End[i]    = (long) end[i];
            if(stride) 
                Stride[i] = (long) stride[i];
          }
    }

#else

    Start  = (long *)start;
    End    = (long *)end;
    Stride = (long *)stride;

#endif

    /* Check if this data is being written out to a newly created dataset */
    {
        NC_var *var = SDIget_var(handle, sdsid);

        if(var->created) {
            if(!IS_RECVAR(var) && (handle->flags & NC_NOFILL) ) {
              var->set_length=TRUE;
            } /* end if */
            var->created=FALSE;
        } /* end if */
    }

    /* call the writeg routines if a stride is given */
    if(stride == NULL || no_strides==1)
        status = NCvario(handle, varid, Start, End, (Void *)data);
    else
        status = NCgenio(handle, varid, Start, End, Stride, NULL, data);

    if(status == -1)
        ret_value = FAIL;
    else
        ret_value = SUCCEED;
            
done:
    if (ret_value == FAIL)
      { /* Failure cleanup */

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDwritedata);
#endif

    return ret_value;    
} /* SDwritedata */


/******************************************************************************
 NAME
	SDsetdatastrs -- set "data strings"

 DESCRIPTION
    Store information about the 'label', 'units', 'format' and 
    'cordsys' attributes of a dataset.  All of the values 
    are optional.  If no string is desired NULL should be passed 
    in its place.  

 RETURNS
    SUCCEED / FAIL 

******************************************************************************/
intn
SDsetdatastrs(int32 sdsid, /* IN: dataset ID */
              const char *l,     /* IN: label string ("long_name") */
              const char *u,     /* IN: units string ("units") */
              const char *f,     /* IN: format string ("format") */
              const char *c      /* IN: coordsys string ("coordsys") */)
{
    NC     *handle = NULL;
    NC_var *var = NULL;
    intn    ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetdatastrs: I've been called\n");
#endif
    
#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetdatastrs );
#endif

    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    if(l && l[0] != '\0') 
      {
        if(SDIputattr(&var->attrs, _HDF_LongName, DFNT_CHAR, 
                      (intn) HDstrlen(l), l) == FAIL)
          {
              ret_value = FAIL;
              goto done;
          }
      }

    if(u && u[0] != '\0') 
      {
        if(SDIputattr(&var->attrs, _HDF_Units, DFNT_CHAR, 
                      (intn) HDstrlen(u), u) == FAIL)
          {
              ret_value = FAIL;
              goto done;
          }
      }

    if(f && f[0] != '\0') 
      {
        if(SDIputattr(&var->attrs, _HDF_Format, DFNT_CHAR, 
                      (intn) HDstrlen(f), f) == FAIL)
          {
              ret_value = FAIL;
              goto done;
          }
      }
    
    if(c && c[0] !='\0') 
      {
        if(SDIputattr(&var->attrs, _HDF_CoordSys, DFNT_CHAR, 
                      (intn) HDstrlen(c), c) == FAIL)
          {
              ret_value = FAIL;
              goto done;
          }
      }
    
    /* make sure it gets reflected in the file */
    if(l || u || f || c)
        handle->flags |= NC_HDIRTY;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetdatastrs);
#endif

    return ret_value;    
} /* SDsetdatastrs */


/******************************************************************************
 NAME
	SDsetcal -- set calibration information

 DESCRIPTION
    Store calibration information.  What is the formula? Good question -GV

 RETURNS
    SUCCEED / FAIL

******************************************************************************/
intn
SDsetcal(int32   sdsid,/* IN: dataset ID */
         float64 cal,  /* IN: multiplicative factor */
         float64 cale, /* IN: multiplicative factor error */
         float64 ioff, /* IN: integer offset */
         float64 ioffe,/* IN: integer offset error */
         int32   nt    /* IN: number type of uncalibrated data */)
{
    NC     *handle = NULL;
    NC_var *var = NULL;
    intn    ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetcal: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetcal );
#endif
    
    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    if(SDIputattr(&var->attrs, _HDF_ScaleFactor, DFNT_FLOAT64, 
                  (intn) 1, &cal) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    if(SDIputattr(&var->attrs, _HDF_ScaleFactorErr, DFNT_FLOAT64, 
                  (intn) 1, &cale) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    if(SDIputattr(&var->attrs, _HDF_AddOffset, DFNT_FLOAT64, 
                  (intn) 1, &ioff) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    if(SDIputattr(&var->attrs, _HDF_AddOffsetErr, DFNT_FLOAT64, 
                  (intn) 1, &ioffe) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    if(SDIputattr(&var->attrs, _HDF_CalibratedNt, DFNT_INT32, 
                  (intn) 1, &nt) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* make sure it gets reflected in the file */
    handle->flags |= NC_HDIRTY;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetcal);
#endif

    return ret_value;    
} /* SDsetcal */


/******************************************************************************
 NAME
	SDsetfillvalue -- set the fill value

 DESCRIPTION
    Set the fill value for this data set.  The fill value 
    is assumed to have the same number type as the dataset

 RETURNS
    SUCCEED / FAIL 

******************************************************************************/
intn
SDsetfillvalue(int32 sdsid, /* IN: dataset ID */
               void * val    /* IN: fillvalue */)
{
    NC     *handle = NULL;
    NC_var *var = NULL;
    intn    ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetfillvalue: I've been called\n");
#endif
    
#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetfillvalue );
#endif

    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    if(SDIputattr(&var->attrs, _FillValue, var->HDFtype, 
                  (intn) 1, val) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }
    
    /* make sure it gets reflected in the file */
    handle->flags |= NC_HDIRTY;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetfillvalue);
#endif

    return ret_value;    
} /* SDsetfillvalue */


/******************************************************************************
 NAME
	SDgetfillvalue -- get the fill value

 DESCRIPTION
    Retreive the fill value for this data set if one has been
    stored.  The fill value has the same number type as the
    dataset

 RETURNS
    SUCCEED / FAIL
 
******************************************************************************/
intn
SDgetfillvalue(int32 sdsid, /* IN:  dataset ID */
               void * val    /* OUT: fillvalue */)
{
    CONSTR(FUNC, "SDgetfillvalue");    /* for HGOTO_ERROR */
    NC       *handle = NULL;
    NC_var   *var = NULL;
    NC_attr **attr = NULL;
    intn      ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDgetfillvalue: I've been called\n");
#endif
    
#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDgetfillvalue );
#endif

    /* sanity check args */
    if(val == NULL)
        HGOTO_ERROR(DFE_ARGS, FAIL);

    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    attr = (NC_attr **) NC_findattr(&(var->attrs), _FillValue);
    if(attr == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    NC_copy_arrayvals((char *)val, (*attr)->data) ;    
    
done:
    if (ret_value == FAIL)
      { /* Failure cleanup */

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDgetfillvalue);
#endif

    return ret_value;    
} /* SDgetfillvalue */


/******************************************************************************
 NAME
	SDgetdatastrs -- get "data strings"

 DESCRIPTION
    Read information about the 'label', 'units', 'format' and 
    'cordsys' attributes of a dataset.  All of the values 
     are optional.  If no string is desired NULL should be passed 
     in its place.  Assume all buffers are len bytes long.

 RETURNS
    SUCCEED / FAIL
 
******************************************************************************/
intn
SDgetdatastrs(int32 sdsid, /* IN:  dataset ID */
              char *l,     /* OUT: label string ("long_name") */
              char *u,     /* OUT: units string ("units") */
              char *f,     /* OUT: format string ("format") */
              char *c,     /* OUT: coordsys string ("coordsys") */
              intn  len    /* IN:  buffer length */)
{
    NC       *handle = NULL;
    NC_var   *var = NULL;
    NC_attr **attr = NULL;
    intn      ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDgetdatastrs: I've been called\n");
#endif
    
#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDgetdatastrs );
#endif

    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    if(l) 
      {
          attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_LongName);
          if(attr != NULL) 
            {
                if((*attr)->data->count < len)
                  {
                      HDstrncpy((char *)l, (*attr)->data->values,(*attr)->data->count );
                      l[(*attr)->data->count] = '\0';
                  }
                else
                    HDstrncpy((char *)l, (*attr)->data->values,len );
            } 
          else 
              l[0] = '\0';
      }

    if(u) 
      {
          attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_Units);
          if(attr != NULL) 
            {
                if((*attr)->data->count < len)
                  {
                      HDstrncpy((char *)u, (*attr)->data->values,(*attr)->data->count );
                      u[(*attr)->data->count] = '\0';
                  }
                else
                    HDstrncpy((char *)u, (*attr)->data->values, len);

            } 
          else 
              u[0] = '\0';
      }

    if(f) 
      {
          attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_Format);
          if(attr != NULL) 
            {
                if((*attr)->data->count < len)
                  {
                      HDstrncpy((char *)f, (*attr)->data->values, (*attr)->data->count);
                      f[(*attr)->data->count] = '\0';
                  }
                else
                    HDstrncpy((char *)f, (*attr)->data->values, len);
            } 
          else 
              f[0] = '\0';
      }

    if(c) 
      {
          attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_CoordSys);
          if(attr != NULL) 
            {
                if((*attr)->data->count < len)
                  {
                      HDstrncpy((char *)c, (*attr)->data->values, (*attr)->data->count);
                      c[(*attr)->data->count] = '\0';
                  }
                else
                    HDstrncpy((char *)c, (*attr)->data->values, len);

            } 
          else 
              c[0] = '\0';
      }
        
done:
    if (ret_value == FAIL)
      { /* Failure cleanup */

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDgetdatastrs);
#endif

    return ret_value;    
} /* SDgetdatastrs */


/******************************************************************************
 NAME
	SDgetcal -- get calibration information

 DESCRIPTION
    Retreive calibration information.  What is the formula?

 RETURNS
    SUCCEED / FAIL

******************************************************************************/
intn
SDgetcal(int32    sdsid, /* IN:  dataset ID */
         float64 *cal,   /* OUT: multiplicative factor */
         float64 *cale,  /* OUT: multiplicative factor error */
         float64 *ioff,  /* OUT: integer offset  */
         float64 *ioffe, /* OUT: integer offset error */
         int32   *nt     /* OUT: number type of uncalibrated data */)
{
    NC       *handle = NULL;
    NC_var   *var    = NULL;
    NC_attr **attr   = NULL;
    intn      ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDgetcal: I've been called\n");
#endif
    
#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDgetcal );
#endif

    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_ScaleFactor);
    if(attr == NULL)
      {
          ret_value = FAIL;
          goto done;
      }
    NC_copy_arrayvals((char *)cal, (*attr)->data) ;    

    attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_ScaleFactorErr);
    if(attr == NULL)
      {
          ret_value = FAIL;
          goto done;
      }
    NC_copy_arrayvals((char *)cale, (*attr)->data) ;    

    attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_AddOffset);
    if(attr == NULL)
      {
          ret_value = FAIL;
          goto done;
      }
    NC_copy_arrayvals((char *)ioff, (*attr)->data) ;    

    attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_AddOffsetErr);
    if(attr == NULL)
      {
          ret_value = FAIL;
          goto done;
      }
    NC_copy_arrayvals((char *)ioffe, (*attr)->data) ;    

    attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_CalibratedNt);
    if(attr == NULL)
      {
          ret_value = FAIL;
          goto done;
      }
    NC_copy_arrayvals((char *)nt, (*attr)->data) ;    
        
done:
    if (ret_value == FAIL)
      { /* Failure cleanup */

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDgetcal);
#endif

    return ret_value;    
} /* SDgetcal */


/******************************************************************************
 NAME
	SDgetcoordvar -- get index of coordinate variable

 DESCRIPTION
    Given a dimension return the index of its coordinate variable
    creating one if needed.  If we need to create a variable and an nt
    is not supplied (i.e. is equal to zero) use 32bit floats.
        
    If the variable already exists and the existing nt is different
    from the supplied one (and the supplied one is not zero) replace
    the nt by the new one.  ID is needed so that we can set the 
    dimension of the variable correctly if we need to.  Yuck.

 RETURNS
     A variable index or FAIL on error

******************************************************************************/ 
PRIVATE int32
SDIgetcoordvar(NC     *handle, /* IN: file handle */
               NC_dim *dim,    /* IN: dimension to find coord var of */
               int32   id,     /* IN: dimension ID */
               int32   nt      /* IN: number type to use if new variable*/)
{ 
    int32      ii;
    int32      len;
    nc_type    nctype;
    intn       dimindex;
    NC_string *name = NULL;
    NC_var   **dp = NULL;
    NC_var    *var = NULL;
    int32      ret_value = FAIL;

    /* look for a variable with the same name */
    name = dim->name;
    len = dim->name->len;
    dp = (NC_var**)handle->vars->values ;
    for(ii = 0 ; ii < handle->vars->count ; ii++, dp++) 
      {
          if( len == (*dp)->name->len 
              && HDstrncmp(name->values, (*dp)->name->values, (size_t)len) == 0) 
            {
                /* see if we need to change the number type */
                if((nt != 0) && (nt != (*dp)->type)) 
                  {
#ifdef SDDEBUG
                      fprintf(stderr, "SDIgetcoordvar redefining type\n");
#endif
                      if (((*dp)->type = hdf_unmap_type((int)nt)) == FAIL)
                        {
#ifdef SDDEBUG
                            /* replace it with NCAdvice or HERROR? */
                            fprintf(stderr "SDIgetcoordvar: hdf_unmap_type failed for %d\n", nt);
#endif
                            ret_value = FAIL;
                            goto done;
                        }

                      (*dp)->HDFtype = nt;
                      (*dp)->cdf = handle;
                      /* don't forget to reset the sizes  */
                      (*dp)->szof = NC_typelen((*dp)->type);
                      if (FAIL == ((*dp)->HDFsize = DFKNTsize(nt)))
                        {
                            ret_value = FAIL;
                            goto done;
                        }
                
                      /* recompute all of the shape information */
                      /* BUG: this may be a memory leak ??? */
                      if(NC_var_shape((*dp), handle->dims) == -1)
                        {
                            ret_value = FAIL;
                            goto done;
                        }
                  }

                ret_value = ii; /* found it? */
                goto done;
            }
      }

    /* create a new var with this dim as only coord */
    if(nt == 0) 
        nt = DFNT_FLOAT32;

    if ((nctype = hdf_unmap_type((int)nt)) == FAIL)
      {
#ifdef SDDEBUG
          /* replace it with NCAdvice or HERROR? */
          fprintf(stderr "SDIgetcoordvar: hdf_unmap_type failed for %d\n", nt);
#endif
          ret_value = FAIL;
          goto done;
      }

    dimindex = (intn)id;
    var = (NC_var *) NC_new_var(name->values, nctype, (unsigned)1, &dimindex);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* BMR: put back hdf type that was set wrong by NC_new_var; please refer
       to the cvs history of bug #172 for reason on this statement - 4/17/01*/
    var->HDFtype = nt;

    /* get a new NDG ref for this sucker */
#ifdef NOT_YET
    var->ndg_ref = Htagnewref(handle->hdf_file,DFTAG_NDG);
#else /* NOT_YET */
    var->ndg_ref = Hnewref(handle->hdf_file);
#endif /* NOT_YET */

    /* add it to the handle */
    if(handle->vars->count >= MAX_NC_VARS)
      {
          ret_value = FAIL;
          goto done;
      }

    var->cdf = handle; /* set cdf before calling NC_var_shape */
    /* compute all of the shape information */
    if(NC_var_shape(var, handle->dims) == -1)
      {
          ret_value = FAIL;
          goto done;
      }

    if(NC_incr_array(handle->vars, (Void *)&var) == NULL)
      {
          ret_value = FAIL;
          goto done;
      }
    
    ret_value = handle->vars->count - 1;

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

      }
    /* Normal cleanup */

    return ret_value;    
} /* SDIgetcoordvar */


/******************************************************************************
 NAME
	SDsetdimstrs -- set "dimension strings"

 DESCRIPTION
    Store information about the 'label', 'units' and 'format' 
    attributes of a dimension.  All three of the values are optional.  
    If no string is desired NULL should be passed in its place.

 RETURNS
    SUCCEED / FAIL

******************************************************************************/ 
intn
SDsetdimstrs(int32 id, /* IN: dimension ID */
             const char *l,  /* IN: label string ("long_name") */
             const char *u,  /* IN: units string ("units") */
             const char *f   /* IN: format string ("format") */)
{
    intn       varid;
    NC        *handle = NULL;
    NC_dim    *dim = NULL;
    NC_var    *var = NULL;
    intn       ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetdimstrs: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetdimstrs );
#endif

    /* get the handle */
    handle = SDIhandle_from_id(id, DIMTYPE);
    if(handle == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* get the dimension structure */
    dim = SDIget_dim(handle, id);
    if(dim == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* look for a variable with the same name */
    varid = (intn)SDIgetcoordvar(handle, dim, (int32)(id & 0xffff), (int32)0);
    if(varid == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* get the variable object */
    var = NC_hlookupvar(handle, varid);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* set the attributes */
    if(l && l[0] != '\0')
      {
        if(SDIputattr(&var->attrs, _HDF_LongName, DFNT_CHAR,
                      (intn) HDstrlen(l), l) == FAIL)
          {
              ret_value = FAIL;
              goto done;
          }
      }

    if(u && u[0] != '\0')
      {
        if(SDIputattr(&var->attrs, _HDF_Units, DFNT_CHAR,
                      (intn) HDstrlen(u), u) == FAIL)
          {
              ret_value = FAIL;
              goto done;
          }
      }

    if(f && f[0] != '\0')
      {
        if(SDIputattr(&var->attrs, _HDF_Format, DFNT_CHAR,
                      (intn) HDstrlen(f), f) == FAIL)
          {
              ret_value = FAIL;
              goto done;
          }
      }

    /* make sure it gets reflected in the file */
    handle->flags |= NC_HDIRTY;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetdimstrs);
#endif

    return ret_value;    
} /* SDsetdimstrs */


/******************************************************************************
 NAME
	SDIfreevarAID -- free a variables AID

 DESCRIPTION
    Free the AID of the variable with the given index

 RETURNS
    SUCCEED / FAIL 

******************************************************************************/
PRIVATE int32
SDIfreevarAID(NC   *handle, /* IN: file handle */
              int32 index   /* IN: variable index */)
{
    NC_array **ap = NULL;
    NC_var    *var = NULL;
    int32      ret_value = SUCCEED;

    if(handle == NULL || !handle->vars)
      {
          ret_value = FAIL;
          goto done;
      }

    if(index < 0 || index > handle->vars->count)
      {
          ret_value = FAIL;
          goto done;
      }

    ap = (NC_array **)handle->vars->values;
    ap += index;

    var = (NC_var *) *ap;

    if(var->aid != 0 && var->aid != FAIL)
      {
        if (Hendaccess(var->aid) == FAIL)
          {
              ret_value = FAIL;
              goto done;
          }
      }

    var->aid = FAIL;

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

      }
    /* Normal cleanup */

    return ret_value;    
} /* SDIfreevarAID */
 

/******************************************************************************
 NAME
	SDsetdimscale -- store scale information for the dimension

 DESCRIPTION
    Store information about the 'scales' of a dimension.  Dimensions
    do not have to have the same number type as the dataset.

 RETURNS
    SUCCEED / FAIL

******************************************************************************/ 
intn
SDsetdimscale(int32 id,    /* IN: dimension ID */
              int32 count, /* IN: number of values */
              int32 nt,    /* IN: number type of data */
              void * data   /* IN: scale values */)
{
    NC        *handle = NULL;
    NC_dim    *dim = NULL;
    int32      status;
    intn       varid;
    long       start[1];
    long       end[1];
    intn       ret_value = SUCCEED;
	
#ifdef SDDEBUG
    fprintf(stderr, "SDsetdimscales: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetdimscale );
#endif

    /* get the handle */
    handle = SDIhandle_from_id(id, DIMTYPE);
    if(handle == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* get the dimension structure */
    dim = SDIget_dim(handle, id);
    if(dim == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* sanity check, if not SD_UNLIMITED */
    if( dim->size != 0 && count != dim->size)
      {
          ret_value = FAIL;
          goto done;
      }

    /* look for a variable with the same name */
    varid = (intn)SDIgetcoordvar(handle, dim, id & 0xffff, nt);
    if(varid == -1)
      {
          ret_value = FAIL;
          goto done;
      }

    /* store the data */
    handle->xdrs->x_op = XDR_ENCODE;
    start[0] = 0;
    end[0]   = count;
    status = NCvario(handle, varid, start, end, (Void *)data);
    if(status == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* free the AID */
    status = SDIfreevarAID(handle, varid);
    if(status == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* make sure it gets reflected in the file */
    handle->flags |= NC_HDIRTY;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetdimscale);
#endif

    return ret_value;    
} /* SDsetdimsacle */


/******************************************************************************
 NAME
	SDgetdimscale -- get scale information for the dimension

 DESCRIPTION
    Retreive the scale information stored with a dimension.  It is 
    assumed that the user has called SDdiminfo() and that the data 
    array is long enough to hold the values.

 RETURNS
    SUCCEED / FAIL

******************************************************************************/ 
intn 
SDgetdimscale(int32 id,   /* IN:  dimension ID */
              void * data  /* OUT: scale values */)
{
    NC        *handle = NULL;
    NC_dim    *dim = NULL;
    NC_var    *vp = NULL;
    int32      status;
    intn       varid;
    long       start[1];
    long       end[1];
    intn       ret_value = SUCCEED;
	
#ifdef SDDEBUG
    fprintf(stderr, "SDgetdimscale: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDgetdimscale );
#endif

    /* sanity check args */
    if(data == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* get the handle */
    handle = SDIhandle_from_id(id, DIMTYPE);
    if(handle == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    /* get the dimension structure */
    dim = SDIget_dim(handle, id);
    if(dim == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* look for a variable with the same name */
    /* assume type Float32 can be over-ridden later */
    varid = (intn)SDIgetcoordvar(handle, dim, (int32)(id & 0xffff), (int32)0);
    if(varid == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* store the data */
    handle->xdrs->x_op = XDR_DECODE;
    start[0] = 0;
    if (dim->size != 0)
        end[0] = dim->size;
    else   
      {
        if (handle->file_type != HDF_FILE)
            end[0] = handle->numrecs;
        else   
          {
            vp = SDIget_var(handle, varid);
            if (vp == NULL)
              {
                  ret_value = FAIL;
                  goto done;
              }

            end[0] = vp->numrecs;
          }
      }
  
    status = NCvario(handle, varid, start, end, (Void *)data);
    if(status == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* free the AID */
    status = SDIfreevarAID(handle, varid);
    if(status == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDgetdimscale);
#endif

    return ret_value;    
} /* SDsetdimsacle */


/******************************************************************************
 NAME
	SDdiminfo -- get info about a dimension

 DESCRIPTION
    Return basic information about a dimension (name, sizes, number
    of attributes, number type, etc...) The user is repsonsible for 
    allocating space to hold the dataset name.  It can be at most 
    MAX_NC_NAME characters in length.  NULL can be passed for the 
    name if it is not required.

 RETURNS
    SUCCEED / FAIL

******************************************************************************/ 
intn
SDdiminfo(int32  id,    /* IN:  dimension ID */
          char  *name,  /* OUT: name of the dimension */
          int32 *size,  /* OUT: size of the dimension */
          int32 *nt,    /* OUT: number type of scales */
          int32 *nattr  /* OUT: the number of local attributes */)
{
    NC      *handle = NULL;
    NC_dim  *dim = NULL;
    NC_var **dp = NULL;
    intn     ii;
    intn     len;
    int      ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDdiminfo: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDdiminfo );
#endif

    handle = SDIhandle_from_id(id, DIMTYPE);
    if(handle == NULL)
      {
          ret_value = FAIL;
          goto done;
      }
 
    if(handle->dims == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    dim = SDIget_dim(handle, id);
    if(dim == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    if(name != NULL) 
      {
#if 0
        HDstrncpy(name, dim->name->values, dim->name->len);
#endif
        HDmemcpy(name, dim->name->values, dim->name->len);
        name[dim->name->len] = '\0';
      } 
    else 
        name = dim->name->values;

    *size  = dim->size;

    if(handle->vars) 
      {
          len = dim->name->len;
          dp = (NC_var**)handle->vars->values;
          for(ii = 0 ; ii < handle->vars->count ; ii++, dp++) 
            {
                if( len == (*dp)->name->len 
                    && HDstrncmp(name, (*dp)->name->values, (*dp)->name->len) == 0)
                  {
                      if (handle->file_type == HDF_FILE)
                          *nt = ((*dp)->numrecs ? (*dp)->HDFtype : 0);
                      else 
                          *nt = (*dp)->HDFtype;

                      *nattr = ((*dp)->attrs ? (*dp)->attrs->count : 0);
                      ret_value = SUCCEED;
                      goto done;
                  }
            }
      }

    /* no var so return NULL values */
    *nt    = 0;
    *nattr = 0;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDdiminfo);
#endif

    return ret_value;    
} /* SDdiminfo */


/******************************************************************************
 NAME
	SDgetdimstrs -- get "data strings"

 DESCRIPTION
    Read as many of the dimension strings as possible.  Assume that 
    if a pointer is not NULL that we have len bytes that we can use 
    to return the values

 RETURNS
     SUCCEED / FAIL 

******************************************************************************/ 
intn
SDgetdimstrs(int32 id,  /* IN:  dataset ID */
             char *l,   /* OUT: label string ("long_name") */
             char *u,   /* OUT: units string ("units") */
             char *f,   /* OUT: format string ("format") */
             intn  len  /* IN:  buffer length */)
{
    NC       *handle = NULL;
    NC_var   *var = NULL;
    NC_var  **dp = NULL;
    NC_dim   *dim = NULL;
    NC_attr **attr = NULL;
    char     *name = NULL;
    int32     ii;
    int32     namelen;
    intn      ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDgetdimstrs: I've been called\n");
#endif
    
#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDgetdimstrs );
#endif

    handle = SDIhandle_from_id(id, DIMTYPE);
    if(handle == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    dim = SDIget_dim(handle, id);
    if(dim == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* need to get a pointer to the var now */
    var = NULL;
    if(handle->vars) 
      {
          name = dim->name->values;
          namelen = HDstrlen(name);
          dp = (NC_var**)handle->vars->values;
          for(ii = 0 ; ii < handle->vars->count ; ii++, dp++) 
            {
                if( namelen == (*dp)->name->len 
                    && HDstrncmp(name, (*dp)->name->values, (size_t)namelen) == 0)
                  {
                      var = (*dp);
                  }
            }
      }

    if(!var)
      {
          ret_value = FAIL;
          goto done;
      }

    if(l) 
      {
          attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_LongName);
          if(attr != NULL) 
            {
                intn minlen;
                minlen = (len > (*attr)->data->count)? (*attr)->data->count: len;
                HDstrncpy((char *)l, (*attr)->data->values, minlen);
                if((*attr)->data->count < len)
                    l[(*attr)->data->count] = '\0';
            } 
          else 
              l[0] = '\0';
      }

    if(u) 
      {
          attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_Units);
          if(attr != NULL) 
            {
                intn minlen;
                minlen = (len > (*attr)->data->count)? (*attr)->data->count: len;
                HDstrncpy((char *)u, (*attr)->data->values, minlen);
                if((*attr)->data->count < len)
                    u[(*attr)->data->count] = '\0';
            } 
          else 
              u[0] = '\0';
      }

    if(f) 
      {
          attr = (NC_attr **) NC_findattr(&(var->attrs), _HDF_Format);
          if(attr != NULL) 
            {
                intn minlen;
                minlen = (len > (*attr)->data->count)? (*attr)->data->count: len;
                HDstrncpy((char *)f, (*attr)->data->values, minlen);
                if((*attr)->data->count < len)
                    f[(*attr)->data->count] = '\0';
            } 
          else 
              f[0] = '\0';
      }

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDgetdimstrs);
#endif

    return ret_value;    
} /* SDgetdimstrs */


/******************************************************************************
 NAME
	SDsetexternalfile -- store info in a separate file
 USAGE
	int32 SDsetexternalfile(id, filename, offset)
        int32   id;                  
        const char  * filename;            
        int32   offset;              

 DESCRIPTION
    Specify that the actual data for this dataset be stored in a 
    separate file (and "external file" in HDF terms).

    Only the data (as in SDwritedata()) will be stored externally.  
    Attributes and such will still be in the main file

    IMPORTANT:  It is the user's responsibility to see that the 
    separate files are transported when the main file is moved.

    IMPORTANT:  This can only be called *once* for a given dataset.  
    The HDF utility 'hdfpack' may be able to undo it.

    IMPORTANT:  This will only work on datasets stored in HDF files.

    FORTRAN	- sfsextf

 RETURNS
    SUCCEED/FAIL

******************************************************************************/ 
intn 
SDsetexternalfile(int32 id,       /* IN: dataset ID */
                  const char *filename, /* IN: name of external file */
                  int32 offset    /* IN: offset in external file */)
{
    NC       *handle = NULL;
    NC_var   *var = NULL;
    intn      status;
    int       ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetexternalfile: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetexternalfile );
#endif

    if(NULL == filename || offset < 0)
      {
          ret_value = FAIL;
          goto done;
      }

    handle = SDIhandle_from_id(id, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE)
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, id);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* already exists */
    if(var->data_ref) 
      {
          /* no need to give a length since the element already exists */
          status = (intn)HXcreate(handle->hdf_file, (uint16)DATA_TAG, 
                                  (uint16) var->data_ref,
                                  filename, offset, (int32)0);
      } 
    else 
      {
          int32   length;

          /* look up the length */
          length = var->len;

          /* element doesn't exist so we need a reference number */
#ifdef NOT_YET
          var->data_ref = Htagnewref(handle->hdf_file,DATA_TAG);
#else /* NOT_YET */
          var->data_ref = Hnewref(handle->hdf_file);
#endif /* NOT_YET */
          if(var->data_ref == 0)
            {
                ret_value = FAIL;
                goto done;
            }

          /* need to give a length since the element does not exist yet */
          status = (intn)HXcreate(handle->hdf_file, (uint16)DATA_TAG, 
                                  (uint16) var->data_ref,
                                  filename, offset, length);

      }

    if(status != FAIL) 
      {
          if((var->aid != 0) && (var->aid != FAIL))
            {
                if (Hendaccess(var->aid) == FAIL)
                  {
                      ret_value = FAIL;
                      goto done;
                  }

            }

          var->aid = status;
          ret_value = SUCCEED;
      }
    else
        ret_value = FAIL;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetexternalfile);
#endif

    return ret_value;    
} /* SDsetexternalfile */


/******************************************************************************
 NAME
	SDsetnbitdataset -- Create/convert a dataset to n-bit representation


 DESCRIPTION
    Specify that the actual data for this dataset be represented as a
    n-bit dataset internally in the HDF file.

    The start_bit parameter determines the lowest bit to write out,
    the bit_len parameter determines how many bits to write out.  The
    bits in the data include the lowest bit (start_bit) and count up
    bit_len-1 bits to write out.  For example, starting at bit 2
    and writing 4 bits from the following bit data, "01111011", would
    write out the bit data, "1110", to the dataset on disk.

    The sign_ext parameter determines whether the top bit (highest bit #)
    is used to sign extend the bits whether data is read back from the
    disk.  The fill_one parameter is used to determine whether to
    fill the "background bits" (the bits not in the data written to the
    file) with 1's or 0's when the data is read back from the file.

    Only the data (as in SDwritedata()) will be stored in n-bit
    representation.  Attributes and such will still be stored normally.

    IMPORTANT:  This will only work on datasets stored in HDF files.

    NOTE: n-bit "compression" is described more fully in the cnbit.c file.

 RETURNS
    SUCCEED/FAIL

******************************************************************************/ 
intn 
SDsetnbitdataset(int32 id,       /* IN: dataset ID */
                 intn start_bit, /* IN: starting bit offset (lowest=0) */
                 intn bit_len,   /* IN: # of bits to write */
                 intn sign_ext,  /* IN: Whether to sign extend */
                 intn fill_one   /* IN: Whether to fill background w/1's */)
{
    NC        *handle = NULL;
    NC_var    *var = NULL;
    model_info m_info;  /* modeling information for the HCcreate() call */
    comp_info  c_info;  /* "compression" information for the HCcreate() call */
    intn       status;
    intn       ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetnbitdataset: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetnbitdataset );
#endif

    if(start_bit < 0 || bit_len <= 0)
      {
          ret_value = FAIL;
          goto done;
      }

    handle = SDIhandle_from_id(id, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE)
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, id);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* set up n-bit parameters */
    c_info.nbit.nt        = var->HDFtype;
    c_info.nbit.sign_ext  = sign_ext;
    c_info.nbit.fill_one  = fill_one;
    c_info.nbit.start_bit = start_bit;
    c_info.nbit.bit_len   = bit_len;

#ifdef SDDEBUG
    printf("SDsetnbitdata(): nt=%d, sign_ext=%d, fill_one=%d, start_bit=%d, bit_len=%d\n",(intn)c_info.nbit.nt,(intn)c_info.nbit.sign_ext,(intn)c_info.nbit.fill_one,(intn)c_info.nbit.start_bit,(intn)c_info.nbit.bit_len);
#endif
    if(!var->data_ref) 
      {   /* doesn't exist */
#ifdef SDDEBUG
          printf("SDsetnbitdata(): dataset doesn't exist\n");
#endif

          /* element doesn't exist so we need a reference number */
#ifdef NOT_YET
          var->data_ref=Htagnewref(handle->hdf_file,DATA_TAG);
#else /* NOT_YET */
          var->data_ref=Hnewref(handle->hdf_file);
#endif /* NOT_YET */
          if(var->data_ref == 0)
            {
                ret_value = FAIL;
                goto done;
            }
      } /* end if */

    status=(intn)HCcreate(handle->hdf_file,(uint16)DATA_TAG,
                          (uint16) var->data_ref,COMP_MODEL_STDIO,&m_info,
                          COMP_CODE_NBIT, &c_info);

#ifdef SDDEBUG
    printf("SDsetnbitdata(): HCcreate() status=%d\n",(intn)status);
#endif
    if(status != FAIL) 
      {
          if((var->aid != 0) && (var->aid != FAIL))
            {
                if (Hendaccess(var->aid) == FAIL)
                  {
                      ret_value = FAIL;
                      goto done;
                  }
            }

          var->aid = status;
      } /* end if */

    ret_value = status;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetnbitdataset);
#endif

    return ret_value;    
} /* SDsetnbitdataset */


/******************************************************************************
 NAME
	SDsetcompress -- Create/convert a dataset to compressed representation

 DESCRIPTION
    Specify a compression scheme for an SD dataset.

    Valid compression types available for this interface are listed in
    hcomp.h as COMP_nnnn.

    IMPORTANT:  This will only work on datasets stored in HDF files.

 RETURNS
    SUCCEED/FAIL

******************************************************************************/ 
intn 
SDsetcompress(int32 id,                /* IN: dataset ID */
              comp_coder_t comp_type,  /* IN: the type of compression to 
					  perform on the next image */
              comp_info *c_info        /* IN: ptr to compression info struct*/)
{
    NC        *handle;
    NC_var    *var;
    NC_dim    *dim;     /* to check if the dimension is unlimited */
    int32      dimindex;/* to obtain the NC_dim record */
    model_info m_info;  /* modeling information for the HCcreate() call */
    intn       status = FAIL;
    intn       ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetcompress: I've been called\n");
#endif /* SDDEBUG */

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetcompress );
#endif

    if (comp_type < COMP_CODE_NONE || comp_type >= COMP_CODE_INVALID)
      {
          ret_value = FAIL;
          goto done;
      }

    handle = SDIhandle_from_id(id, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE)
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, id);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* When the compression is SZIP, disallow the use of unlimited dimension */
    if (comp_type == COMP_CODE_SZIP)
    {
        /* Get the index of the SDS' first dimension from the list of indices
         * branching out from NC_var.  This index indicates where this dim
         * is in the "dims" list branching out from NC. */
        dimindex = var->assoc->values[0];

        /* Retrieve the NC_dim record to check for unlimited dimension */
        dim = SDIget_dim(handle, dimindex);
        if(dim == NULL)
        {
            ret_value = FAIL;
            goto done;
        }

        /* If this dimension is unlimited, then return FAIL; the subsequent
         * writing of this SDS will write uncompressed data */
        if (dim->size == SD_UNLIMITED)
        {
            ret_value = FAIL;
            goto done;
        }
    }

#ifdef SDDEBUG
    printf("SDsetcompress(): var->data_ref=%d\n",(int)var->data_ref);
#endif /* SDDEBUG */
    if(!var->data_ref) 
      {   /* doesn't exist */
#ifdef SDDEBUG
          printf("SDsetcompress(): dataset doesn't exist\n");
#endif /* SDDEBUG */

          /* element doesn't exist so we need a reference number */
#ifdef NOT_YET
          var->data_ref=Htagnewref(handle->hdf_file,DATA_TAG);
#else /* NOT_YET */
          var->data_ref=Hnewref(handle->hdf_file);
#endif /* NOT_YET */
          if(var->data_ref == 0)
            {
                ret_value = FAIL;
                goto done;
            }
      } /* end if */

    status=(intn)HCcreate(handle->hdf_file,(uint16)DATA_TAG,
                          (uint16) var->data_ref,COMP_MODEL_STDIO,&m_info,
                          comp_type, c_info);

#ifdef SDDEBUG
    printf("SDsetcompress(): HCcreate() status=%d\n",(intn)status);
    if(status==FAIL)
        HEprint(stderr,0);
#endif /* SDDEBUG */

    if(status != FAIL) 
      {
          if((var->aid != 0) && (var->aid != FAIL))
            {
                if (Hendaccess(var->aid) == FAIL)
                  {
                      ret_value = FAIL;
                      goto done;
                  }
            }

          var->aid = status;
      } /* end if */

    /* Insert data tag/ref into the variable's Vgroup */
    if(var->vgid) 
      {
          int32 vg;

          /* attach to the variable's Vgroup */
          vg = Vattach(handle->hdf_file, var->vgid, "w");
          if(vg == FAIL)
            {
                ret_value = FAIL;
                goto done;
            }
        
          /* add new Vdata to existing Vgroup */
          if (Vaddtagref(vg, (int32) DATA_TAG, (int32) var->data_ref) == FAIL)
            {
                ret_value = FAIL;
                goto done;
            }
        
          /* detach from the variable's VGroup --- will no longer need it */
          if (Vdetach(vg) == FAIL)
            {
                ret_value = FAIL;
                goto done;
            }
      }

    /* added a new object -- make sure we flush the header */
    handle->flags |= NC_HDIRTY;
            
    ret_value = (status != FAIL ? SUCCEED : FAIL);

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetcompress);
#endif

    return ret_value;    
} /* SDsetcompress */

/******************************************************************************
 NAME
	SDgetcompress -- Retrieves compression information of a dataset

 DESCRIPTION
    This routine uses HCgetcompress to retrieve the compression type
    and the compression information of the identified dataset.

 RETURNS
    SUCCEED/FAIL

 MODIFICATION
    July 2001: Added to fix bug #307 - BMR 

******************************************************************************/ 
intn 
SDgetcompress(int32     id,     /* IN: dataset ID */
              comp_coder_t*    comp_type,   /* OUT: the type of compression */
              comp_info* c_info)/* OUT: ptr to compression information 
				structure for storing the retrieved info */
{
    CONSTR(FUNC, "SDgetcompress");    /* for HGOTO_ERROR */
    NC        *handle;
    NC_var    *var;
    intn       status = FAIL;
    intn       ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDgetcompress: I've been called\n");
#endif /* SDDEBUG */

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDgetcompress );
#endif

    /* clear error stack */
    HEclear();

    if(comp_type == NULL || c_info == NULL)
	HGOTO_ERROR(DFE_ARGS, FAIL);

    handle = SDIhandle_from_id(id, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE)
	HGOTO_ERROR(DFE_ARGS, FAIL);
    if(handle->vars == NULL)
	HGOTO_ERROR(DFE_ARGS, FAIL);

    var = SDIget_var(handle, id);
    if(var == NULL) HGOTO_ERROR(DFE_ARGS, FAIL);

#ifdef SDDEBUG
    printf("SDgetcompress(): var->data_ref=%d, var->aid=%d\n",(int)var->data_ref, (int)var->aid);
#endif /* SDDEBUG */
    if(!var->data_ref) 
	HGOTO_ERROR(DFE_ARGS, FAIL);

    /* use lower-level routine to get the compression information */
    status = HCgetcompress(handle->hdf_file, var->data_tag, var->data_ref, 
		comp_type, c_info);
    if(status==FAIL) HGOTO_ERROR(DFE_INTERNAL, FAIL);

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

      }
    /* Normal cleanup */

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

/******************************************************************************
 NAME
	SDfindattr -- find an attribute's index by name

 DESCRIPTION
    Given an ID to an object and an attribute name return the index 
    of the attribute with that name.  This does not support any
    form of wildcards / regular expressions

 RETURNS
     An attribute index or FAIL

******************************************************************************/ 
int32
SDfindattr(int32 id,       /* IN: object ID */
           const char *attrname  /* IN: attribute name */)
{
    NC_array  *ap = NULL;
    NC_array **app = NULL;
    NC_attr  **attr = NULL;
    NC        *handle = NULL;
    int32      attrid;
    int32      len;
    int32      ret_value = FAIL;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDfindattr );
#endif

    /* determine what type of ID we've been given */
    if(SDIapfromid(id, &handle, &app) == FAIL)
      {
          ret_value = FAIL;
          goto done;
      }

    ap = (*app);
    if(ap == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* 
     * ap is the proper attribute list now look up something with this
     *  name
     */

    attr = (NC_attr **) ap->values;
    len = HDstrlen(attrname);
    
    for(attrid = 0 ; attrid < ap->count ; attrid++, attr++)
      {
          if( len == (*attr)->name->len 
              && HDstrncmp(attrname, (*attr)->name->values, (size_t)len) == 0)
            {
                ret_value = attrid ; /* found it */
                goto done;
            }
      }

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDfindattr);
#endif

    return ret_value;        
} /* SDfindattr */


/******************************************************************************
 NAME
	SDidtoref -- get a unique reference number for this dataset

 DESCRIPTION
    Given an index return the ref of the associated NDG for 
    inclusion in Vgroups and annotations

 RETURNS
     A reference number or FAIL

******************************************************************************/
int32
SDidtoref(int32 id /* IN: dataset ID */)
{
    NC       *handle = NULL;
    NC_var   *var = NULL;
    int32     ret_value = FAIL;

#ifdef SDDEBUG
    fprintf(stderr, "SDidtoref: I've been called\n");
#endif
    
#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDidtoref );
#endif

    handle = SDIhandle_from_id(id, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE) 
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, id);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    ret_value = (int32) var->ndg_ref;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDidtoref);
#endif

    return ret_value;    
} /* SDidtoref */


/******************************************************************************
 NAME
	SDreftoindex -- map a reference number to a dataset index

 DESCRIPTION
    Given a ref number return the index of the cooresponding dataset

 RETURNS
    A dataset index or FAIL

******************************************************************************/
int32
SDreftoindex(int32 fid, /* IN: file ID */
             int32 ref  /* IN: reference number */)
{
    NC       *handle = NULL;
    NC_var  **dp = NULL;
    intn      ii;
    int32     ret_value = FAIL;

#ifdef SDDEBUG
    fprintf(stderr, "SDreftoindex: I've been called\n");
#endif
    
#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDreftoindex );
#endif

    handle = SDIhandle_from_id(fid, CDFTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE) 
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    dp = (NC_var**) handle->vars->values;
    for(ii = 0 ; ii < handle->vars->count ; ii++, dp++)
      {
        if((*dp)->ndg_ref == ref)
          {
            ret_value = ii;
            goto done;
          }
      }
    
    ret_value = FAIL;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDreftoindex);
#endif

    return ret_value;    
} /* SDreftoindex */


/******************************************************************************
 NAME
	SDisrecord -- check is var is a record variable

 DESCRIPTION
    Return TRUE if the dataset in question is a record variable
    else FALSE

 RETURNS
    TRUE/FALSE

******************************************************************************/
int32
SDisrecord(int32 id /* IN: dataset ID */)
{
    NC       *handle;
    NC_var   *var;
    int32     ret_value = TRUE;

#ifdef SDDEBUG
    fprintf(stderr, "SDisrecord: I've been called\n");
#endif
    
#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDisrecord );
#endif

    handle = SDIhandle_from_id(id, SDSTYPE);
    if(handle == NULL)
      {
        ret_value = FALSE;
        goto done;
      }

    if(handle->vars == NULL)
      {
        ret_value = FALSE;
        goto done;
      }

    var = SDIget_var(handle, id);
    if(var == NULL)
      {
        ret_value = FALSE;
        goto done;
      }

    if(var->shape[0] == SD_UNLIMITED)
        ret_value = TRUE;
    else
        ret_value = FALSE;

done:
    if (ret_value == FALSE)
      { /* FALSE cleanup */

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
   TRACE_OFF (PABLO_mask, ID_SDisrecord);
#endif

    return ret_value;    
} /* SDisrecord */


/******************************************************************************
 NAME
	SDiscoordvar -- check is var is a coord var

 DESCRIPTION
    Return TRUE if the dataset in question is a coordinate variable

 RETURNS
     TRUE/FALSE

******************************************************************************/
intn
SDiscoordvar(int32 id /* IN: dataset ID */)
{
    NC       *handle = NULL;
    NC_var   *var = NULL;
    NC_dim   *dim = NULL;
    int32     dimindex;
    intn      ret_value = TRUE;

#ifdef SDDEBUG
    fprintf(stderr, "SDiscoordvar: I've been called\n");
#endif
    
#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDiscoordvar );
#endif

    handle = SDIhandle_from_id(id, SDSTYPE);
    if(handle == NULL)
      {
        ret_value = FALSE;
        goto done;
      }

    if(handle->vars == NULL)
      {
        ret_value = FALSE;
        goto done;
      }

    var = SDIget_var(handle, id);
    if(var == NULL)
      {
        ret_value = FALSE;
        goto done;
      }

    if(var->assoc->count != 1)
      {
        ret_value = FALSE;
        goto done;
      }

    dimindex = var->assoc->values[0];

    dim = SDIget_dim(handle, dimindex);
    if(dim == NULL)
      {
        ret_value = FALSE;
        goto done;
      }

    if(var->name->len != dim->name->len)
      {
        ret_value = FALSE;
        goto done;
      }

    if(HDstrcmp(var->name->values, dim->name->values))
      {
        ret_value = FALSE;
        goto done;
      }

    ret_value = TRUE;

done:
    if (ret_value == FALSE)
      { /* FALSE cleanup */

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDiscoordvar);
#endif

    return ret_value;    
} /* SDiscoordvar */


#if 0
/* ---------------------------- RAGGED ARRAYS ----------------------------- */
/*

  Ragged arrays are a cross between datasets and index structures.  The
  basic idea is that all but one of the dimensions is constant.  The
  other dimension can vary over the course of the dataset.  This is
  useful for storing equalarea grids and for making alogrithms much
  more complex.  

  Ragged arrays can be multi-dimensional and, eventually, record variables
  too.  A 2-dimensional ragged array would look like:

  **********
  ********
  *********    <---------- This is a line
  *****
  ***
  *********
  *******

  The above ragged array has 7 "lines" the "line length" of the fifth line is 
  three.  It is not necessary to set all of the line lengths at the same time
  nor retreive them all at the same time.   However, to specify the line
  length for line X, the length must be specified for all Y < X (is this
  really necessary?)

  Internally, the above ragged array would be stored as a one-dimensional
  dataset.  In addition, there will be a rag_fill array that contains the
  line lengths.  This rag_fill array will get written to a separate
  structure in the file (tag DFTAG_SDRAG).

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


/******************************************************************************
  Set the lengths of the lines of a ragged array.

  Currently, these lines must be specified in increasing order (i.e. can't
  use hyperslab type locations to set them).  This should probably be made
  nicer once everything else works.

******************************************************************************/
int32
SDsetrag(int32 sdsid, 
         int32 low, 
         int32 count, 
         int32 *sizes)
{
    NC       *handle = NULL;
    NC_var   *var = NULL;
    int32     ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetrag: I've been called\n");
#endif
    
#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetrag );
#endif

    /* get the variable */
    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE) 
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, sdsid);
    if((var == NULL) || (var->is_ragged == FALSE))
      {
          ret_value = FAIL;
          goto done;
      }

    /* verify writing to a valid area */
    if(var->rag_fill != low) 
      {
        printf("var->rag_fill %d    low %d\n", var->rag_fill, low); 
        ret_value = FAIL;
        goto done;
      }

    /* allocate some space for the ragged dimension if needed */
    /* BUG: will need to be changed for ragged records */
    if(var->rag_list == NULL) 
      {
        var->rag_list = (int32 *) HDmalloc(sizeof(int32) * var->dsizes[0]);
        if(var->rag_list == NULL) 
          {
              ret_value = FAIL;
              goto done;
          }
      }

    /* copy over the new values */
    HDmemcpy(&(var->rag_list[low]), sizes, sizeof(int32) * count);
    
    /* update count */
    var->rag_fill += count;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetrag);
#endif

    return ret_value;    
} /* SDsetrag */

#endif /* 0 */


/******************************************************************************
 NAME
	SDsetaccesstype -- set the I/O access type of an SD

 DESCRIPTION
    Set the type of I/O (serial, parallel, ...) for accessing the
	data of the SD.  Access types can be DFACC_SERIAL, DFACC_PARALLEL,
	DFACC_DEFAULT.

 RETURNS
    Return SUCCEED if the SD data can be accessed via accesstype.
	Otherwise return FAIL.

******************************************************************************/	
intn 
SDsetaccesstype(int32 id,         /* IN: dataset ID */
                uintn accesstype  /* IN: access type */)
{
    NC       *handle = NULL;
    NC_var   *var = NULL;
    intn      ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetaccesstype: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetaccesstype );
#endif

    switch (accesstype)
      {
        case DFACC_DEFAULT:
        case DFACC_SERIAL:
        case DFACC_PARALLEL:
            break;
        default:
            ret_value = FAIL;
            goto done;
      }

    handle = SDIhandle_from_id(id, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE)
      {
          ret_value = FAIL;
          goto done;
      }

    if(handle->vars == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    var = SDIget_var(handle, id);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* if aid is not valid yet, there is no access_rec setup yet. */
    /* Go ahead and try set it up. */
    if(var->aid == FAIL && hdf_get_vp_aid(handle, var) == FAIL)
        ret_value = FAIL;
    else 
        ret_value = (intn)Hsetaccesstype(var->aid, accesstype);

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetaccesstype);
#endif

    return ret_value;    
} /* SDsetaccesstype */


/******************************************************************************
 NAME
	SDsetblocksize -- set the size of the linked blocks created.

 DESCRIPTION
    Set the size of the blocks used for storing the data for unlimited
	dimension datasets.  This is used when creating new datasets only,
	it does not have any affect on existing datasets.  The block_size 
	should probably be set to a multiple of the "slice" size.

 RETURNS
    SUCCEED/FAIL

******************************************************************************/
intn
SDsetblocksize(int32 sdsid,      /* IN: dataset ID */
               int32 block_size  /* IN: size of the block in bytes */)
{
    NC      *handle = NULL;
    NC_var  *var = NULL;
    intn     ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetblocksize: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetblocksize );
#endif

    /* get the handle */
    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
          ret_value = FAIL;
          goto done;
      }

    /* get the variable */
    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* set the block size */
    var->block_size = block_size;

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask,  ID_SDsetblocksize);
#endif

    return ret_value;    
} /* SDsetblocksize */

/******************************************************************************
 NAME
   SDsetfillmode -- set fill mode as fill or nofill

 DESCRIPTION
   Calls ncsetfill().

 RETURNS
   The current fill mode of the file, or FAIL for error.

******************************************************************************/
intn
SDsetfillmode(int32 sd_id,  /* IN: HDF file ID, returned from SDstart */
              intn fillmode /* IN: Desired fill mode for the file,
                                   either SD_FILL or SD_NOFILL.
                                   SD_FILL is the default mode. */)
{
    NC     *handle = NULL;
    intn    cdfid;
    intn    ret_value = FAIL;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetfillmode: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetfillmode );
#endif

    /* get the handle */
    handle = SDIhandle_from_id(sd_id, CDFTYPE);
    if(handle == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    cdfid = (intn)sd_id & 0xffff;
    ret_value = ncsetfill(cdfid, fillmode);

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetfillmode);
#endif

    return ret_value;    
} /* SDsetfillmode() */

/******************************************************************************
 NAME
    SDsetdimval_comp -- set dimval backward compatibility

 DESCRIPTION

 RETURNS
    SUCCEED/FAIL

******************************************************************************/
intn
SDsetdimval_comp(int32 dimid,    /* IN: dimension ID, returned from SDgetdimid */
                 intn comp_mode  /* IN: backward compatibility:
                                    SD_DIMVAL_BW_COMP -- compatible
                                    SD_DIMVAL_BW_INCOMP -- incompatible.
                                    (defined in mfhdf.h ) */)
{
    NC      *handle = NULL;
    NC_dim  *dim = NULL;
    intn     ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDsetdimval_comp: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetdimval_comp );
#endif

    /* get the handle */
    handle = SDIhandle_from_id(dimid, DIMTYPE);
    if(handle == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* get the dimension structure */
    dim = SDIget_dim(handle, dimid);
    if(dim == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

/*    if (dim->size != SD_UNLIMITED  
        && dim->dim00_compat != comp_mode )  
*/
    if (dim->dim00_compat != comp_mode)
      {
        dim->dim00_compat = comp_mode;

        /* make sure it gets reflected in the file */
        handle->flags |= NC_HDIRTY;
      }

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetdimval_comp);
#endif

    return ret_value;    
} /* SDsetdimval_comp */


/******************************************************************************
 NAME
   SDisdimval_bwcomp -- get dimval backward compatibility

 DESCRIPTION

 RETURNS
    SD_DIMVAL_BW_COMP if dimval is backward compatible;
    SD_DIMVAL_BW_INCOMP for not compatible; FAIL for error. 

******************************************************************************/
intn
SDisdimval_bwcomp(int32 dimid /* IN: dimension ID, returned from SDgetdimid */)
{
    NC      *handle = NULL;
    NC_dim  *dim = NULL;
    intn    ret_value = FAIL;

#ifdef SDDEBUG
    fprintf(stderr, "SDisdimval_bwcomp: I've been called\n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDisdimval_bwcomp );
#endif

    /* get the handle */
    handle = SDIhandle_from_id(dimid, DIMTYPE);
    if(handle == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* get the dimension structure */
    dim = SDIget_dim(handle, dimid);
    if(dim == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

/* Default is incompatible. Return dim->dim00_compat.
    if (dim->size == SD_UNLIMITED) 
        ret_value = SD_DIMVAL_BW_COMP;
    else 
*/
    ret_value = dim->dim00_compat; 

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDisdimval_bwcomp);
#endif

    return ret_value;    
} /* SDisdimval_bwcomp */

/*====================== Chunking Routines ================================*/

/******************************************************************************
 NAME
      SDsetchunk  -- make SDS a chunked SDS

 DESCRIPTION
      This routine makes the SDS a chunked SDS according to the chunk
      definiton passed in.

      The dataset currently cannot be special already.  i.e. NBIT,
      COMPRESSED, or EXTERNAL. This is an Error.

      The defintion of the HDF_CHUNK_DEF union with relvant fields is:

      typedef union hdf_chunk_def_u
      {
         int32   chunk_lengths[MAX_VAR_DIMS];  Chunk lengths along each dimension

         struct 
          {   
            int32     chunk_lengths[MAX_VAR_DIMS]; Chunk lengths along each dimension
            int32     comp_type;                   Compression type 
            comp_info cinfo;                       Compression info struct 
          }comp;

      } HDF_CHUNK_DEF

      The variable agruement 'flags' is a bit-or'd value which can currently be
      'HDF_CHUNK' or 'HDF_CHUNK | HDF_COMP'.

      The simplist is the 'chunk_lengths' array specifiying chunk 
      lengths for each dimension where the 'flags' argument set to 
      'HDF_CHUNK';

      COMPRESSION is set by using the 'HDF_CHUNK_DEF' union to set the
      appropriate compression information along with the required chunk lengths
      for each dimension. The compression information is the same as 
      that set in 'SDsetcompress()'. The bit-or'd 'flags' argument' is set to 
      'HDF_CHUNK | HDF_COMP'.

      See the example in pseudo-C below for further usage.

      The maximum number of Chunks in an HDF file is 65,535.

      The dataset currently cannot have an UNLIMITED dimension.

      The performance of the SDxxx interface with chunking is greatly
      affected by the users access pattern over the dataset and by
      the maximum number of chunks set in the chunk cache. The cache contains 
      the Least Recently Used(LRU cache replacment policy) chunks. See the
      routine SDsetchunkcache() for further info on the chunk cache and how 
      to set the maximum number of chunks in the chunk cache. A default chunk 
      cache is always created.

      The following example shows the organization of chunks for a 2D array.
      e.g. 4x4 array with 2x2 chunks. The array shows the layout of
           chunks in the chunk array.

            4 ---------------------                                           
              |         |         |                                                 
        Y     |  (0,1)  |  (1,1)  |                                       
        ^     |         |         |                                      
        |   2 ---------------------                                       
        |     |         |         |                                               
        |     |  (0,0)  |  (1,0)  |                                      
        |     |         |         |                                           
        |     ---------------------                                         
        |     0         2         4                                       
        ---------------> X                                                       

        --Without compression--:
        {                                                                    
        HDF_CHUNK_DEF chunk_def;
                                                                            
        .......                                                                    
        -- Set chunk lengths --                                                    
        chunk_def.chunk_lengths[0]= 2;                                                     
        chunk_def.chunk_lengths[1]= 2; 

        -- Set Chunking -- 
        SDsetchunk(sdsid, chunk_def, HDF_CHUNK);                      
         ......                                                                  
        }                                                                           

        --With compression--:
        {                                                                    
        HDF_CHUNK_DEF chunk_def;
                                                                            
        .......                
        -- Set chunk lengths first --                                                    
        chunk_def.chunk_lengths[0]= 2;                                                     
        chunk_def.chunk_lengths[1]= 2;

        -- Set compression --
        chunk_def.comp.cinfo.deflate.level = 9;
        chunk_def.comp.comp_type = COMP_CODE_DEFLATE;

        -- Set Chunking with Compression --
        SDsetchunk(sdsid, chunk_def, HDF_CHUNK | HDF_COMP);                      
         ......                                                                  
        }                                                                           

        NOTE:
           This routine directly calls a Special Chunked Element fcn HMCxxx.

 RETURNS
        SUCCEED/FAIL

 AUTHOR 
        -GeorgeV
******************************************************************************/
intn 
SDsetchunk(int32         sdsid,     /* IN: sds access id */
           HDF_CHUNK_DEF chunk_def, /* IN: chunk definition */
           int32         flags      /* IN: flags */)
{
    NC        *handle = NULL;      /* file handle */
    NC_var    *var    = NULL;      /* SDS variable */
    NC_attr  **fill_attr = NULL;   /* fill value attribute */
    HCHUNK_DEF chunk[1];           /* H-level chunk defintion */
    HDF_CHUNK_DEF *cdef   = NULL;  /* SD Chunk definition */
    model_info minfo;              /* dummy model info struct */
    comp_info  cinfo;              /* compression info - NBIT */
    int32     *cdims    = NULL;    /* array of chunk lengths */
    int32      fill_val_len = 0;   /* fill value length */
    void      *fill_val    = NULL; /* fill value */
    int32      ndims    = 0;       /* # dimensions i.e. rank */
    uint8      nlevels  = 1;       /* default # levels is 1 */
    int8       platntsubclass;     /* the machine type of the current platform */
    int8       outntsubclass;      /* the data's machine type */
    uintn      convert;            /* whether to convert or not */
    static     int32 tBuf_size = 0;/* statc conversion buffer size */
    static     void  *tBuf = NULL; /* static buffer used for conversion */
    intn       i;                  /* loop variable */
    intn       ret_value = SUCCEED;   /* return value */

#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: called  \n");
#endif

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDsetchunk );
#endif

    /* Check some args */

    /* get file handle and verify it is an HDF file 
       we only handle dealing with SDS only not coordinate variables */
    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE || handle->vars == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* get variable from id */
    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* Decide type of defintion passed in  */
    switch (flags)
      {
      case HDF_CHUNK: /* case where chunk_def only has chunk lengths */
          cdef  = (HDF_CHUNK_DEF *)&chunk_def;
          cdims = cdef->chunk_lengths;
          chunk[0].chunk_flag = 0;  /* nothing set for this now */
          chunk[0].comp_type = COMP_CODE_NONE; /* nothing set */
          chunk[0].model_type = COMP_MODEL_STDIO; /* Default */
          chunk[0].cinfo = &cinfo; /* dummy */
          chunk[0].minfo = &minfo; /* dummy */
          break;
      case (HDF_CHUNK | HDF_COMP):
      /*  EIP 9/11/03 
       *  We have to take special care if SZIP library is not available;
       *  Fow all other compression types do
       */
          cdef  = (HDF_CHUNK_DEF *)&chunk_def;

      if ((comp_coder_t)cdef->comp.comp_type != COMP_CODE_SZIP) {
          cdims = cdef->comp.chunk_lengths;
          chunk[0].chunk_flag = SPECIAL_COMP;  /* Compression */
          chunk[0].comp_type  = (comp_coder_t)cdef->comp.comp_type; 
          chunk[0].model_type = COMP_MODEL_STDIO; /* Default */
          chunk[0].cinfo = &cdef->comp.cinfo; 
          chunk[0].minfo = &minfo; /* dummy */
       }
       else /* requested compression is SZIP */

#ifdef H4_HAVE_LIBSZ          /* we have the library */
          {
          cdims = cdef->comp.chunk_lengths;
          chunk[0].chunk_flag = SPECIAL_COMP;  /* Compression */
          chunk[0].comp_type  = (comp_coder_t)cdef->comp.comp_type; 
          chunk[0].model_type = COMP_MODEL_STDIO; /* Default */
          chunk[0].cinfo = &cdef->comp.cinfo; 
          chunk[0].minfo = &minfo; /* dummy */
          }
#else                         /* we do not have the SZIP library */
          {
          cdims = cdef->comp.chunk_lengths;
          chunk[0].chunk_flag = 0;
          chunk[0].comp_type = COMP_CODE_NONE;
          chunk[0].model_type = COMP_MODEL_STDIO;
          chunk[0].cinfo = &cinfo;
          chunk[0].minfo = &minfo;
          }
#endif /* H4_HAVE_LIBSZ */

          break;
      case (HDF_CHUNK | HDF_NBIT):
          cdef  = (HDF_CHUNK_DEF *)&chunk_def;
          cdims = cdef->nbit.chunk_lengths;
          chunk[0].chunk_flag = SPECIAL_COMP;  /* NBIT is a type of compression */
          chunk[0].comp_type  = COMP_CODE_NBIT;   /* Nbit compression? */
          chunk[0].model_type = COMP_MODEL_STDIO; /* Default */
          /* set up n-bit parameters */
          cinfo.nbit.nt        = var->HDFtype;
          cinfo.nbit.sign_ext  = cdef->nbit.sign_ext;
          cinfo.nbit.fill_one  = cdef->nbit.fill_one;
          cinfo.nbit.start_bit = cdef->nbit.start_bit;
          cinfo.nbit.bit_len   = cdef->nbit.bit_len;
          chunk[0].cinfo = &cinfo; 
          chunk[0].minfo = &minfo; /* dummy */
          break;
      default:
          ret_value = FAIL;
          goto done;
      }

#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: does data ref exist?  \n");
#endif
    /* Does data exist yet */
    if(!var->data_ref) 
      {   /* doesn't exist */
#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: data ref does not exist  \n");
#endif
          /* element doesn't exist so we need a reference number */
          var->data_ref=Hnewref(handle->hdf_file);
          if(var->data_ref == 0)
            {
#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: failed to get data ref  \n");
#endif
              ret_value = FAIL;
              goto done;
            }
      } 
    else /* data ref exists, Error since can't convert existing SDS to chunked */
      {
          ret_value = FAIL;
          goto done;
      }

    /* Now start setting chunk info */
    ndims = var->assoc->count; /* set number of dims i.e. rank */
#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: got data ref, ndims =%d  \n",ndims);
#endif

    /* allocate space for chunk dimensions */
    if ((chunk[0].pdims = (DIM_DEF *)HDmalloc(ndims*sizeof(DIM_DEF))) == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* initialize datset/chunk sizes using CHUNK defintion structure */
    chunk[0].chunk_size = 1;
    chunk[0].num_dims = ndims;
    for (i = 0; i < ndims; i++)
      {   /* get dimension length from shape arrays */
          /* check if dimension in unlimited since we don't 
             handle that yet */
          if (var->shape[i] != SD_UNLIMITED)
              chunk[0].pdims[i].dim_length = (int32) var->shape[i];
          else
            { /* UNLIMITED dimension case */
#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: unlimited dimension case  \n");
    fflush(stderr);
#endif
                ret_value = FAIL;
                goto done;
            }

#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: (int32) var->shape[%d]=%d\n",i,(int32) var->shape[i]);
    fflush(stderr);
#endif
          /* set chunk lengths */
          if (cdims[i] >= 1)
              chunk[0].pdims[i].chunk_length = cdims[i];
          else
            { /* chunk length is less than 1 */
#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: chunk length less than 1, cdims[%d]=%d \n",i,cdims[i]);
    fflush(stderr);
#endif
                ret_value = FAIL;
                goto done;
            }
#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: cdims[%d]=%d \n",i,cdims[i]);
    fflush(stderr);
#endif          
          /* Data distribution along dimensions 
          *  Check dimension length agains chunk length */
          if (cdims[i] == (int32)var->shape[i])
              chunk[0].pdims[i].distrib_type = 0;     /* NONE */
          else
              chunk[0].pdims[i].distrib_type = 1;     /* BLOCK */

          /* compute chunk size */
          chunk[0].chunk_size *= cdims[i];
      } /* end for ndims */

    /* Set number type size i.e. size of data type */
    chunk[0].nt_size = var->HDFsize;

#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: var->HDFsize=%d\n",var->HDFsize);
    fflush(stderr);
#endif
    /* allocate space for fill value whose number type is the same as
       the dataset */
    fill_val_len = var->HDFsize;
    if ((fill_val = (void *)HDmalloc(fill_val_len)) == NULL)
      {
          ret_value = FAIL;
          goto done;
      }

    /* get fill value if one is set for this Dataset.
       The number type is the same as that for the dataset. */
    fill_attr = (NC_attr **) NC_findattr(&(var->attrs), _FillValue);
    if(fill_attr != NULL)
      {
          NC_copy_arrayvals((char *)fill_val, (*fill_attr)->data) ;    
      }
    else /* copy standard default fill value for now */
      {
        void *p = fill_val;

        switch(var->HDFtype & 0xff) 
          {
          case DFNT_CHAR:
          case DFNT_UCHAR:
              *((uint8 *)p) = FILL_CHAR;
              break;
          case DFNT_INT8:
          case DFNT_UINT8:
              *((int8 *)p) = FILL_BYTE;
              break;
          case DFNT_INT16:
          case DFNT_UINT16:
              *((int16 *)p) = FILL_SHORT;
              break;
          case DFNT_INT32:
          case DFNT_UINT32:
              *((int32 *)p) = FILL_LONG;
              break;
          case DFNT_FLOAT32:
              *((float32 *)p) = FILL_FLOAT;
              break;
          case DFNT_FLOAT64:
              *((float64 *)p) = FILL_DOUBLE;
              break;
        default:
            ret_value = FAIL;
            goto done;
        }
      }

    /* figure out if fill value has to be converted */
    if (FAIL == (platntsubclass = DFKgetPNSC(var->HDFtype, DF_MT)))
      {
          ret_value = FAIL;
          goto done;
      }
 
    if (DFKisnativeNT(var->HDFtype))
      {
        if (FAIL == (outntsubclass = DFKgetPNSC(var->HDFtype, DF_MT)))
          {
              ret_value = FAIL;
              goto done;
          }
       }
    else
      {
          outntsubclass = DFKislitendNT(var->HDFtype) ? DFNTF_PC : DFNTF_HDFDEFAULT;
      }
            
    convert = (uintn)(platntsubclass != outntsubclass);

    /* make sure our tmp buffer is big enough to hold fill value */
    if(convert && tBuf_size < fill_val_len) 
      {
          if(tBuf != NULL) 
              HDfree(tBuf);
          tBuf_size = fill_val_len;
          tBuf      = HDmalloc(tBuf_size);
          if(tBuf == NULL) 
            {
                tBuf_size = 0;
                ret_value    = FAIL;
                goto done;
            } /* end if */
      } /* end if */

#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: get ready to create, convert=%d\n",convert);
#endif
    if (convert)
      { /* convert fill value */
          if (FAIL == DFKconvert(fill_val, tBuf, var->HDFtype,
                                (uint32) (fill_val_len/var->HDFsize), DFACC_WRITE, 0, 0))
            {
                ret_value    = FAIL;
                goto done;
            } 

        /* check to see already special.
           Error if already special since doubly special elements are
           not yet handled. HMCcreate should catch this....*/
        /* Create SDS as chunked element  */
        ret_value = HMCcreate(handle->hdf_file,       /* HDF file handle */
                           (uint16)DATA_TAG,       /* Data tag */
                           (uint16) var->data_ref, /* Data ref */
                           nlevels,                /* nlevels */
                           fill_val_len,           /* fill value length */
                           tBuf,           /* fill value */
                           (HCHUNK_DEF *)chunk      /* chunk definition */);
      }
    else /* no need to convert fill value */
      {
          /* check to see already special.
             Error if already special since doubly special elements are
             not yet handled. HMCcreate should catch this....*/
          /* Create SDS as chunked element  */
          ret_value = HMCcreate(handle->hdf_file,       /* HDF file handle */
                             (uint16)DATA_TAG,       /* Data tag */
                             (uint16) var->data_ref, /* Data ref */
                             nlevels,                /* nlevels */
                             fill_val_len,           /* fill value length */
                             fill_val,       /* fill value */
                             (HCHUNK_DEF *)chunk      /* chunk definition */);
      }

#ifdef CHK_DEBUG
    fprintf(stderr,"SDsetchunk: ret_value =%d \n", ret_value);
#endif
    /* check return */
    if(ret_value != FAIL) 
      { /* close old aid and set new one
         ..hmm......maybe this is for the doubly specail hack since
         this code framework came from SDsetcompress()....*/
          if((var->aid != 0) && (var->aid != FAIL))
            {
              if (Hendaccess(var->aid) == FAIL)
                  {
                      ret_value = FAIL;
                      goto done;
                  }
            }

          var->aid = ret_value;
          ret_value = SUCCEED; /* re-set to successful */
      } /* end if */

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

      }
    /* Normal cleanup */

    /* free fill value */
    if (fill_val != NULL)
        HDfree(fill_val);

    /* free chunk dims */
    if (chunk[0].pdims != NULL)
        HDfree(chunk[0].pdims);

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetchunk);
#endif

    return ret_value;
} /* SDsetchunk */

/******************************************************************************
 NAME
     SDgetchunkinfo -- get Info on SDS

 DESCRIPTION
     This routine gets any special information on the SDS. If its chunked,
     chunked and compressed or just a regular SDS. Currently it will only
     fill the array of chunk lengths for each dimension as specified in
     the 'HDF_CHUNK_DEF' union. It does not tell you the type of compression
     or the compression parameters used. You can pass in a NULL for 'chunk_def'
     if don't want the chunk lengths for each dimension.
     If successfull it will return a bit-or'd value in 'flags' indicating 
     if the SDS is  chunked(HDF_CHUNK), chunked and compressed(HDF_CHUNK | HDF_COMP) 
     or non-chunked(HDF_NONE).
 
     e.g. 4x4 array - Pseudo-C
     {
     HDF_CHUNK_DEF rchunk_def;
     int32   cflags;
     ...
     SDgetchunkinfo(sdsid, &rchunk_def, &cflags);
     ...
     }

 RETURNS
        SUCCEED/FAIL

 AUTHOR 
        -GeorgeV
******************************************************************************/
intn 
SDgetchunkinfo(int32          sdsid,      /* IN: sds access id */
               HDF_CHUNK_DEF *chunk_def,  /* IN/OUT: chunk definition */
               int32         *flags       /* IN/OUT: flags */)
{
    NC       *handle = NULL;       /* file handle */
    NC_var   *var    = NULL;       /* SDS variable */
    sp_info_block_t info_block;    /* special info block */
    int16     special;             /* Special code */
    intn      i;                   /* loop variable */
    intn      ret_value = SUCCEED; /* return value */

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDgetchunkinfo );
#endif

    /* Check args */

    /* get file handle and verify it is an HDF file 
       we only handle dealing with SDS only not coordinate variables */
    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE || handle->vars == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* get variable from id */
    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* Check to see if data aid exists? i.e. may need to create a ref for SDS */
    if(var->aid == FAIL && hdf_get_vp_aid(handle, var) == FAIL) 
      {
        ret_value = FAIL;
        goto done;
      }

    /* inquire about element */
    ret_value = Hinquire(var->aid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &special);
    if (ret_value != FAIL)
      {   /* make sure it is chunked element */
          if (special == SPECIAL_CHUNKED)
            { /* get info about chunked element */
             if ((ret_value = HDget_special_info(var->aid, &info_block)) != FAIL)
               {   /* Does user want chunk lengths back? */
                   if (chunk_def != NULL)
                     {
                         /* we assume user has allocat space for chunk lengths */
                         /* copy chunk lengths over */
                         for (i = 0; i < info_block.ndims; i++)
                           {
                               chunk_def->chunk_lengths[i] = info_block.cdims[i];
                           }
                     }
                   /* dont forget to free up info is special info block 
                      This space was allocated by the library */
                   HDfree(info_block.cdims);

                   /* Check to see if compressed.
                      Currently we don't fill in the 'comp' structure 
                      because currently only the information about the 
                      compression type is available in get compression
                      info code and not the parameters that went along. */
                   switch(info_block.comp_type)
                     {
                     case COMP_CODE_NONE:
                         *flags = HDF_CHUNK;
                         break;
                     case COMP_CODE_NBIT:
                         *flags = (HDF_CHUNK | HDF_NBIT);
                         break;
                     default:
                         *flags = (HDF_CHUNK | HDF_COMP);
                         break;
                     }
               }
            }
          else /* not special chunked element */
            {
              *flags = HDF_NONE; /* regular SDS */
            }
      }

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDgetchunkinfo);
#endif

    return ret_value;
} /* SDgetchunkinfo() */


/******************************************************************************
 NAME
     SDwritechunk   -- write the specified chunk to the SDS

 DESCRIPTION
     This routine writes a whole chunk of data to the chunked SDS 
     specified by chunk 'origin' for the given SDS and can be used
     instead of SDwritedata() when this information is known. This
     routine has less overhead and is much faster than using SDwritedata().

     Origin specifies the co-ordinates of the chunk according to the chunk
     position in the overall chunk array.

     'datap' must point to a whole chunk of data.

     See SDsetchunk() for a description of the organization of chunks in an SDS.

     NOTE:
           This routine directly calls a Special Chunked Element fcn HMCxxx.

 RETURNS
        SUCCEED/FAIL

 AUTHOR 
       -GeorgeV
******************************************************************************/
intn 
SDwritechunk(int32       sdsid, /* IN: access aid to SDS */
             int32      *origin,/* IN: origin of chunk to write */
             const void *datap  /* IN: buffer for data */)
{
    CONSTR(FUNC, "SDwritechunk");    /* for HGOTO_ERROR */
    NC        *handle = NULL;   /* file handle */
    NC_var    *var    = NULL;   /* SDS variable */
    int16      special;         /* Special code */
    int32      csize;           /* phsical chunk size */
    uint32     byte_count;      /* bytes to write */
    int8       platntsubclass;  /* the machine type of the current platform */
    int8       outntsubclass;   /* the data's machine type */
    uintn      convert;         /* whether to convert or not */
    intn       i;
    sp_info_block_t info_block; /* special info block */
    static uint32 tBuf_size = 0; /* statc conversion buffer size */
    static void  *tBuf = NULL;   /* static buffer used for conversion */
    intn       ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDwritechunk );
#endif

    info_block.cdims = NULL;

    /* Check args */
    if (origin == NULL || datap == NULL)
        HGOTO_ERROR(DFE_ARGS, FAIL);

    /* get file handle and verify it is an HDF file 
       we only handle writinng to SDS only not coordinate variables */
    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE || handle->vars == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* get variable from id */
    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* Check to see if data aid exists? i.e. may need to create a ref for SDS */
    if(var->aid == FAIL && hdf_get_vp_aid(handle, var) == FAIL) 
      {
        ret_value = FAIL;
        goto done;
      }

    /* inquire about element */
    ret_value = Hinquire(var->aid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &special);
    if (ret_value != FAIL)
      {
          if (special == SPECIAL_CHUNKED)
            {  /* yes */

                /* get ready to write */
                handle->xdrs->x_op = XDR_ENCODE;

                /* get info about chunked element */
                if ((ret_value = HDget_special_info(var->aid, &info_block)) != FAIL)
                  {   
                      /* calcualte chunk size  */
                      csize = 1;
                      for (i = 0; i < info_block.ndims; i++)
                          csize *= info_block.cdims[i];

                      /* adjust for number type size */
                      csize *= var->HDFsize;

                      /* figure out if data needs to be converted */
                      byte_count = csize;

                      if (FAIL == (platntsubclass = DFKgetPNSC(var->HDFtype, DF_MT)))
                        {
                            ret_value = FAIL;
                            goto done;
                        }

                      if (DFKisnativeNT(var->HDFtype))
                        {
                            if (FAIL == (outntsubclass = DFKgetPNSC(var->HDFtype, DF_MT)))
                              {
                                  ret_value = FAIL;
                                  goto done;
                              }
                        }
                      else
                        {
                            outntsubclass = DFKislitendNT(var->HDFtype) ? DFNTF_PC : DFNTF_HDFDEFAULT;
                        }

                      convert = (uintn)(platntsubclass != outntsubclass);

                      /* make sure our tmp buffer is big enough to hold everything */
                      if(convert && tBuf_size < byte_count) 
                        {
                            if(tBuf != NULL) 
                                HDfree(tBuf);
                            tBuf_size = byte_count;
                            tBuf      = HDmalloc(tBuf_size);
                            if(tBuf == NULL) 
                              {
                                  tBuf_size = 0;
                                  ret_value    = FAIL;
                                  goto done;
                              } /* end if */
                        } /* end if */

                      /* Write chunk out, */
                      if(convert) 
                        {
#ifdef CHK_DEBUG
        fprintf(stderr,"SDwritechunk: convert, var->HDFsize=%d, var->HDFtype=%d \n",
                var->HDFsize, var->HDFtype);
#endif
                            /* convert it */
                            if (FAIL == DFKconvert((VOIDP)datap, tBuf, var->HDFtype,
                                                  (byte_count/var->HDFsize), DFACC_WRITE, 0, 0))
                              {
                                  ret_value    = FAIL;
                                  goto done;
                              } 

                            /* write it out now */
                            if ((ret_value = HMCwriteChunk(var->aid, origin, tBuf)) 
                                != FAIL)
                              {
                                  ret_value = SUCCEED;
                              }

                            goto done; /* done */
                        } /* end if */
                      else 
                        {
                          if ((ret_value = HMCwriteChunk(var->aid, origin, datap)) 
                              != FAIL)
                            {
                                ret_value = SUCCEED;
                            }

                          goto done; /* done */
                        }
                  } /* end if get special info block */
            }
          else /* not special CHUNKED */
              ret_value = FAIL;
      } /* end if Hinquire */

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

      }
    /* Normal cleanup */
    /* dont forget to free up info is special info block 
       This space was allocated by the library */
    if (info_block.cdims != NULL)
        HDfree(info_block.cdims);

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDwritechunk);
#endif

    return ret_value;
} /* SDwritechunk() */

/******************************************************************************
 NAME
     SDreadchunk   -- read the specified chunk to the SDS

 DESCRIPTION
     This routine reads a whole chunk of data from the chunked SDS
     specified by chunk 'origin' for the given SDS and can be used
     instead of SDreaddata() when this information is known. This
     routine has less overhead and is much faster than using SDreaddata().

     Origin specifies the co-ordinates of the chunk according to the chunk
     position in the overall chunk array.

     'datap' must point to a whole chunk of data.

     See SDsetchunk() for a description of the organization of chunks in an SDS.

     NOTE:
         This routine directly calls a Special Chunked Element fcn HMCxxx.

 RETURNS
        SUCCEED/FAIL

 AUTHOR 
       -GeorgeV
******************************************************************************/
intn 
SDreadchunk(int32  sdsid,  /* IN: access aid to SDS */
            int32 *origin, /* IN: origin of chunk to write */
            void  *datap   /* IN/OUT: buffer for data */)
{
    CONSTR(FUNC, "SDreadchunk");    /* for HGOTO_ERROR */
    NC        *handle = NULL;   /* file handle */
    NC_var    *var    = NULL;   /* SDS variable */
    int16      special;         /* Special code */
    int32      csize;           /* phsical chunk size */
    uint32     byte_count;      /* bytes to read */
    int8       platntsubclass;  /* the machine type of the current platform */
    int8       outntsubclass;   /* the data's machine type */
    uintn      convert;         /* whether to convert or not */
    intn       i;
    sp_info_block_t info_block; /* special info block */
    static uint32 tBuf_size = 0; /* statc conversion buffer size */
    static void  *tBuf = NULL; /* static buffer used for conversion */
    intn       ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask, ID_SDreadchunk );
#endif

    info_block.cdims = NULL;

    /* Check args */
    if (origin == NULL || datap == NULL)
        HGOTO_ERROR(DFE_ARGS, FAIL);

    /* get file handle and verify it is an HDF file 
       we only handle reading from SDS only not coordinate variables */
    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE || handle->vars == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* get variable from id */
    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* Check to see if data aid exists? i.e. may need to create a ref for SDS */
    if(var->aid == FAIL && hdf_get_vp_aid(handle, var) == FAIL) 
      {
        ret_value = FAIL;
        goto done;
      }

    /* inquire about element */
    ret_value = Hinquire(var->aid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &special);
    if (ret_value != FAIL)
      {
          if (special == SPECIAL_CHUNKED)
            {  /* yes */
                /* get ready to read */
                handle->xdrs->x_op = XDR_DECODE;

                /* get info about chunked element */
                if ((ret_value = HDget_special_info(var->aid, &info_block)) != FAIL)
                  {   
                      /* calcualte chunk size  */
                      csize = 1;
                      for (i = 0; i < info_block.ndims; i++)
                          csize *= info_block.cdims[i];

                      /* adjust for number type size */
                      csize *= var->HDFsize;

                      /* figure out if data needs to be converted */
                      byte_count = csize;

                      if (FAIL == (platntsubclass = DFKgetPNSC(var->HDFtype, DF_MT)))
                        {
                            ret_value = FAIL;
                            goto done;
                        }

                      if (DFKisnativeNT(var->HDFtype))
                        {
                            if (FAIL == (outntsubclass = DFKgetPNSC(var->HDFtype, DF_MT)))
                              {
                                  ret_value = FAIL;
                                  goto done;
                              }
                        }
                      else
                        {
                            outntsubclass = DFKislitendNT(var->HDFtype) ? DFNTF_PC : DFNTF_HDFDEFAULT;
                        }

                      convert = (uintn)(platntsubclass != outntsubclass);

                      /* make sure our tmp buffer is big enough to hold everything */
                      if(convert && tBuf_size < byte_count) 
                        {
                            if(tBuf != NULL) 
                                HDfree(tBuf);
                            tBuf_size = byte_count;
                            tBuf      = HDmalloc(tBuf_size);
                            if(tBuf == NULL) 
                              {
                                  tBuf_size = 0;
                                  ret_value = FAIL;
                                  goto done;
                              } /* end if */
                        } /* end if */

                      /* read chunk in */
                      if(convert) 
                        {
#ifdef CHK_DEBUG
        fprintf(stderr,"SDreadchunk: convert, var->HDFsize=%d, var->HDFtype=%d \n",
                var->HDFsize, var->HDFtype);
#endif
                            /* read it in */
                            if ((ret_value = HMCreadChunk(var->aid, origin, tBuf)) 
                                != FAIL)
                                {
                                    /* convert chunk */
                                    if (FAIL == DFKconvert(tBuf, datap, var->HDFtype,
                                                         (byte_count/var->HDFsize), DFACC_READ, 0, 0))
                                      {
                                          ret_value = FAIL;
                                          goto done;
                                      }

                                    ret_value = SUCCEED;
                                }

                            goto done; /* done */
                        } /* end if */
                      else 
                        {
                          if ((ret_value = HMCreadChunk(var->aid, origin, datap))
                              != FAIL)
                            {
                              ret_value = SUCCEED;
                            }

                          goto done; /* done */
                        }
                  } /* end if get special info block */
            }
          else /* not special CHUNKED */
              ret_value = FAIL;
      } /* end if Hinquire */

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

      }
    /* Normal cleanup */
    /* dont forget to free up info is special info block 
       This space was allocated by the library */
    if (info_block.cdims != NULL)
        HDfree(info_block.cdims);

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDreadchunk);
#endif

    return ret_value;
} /* SDreadchunk() */

/******************************************************************************
NAME
     SDsetchunkcache - maximum number of chunks to cache 

DESCRIPTION
     Set the maximum number of chunks to cache.

     The cache contains the Least Recently Used(LRU cache replacment policy) 
     chunks. This routine allows the setting of maximum number of chunks that 
     can be cached, 'maxcache'.

     The performance of the SDxxx interface with chunking is greatly
     affected by the users access pattern over the dataset and by
     the maximum number of chunks set in the chunk cache. The number chunks 
     that can be set in the cache is process memory limited. It is a good 
     idea to always set the maximum number of chunks in the cache as the 
     default heuristic does not take into account the memory available for 
     the application.

     By default when the SDS is promoted to a chunked element the 
     maximum number of chunks in the cache 'maxcache' is set to the number of
     chunks along the last dimension.

     The values set here affects the current SDS object's caching behaviour.

     If the chunk cache is full and 'maxcache' is greater then the
     current 'maxcache' value, then the chunk cache is reset to the new
     'maxcache' value, else the chunk cache remains at the current
     'maxcache' value.

     If the chunk cache is not full, then the chunk cache is set to the
     new 'maxcache' value only if the new 'maxcache' value is greater than the
     current number of chunks in the cache.

     Use flags argument of 'HDF_CACHEALL' if the whole object is to be cached 
     in memory, otherwise pass in zero(0). Currently you can only
     pass in zero.

     See SDsetchunk() for a description of the organization of chunks in an SDS.
     
     NOTE:
          This routine directly calls a Special Chunked Element fcn HMCxxx.

RETURNS
     Returns the 'maxcache' value for the chunk cache if successful 
     and FAIL otherwise

AUTHOR 
      -GeorgeV
******************************************************************************/
intn
SDsetchunkcache(int32 sdsid,     /* IN: access aid to mess with */
                int32 maxcache,  /* IN: max number of chunks to cache */
                int32 flags      /* IN: flags = 0, HDF_CACHEALL */)
{
    NC       *handle = NULL;        /* file handle */
    NC_var   *var    = NULL;        /* SDS variable */
    int16     special;              /* Special code */
    intn      ret_value = SUCCEED;

#ifdef HAVE_PABLO
    TRACE_ON(PABLO_mask,ID_SDsetchunkcache );
#endif

    /* Check args */
    if (maxcache < 1 )
      {
        ret_value = FAIL;
        goto done;
      }

    if (flags != 0 && flags != HDF_CACHEALL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* get file handle and verify it is an HDF file 
       we only handle dealing with SDS only not coordinate variables */
    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL || handle->file_type != HDF_FILE || handle->vars == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* get variable from id */
    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* Check to see if data aid exists? i.e. may need to create a ref for SDS */
    if(var->aid == FAIL && hdf_get_vp_aid(handle, var) == FAIL) 
      {
        ret_value = FAIL;
        goto done;
      }

    /* inquire about element */
    ret_value = Hinquire(var->aid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &special);
    if (ret_value != FAIL)
      {
          if (special == SPECIAL_CHUNKED)
              ret_value = HMCsetMaxcache(var->aid, maxcache, flags); /* set cache*/
          else
              ret_value = FAIL;
      }

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

      }
    /* Normal cleanup */

#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDsetchunkcache);
#endif

    return ret_value;
} /* SDsetchunkcache() */


/******************************************************************************
 NAME
	SDcheckempty -- checks whether an SDS is empty

 DESCRIPTION
    Given an sdsid, set the second parameter, emptySDS, to TRUE if the 
    SDS has not been written with data, and FALSE, otherwise. 

 RETURNS
	SUCCEED/FAIL

 AUTHOR
	bmribler - 9-01-98
        
******************************************************************************/
int32
SDcheckempty(int32 sdsid,  /* IN: dataset ID */
	     intn  *emptySDS /* TRUE if SDS is empty */)
{
    NC     *handle = NULL;
    NC_var *var = NULL;
    int32   ret_value = SUCCEED;

#ifdef SDDEBUG
    fprintf(stderr, "SDcheckempty: I've been called\n");
#endif

#ifdef HAVE_PABLO
#ifndef ID_SDcheckempty
#define ID_SDcheckempty DUMMY_HDF
#endif
    TRACE_ON(PABLO_mask,ID_SDcheckempty );
#endif
    /* get the handle */
    handle = SDIhandle_from_id(sdsid, SDSTYPE);
    if(handle == NULL) 
      {
        ret_value = FAIL;
        goto done;
      }

    /* get the variable */
    var = SDIget_var(handle, sdsid);
    if(var == NULL)
      {
        ret_value = FAIL;
        goto done;
      }

    /* if the SDS has been written with data, a storage is created 
	for the SDS data, and var->data_ref will contain this storage's 
	reference number, not 0 */
    if( var->data_ref != 0 )
      {
        *emptySDS = FALSE;
        goto done;
      }
    *emptySDS = TRUE;  /* SDS is not written with data */

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

      }
    /* Normal cleanup */
#ifdef HAVE_PABLO
    TRACE_OFF (PABLO_mask, ID_SDcheckempty);
#endif

    return ret_value;
} /* SDcheckempty */

#endif /* HDF */
