/******************************************************************************
*
*  NSSDC/CDF                     CDF library miscellaneous functions, part 3.
*
*  Version 1.0e, 29-Oct-97, Hughes STX.
*
*  Modification history:
*
*   V1.0   5-Sep-96, J Love     Original version.
*   V1.0a 21-Feb-97, J Love	Removed RICE.
*   V1.0b  4-Mar-97, J Love	Windows NT for MS Visual C/C++ on an IBM PC.
*   V1.0c 11-Sep-97, J Love	Magic number are now uInt32.
*   V1.0d 20-Oct-97, J Love	Properly cast the uInt32 magic numbers.  More
*				Windows NT.
*   V2.0  29-Jun-04, M Liu      Added LFS (Large File Support > 2G).
*   V3.2  25-Apr-07, D Berger   Changed COPYblockSIZE from 512.
*
******************************************************************************/

#include "cdflib.h"
#include "cdflib64.h"

/******************************************************************************
* Macros/function prototypes.
******************************************************************************/

#define COPYblockSIZE nCACHE_BUFFER_BYTEs	

static CDFstatus SearchForRecord_r_64 PROTOARGs((
  vFILE *fp, OFF_T vxrOffset, Int32 recNum, Int32 *firstRec, Int32 *lastRec,
  OFF_T *offset, Logical *found
));
static CDFstatus IndexingStatistics_r_64 PROTOARGs((
  vFILE *fp, OFF_T vxrOffset, Int32 maxRec, int level, Int32 *nLevels,
  Int32 *nVXRs, Int32 *nEntries, Int32 *nAlloc, Int32 *nRecords
));
static CDFstatus PrevRecord_r_64 PROTOARGs((
  vFILE *fp, OFF_T vxrOffset, Int32 baseRec, Int32 *prevRec, Logical *found
));
static CDFstatus NextRecord_r_64 PROTOARGs((
  vFILE *fp, OFF_T vxrOffset, Int32 baseRec, Int32 *nextRec, Logical *found
));
static CDFstatus CalcCompressionPct_r_64 PROTOARGs((
  vFILE *fp, OFF_T vxrOffset, Int32 nPhyRecBytes, OFF_T *uTotal, OFF_T *cTotal
));
static CDFstatus ModIndexOffset_r_64 PROTOARGs((
  vFILE *fp, OFF_T vxrOffset, Int32 firstRec, Int32 lastRec, OFF_T newOffset
));
static CDFstatus ReadSparseFull PROTOARGs((
  struct CDFstruct *CDF, struct VarStruct *Var, Int32 firstRec, Int32 lastRec,
  void *buffer
));
static CDFstatus ReadSparsePartial PROTOARGs((
  struct CDFstruct *CDF, struct VarStruct *Var, Int32 recNum, OFF_T offset,
  Int32 nValues, void *buffer
));
static CDFstatus ReadCompressedFull PROTOARGs((
  struct CDFstruct *CDF, struct VarStruct *Var, Int32 firstRec, Int32 lastRec,
  void *buffer
));
static CDFstatus ReadCompressedPartial PROTOARGs((
  struct CDFstruct *CDF, struct VarStruct *Var, Int32 recNum, OFF_T offset,
  Int32 nValues, void *buffer
));
static CDFstatus BringToStage PROTOARGs((
  struct CDFstruct *CDF, struct VarStruct *Var, Int32 recNum, Logical *found
));
static CDFstatus WriteCompressedRecords PROTOARGs((
  struct CDFstruct *CDF, struct VarStruct *Var, Int32 firstRec, Int32 lastRec,
  void *buffer, Int32 nValues, OFF_T offset, Logical fullRecord
));

/******************************************************************************
* DecompressCDF64.
******************************************************************************/

STATICforIDL CDFstatus DecompressCDF64 (dotFp, uDotFp)
vFILE *dotFp;           /* In: File pointer to dotCDF file. */
vFILE *uDotFp;          /* In: Uncompressed CDF file pointer. */
{
  CDFstatus pStatus = CDF_OK; struct CCRstruct64 CCR; struct CPRstruct64 CPR;
  uInt32 magicNumber1 = V3magicNUMBER_1, magicNumber2u = V2magicNUMBER_2u;
  OFF_T cSize, cOffset;
  /****************************************************************************
  * Read/validate CCR.
  ****************************************************************************/
  if (!sX(ReadCCR64(dotFp,V3_CCR_OFFSET64,
		    CCR_RECORD,&CCR,
		    CCR_NULL),&pStatus)) return pStatus;
  if (CCR.uSize == 0) return EMPTY_COMPRESSED_CDF;
  /****************************************************************************
  * Read CPR.
  ****************************************************************************/
  if (!sX(ReadCPR64(dotFp,CCR.CPRoffset,
		    CPR_RECORD,&CPR,
		    CPR_NULL),&pStatus)) return pStatus;
  /****************************************************************************
  * Write magic numbers.
  ****************************************************************************/
  if (!SEEKv64(uDotFp,(OFF_T) V3_MAGIC_OFFSET_1,vSEEK_SET)) return CDF_WRITE_ERROR;
  if (!Write32_64(uDotFp,(Int32 *)&magicNumber1)) return CDF_WRITE_ERROR;
  if (!Write32_64(uDotFp,(Int32 *)&magicNumber2u)) return CDF_WRITE_ERROR;
  /****************************************************************************
  * Copy/decompress.
  ****************************************************************************/
  cOffset = (OFF_T) (V3_CCR_OFFSET64 + CCR_BASE_SIZE64);
  cSize = CCR.RecordSize - CCR_BASE_SIZE64;
  if (!sX(Decompress64(dotFp,cOffset,
		       cSize,CDF_READ_ERROR,
		       CPR.cType,CPR.cParms,
		       uDotFp,FIRST_IR_OFFSET,
		       CDF_WRITE_ERROR),&pStatus)) return pStatus;
  return pStatus;
}

/******************************************************************************
* WriteCompressedCDF64.
******************************************************************************/

STATICforIDL CDFstatus WriteCompressedCDF64 (CDF, CPR, empty)
struct CDFstruct *CDF;
struct CPRstruct64 *CPR;
Logical empty;          /* In: If TRUE, write an empty CCR. */
{
  uInt32 magicNumber1 = V3magicNUMBER_1;
  uInt32 magicNumber2c = V2magicNUMBER_2c;
  struct CCRstruct64 CCR; CDFstatus pStatus = CDF_OK;
  /****************************************************************************
  * Write magic numbers.
  ****************************************************************************/
  if (!SEEKv64(CDF->dotFp,(OFF_T) V3_MAGIC_OFFSET_1,vSEEK_SET)) return CDF_WRITE_ERROR;
  if (!Write32_64(CDF->dotFp,(Int32 *)&magicNumber1)) return CDF_WRITE_ERROR;
  if (!Write32_64(CDF->dotFp,(Int32 *)&magicNumber2c)) return CDF_WRITE_ERROR;
  /****************************************************************************
  * Write CCR.
  ****************************************************************************/
  if (empty) {
    CCR.RecordSize = CCR_BASE_SIZE64;
    CCR.RecordType = CCR_;
    CCR.CPRoffset = (OFF_T) V3_CCR_OFFSET64 + CCR.RecordSize;
    CCR.uSize = 0;
    CCR.rfuA = 0;
    if (!sX(WriteCCR64(CDF->dotFp,V3_CCR_OFFSET64,
		       CCR_RECORD,&CCR,
		       CCR_NULL),&pStatus)) return CDF_WRITE_ERROR;
  }
  else {
    OFF_T uSize, eof, cSize, cOffset, GDRoffset;
    if (!sX(ReadCDR64(CDF->uDotFp,V3_CDR_OFFSET64,
		      CDR_GDROFFSET,&GDRoffset,
		      CDR_NULL),&pStatus)) return pStatus;
    if (!sX(ReadGDR64(CDF->uDotFp,GDRoffset,
		      GDR_EOF,&eof,
		      GDR_NULL),&pStatus)) return pStatus;
    uSize = eof - FIRST_IR_OFFSET;
    cOffset = (OFF_T) (V3_CCR_OFFSET64 + CCR_BASE_SIZE64);
    if (!sX(Compress64(CDF->uDotFp,FIRST_IR_OFFSET,
		       uSize,CDF_READ_ERROR,CPR->cType,
		       CPR->cParms,CDF->dotFp,cOffset,
		       &cSize,CDF_WRITE_ERROR),&pStatus)) return pStatus;
    CCR.RecordSize = (OFF_T) (CCR_BASE_SIZE64 + cSize);
    CCR.RecordType = CCR_;
    CCR.CPRoffset = (OFF_T) (V3_CCR_OFFSET64 + CCR.RecordSize);
    CCR.uSize = uSize;
    CCR.rfuA = 0;
    if (!sX(WriteCCR64(CDF->dotFp,V3_CCR_OFFSET64,
		       CCR_RECORD,&CCR,
		       CCR_NULL),&pStatus)) return CDF_WRITE_ERROR;
  }
  /****************************************************************************
  * Write CPR.
  ****************************************************************************/
  if (!sX(WriteCPR64(CDF->dotFp,CCR.CPRoffset,
		     CPR_RECORD,CPR,
		     CPR_NULL),&pStatus)) return CDF_WRITE_ERROR;
  return pStatus;
}

/******************************************************************************
* CopyCDF64.
******************************************************************************/

STATICforIDL CDFstatus CopyCDF64 (srcFp, destFp)
vFILE *srcFp;
vFILE *destFp;
{
  OFF_T nBytes, offset; Byte buffer[nCACHE_BUFFER_BYTEs];
  CDFstatus pStatus = CDF_OK;
  if (!SEEKv64(srcFp,(OFF_T)0,vSEEK_END)) return CDF_READ_ERROR;
  nBytes = V_tell64 (srcFp);
  if (nBytes == EOF) return CDF_READ_ERROR;
  if (!SEEKv64(srcFp,(OFF_T)0,vSEEK_SET)) return CDF_READ_ERROR;
  if (!SEEKv64(destFp,(OFF_T)0,vSEEK_SET)) return CDF_WRITE_ERROR;
  for (offset = 0; offset < nBytes; offset += nCACHE_BUFFER_BYTEs) {
     OFF_T nBytesRemaining = nBytes - offset;
     size_t count = (size_t) MINIMUM (nBytesRemaining, nCACHE_BUFFER_BYTEs);
     if (!READv64(buffer,count,1,srcFp)) return CDF_READ_ERROR;
     if (!WRITEv64(buffer,count,1,destFp)) return CDF_WRITE_ERROR;
  }
  return pStatus;
}

/******************************************************************************
* WriteVarValues64.
*    NOTE: If more than one record is being written, full records are assumed
* and the `offset' must be zero.
******************************************************************************/

STATICforIDL CDFstatus WriteVarValues64 (CDF, Var, startRec, offset, nValues,
				         buffer)
struct CDFstruct *CDF;
struct VarStruct *Var;
Int32 startRec;          /* Physical record number at which to write. */
Int32 offset;            /* Byte offset within (first) record at which to
			    begin writing. */
