/******************************************************************************
*
*  NSSDC/CDF                                        CDF `open' operations.
*
*  Version 1.4a, 21-Feb-97, Hughes STX.
*
*  Modification history:
*
*   V1.0  20-May-92, J Love     Original version (was part of `cdflib.c').
*   V1.1  29-Sep-92, J Love     CDF V2.3 (shareable/NeXT/zVar).
*   V1.2  25-Jan-94, J Love     CDF V2.4.
*   V1.3  15-Dec-94, J Love     CDF V2.5.
*   V1.3a  9-Jan-95, J Love	Encode/decode changes, etc.
*   V1.3b 24-Feb-95, J Love	Solaris 2.3 IDL i/f.
*   V1.3c  8-May-95, J Love	Only check version/release for FUTURE_CDF.
*   V1.3d  7-Sep-95, J Love	CDFexport-related changes.  Fixed clean up
*				when a CDF is aborted.
*   V1.4  21-Aug-96, J Love	CDF V2.6.
*   V1.4a 21-Feb-97, J Love	Removed RICE.
*
******************************************************************************/

#include "cdflib.h"
#include "cdfrev.h"

/******************************************************************************
* CDFope.
******************************************************************************/

STATICforIDL CDFstatus CDFope (Va, Cur)
struct VAstruct *Va;
struct CurStruct *Cur;
{
CDFstatus pStatus = CDF_OK;

switch (Va->item) {
  /****************************************************************************
  * CDF_, open an existing CDF.
  ****************************************************************************/
  case CDF_: {
    char CDFnameT[CDF_PATHNAME_LEN+1], CDFnameX[DU_MAX_PATH_LEN+1];
    char *CDFnameP; struct CDFstruct *CDF;
    vFILE *dotFp, *uDotFp;
    size_t nBytes; int varN; CDFid *id;
    Logical upper_case_ext, version_numbers, no_append;
    Int32 magicNumber1, magicNumber2, CDRflags, version, release, increment;
    /**************************************************************************
    * Get arguments for this operation/item.
    **************************************************************************/
    CDFnameP = va_arg (Va->ap, char *);
    id = va_arg (Va->ap, CDFid *);
    *id = (CDFid) NULL;
    /**************************************************************************
    * Validate arguments.
    **************************************************************************/
    if (strlen(CDFnameP) > (size_t) CDF_PATHNAME_LEN) {
      if (!sX(CDF_NAME_TRUNC,&pStatus)) return pStatus;
    }
    strcpyX (CDFnameT, CDFnameP, CDF_PATHNAME_LEN);
#if STRIP_TRAILING_BLANKS_FROM_CDFPATH
    StripTrailingBlanks (CDFnameT);
#endif
#if defined(vms) || defined(dos)
    MakeUpperString (CDFnameT);
#endif
    if (!ValidCDFname(CDFnameT)) return BAD_CDF_NAME;
    if (!sX(FindCDF(CDFnameT,&no_append,
		    &upper_case_ext,
		    &version_numbers),&pStatus)) return pStatus;
    /**************************************************************************
    * Open CDF file.
    **************************************************************************/
    BuildFilePath (CDFt, CDFnameT, no_append, upper_case_ext, version_numbers,
		   INT32_ZERO, CDFnameX);
    dotFp = V_open (CDFnameX, READ_ONLY_a_mode);
    if (dotFp == NULL) return CDF_OPEN_ERROR;
    /**************************************************************************
    * Read the magic numbers.
    **************************************************************************/
    if (!Read32(dotFp,&magicNumber1)) {
      V_close (dotFp, NULL);
      return CDF_READ_ERROR;
    }
    if (!Read32(dotFp,&magicNumber2)) {
      V_close (dotFp, NULL);
      return CDF_READ_ERROR;
    }
    /**************************************************************************
    * Determine what version CDF this is, if it is compressed (and if this is
    * actually a CDF).
    **************************************************************************/
    switch (magicNumber1) {
      case V1magicNUMBER_flip:
	V_close (dotFp, NULL);
	return ILLEGAL_ON_V1_CDF;
      case V2magicNUMBER_1pre:
	uDotFp = NULL;
	break;
      case V2magicNUMBER_1:
	switch (magicNumber2) {
	  case V2magicNUMBER_2u:
	    if (!CACHEv(dotFp,NUMcacheUNKNOWN)) {
	      V_close (dotFp, NULL);
	      return BAD_CACHE_SIZE;
	    }
	    uDotFp = NULL;
	    break;
	  case V2magicNUMBER_2c:
	    uDotFp = V_scratch (ScratchDirectory(NULL), "cdf");
	    if (uDotFp == NULL) {
	      V_close (dotFp, NULL);
	      return CDF_CREATE_ERROR;
	    }
	    if (!CACHEv(uDotFp,NUMcacheUNKNOWN)) {
	      V_close (dotFp, NULL);
	      V_delete (uDotFp, NULL);
	      return BAD_CACHE_SIZE;
	    }
	    if (!sX(DecompressCDF(dotFp,uDotFp),&pStatus)) {
	      V_close (dotFp, NULL);
	      V_delete (uDotFp, NULL);
	      return pStatus;
	    }
	    break;
	  default:
	    V_close (dotFp, NULL);
	    return NOT_A_CDF;
	}
	break;
      default:
	V_close (dotFp, NULL);
	return NOT_A_CDF;
    }
    /**************************************************************************
    * Allocate and begin initializing CDF structure.
    **************************************************************************/
    CDF = (struct CDFstruct *) AllocateMemory (sizeof(struct CDFstruct), NULL);
    if (CDF == NULL) {
      V_close (dotFp, NULL);
      if (uDotFp != NULL) V_delete (uDotFp, NULL);
      return BAD_MALLOC;
    }
    CDF->CDFname = (char *) AllocateMemory (strlen(CDFnameT) + 1, NULL);
    if (CDF->CDFname == NULL) {
      V_close (dotFp, NULL);
      if (uDotFp != NULL) V_delete (uDotFp, NULL);
      FreeMemory (CDF, NULL);
      return BAD_MALLOC;
    }
    else
      strcpyX (CDF->CDFname, CDFnameT, 0);
    CDF->magic = VALIDid_MAGIC_NUMBER;
    CDF->dotFp = dotFp;
    CDF->uDotFp = uDotFp;
    CDF->fp = BOO(uDotFp == NULL,dotFp,uDotFp);
    CDF->no_append = no_append;
    CDF->upper_case_ext = upper_case_ext;
    CDF->version_numbers = version_numbers;
    CDF->decoding = HOST_DECODING;
    CDF->readOnly = FALSE;
    CDF->zMode = zMODEoff;
    CDF->negToPosFp0 = FALSE;
    CDF->status = READ_ONLY;
    CDF->pseudo_clock = 0;
    CDF->stage.fp = NULL;
    CDF->stage.mark = ZERO_OFFSET;
    CDF->stage.cacheSize = NUMcacheSTAGE;
    CDF->compressCacheSize = NUMcacheCOMPRESS;
    CDF->compressFp = NULL;
    CDF->scratchDir = NULL;
    CDF->NrVars = 0;		/* Updated below if the read is OK. */
    CDF->NzVars = 0;		/* Updated below if the read is OK. */
    CDF->rVars = NULL;
    CDF->zVars = NULL;
    /**************************************************************************
    * Read necessary fields from the CDR and GDR.
    **************************************************************************/
    CDF->CDRoffset = V2_CDR_OFFSET;
    if (!sX(ReadCDR(CDF->fp,CDF->CDRoffset,
		    CDR_GDROFFSET,&(CDF->GDRoffset),
		    CDR_ENCODING,&(CDF->encoding),
		    CDR_FLAGS,&CDRflags,
		    CDR_VERSION,&version,
		    CDR_RELEASE,&release,
		    CDR_INCREMENT,&increment,
		    CDR_NULL),&pStatus)) {
      AbortAccess (CDF, noUPDATE, noDELETE);
      FreeMemory (CDF, NULL);
      return pStatus;
    }
    if (!sX(ReadGDR(CDF->fp,CDF->GDRoffset,
		    GDR_NrVARS,&(CDF->NrVars),
		    GDR_NzVARS,&(CDF->NzVars),
		    GDR_rMAXREC,&(CDF->rMaxRec),
		    GDR_rNUMDIMS,&(CDF->rNumDims),
		    GDR_rDIMSIZES,CDF->rDimSizes,
		    GDR_NULL),&pStatus)) {
      AbortAccess (CDF, noUPDATE, noDELETE);
      FreeMemory (CDF, NULL);
      return pStatus;
    }
    /**************************************************************************
    * Continue initializing CDF structure.
    **************************************************************************/
    CDF->singleFile = SINGLEfileBITset (CDRflags);
    CDF->rowMajor = ROWmajorBITset (CDRflags);
    CDF->fakeEPOCH = PriorTo("2.1.1",version,release,increment);
    CDF->wastedSpace = PriorTo("2.5",version,release,increment);
    CDF->badEOF = PriorTo("2.1",version,release,increment);
    CDF->badTerminatingOffsets = PriorTo("2.1",version,release,increment);
    CDF->assumedScopes = PriorTo("2.5",version,release,increment);
    CDF->workingCacheSize = BOO(CDF->singleFile,NUMcacheSINGLE,NUMcacheMULTI);
    /**************************************************************************
    * Set the cache size for the "working" dotCDF file based on the format
    * of the CDF.
    **************************************************************************/
    if (!CACHEv(CDF->fp,CDF->workingCacheSize)) {
      AbortAccess (CDF, noUPDATE, noDELETE);
      FreeMemory (CDF, NULL);
      return BAD_CACHE_SIZE;
    }
    /**************************************************************************
    * Allocate and initialize variable data structures kept in memory.
    **************************************************************************/
    if (CDF->NrVars > 0) {
      nBytes = (size_t) (CDF->NrVars * sizeof(struct VarStruct *));
      CDF->rVars = (struct VarStruct **) AllocateMemory (nBytes, NULL);
      if (CDF->rVars == NULL) {
	AbortAccess (CDF, noUPDATE, noDELETE);
        FreeMemory (CDF, NULL);
	return BAD_MALLOC;
      }
      for (varN = 0; varN < CDF->NrVars; varN++) CDF->rVars[varN] = NULL;
      CDF->MAXrVars = (int) CDF->NrVars;
    }
    else {
      CDF->rVars = NULL;
      CDF->MAXrVars = 0;
    }
    if (CDF->NzVars > 0) {
      nBytes = (size_t) (CDF->NzVars * sizeof(struct VarStruct *));
      CDF->zVars = (struct VarStruct **) AllocateMemory (nBytes, NULL);
      if (CDF->zVars == NULL) {
	AbortAccess (CDF, noUPDATE, noDELETE);
        FreeMemory (CDF, NULL);
	return BAD_MALLOC;
      }
      for (varN = 0; varN < CDF->NzVars; varN++) CDF->zVars[varN] = NULL;
      CDF->MAXzVars = (int) CDF->NzVars;
    }
    else {
      CDF->zVars = NULL;
      CDF->MAXzVars = 0;
    }
    /**************************************************************************
    * If this is a multi-file CDF and a CDF pathname that doesn't require file
    * extensions to be appended was specified (ie. a weird naming convention),
    * close/free the CDF and return an error.  This is because the extensions
    * for the variable files would be known only to the user.  We'd rather not
    * guess.
    **************************************************************************/
    if (!CDF->singleFile && CDF->no_append) {
      AbortAccess (CDF, noUPDATE, noDELETE);
      FreeMemory (CDF, NULL);
      return BAD_CDF_EXTENSION;
    }
    /**************************************************************************
    * Initialize the current objects/states and the Vstream statistics.
    **************************************************************************/
    InitCURobjectsStates (CDF);
    AddTOvStats (&CDF->dotCDFvStats, NULL);
    AddTOvStats (&CDF->uDotCDFvStats, NULL);
    /**************************************************************************
    * Select the current CDF and pass back the CDF identifier.
    **************************************************************************/
    Cur->cdf = CDF;
    *id = CDF;
    break;
  }
  /****************************************************************************
  * Unknown item, must be the next function.
  ****************************************************************************/
  default: {
    Va->fnc = Va->item;
    break;
  }
}

return pStatus;
}