Int32 nValues;           /* Number of values to write. */
void *buffer;
{
  OFF_T tOffset; Logical fullRecord; Byte *tBuffer = buffer;
  Int32 numElems, firstRec, lastRec, lastRecInVVR, nBytes, padTo, recNum;
  Int32 writeTo, recCount; CDFstatus pStatus = CDF_OK;
  /****************************************************************************
  * Determine first/last record(s) being written and if full physical
  * record(s).
  ****************************************************************************/
  firstRec = startRec;
  if (nValues < Var->NphyRecValues) {
    fullRecord = FALSE;
    lastRec = startRec;
  }
  else {
    fullRecord = TRUE;
    lastRec = startRec + ((nValues - 1) / Var->NphyRecValues);
  }
  /****************************************************************************
  * Based on variable type...
  ****************************************************************************/
  switch (Var->vType) {
    /**************************************************************************
    * Standard variable in single-file CDF...
    **************************************************************************/
    case STANDARD_: {
      /************************************************************************
      * Allocate/pad records.
      ************************************************************************/
      if (lastRec > Var->maxAllocated) {
	struct AllocStruct alloc;
	Int32 nNeeded = lastRec - Var->maxAllocated;
	Int32 nRecords = MAXIMUM(nNeeded,Var->blockingFactor);
	LoadAllocVVR64 (alloc, Var->maxAllocated + 1,
		        Var->maxAllocated + nRecords, FALSE)
	if (!sX(AllocateRecords64(CDF,Var,alloc),&pStatus)) return pStatus;
	Var->maxAllocated = alloc.last;
      }
      padTo = BOO(fullRecord,firstRec - 1,firstRec);
      if (padTo > Var->maxWritten) {
	Int32 padFrom = Var->maxWritten + 1;
	if (!sX(PadUnRecords64(CDF,Var,padFrom,padTo),&pStatus)) return pStatus;
	Var->maxWritten = padTo;
      }
      /************************************************************************
      * Write value(s).
      ************************************************************************/
      if (fullRecord) {
	recNum = firstRec;
	while (recNum <= lastRec) {
	  if (!sX(SearchForRecord64(CDF,Var->VDRoffset64,
				    Var->zVar,recNum,
				    NULL,&lastRecInVVR,
				    NULL,NULL),&pStatus)) return pStatus;
	  writeTo = MINIMUM(lastRec,lastRecInVVR);
	  recCount = writeTo - recNum + 1;
	  if (!sX(RecordByteOffset64(CDF,Var,
				     recNum,
				     &tOffset),&pStatus)) return pStatus;
	  numElems = recCount * Var->NphyRecElems;
	  if (!sX(WriteVarElems64(Var,CDF->fp,tOffset,
				  numElems,tBuffer),&pStatus)) return pStatus;
	  recNum += recCount;
	  tBuffer += (size_t) (recCount * Var->NphyRecBytes);
	}
      }
      else {
	if (!sX(RecordByteOffset64(CDF,Var,
				   firstRec,
				   &tOffset),&pStatus)) return pStatus;
	tOffset += (OFF_T) offset;
	numElems = nValues * Var->NvalueElems;
	if (!sX(WriteVarElems64(Var,CDF->fp,tOffset,
			        numElems,buffer),&pStatus)) return pStatus;
      }
      /************************************************************************
      * Update the maximum record written.
      ************************************************************************/
      Var->maxWritten = MAXIMUM(Var->maxWritten,lastRec);
      break;
    }
    /**************************************************************************
    * Sparse records...
    **************************************************************************/
    case SPARSE_RECORDS_: {
      Int32 maxRecInStage, recordOffsetInStage, nextRec;
      int how; void *padBuffer; Logical found;
      /************************************************************************
      * Pad records.
      ************************************************************************/
      padTo = BOO(fullRecord,firstRec - 1,firstRec);
      if (padTo > Var->maxWritten) {
	Int32 padFrom = Var->maxWritten + 1;
	if (!sX(PadUnRecords64(CDF,Var,padFrom,padTo),&pStatus)) return pStatus;
	Var->maxWritten = padTo;
      }
      /************************************************************************
      * Write value(s).
      ************************************************************************/
      recNum = firstRec;
      while (recNum <= lastRec) {
	/**********************************************************************
	* Check if this record already exists (is allocated).
	**********************************************************************/
	if (!sX(SearchForRecord64(CDF,Var->VDRoffset64,
				  Var->zVar,recNum,
				  NULL,&lastRecInVVR,
				  NULL,&found),&pStatus)) return pStatus;
	if (found) {
	  writeTo = MINIMUM(lastRec,lastRecInVVR);
	  recCount = writeTo - recNum + 1;
	  if (!sX(RecordByteOffset64(CDF,Var,
				     recNum,&tOffset),&pStatus)) return pStatus;
	  if (fullRecord)
	    numElems = recCount * Var->NphyRecElems;
	  else {
	    tOffset += offset;
	    numElems = nValues * Var->NvalueElems;
	  }
	  if (!sX(WriteVarElems64(Var,CDF->fp,tOffset,
				  numElems,tBuffer),&pStatus)) return pStatus;
	  Var->maxWritten = MAXIMUM(Var->maxWritten,writeTo);
	  recNum += recCount;
	  tBuffer += (size_t) (numElems * Var->NelemBytes);
	  continue;
	}
	/**********************************************************************
	* This record doesn't exist - initialize the staging area.
	**********************************************************************/
	if (Var->stage.areaOffset64 == (OFF_T) NO_OFFSET64) {
	  nBytes = Var->blockingFactor * Var->NphyRecBytes;
	  if (!sX(InitVarStage64(CDF,Var,nBytes),&pStatus)) return pStatus;
	}
	/**********************************************************************
	* Check if this record is in the staging area.
	**********************************************************************/
	if (INCLUSIVE(Var->stage.firstRec,recNum,Var->stage.lastRec)) {
	  maxRecInStage = Var->stage.firstRec + Var->blockingFactor - 1;
	  writeTo = MINIMUM(lastRec,maxRecInStage);
	  if (!sX(NextRecord64(CDF,Var->VDRoffset64,Var->zVar,
			       recNum,&nextRec,&found),&pStatus)) return pStatus;
	  if (found) {
	    Int32 prevRecN = nextRec - 1;
	    writeTo = MINIMUM(writeTo,prevRecN);
	  }
	  recCount = writeTo - recNum + 1;
	  recordOffsetInStage = recNum - Var->stage.firstRec;
	  tOffset = Var->stage.areaOffset64;
	  tOffset += (recordOffsetInStage * Var->NphyRecBytes);
	  if (fullRecord)
	    numElems = recCount * Var->NphyRecElems;
	  else {
	    tOffset += offset;
	    numElems = nValues * Var->NvalueElems;
	  }
	  if (!sX(WriteVarElems64(Var,CDF->stage.fp,tOffset,
				  numElems,tBuffer),&pStatus)) return pStatus;
	  Var->stage.lastRec = MAXIMUM(Var->stage.lastRec,writeTo);
	  Var->stage.modified = TRUE;
	  recNum += recCount;
	  tBuffer += (size_t) (numElems * Var->NelemBytes);
	  continue;
	}
	/**********************************************************************
	* This record is not in the staging area.  Check if it can be added.
	**********************************************************************/
	if (Var->stage.firstRec != NO_RECORD) {
	  if (recNum == Var->stage.lastRec + 1) {
	    maxRecInStage = Var->stage.firstRec + Var->blockingFactor - 1;
	    if (recNum <= maxRecInStage) {
	      writeTo = MINIMUM(lastRec,maxRecInStage);
	      if (!sX(NextRecord64(CDF,Var->VDRoffset64,
				   Var->zVar,recNum,
				   &nextRec,&found),&pStatus)) return pStatus;
	      if (found) {
		Int32 prevRecN = nextRec - 1;
		writeTo = MINIMUM(writeTo,prevRecN);
	      }
	      recCount = writeTo - recNum + 1;
	      recordOffsetInStage = recNum - Var->stage.firstRec;
	      tOffset = Var->stage.areaOffset64;
	      tOffset += (recordOffsetInStage * Var->NphyRecBytes);
	      if (fullRecord)
		numElems = recCount * Var->NphyRecElems;
	      else {
		if (!sX(BuildPadBuffer64(CDF,Var,recCount,
				         &how,&padBuffer,
				         TRUE),&pStatus)) return pStatus;
		if (!sX(WritePadValues64(Var,CDF->stage.fp,tOffset,
				         recCount,how,padBuffer),&pStatus)) {
		  cdf_FreeMemory (padBuffer, NULL);
		  return pStatus;
		}
		cdf_FreeMemory (padBuffer, NULL);
		tOffset += offset;
		numElems = nValues * Var->NvalueElems;
	      }
	      if (!sX(WriteVarElems64(Var,CDF->stage.fp,
				      tOffset,numElems,
				      tBuffer),&pStatus)) return pStatus;
	      Var->stage.lastRec = MAXIMUM(Var->stage.lastRec,writeTo);
	      Var->stage.modified = TRUE;
	      recNum += recCount;
	      tBuffer += (size_t) (numElems * Var->NelemBytes);
	      continue;
	    }
	  }
	}
	/**********************************************************************
	* This record cannot be added to the staging area.  First flush the
	* staging area (if necessary)...
	**********************************************************************/
	if (!sX(FlushStage64(CDF,Var),&pStatus)) return pStatus;
	/**********************************************************************
	* ...and then start a new staging area with this record.
	**********************************************************************/
	maxRecInStage = recNum + Var->blockingFactor - 1;
	writeTo = MINIMUM(lastRec,maxRecInStage);
	if (!sX(NextRecord64(CDF,Var->VDRoffset64,Var->zVar,
			     recNum,&nextRec,&found),&pStatus)) return pStatus;
	if (found) {
	  Int32 prevRecN = nextRec - 1;
	  writeTo = MINIMUM(writeTo,prevRecN);
	}
	recCount = writeTo - recNum + 1;
	tOffset = Var->stage.areaOffset64;
	if (fullRecord)
	  numElems = recCount * Var->NphyRecElems;
	else {
	  if (!sX(BuildPadBuffer64(CDF,Var,recCount,
				   &how,&padBuffer,
				   TRUE),&pStatus)) return pStatus;
	  if (!sX(WritePadValues64(Var,CDF->stage.fp,tOffset,
				   recCount,how,padBuffer),&pStatus)) {
	  cdf_FreeMemory (padBuffer, NULL);
	  return pStatus;
	  }
	  cdf_FreeMemory (padBuffer, NULL);
	  tOffset += offset;
	  numElems = nValues * Var->NvalueElems;
	}
	if (!sX(WriteVarElems64(Var,CDF->stage.fp,tOffset,
			        numElems,tBuffer),&pStatus)) return pStatus;
	Var->stage.firstRec = recNum;
	Var->stage.lastRec = writeTo;
	Var->stage.modified = TRUE;
	recNum += recCount;
	tBuffer += (size_t) (numElems * Var->NelemBytes);
      }
      break;
    }
    /**************************************************************************
    * Compressed records...
    * Sparse/compressed records...
    **************************************************************************/
    case COMPRESSED_:
    case SPARSE_COMPRESSED_RECORDS_: {
      Logical sparseRecords = (Var->vType == SPARSE_COMPRESSED_RECORDS_);
      /************************************************************************
      * Initialize staging area.
      ************************************************************************/
      if (Var->stage.areaOffset64 == (OFF_T) NO_OFFSET64) {
	if (Var->blockingFactor == 0) {
	  if (Var->recVary) {
	    Int32 bf = ((MIN_BLOCKING_BYTES_compressed-1)/Var->NphyRecBytes)+1;
	    Var->blockingFactor = MAXIMUM(bf,MIN_BLOCKING_RECS_compressed);
	  }
	  else
	    Var->blockingFactor = 1;
	  if (!sX(WriteVDR64(CDF,CDF->fp,Var->VDRoffset64,Var->zVar,
			     VDR_BLOCKING,&(Var->blockingFactor),
			     VDR_NULL),&pStatus)) return pStatus;
	}
	nBytes = Var->blockingFactor * Var->NphyRecBytes;
	if (!sX(InitVarStage64(CDF,Var,nBytes),&pStatus)) return pStatus;
      }
      /************************************************************************
      * Pad records (if necessary).
      ************************************************************************/
      if (!sparseRecords) {
	padTo = firstRec - 1;
	if (Var->maxRec < padTo) {
	  Int32 nRecords = padTo + 1, recN, valueN; void *padBuffer; int how;
	  if (!sX(BuildPadBuffer64(CDF,Var,
				   nRecords,&how,
				   &padBuffer,FALSE),&pStatus)) return pStatus;
	  switch (how) {
	    case ALLrecordsATonce:
	      if (!sX(WriteCompressedRecords(CDF,Var,Var->maxRec+1,
					     padTo,padBuffer,
					     nRecords*Var->NphyRecValues,
					     (OFF_T) ZERO_OFFSET64,
					     TRUE),&pStatus)) return pStatus;
	      break;
	    case ONErecordATaTIME:
	      for (recN = Var->maxRec + 1; recN <= padTo; recN++) {
		 if (!sX(WriteCompressedRecords(CDF,Var,recN,recN,
					        padBuffer,Var->NphyRecValues,
						(OFF_T) ZERO_OFFSET64,TRUE),&pStatus)) {
		   cdf_FreeMemory (padBuffer, NULL);
		   return pStatus;
		 }
	      }
	      break;
	    case ONEvalueATaTIME:
	      for (recN = 0; recN < nRecords; recN++) {
		 for (valueN = 0; valueN < Var->NphyRecValues; valueN++) {
		    tOffset = valueN * Var->NvalueBytes;
		    if (!sX(WriteCompressedRecords(CDF,Var,recN,recN,
						   padBuffer,INT32_ONE,
						   tOffset,FALSE),&pStatus)) {
		      cdf_FreeMemory (padBuffer, NULL);
		      return pStatus;
		    }
		 }
	      }
	      break;
	  }
	  cdf_FreeMemory (padBuffer, NULL);
	}
      }
      /************************************************************************
      * Write the record(s).
      ************************************************************************/
      if (!sX(WriteCompressedRecords(CDF,Var,firstRec,lastRec,
				     buffer,nValues,offset,
				     fullRecord),&pStatus)) return pStatus;
      break;
    }
    /**************************************************************************
    * Can't do sparse arrays yet...
    **************************************************************************/
    case SPARSE_ARRAYS_:
    case SPARSE_RECORDS_AND_ARRAYS_:
      return UNKNOWN_SPARSENESS;
    /**************************************************************************
    * Variable in multi-file CDF...
    **************************************************************************/
    case IN_MULTI_: {
      padTo = BOO(fullRecord,firstRec - 1,firstRec);
      /************************************************************************
      * Extend variable (if necessary).
      ************************************************************************/
      if (padTo > Var->maxRec) {
	Int32 padFrom = Var->maxRec + 1;
	if (!sX(PadUnRecords64(CDF,Var,padFrom,padTo),&pStatus)) return pStatus;
      }
      /************************************************************************
      * Write value(s).
      ************************************************************************/
      if (!sX(RecordByteOffset64(CDF,Var,
			         firstRec,
			         &tOffset),&pStatus)) return pStatus;
      tOffset += (OFF_T) offset;
      numElems = nValues * Var->NvalueElems;
      if (!sX(WriteVarElems64(Var,Var->fp,tOffset,
			      numElems,buffer),&pStatus)) return pStatus;
      break;
    }
    /**************************************************************************
    * Unknown variable type - this should never happen.
    **************************************************************************/
    default:
      return CDF_INTERNAL_ERROR;
  }
  /****************************************************************************
  * Update maximum record numbers.
  ****************************************************************************/
  if (!sX(UpdateMaxRec64(CDF,Var,lastRec),&pStatus)) return pStatus;
  return pStatus;
}

/******************************************************************************
* WriteVarElems64.
*   NOTE: On IBM PCs, it is assumed that the number of bytes being written
* will not exceed 65535.
******************************************************************************/

STATICforIDL CDFstatus WriteVarElems64 (Var, fp, offset, numElems, buffer)
struct VarStruct *Var;
vFILE *fp;
OFF_T offset;
Int32 numElems;
void *buffer;
{
  CDFstatus pStatus = CDF_OK; Int32 elemCount;
  /****************************************************************************
  * Seek to the desired offset.
  ****************************************************************************/
  if (!SEEKv64(fp,offset,vSEEK_SET)) return VAR_WRITE_ERROR;
  /****************************************************************************
  * If no encoding is necessary, simply write the buffer and return.
  ****************************************************************************/
  if (Var->EncodeFunction == NULL) {
    Int32 nBytes = numElems * Var->NelemBytes;
    if (!WRITEv64(buffer,1,(size_t)nBytes,fp)) return VAR_WRITE_ERROR;
    return pStatus;
  }
  /****************************************************************************
  * Use as large a temporary buffer as possible for the encoding conversion.
  * Start at the full number of elements and then halve that number until an
  * allocation succeeds.
  ****************************************************************************/
  elemCount = numElems;
  for (;;) {
     OFF_T xBytes = (OFF_T) elemCount * Var->NelemBytes;
#if defined(win32)
     if (xBytes < (OFF_T) ((1i64 << 31) - 1)) {
#else
     if (xBytes < (OFF_T) ((1LL << 31) - 1)) {
#endif
       size_t nBytes = (size_t) (elemCount * Var->NelemBytes);
       void *tBuffer = cdf_AllocateMemory (nBytes, NULL);
       if (tBuffer != NULL) {
         Int32 elemN = 0; Byte *bOffset = buffer;
         while (elemN < numElems) {
	   Int32 thisElemCount = MinInt32 (elemCount, numElems - elemN);
	   size_t thisByteCount = (size_t) (thisElemCount * Var->NelemBytes);
	   memmove (tBuffer, bOffset, thisByteCount);
	   if (!sX(Var->EncodeFunction(tBuffer,thisElemCount),&pStatus)) {
	     cdf_FreeMemory (tBuffer, NULL);
	     return pStatus;
	   }
	   if (!WRITEv64(tBuffer,1,thisByteCount,fp)) {
	     cdf_FreeMemory (tBuffer, NULL);
	     return VAR_WRITE_ERROR;
	   }
	   elemN += thisElemCount;
	   bOffset += thisByteCount;
         }
         cdf_FreeMemory (tBuffer, NULL);
         return pStatus;
       }
     }
     if (elemCount == 1) break;
     elemCount = (elemCount + 1) / 2;
  }
  return BAD_MALLOC;
}

/******************************************************************************
* PrevRecord64.
*   Determine the last record allocated AT or BEFORE `baseRec'.  This routine
* should only be used for single-file CDFs.
******************************************************************************/

STATICforIDL CDFstatus PrevRecord64 (CDF, VDRoffset, zVar, baseRec, prevRec,
				     found)
struct CDFstruct *CDF;
OFF_T VDRoffset;
Logical zVar;
Int32 baseRec;
Int32 *prevRec;
Logical *found;         /* If NULL, return NO_SUCH_RECORD if the previous
			   record doesn't exist.  Otherwise, set according to
			   whether or not the previous record exists and
			   return the pending status. */
{
  CDFstatus pStatus = CDF_OK; OFF_T VXRoffset;
  /****************************************************************************
  * If multi-file...
  ****************************************************************************/
  if (!CDF->singleFile) return CDF_INTERNAL_ERROR;
  /****************************************************************************
  * Single-file...read the offset of the first VXR.
  ****************************************************************************/
  if (!sX(ReadVDR64(CDF,CDF->fp,VDRoffset,zVar,
		    VDR_VXRHEAD,&VXRoffset,
		    VDR_NULL),&pStatus)) return pStatus;
  /****************************************************************************
  * ...and start there (unless there are no VXRs).
  ****************************************************************************/
  if (VXRoffset != (OFF_T) ZERO_OFFSET64) {
    if (!sX(PrevRecord_r_64(CDF->fp,VXRoffset,
			    baseRec,prevRec,found),&pStatus)) return pStatus;
  }
  else {
    ASSIGNnotNULL (found, FALSE)
    if (found == NULL) pStatus = NO_SUCH_RECORD;
  }
  return pStatus;
}

static CDFstatus PrevRecord_r_64 (fp, vxrOffset, baseRec, prevRec, found)
vFILE *fp;
OFF_T vxrOffset;
Int32 baseRec;
Int32 *prevRec;
Logical *found;
{
  CDFstatus pStatus = CDF_OK; int entryN = 0;
  struct VXRstruct64 VXR, nextVXR; Int32 irType;
  /****************************************************************************
  * Read the first VXR.
  ****************************************************************************/
  if (!sX(ReadVXR64(fp,vxrOffset,
		    VXR_RECORD,&VXR,
		    VXR_NULL),&pStatus)) return pStatus;
  /****************************************************************************
  * Check if there isn't a previous allocated record.  This could only be the
  * case at the top level of VXRs (ie. not when this routine is recursively
  * called).
  ****************************************************************************/
  if (baseRec < VXR.First[0]) {
    ASSIGNnotNULL (found, FALSE)
    return BOO(found == NULL,NO_SUCH_RECORD,pStatus);
  }
  /****************************************************************************
  * Until it's time to return...
  ****************************************************************************/
  for (;;) {
     /*************************************************************************
     * If the record is in the current entry...
     *************************************************************************/
     if (INCLUSIVE(VXR.First[entryN],baseRec,VXR.Last[entryN])) {
       if (!sX(ReadIrType64(fp,VXR.Offset[entryN],&irType),&pStatus)) {
	 return pStatus;
       }
       switch (irType) {
	 case VXR_:
	   return PrevRecord_r_64(fp,VXR.Offset[entryN],baseRec,prevRec,found);
	 case VVR_:
	 case CVVR_:
	   *prevRec = baseRec;
	   ASSIGNnotNULL (found, TRUE)
	   return pStatus;
	 default:
	   return CORRUPTED_V3_CDF;
       }
     }
     /*************************************************************************
     * If this is the last entry in the current VXR...
     *************************************************************************/
     if (entryN == VXR.NusedEntries - 1) {
       if (VXR.VXRnext == (OFF_T) ZERO_OFFSET64) {
	 *prevRec = VXR.Last[entryN];
	 ASSIGNnotNULL (found, TRUE)
	 return pStatus;
       }
       if (!sX(ReadVXR64(fp,VXR.VXRnext,
		         VXR_RECORD,&nextVXR,
		         VXR_NULL),&pStatus)) return pStatus;
       if (baseRec < nextVXR.First[0]) {
	 *prevRec = VXR.Last[entryN];
	 ASSIGNnotNULL (found, TRUE)
	 return pStatus;
       }
       VXR = nextVXR;
       entryN = 0;
     }
     else {
       if (baseRec < VXR.First[entryN+1]) {
	 *prevRec = VXR.Last[entryN];
	 ASSIGNnotNULL (found, TRUE)
	 return pStatus;
       }
       entryN++;
     }
  }
}

/******************************************************************************
* NextRecord64.
*   Determine the next allocated record AT or AFTER `baseRec'.  I.e., if
* `baseRec' is allocated, it is returned as `nextRec'.
******************************************************************************/

STATICforIDL CDFstatus NextRecord64 (CDF, VDRoffset, zVar, baseRec, nextRec,
				     found)
struct CDFstruct *CDF;
OFF_T VDRoffset;
Logical zVar;
Int32 baseRec;
Int32 *nextRec;
Logical *found;         /* If NULL, return NO_SUCH_RECORD if the next record
			   doesn't exist.  Otherwise, set according to whether
			   or not the next record exists and return the pending
			   status. */
{
  CDFstatus pStatus = CDF_OK; Int32 maxRec;
  OFF_T VXRoffset;
  /****************************************************************************
  * If multi-file...
  ****************************************************************************/
  if (!CDF->singleFile) {
    if (!sX(ReadVDR64(CDF,CDF->fp,VDRoffset,zVar,
		      VDR_MAXREC,&maxRec,
		      VDR_NULL),&pStatus)) return pStatus;
    if (baseRec <= maxRec) {
      *nextRec = baseRec;
      ASSIGNnotNULL (found, TRUE)
    }
    else {
      ASSIGNnotNULL (found, FALSE)
      if (found == NULL) pStatus = NO_SUCH_RECORD;
    }
    return pStatus;
  }
  /****************************************************************************
  * ...single-file, read the offset of the first VXR.
  ****************************************************************************/
  if (!sX(ReadVDR64(CDF,CDF->fp,VDRoffset,zVar,
		    VDR_VXRHEAD,&VXRoffset,
		    VDR_NULL),&pStatus)) return pStatus;
  /****************************************************************************
  * ...and start there.
  ****************************************************************************/
  if (!sX(NextRecord_r_64(CDF->fp,VXRoffset,
		          baseRec,nextRec,found),&pStatus)) return pStatus;
  return pStatus;
}

static CDFstatus NextRecord_r_64 (fp, vxrOffset, baseRec, nextRec, found)
vFILE *fp;
OFF_T vxrOffset;
Int32 baseRec;
Int32 *nextRec;
Logical *found;
{
  CDFstatus pStatus = CDF_OK;
  int entryN; struct VXRstruct64 VXR; Int32 irType;
  /****************************************************************************
  * While more VXRs...
  ****************************************************************************/
  while (vxrOffset != (OFF_T) ZERO_OFFSET64) {
    /**************************************************************************
    * Read the VXR.
    **************************************************************************/
    if (!sX(ReadVXR64(fp,vxrOffset,
		      VXR_RECORD,&VXR,
		      VXR_NULL),&pStatus)) return pStatus;
    /**************************************************************************
    * Search entries...
    **************************************************************************/
    if (baseRec <= VXR.Last[(int)(VXR.NusedEntries-1)]) {
      for (entryN = 0; entryN < VXR.NusedEntries; entryN++) {
	 if (baseRec <= VXR.Last[entryN]) {
	   if (!sX(ReadIrType64(fp,
			        VXR.Offset[entryN],
			        &irType),&pStatus)) return pStatus;
	   switch (irType) {
	     case VXR_:
	       return NextRecord_r_64(fp,VXR.Offset[entryN],
				      baseRec,nextRec,found);
	     case VVR_:
	     case CVVR_:
	       *nextRec = BOO(VXR.First[entryN] <= baseRec,
			      baseRec,VXR.First[entryN]);
	       ASSIGNnotNULL (found, TRUE)
	       return pStatus;
	     default:
	       return CORRUPTED_V3_CDF;
	   }
	 }
      }
    }
    vxrOffset = VXR.VXRnext;
  }
  /****************************************************************************
  * No (more) VXRs.  The record number was never found.
  ****************************************************************************/
  ASSIGNnotNULL (found, FALSE)
  return BOO(found == NULL,NO_SUCH_RECORD,pStatus);
}

/******************************************************************************
* SearchForRecord64.
******************************************************************************/

STATICforIDL CDFstatus SearchForRecord64 (CDF, VDRoffset, zVar, recNum,
					  firstRec, lastRec, offset, found)
struct CDFstruct *CDF;
OFF_T VDRoffset;
Logical zVar;
Int32 recNum;
Int32 *firstRec;
Int32 *lastRec;
OFF_T *offset;
Logical *found;         /* If NULL, return NO_SUCH_RECORD if the record is
			   not found.  Otherwise, set according to whether
			   or not the record is found and return the pending
			   status. */
{
  CDFstatus pStatus = CDF_OK; Int32 maxRec;
  OFF_T vxrOffset;
  /****************************************************************************
  * If multi-file...
  ****************************************************************************/
  if (!CDF->singleFile) {
    if (!sX(ReadVDR64(CDF,CDF->fp,VDRoffset,zVar,
		      VDR_MAXREC,&maxRec,
		      VDR_NULL),&pStatus)) return pStatus;
    if (recNum <= maxRec) {
      ASSIGNnotNULL (firstRec, 0)
      ASSIGNnotNULL (lastRec, maxRec)
      ASSIGNnotNULL (offset, (OFF_T) ZERO_OFFSET64)
      ASSIGNnotNULL (found, TRUE)
    }
    else {
      ASSIGNnotNULL (found, FALSE)
      if (found == NULL) pStatus = NO_SUCH_RECORD;
    }
    return pStatus;
  }
  /****************************************************************************
  * ...single-file, read the offset of the first VXR.
  ****************************************************************************/
  if (!sX(ReadVDR64(CDF,CDF->fp,VDRoffset,zVar,
		    VDR_VXRHEAD,&vxrOffset,
		    VDR_NULL),&pStatus)) return pStatus;
  /****************************************************************************
  * ...and start searching there.
  ****************************************************************************/
  if (!sX(SearchForRecord_r_64(CDF->fp,vxrOffset,recNum,
			       firstRec,lastRec,
			       offset,found),&pStatus)) return pStatus;
  return pStatus;
}

static CDFstatus SearchForRecord_r_64 (fp, vxrOffset, recNum, firstRec, lastRec,
				       offset, found)
vFILE *fp;
OFF_T vxrOffset;
Int32 recNum;
Int32 *firstRec;
Int32 *lastRec;
OFF_T *offset;
Logical *found;
{
  CDFstatus pStatus = CDF_OK;
  int entryN; struct VXRstruct64 VXR; Int32 irType;
  /****************************************************************************
  * While more VXRs...
  ****************************************************************************/
  while (vxrOffset != (OFF_T) ZERO_OFFSET64) {
    /**************************************************************************
    * Read the VXR.
    **************************************************************************/
    if (!sX(ReadVXR64(fp,vxrOffset,
		      VXR_RECORD,&VXR,
		      VXR_NULL),&pStatus)) return pStatus;
    /**************************************************************************
    * Search entries...
    **************************************************************************/
    if (recNum <= VXR.Last[(int)(VXR.NusedEntries-1)]) {
      for (entryN = 0; entryN < VXR.NusedEntries; entryN++) {
	 if (recNum <= VXR.Last[entryN]) {
	   if (VXR.First[entryN] <= recNum) {
	     if (!sX(ReadIrType64(fp,
				  VXR.Offset[entryN],
				  &irType),&pStatus)) return pStatus;
	     switch (irType) {
	       case VXR_:
		 return SearchForRecord_r_64(fp,VXR.Offset[entryN],recNum,
					     firstRec,lastRec,offset,found);
	       case VVR_:
	       case CVVR_:
		 ASSIGNnotNULL (firstRec, VXR.First[entryN])
		 ASSIGNnotNULL (lastRec, VXR.Last[entryN])
		 ASSIGNnotNULL (offset, VXR.Offset[entryN])
		 ASSIGNnotNULL (found, TRUE)
		 return pStatus;
	       default:
		 return CORRUPTED_V3_CDF;
	     }
	   }
	   else {
	     ASSIGNnotNULL (found, FALSE)
	     return BOO(found == NULL,NO_SUCH_RECORD,pStatus);
	   }
	 }
      }
    }
    vxrOffset = VXR.VXRnext;
  }
  /****************************************************************************
  * No (more) VXRs.  The record number was never found.
  ****************************************************************************/
  ASSIGNnotNULL (found, FALSE)
  return BOO(found == NULL,NO_SUCH_RECORD,pStatus);
}

/******************************************************************************
* IndexingStatistics64.
******************************************************************************/

STATICforIDL CDFstatus IndexingStatistics64 (CDF, VDRoffset, zVar, nVXRsP,
					     nEntriesP, nAllocP, nRecordsP,
					     nLevelsP)
struct CDFstruct *CDF;
OFF_T VDRoffset;
Logical zVar;
Int32 *nVXRsP;
Int32 *nEntriesP;
Int32 *nAllocP;
Int32 *nRecordsP;
Int32 *nLevelsP;
{
  CDFstatus pStatus = CDF_OK; Int32 maxRec; int level = 1;
  Int32 nVXRs = 0, nEntries = 0, nAlloc = 0, nRecords = 0, nLevels = 0;
  OFF_T vxrOffset;
  /****************************************************************************
  * Read the maximum record and the offset of the first VXR...
  ****************************************************************************/
  if (!sX(ReadVDR64(CDF,CDF->fp,VDRoffset,zVar,
		    VDR_VXRHEAD,&vxrOffset,
		    VDR_MAXREC,&maxRec,
		    VDR_NULL),&pStatus)) return pStatus;
  /****************************************************************************
  * ...and start there.
  ****************************************************************************/
  if (vxrOffset != (OFF_T) ZERO_OFFSET64) {
    if (!sX(IndexingStatistics_r_64(CDF->fp,vxrOffset,
				    maxRec,level,&nLevels,
				    &nVXRs,&nEntries,
				    &nAlloc,&nRecords),&pStatus)) return pStatus;
  }
  /****************************************************************************
  * Pass back requested statistics.
  ****************************************************************************/
  ASSIGNnotNULL (nVXRsP, nVXRs)
  ASSIGNnotNULL (nEntriesP, nEntries)
  ASSIGNnotNULL (nAllocP, nAlloc)
  ASSIGNnotNULL (nRecordsP, nRecords)
  ASSIGNnotNULL (nLevelsP, nLevels)
  return pStatus;
}

static CDFstatus IndexingStatistics_r_64 (fp, vxrOffset, maxRec, level, nLevels,
				          nVXRs, nEntries, nAlloc, nRecords)
vFILE *fp;
OFF_T vxrOffset;
Int32 maxRec;
int level;
Int32 *nLevels;
Int32 *nVXRs;
Int32 *nEntries;
Int32 *nAlloc;
Int32 *nRecords;
{
  CDFstatus pStatus = CDF_OK;
  int e; Int32 irType; struct VXRstruct64 VXR;
  /****************************************************************************
  * Check if a new level has been reached.
  ****************************************************************************/
  *nLevels = MAXIMUM(*nLevels,level);
  /****************************************************************************
  * While more VXRs...
  ****************************************************************************/
  while (vxrOffset != (OFF_T) ZERO_OFFSET64) {
    /**************************************************************************
    * Read/tally the VXR.
    **************************************************************************/
    if (!sX(ReadVXR64(fp,vxrOffset,
		      VXR_RECORD,&VXR,
		      VXR_NULL),&pStatus)) return pStatus;
    (*nVXRs)++;
    /**************************************************************************
    * Scan/count entries...
    **************************************************************************/
    for (e = 0; e < VXR.NusedEntries; e++) {
       (*nEntries)++;
       if (!sX(ReadIrType64(fp,
			    VXR.Offset[e],
			    &irType),&pStatus)) return pStatus;
       switch (irType) {
	 case VXR_:
	   if (!sX(IndexingStatistics_r_64(fp,VXR.Offset[e],
					   maxRec,level+1,
					   nLevels,nVXRs,
					   nEntries,nAlloc,
					   nRecords),&pStatus)) return pStatus;
	   break;
	 case VVR_:
	 case CVVR_:
	   *nAlloc += (VXR.Last[e] - VXR.First[e] + 1);
	   if (VXR.First[e] <= maxRec) {
	     *nRecords += MINIMUM(maxRec,VXR.Last[e]) - VXR.First[e] + 1;
	   }
	   break;
	 default:
	   return CORRUPTED_V3_CDF;
       }
    }
    vxrOffset = VXR.VXRnext;
  }
  return pStatus;
}

/******************************************************************************
* BuildPadBuffer64.
******************************************************************************/

STATICforIDL CDFstatus BuildPadBuffer64 (CDF, Var, nRecords, how, buffer, encode)
struct CDFstruct *CDF;
struct VarStruct *Var;
Int32 nRecords;
int *how;
void **buffer;
Logical encode;         /* If TRUE, return values in CDF's encoding.  If FALSE,
			   return values in host computer's encoding. */
{
  Byte *ptr; Int32 nBytes, nValues, valueN; void *padValue;
  Int32 VDRflags, dataType, numElems; CDFstatus pStatus = CDF_OK;
  OFF_T xBytes;
  /****************************************************************************
  * Determine pad value.
  ****************************************************************************/
  if (!sX(ReadVDR64(CDF,CDF->fp,Var->VDRoffset64,Var->zVar,
		    VDR_DATATYPE,&dataType,
		    VDR_NUMELEMS,&numElems,
		    VDR_FLAGS,&VDRflags,
		    VDR_NULL),&pStatus)) return pStatus;
  padValue = (void *) cdf_AllocateMemory ((size_t) Var->NvalueBytes, NULL);
  if (padValue == NULL) return BAD_MALLOC;
  if (PADvalueBITset(VDRflags)) {
    if (!sX(ReadVDR64(CDF,CDF->fp,Var->VDRoffset64,Var->zVar,
		      VDR_PADVALUE,padValue,
		      VDR_NULL),&pStatus)) {
      cdf_FreeMemory (padValue, NULL);
      return pStatus;
    }
    if (!encode) {
      if (!sX(ConvertBuffer(CDF->encoding,HostDecoding(),
			    CDF->negToPosFp0,dataType,
			    numElems,padValue,padValue),&pStatus)) {
	cdf_FreeMemory (padValue, NULL);
	return pStatus;
      }
    }
  }
  else {
    DefaultPadValue (dataType, numElems, padValue);
    if (encode) {
      if (!sX(ConvertBuffer(HostEncoding(),CDF->encoding,
			    CDF->negToPosFp0,dataType,
			    numElems,padValue,padValue),&pStatus)) {
	cdf_FreeMemory (padValue, NULL);
	return pStatus;
      }
    }
  }
  /****************************************************************************
  * Try to allocate all of the records at once...
  ****************************************************************************/
  nValues = nRecords * Var->NphyRecValues;
  xBytes = (OFF_T) nValues * Var->NvalueBytes;
#if defined(win32)
  if (xBytes < (OFF_T) ((1i64 << 31) - 1)) {
#else
  if (xBytes < (OFF_T) ((1LL << 31) - 1)) {
#endif
    nBytes = nValues * Var->NvalueBytes;
#if LIMITof64K
    if (nBytes < 65536L) {
#endif
      *buffer = (void *) cdf_AllocateMemory ((size_t) nBytes, NULL);
      if (*buffer != NULL) {
        for (valueN = 0, ptr = (Byte *) *buffer;
	   valueN < nValues; valueN++, ptr += (size_t) Var->NvalueBytes) {
	   memmove (ptr, padValue, (size_t) Var->NvalueBytes);
        }
        cdf_FreeMemory (padValue, NULL);
        *how = ALLrecordsATonce;
        return pStatus;
      }
#if LIMITof64K
    }
#endif
  }
  /****************************************************************************
  * Not enough memory for that, try allocating one record...
  ****************************************************************************/
#if LIMITof64K
  if (Var->NphyRecBytes < 65536L) {
#endif
    *buffer = (void *) cdf_AllocateMemory ((size_t) Var->NphyRecBytes, NULL);
    if (*buffer != NULL) {
      for (valueN = 0, ptr = (Byte *) *buffer;
	   valueN < Var->NphyRecValues;
	   valueN++, ptr += (size_t) Var->NvalueBytes) {
	 memmove (ptr, padValue, (size_t) Var->NvalueBytes);
      }
      cdf_FreeMemory (padValue, NULL);
      *how = ONErecordATaTIME;
      return pStatus;
    }
#if LIMITof64K
  }
#endif
  /****************************************************************************
  * Not enough memory for that either, use the one allocated value...
  ****************************************************************************/
  *buffer = padValue;
  *how = ONEvalueATaTIME;
  return pStatus;
}

/******************************************************************************
* ReadVarValues64.
*   NOTE: If more than one record is being read, full records are assumed
* and the `offset' must be zero.
******************************************************************************/

STATICforIDL CDFstatus ReadVarValues64 (CDF, Var, startRec, offset, nValues,
				        buffer)
struct CDFstruct *CDF;
struct VarStruct *Var;
Int32 startRec;         /* Physical record number at which to start reading. */
Int32 offset;           /* Byte offset within (first) record at which to
			   begin reading. */
Int32 nValues;          /* Number of values to read. */
void *buffer;
{
  CDFstatus pStatus = CDF_OK; OFF_T tOffset; Logical fullRecord;
  Int32 numElems, firstRec, lastRec, lastRecInVVR, nPadValues;
  Int32 readTo, recCount, nBytes;
  /****************************************************************************
  * Determine first/last record(s) being read and if full physical record(s).
  ****************************************************************************/
  firstRec = startRec;
  if (nValues < Var->NphyRecValues) {
    fullRecord = FALSE;
    lastRec = startRec;
  }
  else {
    fullRecord = TRUE;
    lastRec = startRec + ((nValues - 1) / Var->NphyRecValues);
  }
  /****************************************************************************
  * Read value(s).
  ****************************************************************************/
  switch (Var->vType) {
    /**************************************************************************
    * Standard variable in a single-file CDF...
    **************************************************************************/
    case STANDARD_:
      if (fullRecord) {
	/**********************************************************************
	* Full record(s) - read from contiguous groups...
	**********************************************************************/
	Byte *tBuffer = buffer; Int32 recNum = firstRec;
	while (recNum <= lastRec) {
	  if (recNum <= Var->maxRec) {
	    if (!sX(SearchForRecord64(CDF,Var->VDRoffset64,
				      Var->zVar,recNum,
				      NULL,&lastRecInVVR,
				      NULL,NULL),&pStatus)) return pStatus;
	    readTo = MINIMUMof3(Var->maxRec,lastRec,lastRecInVVR);
	    recCount = readTo - recNum + 1;
	    if (!sX(RecordByteOffset64(CDF,Var,
				       recNum,
				       &tOffset),&pStatus)) return pStatus;
	    numElems = recCount * Var->NphyRecElems;
	    if (!sX(ReadVarElems64(Var,CDF->fp,tOffset,
				   numElems,tBuffer),&pStatus)) return pStatus;
	  }
	  else {
	    recCount = lastRec - recNum + 1;
	    nPadValues = recCount * Var->NphyRecValues;
	    if (!sX(PadBuffer64(CDF,Var,
			        nPadValues,
			        tBuffer),&pStatus)) return pStatus;
	    sX (VIRTUAL_RECORD_DATA, &pStatus);
	  }
	  recNum += recCount;
	  tBuffer += (size_t) (recCount * Var->NphyRecBytes);
	}
      }
      else {
	/**********************************************************************
	* Partial record...
	**********************************************************************/
	if (firstRec <= Var->maxRec) {
	  if (!sX(RecordByteOffset64(CDF,Var,
				     firstRec,
				     &tOffset),&pStatus)) return pStatus;
	  tOffset += (OFF_T) offset;
	  numElems = nValues * Var->NvalueElems;
	  if (!sX(ReadVarElems64(Var,CDF->fp,tOffset,
			         numElems,buffer),&pStatus)) return pStatus;
	}
	else {
	  if (!sX(PadBuffer64(CDF,Var,nValues,buffer),&pStatus)) return pStatus;
	  sX (VIRTUAL_RECORD_DATA, &pStatus);
	}
      }
      break;
    /**************************************************************************
    * Sparse records...
    **************************************************************************/
    case SPARSE_RECORDS_: {
      if (fullRecord) {
	if (!sX(ReadSparseFull(CDF,Var,firstRec,
			       lastRec,buffer),&pStatus)) return pStatus;
      }
      else {
	if (!sX(ReadSparsePartial(CDF,Var,
				  startRec,offset,
				  nValues,buffer),&pStatus)) return pStatus;
      }
      break;
    }
    /**************************************************************************
    * Compressed variable...
    **************************************************************************/
    case COMPRESSED_:
    case SPARSE_COMPRESSED_RECORDS_: {
      /************************************************************************
      * Initialize staging area.  Note that the staging area is only
      * initialized if records have been written.  This prevents the
      * CORRUPTED_V3_CDF error code from being returned if an explicit
      * blocking factor has not been specified for the variable.
      ************************************************************************/
      if (Var->stage.areaOffset64 == (OFF_T) NO_OFFSET64 && Var->maxRec > NO_RECORD) {
	if (Var->blockingFactor == 0) return CORRUPTED_V3_CDF;
	nBytes = Var->blockingFactor * Var->NphyRecBytes;
	if (!sX(InitVarStage64(CDF,Var,nBytes),&pStatus)) return pStatus;
      }
      /************************************************************************
      * If full record(s) or partial record...
      ************************************************************************/
      if (fullRecord) {
	if (!sX(ReadCompressedFull(CDF,Var,firstRec,
				   lastRec,buffer),&pStatus)) return pStatus;
      }
      else {
	if (!sX(ReadCompressedPartial(CDF,Var,startRec,
				      offset,nValues,
				      buffer),&pStatus)) return pStatus;
      }
      break;
    }
    /**************************************************************************
    * Can't do sparse arrays yet...
    **************************************************************************/
    case SPARSE_ARRAYS_:
    case SPARSE_RECORDS_AND_ARRAYS_:
      return UNKNOWN_SPARSENESS;
    case IN_MULTI_:
      if (fullRecord) {
	/********************************************************************
	* Full record(s).
	********************************************************************/
	Byte *tBuffer = buffer;
	if (firstRec <= Var->maxRec) {
	  if (!sX(RecordByteOffset64(CDF,Var,
				     firstRec,
				     &tOffset),&pStatus)) return pStatus;
	  recCount = MINIMUM(lastRec,Var->maxRec) - firstRec + 1;
	  numElems = recCount * Var->NphyRecElems;
	  if (!sX(ReadVarElems64(Var,Var->fp,tOffset,
			         numElems,tBuffer),&pStatus)) return pStatus;
	  tBuffer += (size_t) (recCount * Var->NphyRecBytes);
	}
	if (lastRec > Var->maxRec) {
	  recCount = BOO(Var->maxRec < firstRec,
			 lastRec - firstRec + 1,
			 lastRec - Var->maxRec);
	  nPadValues = recCount * Var->NphyRecValues;
	  if (!sX(PadBuffer64(CDF,Var,
			      nPadValues,
			      tBuffer),&pStatus)) return pStatus;
	  sX (VIRTUAL_RECORD_DATA, &pStatus);
	}
      }
      else {
	/********************************************************************
	* Partial record.
	********************************************************************/
	if (firstRec <= Var->maxRec) {
	  if (!sX(RecordByteOffset64(CDF,Var,
				     firstRec,
				     &tOffset),&pStatus)) return pStatus;
	  tOffset += (OFF_T) offset;
	  numElems = nValues * Var->NvalueElems;
	  if (!sX(ReadVarElems64(Var,Var->fp,tOffset,
			         numElems,buffer),&pStatus)) return pStatus;
	}
	else {
	  if (!sX(PadBuffer64(CDF,Var,nValues,buffer),&pStatus)) return pStatus;
	  sX (VIRTUAL_RECORD_DATA, &pStatus);
	}
      }
      break;
  }
  return pStatus;
}

/******************************************************************************
* ReadVarElems64.
*   NOTE: On IBM PCs, it is assumed that the number of bytes being read
* will not exceed 65535.
******************************************************************************/

STATICforIDL CDFstatus ReadVarElems64 (Var, fp, offset, numElems, buffer)
struct VarStruct *Var;
vFILE *fp;
OFF_T offset;
Int32 numElems;
void *buffer;
{
  CDFstatus pStatus = CDF_OK; size_t nBytes;
  /****************************************************************************
  * Seek to the desired offset.
  ****************************************************************************/
  if (!SEEKv64(fp,offset,vSEEK_SET)) return VAR_READ_ERROR;
  /****************************************************************************
  * Read the value(s).
  ****************************************************************************/
  nBytes = (size_t) (numElems * Var->NelemBytes);
  if (!READv64(buffer,nBytes,1,fp)) return VAR_READ_ERROR;
  /****************************************************************************
  * Decode value(s).
  ****************************************************************************/
  if (!sX(DECODE(Var->DecodeFunction,buffer,numElems),&pStatus)) {
    return pStatus;
  }
  return pStatus;
}

/******************************************************************************
* InitVarStage64.
******************************************************************************/

STATICforIDL CDFstatus InitVarStage64 (CDF, Var, nBytes)
struct CDFstruct *CDF;
struct VarStruct *Var;
Int32 nBytes;
{
  if (CDF->stage.fp == NULL) {
    CDF->stage.fp = V_scratch (ScratchDirectory(CDF), "stg");
    if (CDF->stage.fp == NULL) return SCRATCH_CREATE_ERROR;
    if (!CACHEv64(CDF->stage.fp,CDF->stage.cacheSize)) {
      V_delete64 (CDF->stage.fp, NULL);
      CDF->stage.fp = NULL;
      return BAD_CACHE_SIZE;
    }
    CDF->stage.mark64 = (OFF_T) ZERO_OFFSET64;
  }
  Var->stage.areaOffset64 = CDF->stage.mark64;
  Var->stage.firstRec = NO_RECORD;
  Var->stage.lastRec = NO_RECORD;
  Var->stage.dotOffset64 = (OFF_T) NO_OFFSET64;
  Var->stage.modified = FALSE;
  CDF->stage.mark64 += nBytes;
  return CDF_OK;
}

/******************************************************************************
* InitScratch64.
******************************************************************************/

STATICforIDL CDFstatus InitScratch64 (scratchDir, scratchFpH, cacheSize)
char *scratchDir;	/* Scratch directory to be used. */
vFILE **scratchFpH;	/* Scratch file handle (pointer to pointer). */
int cacheSize;		/* Number of cache buffers to request. */
{
  if (*scratchFpH == NULL) {
    *scratchFpH = V_scratch (scratchDir, NULL);
    if (*scratchFpH == NULL) return SCRATCH_CREATE_ERROR;
    if (!CACHEv64(*scratchFpH,cacheSize)) {
      V_delete64 (*scratchFpH, NULL);
      *scratchFpH = NULL;
      return BAD_CACHE_SIZE;
    }
  }
  else {
    if (V_clear(*scratchFpH) != 0) return SCRATCH_READ_ERROR;
    if (!SEEKv64(*scratchFpH,(OFF_T)0,vSEEK_SET)) return SCRATCH_READ_ERROR;
  }
  return CDF_OK;
}

/******************************************************************************
* FlushStage64.
******************************************************************************/

STATICforIDL CDFstatus FlushStage64 (CDF, Var)
struct CDFstruct *CDF;
struct VarStruct *Var;
{
  CDFstatus pStatus = CDF_OK;
  /****************************************************************************
  * Based on the variable type...
  ****************************************************************************/
  switch (Var->vType) {
    case SPARSE_RECORDS_: {
      Int32 nRecords, nBytes; struct AllocStruct alloc;
      OFF_T tOffset;
      /************************************************************************
      * First check if there are records to be flushed in the staging area.
      ************************************************************************/
      if (!Var->stage.modified) return pStatus;
      /************************************************************************
      * Allocate a new VVR.
      ************************************************************************/
      LoadAllocVVR64 (alloc, Var->stage.firstRec, Var->stage.lastRec, FALSE)
      if (!sX(AllocateRecords64(CDF,Var,alloc),&pStatus)) return pStatus;
      Var->maxAllocated = MAXIMUM(Var->maxAllocated,Var->stage.lastRec);
      /************************************************************************
      * Copy the records to the new VVR.
      ************************************************************************/
      if (!sX(RecordByteOffset64(CDF,Var,
			         Var->stage.firstRec,
			         &tOffset),&pStatus)) return pStatus;
      nRecords = Var->stage.lastRec - Var->stage.firstRec + 1;
      nBytes = nRecords * Var->NphyRecBytes;
      if (!sX(CopyBytes64(CDF->stage.fp,Var->stage.areaOffset64,
		 	  SCRATCH_READ_ERROR,nBytes,CDF->fp,
			  tOffset,CDF_WRITE_ERROR),&pStatus)) return pStatus;
      Var->maxWritten = MAXIMUM(Var->maxWritten,Var->stage.lastRec);
      /************************************************************************
      * Clear the staging area control.
      ************************************************************************/
      Var->stage.firstRec = NO_RECORD;
      Var->stage.lastRec = NO_RECORD;
      Var->stage.dotOffset64 = (OFF_T) NO_OFFSET64;
      Var->stage.modified = FALSE;
      break;
    }
    case COMPRESSED_:
    case SPARSE_COMPRESSED_RECORDS_: {
      Int32 nRecords;
      OFF_T cSize, xSize, irSize, uSize, newOffset;
      struct CVVRstruct64 CVVR; struct VVRstruct64 VVR; struct AllocStruct alloc;
      /************************************************************************
      * First check if there are records to be flushed in the staging area.
      ************************************************************************/
      if (!Var->stage.modified) return pStatus;
      /************************************************************************
      * Check if the scratch file is created/initialized.
      ************************************************************************/
      if (!sX(InitScratch64(ScratchDirectory(CDF),
			    &(CDF->compressFp),
			    CDF->compressCacheSize),&pStatus)) return pStatus;
      /************************************************************************
      * Compress the records in the stage.
      ************************************************************************/
      nRecords = Var->stage.lastRec - Var->stage.firstRec + 1;
      uSize = nRecords * Var->NphyRecBytes;
      if (!sX(Compress64(CDF->stage.fp,Var->stage.areaOffset64,
		         uSize,SCRATCH_READ_ERROR,Var->cType,
		         Var->cParms,CDF->compressFp,(OFF_T) ZERO_OFFSET64,
		         &cSize,SCRATCH_WRITE_ERROR),&pStatus)) return pStatus;
      /************************************************************************
      * Does a VVR/CVVR for this block of records already exist?
      ************************************************************************/
      if (Var->stage.dotOffset64 != (OFF_T) NO_OFFSET64) {
	/**********************************************************************
	* Read the size of the existing VVR/CVVR.
	**********************************************************************/
	if (!sX(ReadIrSize64(CDF->fp,
			     Var->stage.dotOffset64,
			     &irSize),&pStatus)) return pStatus;
	/**********************************************************************
	* Will a CVVR fit?  Note that reserve space is only added to new CVVRs.
	**********************************************************************/
	if (CVVR_BASE_SIZE64 + cSize <= irSize) {
	  LoadCVVRx64 (CVVR, irSize, cSize)
	  if (!sX(WriteCVVR64(CDF->fp,Var->stage.dotOffset64,
			      CVVR_RECORDx,&CVVR,
			      CVVR_NULL),&pStatus)) return pStatus;
	  if (!sX(CopyBytes64(CDF->compressFp,(OFF_T) ZERO_OFFSET64,
			      SCRATCH_READ_ERROR,cSize,CDF->fp,
			      Var->stage.dotOffset64 + CVVR_BASE_SIZE64,
			      CDF_WRITE_ERROR),&pStatus)) return pStatus;
	  Var->stage.modified = FALSE;
	  return pStatus;
	}
	/**********************************************************************
	* Will a VVR fit?
	**********************************************************************/
	if (VVR_BASE_SIZE64 + uSize <= irSize) {
	  LoadVVRx64 (VVR, irSize)
	  if (!sX(WriteVVR64(CDF->fp,Var->stage.dotOffset64,
			     VVR_RECORDx,&VVR,
			     VVR_NULL),&pStatus)) return pStatus;
	  if (!sX(CopyBytes64(CDF->stage.fp,Var->stage.areaOffset64,
			      SCRATCH_READ_ERROR,uSize,CDF->fp,
			      Var->stage.dotOffset64 + VVR_BASE_SIZE64,
			      CDF_WRITE_ERROR),&pStatus)) return pStatus;
	  Var->stage.modified = FALSE;
	  sX (DID_NOT_COMPRESS, &pStatus);
	  return pStatus;
	}
	/**********************************************************************
	* The existing VVR/CVVR will have to be resized.  Will a CVVR be
	* smaller than a VVR?  Note that reserve space is not added to the
	* size of the CVVR because a new one isn't being created.
	**********************************************************************/
	if (CVVR_BASE_SIZE64 + cSize < VVR_BASE_SIZE64 + uSize) {
	  if (!sX(ResizeIR64(CDF,Var->stage.dotOffset64,
			     CVVR_BASE_SIZE64 + cSize,
			     &newOffset,TRUE,NULL),&pStatus)) return pStatus;
	  LoadCVVRx64 (CVVR, CVVR_BASE_SIZE64 + cSize, cSize)
	  if (!sX(WriteCVVR64(CDF->fp,newOffset,
			      CVVR_RECORDx,&CVVR,
			      CVVR_NULL),&pStatus)) return pStatus;
	  if (!sX(CopyBytes64(CDF->compressFp,(OFF_T) ZERO_OFFSET64,
			      SCRATCH_READ_ERROR,cSize,
			      CDF->fp,newOffset + CVVR_BASE_SIZE64,
			      CDF_WRITE_ERROR),&pStatus)) return pStatus;
	}
	else {
	  if (!sX(ResizeIR64(CDF,Var->stage.dotOffset64,
			     VVR_BASE_SIZE64 + uSize,
			     &newOffset,TRUE,NULL),&pStatus)) return pStatus;
	  LoadVVRx64 (VVR, VVR_BASE_SIZE64 + uSize)
	  if (!sX(WriteVVR64(CDF->fp,newOffset,
			     VVR_RECORDx,&VVR,
			     VVR_NULL),&pStatus)) return pStatus;
	  if (!sX(CopyBytes64(CDF->stage.fp,Var->stage.areaOffset64,
			      SCRATCH_READ_ERROR,uSize,
			      CDF->fp,newOffset + VVR_BASE_SIZE64,
			      CDF_WRITE_ERROR),&pStatus)) return pStatus;
	  sX (DID_NOT_COMPRESS, &pStatus);
	}
	if (!sX(ModIndexOffset64(CDF,Var,
			         Var->stage.firstRec,
			         Var->stage.lastRec,
			         newOffset),&pStatus)) return pStatus;
	Var->stage.dotOffset64 = newOffset;
	Var->stage.modified = FALSE;
	return pStatus;
      }
      /************************************************************************
      * A new VVR/CVVR will have to be created.  First calculate the reserve
      * size.
      ************************************************************************/
      if (Var->reservePct <= 0)
	xSize = 0;
      else {
	if (Var->reservePct <= 100) {
	  Int32 tSize = (Int32) ((uSize * (Var->reservePct/100.0)) + 0.5);
	  xSize = MAXIMUM(cSize,tSize) - cSize;
	}
	else {
	  Int32 tSize = (Int32) ((cSize * (Var->reservePct/100.0)) + 0.5);
	  xSize = tSize - cSize;
	}
      }
      /************************************************************************
      * Will a CVVR be smaller than a VVR?
      ************************************************************************/
      if (CVVR_BASE_SIZE64 + cSize + xSize < VVR_BASE_SIZE64 + uSize) {
	LoadAllocCVVR64 (alloc, Var->stage.firstRec, Var->stage.lastRec,
		         cSize, xSize)
	if (!sX(AllocateRecords64(CDF,Var,alloc),&pStatus)) return pStatus;
	if (!sX(SearchForRecord64(CDF,Var->VDRoffset64,Var->zVar,
				  Var->stage.firstRec,NULL,NULL,
				  &newOffset,NULL),&pStatus)) return pStatus;
	LoadCVVRx64 (CVVR, CVVR_BASE_SIZE64 + cSize + xSize, cSize)
	if (!sX(WriteCVVR64(CDF->fp,newOffset,
			    CVVR_RECORDx,&CVVR,
			    CVVR_NULL),&pStatus)) return pStatus;
	if (!sX(CopyBytes64(CDF->compressFp,(OFF_T) ZERO_OFFSET64,
			    SCRATCH_READ_ERROR,cSize,
			    CDF->fp,newOffset + CVVR_BASE_SIZE64,
			    CDF_WRITE_ERROR),&pStatus)) return pStatus;
	Var->stage.dotOffset64 = newOffset;
	Var->stage.modified = FALSE;
	return pStatus;
      }
      /************************************************************************
      * The CVVR will be too big - create a VVR.
      ************************************************************************/
      LoadAllocVVR64 (alloc, Var->stage.firstRec, Var->stage.lastRec, TRUE)
      if (!sX(AllocateRecords64(CDF,Var,alloc),&pStatus)) return pStatus;
      if (!sX(SearchForRecord64(CDF,Var->VDRoffset64,Var->zVar,
				Var->stage.firstRec,NULL,NULL,
				&newOffset,NULL),&pStatus)) return pStatus;
      LoadVVRx64 (VVR, VVR_BASE_SIZE64 + uSize)
      if (!sX(WriteVVR64(CDF->fp,newOffset,
		         VVR_RECORDx,&VVR,
		         VVR_NULL),&pStatus)) return pStatus;
      if (!sX(CopyBytes64(CDF->stage.fp,Var->stage.areaOffset64,
			  SCRATCH_READ_ERROR,uSize,
			  CDF->fp,newOffset + VVR_BASE_SIZE64,
			  CDF_WRITE_ERROR),&pStatus)) return pStatus;
      Var->stage.dotOffset64 = newOffset;
      Var->stage.modified = FALSE;
      sX (DID_NOT_COMPRESS, &pStatus);
      return pStatus;
    }
    default:
      return CDF_INTERNAL_ERROR;
  }
  return pStatus;
}

/******************************************************************************
* BringToStage.
******************************************************************************/

static CDFstatus BringToStage (CDF, Var, recNum, found)
struct CDFstruct *CDF;
struct VarStruct *Var;
Int32 recNum;
Logical *found;
{
  CDFstatus pStatus = CDF_OK;
  /****************************************************************************
  * First check if record is already in the stage.
  ****************************************************************************/
  if (INCLUSIVE(Var->stage.firstRec,recNum,Var->stage.lastRec)) {
    ASSIGNnotNULL (found, TRUE)
    return pStatus;
  }
  /****************************************************************************
  * Then, based on variable type...
  ****************************************************************************/
  switch (Var->vType) {
    case COMPRESSED_:
    case SPARSE_COMPRESSED_RECORDS_: {
      Int32 firstRec, lastRec, nRecords; OFF_T uSize; Logical foundX;
      OFF_T offset;
      /************************************************************************
      * Determine if the record exists.
      ************************************************************************/
      if (!sX(SearchForRecord64(CDF,Var->VDRoffset64,Var->zVar,
			        recNum,&firstRec,&lastRec,
			        &offset,&foundX),&pStatus)) return pStatus;
      ASSIGNnotNULL (found, foundX)
      if (!foundX) return BOO(found == NULL,NO_SUCH_RECORD,pStatus);
      /************************************************************************
      * Flush the stage before...
      ************************************************************************/
      if (!sX(FlushStage64(CDF,Var),&pStatus)) return pStatus;
      /************************************************************************
      * ...decompressing the CVVR (or VVR if the records did not compress) to
      * the staging area.
      ************************************************************************/
      nRecords = lastRec - firstRec + 1;
      uSize = nRecords * Var->NphyRecBytes;
      if (!sX(DecompressToStage64(CDF,Var,
				  offset,uSize),&pStatus)) return pStatus;
      /************************************************************************
      * Update staging control.
      ************************************************************************/
      Var->stage.firstRec = firstRec;
      Var->stage.lastRec = lastRec;
      Var->stage.dotOffset64 = offset;
      Var->stage.modified = FALSE;
      break;
    }
    default:
      return CDF_INTERNAL_ERROR;
  }
  return pStatus;
}

/******************************************************************************
* CopyBytes64.
******************************************************************************/

STATICforIDL CDFstatus CopyBytes64 (iFp, iStart, iError, nBytes, oFp, oStart,
				    oError)
vFILE *iFp;
OFF_T iStart;
CDFstatus iError;
OFF_T nBytes;
vFILE *oFp;
OFF_T oStart;
CDFstatus oError;
{
  Int32 nBlocks = (Int32) (nBytes / COPYblockSIZE);	/* Number of full blocks 
							   that can be copied. */
  Int32 lastCount = (Int32) (nBytes % COPYblockSIZE);	/* Number of bytes 
							   remaining to be copied 
							   after the full blocks 
							   have been copied. */
  Int32 i; Byte buffer[COPYblockSIZE];
  /****************************************************************************
  * If the copy is within the same file...
  ****************************************************************************/
  if (iFp == oFp) {
    /**************************************************************************
    * If the input group (of bytes) is before the output group start at the
    * end in case they overlap.
    **************************************************************************/
    if (iStart < oStart) {
      if (nBlocks > 0) {
	OFF_T iOffset = iStart + (OFF_T) (nBytes - COPYblockSIZE);
	OFF_T oOffset = oStart + (OFF_T) (nBytes - COPYblockSIZE);
	for (i = 0; i < nBlocks; i++) {
	   if (!SEEKv64(iFp,iOffset,vSEEK_SET)) return iError;
	   if (!READv64(buffer,(size_t)COPYblockSIZE,(size_t)1,iFp)) return
								   iError;
	   if (!SEEKv64(oFp,oOffset,vSEEK_SET)) return oError;
	   if (!WRITEv64(buffer,(size_t)COPYblockSIZE,(size_t)1,oFp)) return
								    oError;
	   iOffset -= (OFF_T) COPYblockSIZE;
	   oOffset -= (OFF_T) COPYblockSIZE;
	}
      }
      if (lastCount > 0) {
	if (!SEEKv64(iFp,iStart,vSEEK_SET)) return iError;
	if (!READv64(buffer,(size_t)lastCount,(size_t)1,iFp)) return iError;
	if (!SEEKv64(oFp,oStart,vSEEK_SET)) return oError;
	if (!WRITEv64(buffer,(size_t)lastCount,(size_t)1,oFp)) return oError;
      }
    }
    /**************************************************************************
    * If the input group (of bytes) is after the output group start at the
    * beginning in case they overlap.
    **************************************************************************/
    if (iStart > oStart) {
      OFF_T iOffset = iStart;
      OFF_T oOffset = oStart;
      if (nBlocks > 0) {
	for (i = 0; i < nBlocks; i++) {
	   if (!SEEKv64(iFp,iOffset,vSEEK_SET)) return iError;
	   if (!READv64(buffer,(size_t)COPYblockSIZE,(size_t)1,iFp)) return
								   iError;
	   if (!SEEKv64(oFp,oOffset,vSEEK_SET)) return oError;
	   if (!WRITEv64(buffer,(size_t)COPYblockSIZE,(size_t)1,oFp)) return
								    oError;
	   iOffset += (OFF_T) COPYblockSIZE;
	   oOffset += (OFF_T) COPYblockSIZE;
	}
      }
      if (lastCount > 0) {
	if (!SEEKv64(iFp,iOffset,vSEEK_SET)) return iError;
	if (!READv64(buffer,(size_t)lastCount,(size_t)1,iFp)) return iError;
	if (!SEEKv64(oFp,oOffset,vSEEK_SET)) return oError;
	if (!WRITEv64(buffer,(size_t)lastCount,(size_t)1,oFp)) return oError;
      }
    }
    /**************************************************************************
    * Offsets are the same - do nothing.
    **************************************************************************/
  }
  else {
    /**************************************************************************
    * Different files...
    **************************************************************************/
    if (!SEEKv64(iFp,iStart,vSEEK_SET)) return iError;
    if (!SEEKv64(oFp,oStart,vSEEK_SET)) return oError;
    for (i = 0; i < nBlocks; i++) {
       if (!READv64(buffer,(size_t)COPYblockSIZE,(size_t)1,iFp)) return iError;
       if (!WRITEv64(buffer,(size_t)COPYblockSIZE,(size_t)1,oFp)) return oError;
    }
    if (lastCount > 0) {
      if (!READv64(buffer,(size_t)lastCount,(size_t)1,iFp)) return iError;
      if (!WRITEv64(buffer,(size_t)lastCount,(size_t)1,oFp)) return oError;
    }
  }
  return CDF_OK;
}

/******************************************************************************
* ModIndexOffset64.
******************************************************************************/

STATICforIDL CDFstatus ModIndexOffset64 (CDF, Var, firstRec, lastRec, newOffset)
struct CDFstruct *CDF;  /* Pointer to CDF. */
struct VarStruct *Var;  /* Pointer to variable. */
Int32 firstRec;         /* First record of entry. */
Int32 lastRec;          /* Last record of entry. */
OFF_T newOffset;        /* New VVR/CVVR/SVVR offset. */
{
  CDFstatus pStatus = CDF_OK; OFF_T vxrOffset;
  /****************************************************************************
  * Read offset of first VXR...
  ****************************************************************************/
  if (!sX(ReadVDR64(CDF,CDF->fp,Var->VDRoffset64,Var->zVar,
		    VDR_VXRHEAD,&vxrOffset,
		    VDR_NULL),&pStatus)) return pStatus;
  /****************************************************************************
  * ...and start there.
  ****************************************************************************/
  if (!sX(ModIndexOffset_r_64(CDF->fp,vxrOffset,
			      firstRec,lastRec,
			      newOffset),&pStatus)) return pStatus;
  return pStatus;
}

static CDFstatus ModIndexOffset_r_64 (fp, vxrOffset, firstRec, lastRec, newOffset)
vFILE *fp;              /* File pointer to dotCDF file. */
OFF_T vxrOffset;        /* VXR at which to start. */
Int32 firstRec;         /* First record of entry. */
Int32 lastRec;          /* Last record of entry. */
OFF_T newOffset;        /* New VVR/CVVR/SVVR offset. */
{
  CDFstatus pStatus = CDF_OK;
  struct VXRstruct64 VXR; int entryN; Int32 irType;
  /****************************************************************************
  * While more VXRs...
  ****************************************************************************/
  while (vxrOffset != (OFF_T) ZERO_OFFSET64) {
    /**************************************************************************
    * Read the VXR.
    **************************************************************************/
    if (!sX(ReadVXR64(fp,vxrOffset,
		      VXR_RECORD,&VXR,
		      VXR_NULL),&pStatus)) return pStatus;
    /**************************************************************************
    * Search through index entries...
    **************************************************************************/
    for (entryN = 0; entryN < VXR.NusedEntries; entryN++) {
       if (VXR.First[entryN] <= firstRec && lastRec <= VXR.Last[entryN]) {
	 if (!sX(ReadIrType64(fp,
			      VXR.Offset[entryN],
			      &irType),&pStatus)) return pStatus;
	 if (irType == VXR_) return ModIndexOffset_r_64(fp,VXR.Offset[entryN],
						        firstRec,lastRec,
						        newOffset);
	 if (VXR.First[entryN] == firstRec && lastRec == VXR.Last[entryN]) {
	   VXR.Offset[entryN] = newOffset;
	   if (!sX(WriteVXR64(fp,vxrOffset,
			      VXR_RECORD,&VXR,
			      VXR_NULL),&pStatus)) return pStatus;
	   return pStatus;
	 }
	 return CDF_INTERNAL_ERROR;             /* or CORRUPTED_V3_CDF? */
       }
    }
    /**************************************************************************
    * Go to next VXR.
    **************************************************************************/
    vxrOffset = VXR.VXRnext;
  }
  /****************************************************************************
  * No more VXRs.
  ****************************************************************************/
  return CDF_INTERNAL_ERROR;
}

/******************************************************************************
* WriteCompressedRecords.
******************************************************************************/

static CDFstatus WriteCompressedRecords (CDF, Var, firstRec, lastRec, buffer,
					 nValues, offset, fullRecord)
struct CDFstruct *CDF;  /* Pointer to CDF. */
struct VarStruct *Var;  /* Pointer to variable. */
Int32 firstRec;         /* First record being written. */
Int32 lastRec;          /* Last record being written. */
void *buffer;           /* Buffer of values. */
Int32 nValues;          /* Number of values being written. */
OFF_T offset;           /* If not full record(s), byte offset in record at
			   which to write. */
Logical fullRecord;     /* Full record(s) being written? */
{
  CDFstatus pStatus = CDF_OK; Logical found;
  Int32 recCount, recordOffsetInStage, maxRecThisBlock;
  Int32 nextRec, writeTo, numElems; void *padBuffer; int how;
  Int32 recNum = firstRec; Byte *tBuffer = (Byte *) buffer;
  OFF_T tOffset;
  /****************************************************************************
  * From first to last record...
  ****************************************************************************/
  while (recNum <= lastRec) {
     /*************************************************************************
     * Check if this record is in the staging area.
     *************************************************************************/
     if (INCLUSIVE(Var->stage.firstRec,recNum,Var->stage.lastRec)) {
       recordOffsetInStage = recNum - Var->stage.firstRec;
       tOffset = Var->stage.areaOffset64;
       tOffset += (recordOffsetInStage * Var->NphyRecBytes);
       if (fullRecord) {
	 if (Var->stage.dotOffset64 == (OFF_T) NO_OFFSET64) {
	   maxRecThisBlock = Var->stage.firstRec + Var->blockingFactor - 1;
	   if (!sX(NextRecord64(CDF,Var->VDRoffset64,
			        Var->zVar,recNum,
			        &nextRec,&found),&pStatus)) return pStatus;
	   if (!found)
	     writeTo = MINIMUM(maxRecThisBlock,lastRec);
	   else {
	     Int32 prevRecN = nextRec - 1;
	     writeTo = MINIMUMof3(maxRecThisBlock,prevRecN,lastRec);
	   }
	 }
	 else
	   writeTo = MINIMUM(Var->stage.lastRec,lastRec);
	 recCount = writeTo - recNum + 1;
	 numElems = recCount * Var->NphyRecElems;
       }
       else {
	 writeTo = recNum;
	 recCount = 1;
	 numElems = nValues * Var->NvalueElems;
	 tOffset += offset;
       }
       if (!sX(WriteVarElems64(Var,CDF->stage.fp,
			       tOffset,numElems,
			       tBuffer),&pStatus)) return pStatus;
       Var->stage.lastRec = MAXIMUM(Var->stage.lastRec,writeTo);
       Var->stage.modified = TRUE;
       tBuffer += (size_t) (numElems * Var->NelemBytes);
       recNum += recCount;
       continue;
     }
     /*************************************************************************
     * Not in the staging area...check if this record is in an existing CVVR.
     *************************************************************************/
     if (!sX(BringToStage(CDF,Var,recNum,&found),&pStatus)) return pStatus;
     if (found) {
       recordOffsetInStage = recNum - Var->stage.firstRec;
       tOffset = Var->stage.areaOffset64;
       tOffset += (recordOffsetInStage * Var->NphyRecBytes);
       if (fullRecord) {
	 writeTo = MINIMUM(Var->stage.lastRec,lastRec);
	 recCount = writeTo - recNum + 1;
	 numElems = recCount * Var->NphyRecElems;
       }
       else {
	 writeTo = recNum;
	 recCount = 1;
	 numElems = nValues * Var->NvalueElems;
	 tOffset += offset;
       }
       if (!sX(WriteVarElems64(Var,CDF->stage.fp,
			       tOffset,numElems,
			       tBuffer),&pStatus)) return pStatus;
       Var->stage.modified = TRUE;
       tBuffer += (size_t) (numElems * Var->NelemBytes);
       recNum += recCount;
       continue;
     }
     /*************************************************************************
     * Not in an existing CVVR...this record does not exist.  First check
     * if the record(s) can be added to the records in the stage.
     *************************************************************************/
     if (Var->stage.firstRec != NO_RECORD) {
       if (Var->stage.dotOffset64 == (OFF_T) NO_OFFSET64) {
	 if (recNum == Var->stage.lastRec + 1) {
	   maxRecThisBlock = Var->stage.firstRec + Var->blockingFactor - 1;
	   if (recNum <= maxRecThisBlock) {
	     recordOffsetInStage = recNum - Var->stage.firstRec;
	     tOffset = Var->stage.areaOffset64;
	     tOffset += (recordOffsetInStage * Var->NphyRecBytes);
	     if (fullRecord) {
	       if (!sX(NextRecord64(CDF,Var->VDRoffset64,
				    Var->zVar,recNum,
				    &nextRec,&found),&pStatus)) return pStatus;
	       if (!found)
		 writeTo = MINIMUM(maxRecThisBlock,lastRec);
	       else {
		 Int32 prevRecN = nextRec - 1;
		 writeTo = MINIMUMof3(maxRecThisBlock,prevRecN,lastRec);
	       }
	       recCount = writeTo - recNum + 1;
	       numElems = recCount * Var->NphyRecElems;
	     }
	     else {
	       if (!sX(BuildPadBuffer64(CDF,Var,INT32_ONE,
				        &how,&padBuffer,
				        TRUE),&pStatus)) return pStatus;
	       if (!sX(WritePadValues64(Var,CDF->stage.fp,
				        tOffset,INT32_ONE,how,
				        padBuffer),&pStatus)) return pStatus;
	       cdf_FreeMemory (padBuffer, NULL);
	       writeTo = recNum;
	       recCount = 1;
	       numElems = nValues * Var->NvalueElems;
	       tOffset += offset;
	     }
	     if (!sX(WriteVarElems64(Var,CDF->stage.fp,
				     tOffset,numElems,
				     tBuffer),&pStatus)) return pStatus;
	     Var->stage.lastRec = MAXIMUM(Var->stage.lastRec,writeTo);
	     Var->stage.modified = TRUE;
	     tBuffer += (size_t) (numElems * Var->NelemBytes);
	     recNum += recCount;
	     continue;
	   }
	 }
       }
     }
     /*************************************************************************
     * This record cannot be added to the block of records currently in
     * the staging area.  Start a new block of records.
     *************************************************************************/
     if (!sX(FlushStage64(CDF,Var),&pStatus)) return pStatus;
     tOffset = Var->stage.areaOffset64;
     if (fullRecord) {
       maxRecThisBlock = recNum + Var->blockingFactor - 1;
       if (!sX(NextRecord64(CDF,Var->VDRoffset64,Var->zVar,
			    recNum,&nextRec,&found),&pStatus)) return pStatus;
       if (!found)
	 writeTo = MINIMUM(maxRecThisBlock,lastRec);
       else {
	 Int32 prevRecN = nextRec - 1;
	 writeTo = MINIMUMof3(maxRecThisBlock,prevRecN,lastRec);
       }
       recCount = writeTo - recNum + 1;
       numElems = recCount * Var->NphyRecElems;
     }
     else {
       if (!sX(BuildPadBuffer64(CDF,Var,INT32_ONE,
			        &how,&padBuffer,
			        TRUE),&pStatus)) return pStatus;
       if (!sX(WritePadValues64(Var,CDF->stage.fp,
			        tOffset,INT32_ONE,how,
			        padBuffer),&pStatus)) return pStatus;
       cdf_FreeMemory (padBuffer, NULL);
       writeTo = recNum;
       recCount = 1;
       numElems = nValues * Var->NvalueElems;
       tOffset += offset;
     }
     if (!sX(WriteVarElems64(Var,CDF->stage.fp,
			     tOffset,numElems,
			     tBuffer),&pStatus)) return pStatus;
     Var->stage.firstRec = recNum;
     Var->stage.lastRec = writeTo;
     Var->stage.dotOffset64 = (OFF_T) NO_OFFSET64;
     Var->stage.modified = TRUE;
     tBuffer += (size_t) (numElems * Var->NelemBytes);
     recNum += recCount;
  }
  return pStatus;
}

/******************************************************************************
* CalcCompressionPct64.
******************************************************************************/

STATICforIDL CDFstatus CalcCompressionPct64 (CDF, vdrOffset, zVar, cPct)
struct CDFstruct *CDF;
OFF_T vdrOffset;
Logical zVar;
long *cPct;
{
  CDFstatus pStatus = CDF_OK; OFF_T vxrOffset;
  OFF_T uTotal = 0, cTotal = 0; Int32 nPhyRecBytes;
  /****************************************************************************
  * Calculate the number of bytes per physical record.
  ****************************************************************************/
  if (!sX(CalcPhyRecBytes64(CDF,vdrOffset,
			    zVar,&nPhyRecBytes),&pStatus)) return pStatus;
  /****************************************************************************
  * Read the offset of the first VXR (return 0% if no VXRs)...
  ****************************************************************************/
  if (!sX(ReadVDR64(CDF,CDF->fp,vdrOffset,zVar,
		    VDR_VXRHEAD,&vxrOffset,
		    VDR_NULL),&pStatus)) return pStatus;
  if (vxrOffset == (OFF_T) ZERO_OFFSET64) {
    *cPct = 0;
    return pStatus;
  }
  /****************************************************************************
  * ...and start there.
  ****************************************************************************/
  if (!sX(CalcCompressionPct_r_64(CDF->fp,vxrOffset,
			          nPhyRecBytes,
			          &uTotal,&cTotal),&pStatus)) return pStatus;
  /****************************************************************************
  * Calculate percentage.
  ****************************************************************************/
  *cPct = (long) (((100.0*cTotal) / uTotal) + 0.5);
  return pStatus;
}

static CDFstatus CalcCompressionPct_r_64 (fp, vxrOffset, nPhyRecBytes, uTotal,
				          cTotal)
vFILE *fp;
OFF_T vxrOffset;
Int32 nPhyRecBytes;
OFF_T *uTotal;
OFF_T *cTotal;
{
  CDFstatus pStatus = CDF_OK; struct VXRstruct64 VXR; int entryN;
  Int32 nRecords, irType; OFF_T uSize, irSize;
  /****************************************************************************
  * While more VXRs...
  ****************************************************************************/
  while (vxrOffset != (OFF_T) ZERO_OFFSET64) {
    /**************************************************************************
    * Read the VXR.
    **************************************************************************/
    if (!sX(ReadVXR64(fp,vxrOffset,
		      VXR_RECORD,&VXR,
		      VXR_NULL),&pStatus)) return pStatus;
    /**************************************************************************
    * Scan entries...
    **************************************************************************/
    for (entryN = 0; entryN < VXR.NusedEntries; entryN++) {
       if (!sX(ReadIrType64(fp,VXR.Offset[entryN],&irType),&pStatus)) return
								    pStatus;
       switch (irType) {
	 case VXR_:
	   if (!sX(CalcCompressionPct_r_64(fp,VXR.Offset[entryN],
					   nPhyRecBytes,uTotal,
					   cTotal),&pStatus)) return pStatus;
	   break;
	 case VVR_:
	 case CVVR_:
	   /*******************************************************************
	   * Accumulate uncompressed size.
	   *******************************************************************/
	   nRecords = VXR.Last[entryN] - VXR.First[entryN] + 1;
	   uSize = nRecords * nPhyRecBytes;
	   *uTotal += uSize;
	   /*******************************************************************
	   * Accumulate compressed size.
	   *******************************************************************/
	   if (!sX(ReadIrSize64(fp,VXR.Offset[entryN],&irSize),&pStatus)) 
								return pStatus;
	   *cTotal += irSize - BOO(irType == CVVR_,
				   CVVR_BASE_SIZE64,
				   VVR_BASE_SIZE64);
	   break;
	 default:
	   return CORRUPTED_V3_CDF;
       }
    }
    vxrOffset = VXR.VXRnext;
  }
  return pStatus;
}

/******************************************************************************
* CalcPhyRecBytes64.
******************************************************************************/

STATICforIDL CDFstatus CalcPhyRecBytes64 (CDF, vdrOffset, zVar, nPhyRecBytes)
struct CDFstruct *CDF;
OFF_T vdrOffset;
Logical zVar;
Int32 *nPhyRecBytes;
{
  CDFstatus pStatus = CDF_OK; int dimN; Int32 dataType, numElems;
  Int32 numDims, dimSizes[CDF_MAX_DIMS], dimVarys[CDF_MAX_DIMS];
  if (!sX(CalcDimParms64(CDF,vdrOffset,zVar,
		         &numDims,dimSizes,dimVarys),&pStatus)) return pStatus;
  if (!sX(ReadVDR64(CDF,CDF->fp,vdrOffset,zVar,
		    VDR_DATATYPE,&dataType,
		    VDR_NUMELEMS,&numElems,
		    VDR_NULL),&pStatus)) return pStatus;
  *nPhyRecBytes = CDFelemSize((long)dataType) * numElems;
  for (dimN = 0; dimN < numDims; dimN++) {
     if (dimVarys[dimN]) *nPhyRecBytes *= dimSizes[dimN];
  }
  return pStatus;
}

/******************************************************************************
* ReadSparseFull.
******************************************************************************/

static CDFstatus ReadSparseFull (CDF, Var, firstRec, lastRec, buffer)
struct CDFstruct *CDF;
struct VarStruct *Var;
Int32 firstRec;
Int32 lastRec;
void *buffer;
{
  CDFstatus pStatus = CDF_OK; Logical found;
  Int32 readTo, recCount, numElems, recX, prevRecN;
  Int32 nextRec, padTo, nPadValues; OFF_T tOffset;
  Int32 recNum = firstRec;
  Byte *tBuffer = (Byte *) buffer;
  /****************************************************************************
  * While there are more records to be read/generated...
  ****************************************************************************/
  while (recNum <= lastRec) {
    /**************************************************************************
    * Is the record in the staging area?
    **************************************************************************/
    if (INCLUSIVE(Var->stage.firstRec,recNum,Var->stage.lastRec)) {
      readTo = MINIMUM(Var->stage.lastRec,lastRec);
      recCount = readTo - recNum + 1;
      numElems = recCount * Var->NphyRecElems;
      recX = recNum - Var->stage.firstRec;
      tOffset = Var->stage.areaOffset64 + (OFF_T) (recX * Var->NphyRecBytes);
      if (!sX(ReadVarElems64(Var,CDF->stage.fp,
			     tOffset,numElems,tBuffer),&pStatus)) return pStatus;
      tBuffer += (size_t) (recCount * Var->NphyRecBytes);
      recNum += recCount;
      continue;
    }
    /**************************************************************************
    * Does the record exist and has been written to (not just allocated)?
    **************************************************************************/
    if (recNum <= Var->maxRec) {
      Int32 firstRecInVVR, lastRecInVVR;
      if (!sX(SearchForRecord64(CDF,Var->VDRoffset64,
			        Var->zVar,recNum,
			        &firstRecInVVR,
			        &lastRecInVVR,
			        &tOffset,&found),&pStatus)) return pStatus;
      if (found) {
	readTo = MINIMUMof3(Var->maxRec,lastRec,lastRecInVVR);
	recCount = readTo - recNum + 1;
	numElems = recCount * Var->NphyRecElems;
	recX = recNum - firstRecInVVR;
	tOffset += VVR_BASE_SIZE64 + (OFF_T) (recX * Var->NphyRecBytes);
	if (!sX(ReadVarElems64(Var,CDF->fp,tOffset,
			       numElems,tBuffer),&pStatus)) return pStatus;
	tBuffer += (size_t) (recCount * Var->NphyRecBytes);
	recNum += recCount;
	continue;
      }
    }
    /**************************************************************************
    * Determine which records need to be padded.
    **************************************************************************/
    if (recNum <= Var->maxRec) {
      if (!sX(NextRecord64(CDF,Var->VDRoffset64,Var->zVar,
			   recNum,&nextRec,&found),&pStatus)) return pStatus;
      if (found) {
	if (EXCLUSIVE(recNum,
		      Var->stage.firstRec,
		      nextRec)) nextRec = Var->stage.firstRec;
	prevRecN = nextRec - 1;
	padTo = MINIMUM(prevRecN,lastRec);
      }
      else {
	prevRecN = Var->stage.firstRec - 1;
	padTo = MINIMUM(prevRecN,lastRec);
      }
    }
    else
      padTo = lastRec;
    recCount = padTo - recNum + 1;
    /**************************************************************************
    * If sRecords.PREV...
    **************************************************************************/
    if (Var->prevIfMissing) {
      Int32 prevRec; Byte *destBuffer;
      if (!sX(PrevRecord64(CDF,Var->VDRoffset64,Var->zVar,
			   MINIMUM(recNum,Var->maxRec),
			   &prevRec,&found),&pStatus)) return pStatus;
      if (found) {
	if (EXCLUSIVE(prevRec,Var->stage.lastRec,recNum)) {
	  recX = Var->stage.lastRec - Var->stage.firstRec;
	  tOffset = Var->stage.areaOffset64 + (OFF_T) (recX * Var->NphyRecBytes);
	  if (!sX(ReadVarElems64(Var,CDF->stage.fp,
			         tOffset,Var->NphyRecElems,
			         tBuffer),&pStatus)) return pStatus;
	}
	else {
	  if (!sX(RecordByteOffset64(CDF,Var,
				     prevRec,
				     &tOffset),&pStatus)) return pStatus;
	  if (!sX(ReadVarElems64(Var,CDF->fp,tOffset,
			         Var->NphyRecElems,
			         tBuffer),&pStatus)) return pStatus;
	}
	destBuffer = tBuffer + ((size_t) Var->NphyRecBytes);
	for (recX = 1; recX < recCount; recX++) {
	   memmove (destBuffer, tBuffer, (size_t) Var->NphyRecBytes);
	   destBuffer += (size_t) Var->NphyRecBytes;
	}
	sX (VIRTUAL_RECORD_DATA, &pStatus);
	tBuffer += (size_t) (recCount * Var->NphyRecBytes);
	recNum += recCount;
	continue;
      }
      if (EXCLUSIVE(NO_RECORD,Var->stage.lastRec,recNum)) {
	recX = Var->stage.lastRec - Var->stage.firstRec;
	tOffset = Var->stage.areaOffset64 + (OFF_T) (recX * Var->NphyRecBytes);
	if (!sX(ReadVarElems64(Var,CDF->stage.fp,
			       tOffset,Var->NphyRecElems,
			       tBuffer),&pStatus)) return pStatus;
	destBuffer = tBuffer + ((size_t) Var->NphyRecBytes);
	for (recX = 1; recX < recCount; recX++) {
	   memmove (destBuffer, tBuffer, (size_t) Var->NphyRecBytes);
	   destBuffer += (size_t) Var->NphyRecBytes;
	}
	sX (VIRTUAL_RECORD_DATA, &pStatus);
	tBuffer += (size_t) (recCount * Var->NphyRecBytes);
	recNum += recCount;
	continue;
      }
    }
    /**************************************************************************
    * Pad with the variable's pad value.  This occurs if the variable is
    * sRecords.NO, sRecords.PAD, or if sRecords.PREV but a previous record
    * does not exist (in a VVR or the staging area).
    **************************************************************************/
    nPadValues = recCount * Var->NphyRecValues;
    if (!sX(PadBuffer64(CDF,Var,nPadValues,tBuffer),&pStatus)) return pStatus;
    sX (VIRTUAL_RECORD_DATA, &pStatus);
    tBuffer += (size_t) (recCount * Var->NphyRecBytes);
    recNum += recCount;
  }
  return pStatus;
}

/******************************************************************************
* ReadSparsePartial.
******************************************************************************/

static CDFstatus ReadSparsePartial (CDF, Var, recNum, offset, nValues, buffer)
struct CDFstruct *CDF;
struct VarStruct *Var;
Int32 recNum;
OFF_T offset;
Int32 nValues;
void *buffer;
{
  CDFstatus pStatus = CDF_OK; Logical found;
  Int32 firstRec, prevRec; OFF_T tOffset;
  Int32 numElems = nValues * Var->NvalueElems;
  /****************************************************************************
  * If the record is in the staging area...
  ****************************************************************************/
  if (INCLUSIVE(Var->stage.firstRec,recNum,Var->stage.lastRec)) {
    tOffset = Var->stage.areaOffset64;
    tOffset += (OFF_T) (Var->NphyRecBytes * (recNum - Var->stage.firstRec));
    tOffset += offset;
    if (!sX(ReadVarElems64(Var,CDF->stage.fp,
			   tOffset,numElems,buffer),&pStatus)) return pStatus;
    return pStatus;
  }
  /****************************************************************************
  * If the record exists and has been written (ie. not just allocated)...
  ****************************************************************************/
  if (recNum <= Var->maxRec) {
    if (!sX(SearchForRecord64(CDF,Var->VDRoffset64,
			      Var->zVar,recNum,&firstRec,
			      NULL,&tOffset,&found),&pStatus)) return pStatus;
    if (found) {
      tOffset += (OFF_T) VVR_BASE_SIZE64;
      tOffset += (OFF_T) Var->NphyRecBytes * (recNum - firstRec);
      tOffset += offset;
      if (!sX(ReadVarElems64(Var,CDF->fp,
			     tOffset,numElems,buffer),&pStatus)) return pStatus;
      return pStatus;
    }
  }
  /****************************************************************************
  * If missing records are to be read from the previous record...
  ****************************************************************************/
  if (Var->prevIfMissing) {
    /**************************************************************************
    * Check if a previous record exists in a VVR.
    **************************************************************************/
    if (!sX(PrevRecord64(CDF,Var->VDRoffset64,Var->zVar,
		         MINIMUM(recNum,Var->maxRec),
		         &prevRec,&found),&pStatus)) return pStatus;
    /**************************************************************************
    * If so, also make sure that the last record in the staging area isn't
    * really the previous record to use.
    **************************************************************************/
    if (found) {
      if (EXCLUSIVE(prevRec,Var->stage.lastRec,recNum)) {
	Int32 recNumInStage = Var->stage.lastRec - Var->stage.firstRec;
	tOffset = Var->stage.areaOffset64;
	tOffset += (OFF_T) Var->NphyRecBytes * recNumInStage;
	tOffset += offset;
	if (!sX(ReadVarElems64(Var,CDF->stage.fp,tOffset,
			       numElems,buffer),&pStatus)) return pStatus;
      }
      else {
	if (!sX(RecordByteOffset64(CDF,Var,
				   prevRec,
				   &tOffset),&pStatus)) return pStatus;
	tOffset += offset;
	if (!sX(ReadVarElems64(Var,CDF->fp,tOffset,
			       numElems,buffer),&pStatus)) return pStatus;
      }
      sX (VIRTUAL_RECORD_DATA, &pStatus);
      return pStatus;
    }
    /**************************************************************************
    * A previous record does not exist in a VVR...check if the last record in
    * the staging area is the previous record.
    **************************************************************************/
    if (EXCLUSIVE(NO_RECORD,Var->stage.lastRec,recNum)) {
      Int32 recNumInStage = Var->stage.lastRec - Var->stage.firstRec;
      tOffset = Var->stage.areaOffset64;
      tOffset += (OFF_T) Var->NphyRecBytes * recNumInStage;
      tOffset += offset;
      if (!sX(ReadVarElems64(Var,CDF->stage.fp,
			     tOffset,numElems,buffer),&pStatus)) return pStatus;
      sX (VIRTUAL_RECORD_DATA, &pStatus);
      return pStatus;
    }
  }
  /****************************************************************************
  * Pad the buffer with the variable's pad value.  Note that this is also done
  * if the variable is sRecords.PREV but a previous record does not exist.
  ****************************************************************************/
  if (!sX(PadBuffer64(CDF,Var,nValues,buffer),&pStatus)) return pStatus;
  sX (VIRTUAL_RECORD_DATA, &pStatus);
  return pStatus;
}

/******************************************************************************
* ReadCompressedFull.
******************************************************************************/

static CDFstatus ReadCompressedFull (CDF, Var, firstRec, lastRec, buffer)
struct CDFstruct *CDF;
struct VarStruct *Var;
Int32 firstRec;
Int32 lastRec;
void *buffer;
{
  CDFstatus pStatus = CDF_OK; Logical found;
  Int32 readTo, recCount, numElems, recX, nextRec;
  Int32 recNum = firstRec, nPadValues, prevRec, padTo;
  Byte *tBuffer = (Byte *) buffer, *destBuffer;
  OFF_T tOffset;
  /****************************************************************************
  * While there are more records to be read/generated...
  ****************************************************************************/
  while (recNum <= lastRec) {
    /**************************************************************************
    * Try to bring the record to the staging area.
    **************************************************************************/
    if (!sX(BringToStage(CDF,Var,recNum,&found),&pStatus)) return pStatus;
    if (found) {
      readTo = MINIMUM(Var->stage.lastRec,lastRec);
      recCount = readTo - recNum + 1;
      numElems = recCount * Var->NphyRecElems;
      recX = recNum - Var->stage.firstRec;
      tOffset = Var->stage.areaOffset64 + (OFF_T) (recX * Var->NphyRecBytes);
      if (!sX(ReadVarElems64(Var,CDF->stage.fp,
			     tOffset,numElems,tBuffer),&pStatus)) return pStatus;
      tBuffer += (size_t) (recCount * Var->NphyRecBytes);
      recNum += recCount;
      continue;
    }
    /**************************************************************************
    * Determine which records need to be padded.
    **************************************************************************/
    if (!sX(NextRecord64(CDF,Var->VDRoffset64,Var->zVar,
		         recNum,&nextRec,&found),&pStatus)) return pStatus;
    if (found) {
      if (EXCLUSIVE(recNum,
		    Var->stage.firstRec,
		    nextRec)) nextRec = Var->stage.firstRec;
      prevRec = nextRec - 1;
      padTo = MINIMUM(prevRec,lastRec);
    }
    else
      padTo = BOO(Var->stage.firstRec > recNum,
		  Var->stage.firstRec - 1,lastRec);
    recCount = padTo - recNum + 1;
    /**************************************************************************
    * If sRecords.PREV...
    **************************************************************************/
    if (Var->prevIfMissing) {
      if (!sX(PrevRecord64(CDF,Var->VDRoffset64,Var->zVar,
			   recNum,&prevRec,&found),&pStatus)) return pStatus;
      if (!found) prevRec = NO_RECORD;
      if (EXCLUSIVE(prevRec,
		    Var->stage.lastRec,
		    recNum)) prevRec = Var->stage.lastRec;
      if (prevRec > NO_RECORD) {
	if (!sX(BringToStage(CDF,Var,prevRec,NULL),&pStatus)) return pStatus;
	recX = prevRec - Var->stage.firstRec;
	tOffset = Var->stage.areaOffset64 + (OFF_T) (recX * Var->NphyRecBytes);
	if (!sX(ReadVarElems64(Var,CDF->stage.fp,
			       tOffset,Var->NphyRecElems,
			       tBuffer),&pStatus)) return pStatus;
	destBuffer = tBuffer + ((size_t) Var->NphyRecBytes);
	for (recX = 1; recX < recCount; recX++) {
	   memmove (destBuffer, tBuffer, (size_t) Var->NphyRecBytes);
	   destBuffer += (size_t) Var->NphyRecBytes;
	}
	sX (VIRTUAL_RECORD_DATA, &pStatus);
	tBuffer += (size_t) (recCount * Var->NphyRecBytes);
	recNum += recCount;
	continue;
      }
    }
    /**************************************************************************
    * Pad with the variable's pad value.  This occurs if the variable is
    * sRecords.NO, sRecords.PAD, or if sRecords.PREV but a previous record
    * does not exist (in a CVVR/VVR or the staging area).
    **************************************************************************/
    nPadValues = recCount * Var->NphyRecValues;
    if (!sX(PadBuffer64(CDF,Var,nPadValues,tBuffer),&pStatus)) return pStatus;
    sX (VIRTUAL_RECORD_DATA, &pStatus);
    tBuffer += (size_t) (recCount * Var->NphyRecBytes);
    recNum += recCount;
  }
  return pStatus;
}

/******************************************************************************
* ReadCompressedPartial.
******************************************************************************/

static CDFstatus ReadCompressedPartial (CDF, Var, recNum, offset, nValues,
					buffer)
struct CDFstruct *CDF;
struct VarStruct *Var;
Int32 recNum;
OFF_T offset;
Int32 nValues;
void *buffer;
{
  CDFstatus pStatus = CDF_OK;
  Int32 numElems = nValues * Var->NvalueElems;
  Int32 prevRec, recX; Logical found;
  OFF_T tOffset;
  /****************************************************************************
  * Try to bring the record to the staging area.
  ****************************************************************************/
  if (!sX(BringToStage(CDF,Var,recNum,&found),&pStatus)) return pStatus;
  if (found) {
    tOffset = Var->stage.areaOffset64;
    tOffset += (OFF_T) Var->NphyRecBytes * (recNum - Var->stage.firstRec);
    tOffset += offset;
    if (!sX(ReadVarElems64(Var,CDF->stage.fp,
			   tOffset,numElems,buffer),&pStatus)) return pStatus;
    return pStatus;
  }
  /****************************************************************************
  * If sRecords.PREV...
  ****************************************************************************/
  if (Var->prevIfMissing) {
    if (!sX(PrevRecord64(CDF,Var->VDRoffset64,
		         Var->zVar,recNum,
		         &prevRec,&found),&pStatus)) return pStatus;
    if (!found) prevRec = NO_RECORD;
    if (EXCLUSIVE(prevRec,
		  Var->stage.lastRec,
		  recNum)) prevRec = Var->stage.lastRec;
    if (prevRec > NO_RECORD) {
      if (!sX(BringToStage(CDF,Var,prevRec,NULL),&pStatus)) return pStatus;
      recX = Var->stage.lastRec - Var->stage.firstRec;
      tOffset = Var->stage.areaOffset64 + (OFF_T) (Var->NphyRecBytes * recX) + 
		offset;
      if (!sX(ReadVarElems64(Var,CDF->stage.fp,
			     tOffset,numElems,buffer),&pStatus)) return pStatus;
      sX (VIRTUAL_RECORD_DATA, &pStatus);
      return pStatus;
    }
  }
  /****************************************************************************
  * Pad the buffer with the variable's pad value.  Note that this is also done
  * if the variable is sRecords.PREV but a previous record does not exist.
  ****************************************************************************/
  if (!sX(PadBuffer64(CDF,Var,nValues,buffer),&pStatus)) return pStatus;
  sX (VIRTUAL_RECORD_DATA, &pStatus);
  return pStatus;
}

/******************************************************************************
* CheckLFS.
******************************************************************************/

VISIBLE_PREFIX CDFstatus CheckLFS (CDFname, isLFS, CDFfullName)
char *CDFname;
Logical *isLFS;
char *CDFfullName;
{
  CDFstatus pStatus = CDF_OK;
#if defined(win32) && (_FILE_OFFSET_BITS == 64)
  int fp;
#else
  FILE *fp;
#endif
  Int32 temp, magicNumber1;
  OFF_T length64 = (OFF_T) 0;
  Logical upper_case_ext, version_numbers, no_append;
  char CDFpathX[DU_MAX_PATH_LEN+1], CDFpathT[DU_MAX_PATH_LEN+1];

  if (CDFfullName != NULL) strcpyX (CDFfullName, CDFname, DU_MAX_PATH_LEN);
  /**************************************************************************
  * Open CDF file.
  **************************************************************************/
  if (strlen(CDFname) > (size_t) CDF_PATHNAME_LEN) {
    if (!sX(CDF_NAME_TRUNC,&pStatus)) return pStatus;
  }
  strcpyX (CDFpathT, CDFname, CDF_PATHNAME_LEN);
#if STRIP_TRAILING_BLANKS_FROM_CDFPATH
  StripTrailingBlanks (CDFpathT);
#endif
#if defined(vms) || defined(dos)
  MakeUpperString (CDFpathT);
#endif
  if (!ValidCDFname(CDFpathT)) return BAD_CDF_NAME;
  if (!sX(FindCDF(CDFpathT,&no_append,
                  &upper_case_ext,
                  &version_numbers),&pStatus)) return pStatus;
  BuildFilePath (CDFt, CDFpathT, no_append, upper_case_ext, version_numbers,
                 INT32_ZERO, CDFpathX);

#if defined(win32) && (_FILE_OFFSET_BITS == 64)
  fp = FOPEN64 (CDFpathX, _O_RDONLY);
  if (fp == EOF) return CDF_OPEN_ERROR;
#else
  fp = (FILE *) FOPEN64 (CDFpathX, READ_ONLY_a_mode);
  if (fp == NULL) return CDF_OPEN_ERROR;
#endif

  if (FSEEK64(fp,(OFF_T)0,vSEEK_END) == EOF) {
    FCLOSE64 (fp);
    return CDF_OPEN_ERROR;
  }
  length64 = FTELL64 (fp);
  
  if (length64 == 0) /* A new one... Must be a LFS. */
    *isLFS = TRUE;
  else { /* Existing one... Check its magic number at the 1st 4-byte. */
    if (FSEEK64(fp,0,vSEEK_SET) == EOF) return CDF_OPEN_ERROR;

#if defined(win32) && (_FILE_OFFSET_BITS == 64)
    if (FREAD64(fp,&magicNumber1,(unsigned int)4) != 4)
#else
    if (FREAD64(&magicNumber1,(size_t)4,(size_t)1,fp) != 1)
#endif
      return CDF_READ_ERROR;

#ifndef NETWORKbyteORDERcpu
    temp = magicNumber1;
    REVERSE4bIO (&temp, &magicNumber1)
#endif
    if (magicNumber1 == V3magicNUMBER_1)
      *isLFS = TRUE;
    else 
      *isLFS = FALSE;
  }
  FCLOSE64 (fp);
  if (CDFfullName != NULL) strcpyX (CDFfullName, CDFpathX, DU_MAX_PATH_LEN);
  return pStatus;
}

/******************************************************************************
* StrStrIgCaseX.
******************************************************************************/

VISIBLE_PREFIX Logical StrStrIgCaseX (string, chkstring)
char *string;
char *chkstring;
{
  int stringL = strlen(string);
  int chkstringL = strlen(chkstring);
  int i;
  if (stringL == 0 || chkstringL == 0) return FALSE;
  if (stringL != chkstringL) return FALSE;
  for (i = 0; i < stringL; i++) {
     if (MakeLower(chkstring[i]) != MakeLower(string[i])) return FALSE;
  }
  return TRUE;
}

/******************************************************************************
*  Check if a given string ends with the provided substring, case sensitive.
*  TRUE if s1 ends with s2.  Otherwise, FALSE is returned.
******************************************************************************/

int EndsWith (s1, s2)
char *s1;
char *s2;
{
    int  i;
    char *ps1, *ps2;
    
    if (strlen(s2) > strlen(s1))
        return FALSE;
        
    ps1 = s1 + strlen(s1) - strlen(s2);
    ps2 = s2;

    for (i=0; i < (int) strlen(s2); i++)
        if (*(ps1++) != *(ps2++))
            return FALSE;

    return TRUE;
}

/******************************************************************************
*  Check if a given string ends with the provided substring, ignoring case.
*  TRUE if s1 ends with s2.  Otherwise, FALSE is returned.
******************************************************************************/

int EndsWithIgCase (s1, s2)
char *s1;
char *s2;
{
    int  i;
    char *ps1, *ps2;

    if (strlen(s2) > strlen(s1))
        return FALSE;

    ps1 = s1 + strlen(s1) - strlen(s2);
    ps2 = s2;

    for (i=0; i < (int) strlen(s2); i++) {
        if (MakeLower(*ps1) != MakeLower(*ps2))
            return FALSE;
        ps1++;
        ps2++;
    }
    return TRUE;
}

/******************************************************************************
*  Find the last occurence of a substring within a given string, case
*  sensitive.  If the substring is found, it returns the offset where the
*  substring is at in the original string. If the substring is not found, it
*  returns -1.
******************************************************************************/

int StrLaststr (s1, s2)
char *s1;
char *s2;
{
    char *sc2, *psc1, *ps1;

    if (*s2 == '\0')
        return -1;

    ps1 = s1 + strlen(s1);

    while(ps1 != s1) {
      --ps1;
      for (psc1 = ps1, sc2 = s2; ; )
          if (*(psc1++) != *(sc2++))
            break;
          else if (*sc2 == '\0')
            return (int) (ps1 - s1);
    }
    return -1;
}

/******************************************************************************
*  Find the last occurence of a substring within a given string, ignoring case.
*  If the substring is found, it returns the offset where the substring is at
*  in the original string. If the substring is not found, it returns -1.
******************************************************************************/
        
int StrLaststrIgCase (s1, s2)
char *s1;
char *s2;
{
    char *sc2, *psc1, *ps1;

    if (*s2 == '\0')
        return -1;

    ps1 = s1 + strlen(s1);

    while (ps1 != s1) {
      --ps1;
      for (psc1 = ps1, sc2 = s2; ; ) {
          if (MakeLower(*psc1) != MakeLower(*sc2))
            break;
          else if (*sc2 == '\0')
            return (int) (ps1 - s1);
          psc1++;
          sc2++;
      } 
    }
    return -1;
}     
      
/******************************************************************************
*  Remove the ".cdf" file extension from the given file name if it's there.
*  It ignores the case. 
*  Example: 
*      mydata.cdf => mydata
******************************************************************************/

void RemoveCDFFileExtension (fileName, dstPath)
char *fileName;         /* CDF file name. */
char *dstPath;          /* The string holding file name without extension. */
{
    int ptr = -1;

    strcpyX (dstPath, fileName, CDF_PATHNAME_LEN);
    if (EndsWithIgCase (dstPath, ".cdf")) {
      ptr = StrLaststrIgCase(dstPath, ".cdf");
      if (ptr != -1) ((char *) dstPath)[ptr] = (char) '\0';
    }
    return;
}

/******************************************************************************
* CDFgetFileBackwardEnvVar.
******************************************************************************/

VISIBLE_PREFIX int CDFgetFileBackwardEnvVar ()
{
  char *bk = NULL;
#if defined(vms)
  bk = getenv("CDF$FILEBACKWARD");
#else
  bk = getenv("CDF_FILEBACKWARD");
#endif
  if (bk != NULL) {
    if (strlen(bk) == 0) return 0;
    if (StrStrIgCaseX(bk, "TRUE")) return 1;
  }
  return 0;
}

/******************************************************************************
* CDFgetChecksumEnvVar.
******************************************************************************/

VISIBLE_PREFIX int CDFgetChecksumEnvVar ()
{
  char *bk = NULL;
#if defined(vms)
  bk = getenv("CDF$CHECKSUM");
#else
  bk = getenv("CDF_CHECKSUM");
#endif
  if (bk != NULL) {
    if (strlen(bk) == 0) return 0;
    if (StrStrIgCaseX(bk, "none") || StrStrIgCaseX(bk, "no")) return 0;
    if (StrStrIgCaseX(bk, "md5")) return 1;
  }
  return 0;
}

/******************************************************************************
* ChecksumMethod.
******************************************************************************/

VISIBLE_PREFIX long ChecksumMethod (flags)
Int32 flags;
{
  int md5 = 0;
  int other = 0;

  if (!BITSET(flags,CDR_CHECKSUM_BIT)) return NONE_CHECKSUM;
  md5 = (int) BITSET(flags,CDR_CHECKSUM_MD5_BIT);
  other = (int) BITSET(flags,CDR_CHECKSUM_OTHER_BIT); 
  if (md5 == 1) return MD5_CHECKSUM;
  else if (other == 1) return OTHER_CHECKSUM;
  return NONE_CHECKSUM;
}


syntax highlighted by Code2HTML, v. 0.9.1