/******************************************************************************
*
*  NSSDC/CDF                 CDFdump - dump all records found in dotCDF file.
*
*  Version 2.6b, 21-Nov-97, Hughes STX.
*
*  Modification history:
*
*   V1.0  29-May-91, J Love     Original version.
*   V1.1  25-Jun-91, J Love     Moved increment.  Display fill value.  Display
*                               extend records.  CDF_EPOCH added as a data
*                               type.  Added QOP.  Added PageInst.
*   V1.2  26-Jul-91, J Love     Added SEEK_CUR.  Added XDR decoding.
*                               Use 'Exit'/'ExitBAD'.
*   V1.3   7-Oct-91, J Love     Modified for IBM-PC port (and IBM-RS6000).
*   V2.0  21-Apr-92, J Love     IBM PC port/CDF V2.2.
*   V2.1  29-Sep-92, J Love     CDF V2.3 (shareable/NeXT/zVar).
*   V2.2  22-Oct-92, J Love     Newline after pad values.
*   V2.3  24-Jan-94, J Love     CDF V2.4.
*   V2.3a  8-Feb-94, J Love     DEC Alpha/OpenVMS port.
*   V2.4  21-Dec-94, J Love     CDF V2.5.
*   V2.4a 13-Jan-95, J Love     Uppercase file extensions.
*   V2.4b 26-Jan-95, J Love     Display meaning of `flags'.
*   V2.4c 22-Mar-95, J Love     Check for all NULs following copyright.
*   V2.4d  6-Apr-95, J Love     POSIX.
*   V2.5  25-May-95, J Love     EPOCH styles.  Fixed `all NULs' message.
*   V2.5a 19-Sep-95, J Love     CHECKforABORTso.
*   V2.5b 28-Sep-95, J Love     Macintosh dialog filtering.  Outline default
*                               button.
*   V2.6   3-Oct-96, J Love     CDF V2.6.
*   V2.6a  3-Sep-97, J Love	Fixed ScanCDF.
*   V2.6b 21-Nov-97, J Love	Windows NT/Visual C++.
*
******************************************************************************/

#include "cdfdump.h"

/******************************************************************************
* Increased stack size for Borland C on IBM PC.
******************************************************************************/

#if defined(BORLANDC)
extern unsigned _stklen = BORLANDC_STACK_SIZE;
#endif

/******************************************************************************
* Global variables & macro constants.
******************************************************************************/

long CCRcount, CDRcount, GDRcount, ADRcount, AgrEDRcount, AzEDRcount,
     rVDRcount, zVDRcount, VXRcount, VVRcount, CPRcount, SPRcount,
     CVVRcount, UIRcount;
long CCRbytes, CDRbytes, GDRbytes, ADRbytes, AgrEDRbytes, AzEDRbytes,
     rVDRbytes, zVDRbytes, VXRbytes, VVRbytes, CPRbytes, SPRbytes,
     CVVRbytes, UIRbytes;
long usedBytes, wastedBytes;
FILE *OUTfp;
Logical EOFok;		/* When TRUE, an EOF is expected. */
long radix;		/* 10 (decimal) or 16 (hexadecimal). */

/******************************************************************************
* Local function prototypes.
******************************************************************************/

static void DisplaySummary PROTOARGs((void));
static void CheckSizes PROTOARGs((
  vFILE *CDFfp, long startingOffset
));
static void ScanCDF PROTOARGs((
  vFILE *CDFfp, Logical showIndexing, long level, long startingOffset
));
static void DumpVXR PROTOARGs((
  vFILE *CDFfp, Int32 vxrOffset, int indent
));
static char *FileOffset PROTOARGs((Int32 offset));
static char *Hex32 PROTOARGs((Int32 value));
static char *Deci32 PROTOARGs((Int32 value));
static Logical SeekTo PROTOARGs((vFILE *fp, long offset, int how));
static Logical Read4 PROTOARGs((vFILE *fp, Int32 *value));
static Logical Read4s PROTOARGs((vFILE *fp, Int32 *values, int count));
static Logical ReadBytes PROTOARGs((vFILE *fp, void *bytes, Int32 count));
static void DisplayReadFailure PROTOARGs((vFILE *fp));

/******************************************************************************
* Main.
******************************************************************************/

#if !defined(win32)
MAIN {
  Logical success = TRUE;
  strcpyX (pgmName, "CDFdump", MAX_PROGRAM_NAME_LEN);
#if defined(mac)
  MacExecuteSO (DumpCDF, DumpQOPs);
#else
  success = DumpCDF (argc, argv);
#endif
#if defined(DEBUG)
  if (FreeMemory(NULL,FatalError) > 0) DisplayWarning ("Abandoned buffers.");
#else
  FreeMemory (NULL, FatalError);
#endif
  return BOO(success,EXIT_SUCCESS_,EXIT_FAILURE_);
}
#endif

/******************************************************************************
* DumpCDF.
*    Returns FALSE if an error occurred.
******************************************************************************/

Logical DumpCDF (argC, argV)
int argC;
char *argV[];
{
  vFILE *CDFfp;
  Logical checkSizes, showIndexing, showSummary;
  long level, startingOffset;
  char CDFpath[DU_MAX_PATH_LEN+1], tCDFpath[DU_MAX_PATH_LEN+1];
  char oSpec[DU_MAX_PATH_LEN+1], oDir[DU_MAX_DIR_LEN+1];
  char oName[DU_MAX_NAME_LEN+1];
  QOP *qop; Logical qopError = FALSE;
  static char *validQuals[] = {
    "output", "brief", "most", "full", "page", "nopage", "sizes", "nosizes",
    "offset", "indexing", "noindexing", "summary", "nosummary", "about", "10",
    "16", NULL
  };
  static int optRequired[] = {
    TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE,
    FALSE, FALSE, FALSE, FALSE, FALSE, 0
  };
  /****************************************************************************
  * Determine qualifiers/options/parameters.
  ****************************************************************************/
  switch (argC) {
    case 1:
      PageOLH ("cdfdump.olh", argV[0]);
      return TRUE;
    default:
      qop = Qop (argC, argV, validQuals, optRequired);
      if (qop == NULL) return FALSE;
      /************************************************************************
      * Check for `about' qualifier.
      ************************************************************************/
      if (qop->qualEntered[ABOUTqual]) {
        DisplayIdentification (pgmName);
        FreeMemory (qop, FatalError);
        return TRUE;
      }
      /************************************************************************
      * Get CDF path.
      ************************************************************************/
      if (qop->Nparms < 1) {
        DisplayError ("Missing parameter.");
        qopError = TRUE;
      }
      else
        strcpyX (CDFpath, qop->parms[CDFPATHparm], DU_MAX_PATH_LEN);
      /************************************************************************
      * Check for `page' qualifier (this must be done before the `output'
      * qualifier is checked).
      ************************************************************************/
      qopError = qopError | !TFqualifier(qop,&pagingOn,PAGEqual,NOPAGEqual,
				         DEFAULTpageDUMP,"page");
      /************************************************************************
      * Check of offset qualifier.
      ************************************************************************/
      if (qop->qualEntered[OFFSETqual]) {
        if (sscanf(qop->qualOpt[OFFSETqual],"%ld",&startingOffset) != 1) {
	  DisplayError ("Illegal starting offset.");
	  qopError = TRUE;
        }
        if (startingOffset < 0) {
	  DisplayError ("Illegal starting offset.");
	  qopError = TRUE;
        }
      }
      else
        startingOffset = NO_OFFSET;
      /************************************************************************
      * Check for output file qualifier.
      ************************************************************************/
      if (qop->qualEntered[OUTPUTqual]) {
        if (pagingOn) {
	  DisplayError ("Conflicting qualifiers (`output' and `page').");
	  qopError = TRUE;
        }
        else
	  strcpyX (oSpec, qop->qualOpt[OUTPUTqual], DU_MAX_PATH_LEN);
      }
      else
        strcpyX (oSpec, "", DU_MAX_PATH_LEN);
      /************************************************************************
      * Check for level qualifier.
      ************************************************************************/
      qopError = qopError | !S3qualifierLong(qop,&level,BRIEFqual,BRIEF_,
					     MOSTqual,MOST_,FULLqual,FULL_,
					     (long)DEFAULTlevelDUMP,"level");
      /************************************************************************
      * Check for `sizes' qualifier.
      ************************************************************************/
      qopError = qopError | !TFqualifier(qop,&checkSizes,SIZESqual,NOSIZESqual,
				         DEFAULTsizesDUMP,"sizes");
      /************************************************************************
      * Check for `indexing' qualifier.
      ************************************************************************/
      qopError = qopError | !TFqualifier(qop,&showIndexing,INDEXINGqual,
				         NOINDEXINGqual,DEFAULTindexingDUMP,
				         "indexing");
      /************************************************************************
      * Check for `summary' qualifier.
      ************************************************************************/
      qopError = qopError | !TFqualifier(qop,&showSummary,SUMMARYqual,
				         NOSUMMARYqual,DEFAULTsummaryDUMP,
				         "summary");
      /************************************************************************
      * Check for file offset radix qualifiers.
      ************************************************************************/
      qopError = qopError | !S2qualifierLong(qop,&radix,DECIqual,10L,HEXAqual,
					     16L,(long)DEFAULTradixDUMP,
					     "radix [10|16]");
      /************************************************************************
      * Free QOP memory and check for an error.
      ************************************************************************/
      FreeMemory (qop, FatalError);
      if (qopError) return FALSE;
      break;
  }
  /****************************************************************************
  * Open CDF.
  ****************************************************************************/
  strcpyX (tCDFpath, CDFpath, 0);
  strcatX (tCDFpath, ".cdf", 0);
  CDFfp = V_open (tCDFpath, READ_ONLY_a_mode);
  if (CDFfp == NULL) {
    strcpyX (tCDFpath, CDFpath, 0);
    strcatX (tCDFpath, ".CDF", 0);
    CDFfp = V_open (tCDFpath, READ_ONLY_a_mode);
    if (CDFfp == NULL) {
      strcpyX (tCDFpath, CDFpath, 0);
      strcatX (tCDFpath, ".cdf;1", 0);
      CDFfp = V_open (tCDFpath, READ_ONLY_a_mode);
      if (CDFfp == NULL) {
        strcpyX (tCDFpath, CDFpath, 0);
        strcatX (tCDFpath, ".CDF;1", 0);
        CDFfp = V_open (tCDFpath, READ_ONLY_a_mode);
        if (CDFfp == NULL) {
	  CDFfp = V_open (CDFpath, READ_ONLY_a_mode);
	  if (CDFfp == NULL) {
	    WriteOut (stdout, "Error opening CDF.\n");
	    return FALSE;
	  }
        }
      }
    }
  }
  /****************************************************************************
  * Open output file (if specified).
  ****************************************************************************/
  if (NULstring(oSpec))
    OUTfp = stdout;
  else {
    ParsePath (oSpec, oDir, oName);
    if (strchr(oName,'.') == NULL) strcatX (oSpec, ".dmp", DU_MAX_PATH_LEN);
    OUTfp = fopen (oSpec, "w");
    if (OUTfp == NULL) {
      WriteOut (stdout, "Unable to open output file.\n");
      V_close (CDFfp, NULL);
      return FALSE;
    }
  }
  /****************************************************************************
  * Initialize global variables.
  ****************************************************************************/
  EOFok = FALSE;
  usedBytes = 0;
  wastedBytes = 0;
  CCRcount = 0;
  CDRcount = 0;
  GDRcount = 0;
  ADRcount = 0;
  AgrEDRcount = 0;
  AzEDRcount = 0;
  rVDRcount = 0;
  zVDRcount = 0;
  VXRcount = 0;
  VVRcount = 0;
  CVVRcount = 0;
  CPRcount = 0;
  SPRcount = 0;
  UIRcount = 0;
  CCRbytes = 0;
  CDRbytes = 0;
  GDRbytes = 0;
  ADRbytes = 0;
  AgrEDRbytes = 0;
  AzEDRbytes = 0;
  rVDRbytes = 0;
  zVDRbytes = 0;
  VXRbytes = 0;
  VVRbytes = 0;
  CVVRbytes = 0;
  CPRbytes = 0;
  SPRbytes = 0;
  UIRbytes = 0;
  /****************************************************************************
  * Check record sizes (if requested).
  ****************************************************************************/
  if (checkSizes) CheckSizes (CDFfp, startingOffset);
  /****************************************************************************
  * Scan internal records.
  ****************************************************************************/
  ScanCDF (CDFfp, showIndexing, level, startingOffset);
  /****************************************************************************
  * Display summary.
  ****************************************************************************/
  if (showSummary) DisplaySummary ();
  V_close (CDFfp, NULL);
  if (OUTfp != stdout) fclose (OUTfp);
  return TRUE;
}

/******************************************************************************
* CheckSizes.
******************************************************************************/

static void CheckSizes (CDFfp, startingOffset)
vFILE *CDFfp;
long startingOffset;
{
  long offset; Int32 recordSize, recordType, i, nBytes;
  char text[MAX_SCREENLINE_LEN+1]; Byte byte;
  WriteOut (OUTfp, "\nChecking record sizes...\n\n");
  offset = BOO(startingOffset != NO_OFFSET,startingOffset,V2_CDR_OFFSET);
  if (!SeekTo(CDFfp,offset,vSEEK_SET)) return;
  for (;;) {
     EOFok = TRUE;
     if (!Read4(CDFfp,&recordSize)) return;
     EOFok = FALSE;
     if (recordSize <= 0) {
       sprintf (text, "\nIllegal record size detected (%ld).\n",
		(long) recordSize);
       WriteOut (OUTfp, text);
       return;
     }
     if (!Read4(CDFfp,&recordType)) return;
     switch (recordType) {
       case CCR_: strcpyX (text, "CCR", MAX_SCREENLINE_LEN); break;
       case CDR_: strcpyX (text, "CDR", MAX_SCREENLINE_LEN); break;
       case GDR_: strcpyX (text, "GDR", MAX_SCREENLINE_LEN); break;
       case ADR_: strcpyX (text, "ADR", MAX_SCREENLINE_LEN); break;
       case AgrEDR_: strcpyX (text, "AgrEDR", MAX_SCREENLINE_LEN); break;
       case AzEDR_: strcpyX (text, "AzEDR", MAX_SCREENLINE_LEN); break;
       case rVDR_: strcpyX (text, "rVDR", MAX_SCREENLINE_LEN); break;
       case zVDR_: strcpyX (text, "zVDR", MAX_SCREENLINE_LEN); break;
       case VXR_: strcpyX (text, "VXR", MAX_SCREENLINE_LEN); break;
       case VVR_: strcpyX (text, "VVR", MAX_SCREENLINE_LEN); break;
       case CVVR_: strcpyX (text, "CVVR", MAX_SCREENLINE_LEN); break;
       case CPR_: strcpyX (text, "CPR", MAX_SCREENLINE_LEN); break;
       case SPR_: strcpyX (text, "SPR", MAX_SCREENLINE_LEN); break;
       case UIR_: strcpyX (text, "UIR", MAX_SCREENLINE_LEN); break;
       default: {
	 sprintf (text, "\nUnknown record type detected (%ld).\n",
		  (long) recordType);
	 WriteOut (OUTfp, text);
	 return;
       } 
     }
     sprintf (EofS(text), ", %ld bytes (offset: %s)\n",
	      (long) recordSize, FileOffset((Int32)offset));
     WriteOut (OUTfp, text);
     nBytes = recordSize - 8;
     for (i = 0; i < nBytes; i++) {
        if (!ReadBytes(CDFfp,&byte,(Int32)1)) return;
     }
     offset += recordSize;
     CHECKforABORTso
  }
}

/******************************************************************************
* ScanCDF.
******************************************************************************/

static void ScanCDF (CDFfp, showIndexing, level, startingOffset)
vFILE *CDFfp;
Logical showIndexing;
long level;
long startingOffset;
{
  Int32 recordSize, recordType, encoding, nBytes, rNumDims;
  long offset; int dimN;
  Logical VDRwastedSpace;
  Int32 int32; char text[MAX_SCREENLINE_LEN+1];
  /****************************************************************************
  * Dump magic number(s).
  ****************************************************************************/
  WriteOut (OUTfp, "\nScanning records...\n\n");
  if (startingOffset == NO_OFFSET) {
    if (!SeekTo(CDFfp,0,vSEEK_SET)) return;
    if (!Read4(CDFfp,&int32)) return;
    if (MOST(level)) {
      sprintf (text, "Magic number (1): %s\n", Hex32(int32));
      WriteOut (OUTfp, text);
    }
    if (!Read4(CDFfp,&int32)) return;
    if (MOST(level)) {
      sprintf (text, "Magic number (2): %s\n", Hex32(int32));
      WriteOut (OUTfp, text);
    }
    usedBytes += 8;
  }
  else {
    if (!SeekTo(CDFfp,startingOffset,vSEEK_SET)) return;
  }
  /****************************************************************************
  * Read and dump records until EOF (or illegal record) reached.
  ****************************************************************************/
  for (;;) {
     /*************************************************************************
     * Read record size.
     *************************************************************************/
     offset = V_tell (CDFfp);
     EOFok = TRUE;
     if (!Read4(CDFfp,&recordSize)) return;
     EOFok = FALSE;
     if (recordSize <= 0) {
       sprintf (text, "\nIllegal record size detected (%ld)\n", recordSize);
       WriteOut (OUTfp, text);
       return;
     }
     if (MOST(level)) {
       sprintf (text, "\nRecordSize: %ld (@ %s)\n",
		    (long) recordSize, FileOffset((Int32)offset));
       WriteOut (OUTfp, text);
     }
     /*************************************************************************
     * Read record type.
     *************************************************************************/
     if (!Read4(CDFfp,&recordType)) return;
     if (MOST(level)) {
       sprintf (text, "RecordType: %ld ", (long) recordType);
       switch (recordType) {
         case CCR_: strcatX (text, "(CCR)\n", MAX_SCREENLINE_LEN); break;
         case CDR_: strcatX (text, "(CDR)\n", MAX_SCREENLINE_LEN); break;
         case GDR_: strcatX (text, "(GDR)\n", MAX_SCREENLINE_LEN); break;
         case ADR_: strcatX (text, "(ADR)\n", MAX_SCREENLINE_LEN); break;
         case AgrEDR_: strcatX (text, "(AgrEDR)\n", MAX_SCREENLINE_LEN); break;
         case AzEDR_: strcatX (text, "(AzEDR)\n", MAX_SCREENLINE_LEN); break;
         case rVDR_: strcatX (text, "(rVDR)\n", MAX_SCREENLINE_LEN); break;
         case zVDR_: strcatX (text, "(zVDR)\n", MAX_SCREENLINE_LEN); break;
         case VXR_: strcatX (text, "(VXR)\n", MAX_SCREENLINE_LEN); break;
         case VVR_: strcatX (text, "(VVR)\n", MAX_SCREENLINE_LEN); break;
         case CVVR_: strcatX (text, "(CVVR)\n", MAX_SCREENLINE_LEN); break;
         case CPR_: strcatX (text, "(CPR)\n", MAX_SCREENLINE_LEN); break;
         case SPR_: strcatX (text, "(SPR)\n", MAX_SCREENLINE_LEN); break;
         case UIR_: strcatX (text, "(UIR)\n", MAX_SCREENLINE_LEN); break;
         default: {
	   sprintf (text, "Illegal record type detected (%ld)\n",
		    (long) recordType);
	   WriteOut (OUTfp, text);
	   return;
         }
       }
       WriteOut (OUTfp, text);
     }
     /*************************************************************************
     * Based on the record type...
     *************************************************************************/
     switch (recordType) {
        /**********************************************************************
        * Compressed CDF Record (CCR).
        **********************************************************************/
        case CCR_: {
	  Byte byte; Int32 i;
	  CCRcount++;
	  CCRbytes += recordSize;
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "CPRoffset: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "uSize: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuA: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  nBytes = recordSize - CCR_BASE_SIZE;
	  if (MOST(level)) WriteOut (OUTfp, "Skipping compressed IRs...\n");
	  for (i = 0; i < nBytes; i++) {
	     if (!ReadBytes(CDFfp,&byte,(Int32)1)) return;
	  }
	  usedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * CDF Descriptor Record (CDR).
        **********************************************************************/
        case CDR_: {
	  Int32 version, release, increment;
	  char *docText;
	  CDRcount++;
	  CDRbytes += recordSize;
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "GDRoffset: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&version)) return;
	  if (MOST(level)) {
	    sprintf (text, "Version: %ld\n", (long) version);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&release)) return;
	  if (MOST(level)) {
	    sprintf (text, "Release: %ld\n", (long) release);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&encoding)) return;
	  if (MOST(level)) {
	    sprintf (text, "Encoding: %ld\n", (long) encoding);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "Flags: 0x%lX (", (long) int32);
	    sprintf (EofS(text), "%s,",
		     BOO(BITSET(int32,CDR_MAJORITY_BIT),"Row","Column"));
	    sprintf (EofS(text), "%s)\n",
		     BOO(BITSET(int32,CDR_FORMAT_BIT),"Single","Multi"));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuA: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuB: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&increment)) return;
	  if (MOST(level)) {
	    sprintf (text, "Increment: %ld\n", (long) increment);
	    WriteOut (OUTfp, text);
	  }
	  VDRwastedSpace = PriorTo ("2.5", version, release, increment);
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuD: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuE: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  nBytes = recordSize - CDR_BASE_SIZE;
	  docText = (char *) AllocateMemory ((size_t)(nBytes + 1), FatalError);
	  if (!ReadBytes(CDFfp,docText,nBytes)) {
	    FreeMemory (docText, NULL);
	    return;
	  }
	  docText[(int)nBytes] = NUL;
	  if (MOST(level)) {
	    Logical allNULs = TRUE; int i;
	    WriteOut (OUTfp, "copyright...\n");
	    WriteOut (OUTfp, docText);
	    for (i = strlen(docText); i < nBytes; i++) {
	       if (docText[i] != NUL) allNULs = FALSE;
	    }
	    if (allNULs) WriteOut (OUTfp, "...followed by all NULs.\n");
	  }
	  FreeMemory (docText, FatalError);
	  usedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * Global Descriptor Record (GDR).
        **********************************************************************/
        case GDR_: {
	  GDRcount++;
	  GDRbytes += recordSize;
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rVDRhead: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "zVDRhead: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "ADRhead: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "eof: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "NumVar: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "NumAttr: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rMaxRec: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&rNumDims)) return;
	  if (MOST(level)) {
	    sprintf (text, "rNumDims: %ld\n", (long) rNumDims);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "NumZvars: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "UIRhead: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuC: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuD: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuE: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  for (dimN = 0; dimN < rNumDims; dimN++) {
	     if (!Read4(CDFfp,&int32)) return;
	     if (MOST(level)) {
	       sprintf (text, " rDimSizes[%d]: %ld\n", dimN, (long) int32);
	       WriteOut (OUTfp, text);
	     }
	  }
	  usedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * Attribute Descriptor Record (ADR).
        **********************************************************************/
        case ADR_: {
	  char attrName[CDF_ATTR_NAME_LEN+1];
	  ADRcount++;
	  ADRbytes += recordSize;
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "ADRnext: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "AgrEDRhead: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "Scope: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "Num: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "NumRentries: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "MaxRentry: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuA: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "AzEDRhead: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "NumZentries: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "MaxZentry: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuE: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!ReadBytes(CDFfp,attrName,(Int32)CDF_ATTR_NAME_LEN)) return;
	  attrName[CDF_ATTR_NAME_LEN] = NUL;
	  if (MOST(level)) {
	    char delim = PickDelimiter (attrName, strlen(attrName));
	    sprintf (text, "Name: %c%s%c\n", delim, attrName, delim);
	    WriteOut (OUTfp, text);
	  }
	  usedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * Attribute Entry Descriptor Record (AgrEDR or AzEDR).
        **********************************************************************/
        case AgrEDR_:
        case AzEDR_: {
	  Logical zEntry = (recordType == AzEDR_);
	  Int32 dataType, numElems; void *valuePtr;
	  if (zEntry) {
	    AzEDRcount++;
	    AzEDRbytes += recordSize;
	  }
	  else {
	    AgrEDRcount++;
	    AgrEDRbytes += recordSize;
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "A%sEDRnext: %s\n",
		     BOO(zEntry,"z","gr"), FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "AttrNum: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&dataType)) return;
	  if (MOST(level)) {
	    sprintf (text, "DataType: %ld (%s)\n",
		     (long) dataType, DataTypeToken(dataType));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "Num: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&numElems)) return;
	  if (MOST(level)) {
	    sprintf (text, "NumElems: %ld\n", (long) numElems);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuA: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuB: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuC: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuD: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuE: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  nBytes = CDFelemSize((long)dataType) * numElems;
	  valuePtr = AllocateMemory ((size_t) nBytes, FatalError);
	  if (!ReadBytes(CDFfp,valuePtr,nBytes)) {
	    FreeMemory (valuePtr, FatalError);
	    return;
	  }
	  if (MOST(level)) {
	    ConvertBuffer ((long) encoding, HOST_DECODING, NEGtoPOSfp0off,
			   (long) dataType, (long) numElems, valuePtr,
			   valuePtr);
	    strcpyX (text, "Value: ", MAX_SCREENLINE_LEN);
	    EncodeValuesFormat ((long) dataType, (long) numElems, valuePtr,
			        EofS(text), NULL, 0,
			        MAX_SCREENLINE_LEN - strlen(text),
				EPOCH0_STYLE);
	    strcatX (text, "\n", MAX_SCREENLINE_LEN);
	    WriteOut (OUTfp, text);
	  }
	  FreeMemory (valuePtr, FatalError);
	  usedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * Variable Descriptor Record (rVDR or zVDR).
        **********************************************************************/
        case rVDR_:
        case zVDR_: {
	  Logical zVar = (recordType == zVDR_);
	  Int32 dataType, varFlags, numElems, numDims;
	  char varName[CDF_VAR_NAME_LEN+1]; void *padValue;
	  if (zVar) {
	    zVDRcount++;
	    zVDRbytes += recordSize;
	  }
	  else {
	    rVDRcount++;
	    rVDRbytes += recordSize;
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "%sVDRnext: %s\n", BOO(zVar,"z","r"),
		     FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&dataType)) return;
	  if (MOST(level)) {
	    sprintf (text, "DataType: %ld (%s)\n",
		     (long) dataType, DataTypeToken(dataType));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "MaxRec: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "VXRhead: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (showIndexing && int32 != ZERO_OFFSET) {
	    long savedOffset = V_tell (CDFfp);
	    WriteOut (OUTfp, "\n");
	    DumpVXR (CDFfp, int32, 5);
	    WriteOut (OUTfp, "\n");
	    if (!SeekTo(CDFfp,savedOffset,vSEEK_SET)) return;
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "VXRtail: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&varFlags)) return;
	  if (MOST(level)) {
	    sprintf (text, "Flags: 0x%lX (", (long) varFlags);
	    sprintf (EofS(text), "%sVARY,",
		     BOO(RECvaryBITset(varFlags),"","NO"));
	    sprintf (EofS(text), "%sPadValue,",
		     BOO(PADvalueBITset(varFlags),"","No"));
	    sprintf (EofS(text), "%sSparseArrays,",
		     BOO(SPARSEarraysBITset(varFlags),"","No"));
	    sprintf (EofS(text), "%sCompression)\n",
		     BOO(VARcompressionBITset(varFlags),"","No"));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "sRecords: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuB: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuC: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuF: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (VDRwastedSpace) {
	    Byte wastedSpace[VDR_WASTED_SIZE];
	    if (!ReadBytes(CDFfp,wastedSpace,(Int32)VDR_WASTED_SIZE)) return;
	    if (MOST(level)) {
	      WriteOut (OUTfp, "Skipping over wasted space...\n");
	    }
	  }
	  if (!Read4(CDFfp,&numElems)) return;
	  if (MOST(level)) {
	    sprintf (text, "NumElems: %ld\n", (long) numElems);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "Num: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "CPRorSPRoffset: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "BlockingFactor: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!ReadBytes(CDFfp,varName,(Int32)CDF_VAR_NAME_LEN)) return;
	  varName[CDF_VAR_NAME_LEN] = NUL;
	  if (MOST(level)) {
	    char delim = PickDelimiter (varName, strlen(varName));
	    sprintf (text, "Name: %c%s%c\n", delim, varName, delim);
	    WriteOut (OUTfp, text);
	  }
	  if (zVar) {
	    if (!Read4(CDFfp,&numDims)) return;
	    if (MOST(level)) {
	      sprintf (text, "zNumDims: %ld\n", (long) numDims);
	      WriteOut (OUTfp, text);
	    }
	    for (dimN = 0; dimN < numDims; dimN++) {
	       if (!Read4(CDFfp,&int32)) return;
	       if (MOST(level)) {
	         sprintf (text, " zDimSizes[%d]: %ld\n", dimN, (long) int32);
	         WriteOut (OUTfp, text);
	       }
	    }
	  }
	  else
	    numDims = rNumDims;
	  for (dimN = 0; dimN < numDims; dimN++) {
	     if (!Read4(CDFfp,&int32)) return;
	     if (MOST(level)) {
	       sprintf (text, "  DimVarys[%d]: %ld\n", dimN, (long) int32);
	       WriteOut (OUTfp, text);
	     }
	  }
	  if (BITSET(varFlags,VDR_PADVALUE_BIT)) {
	    nBytes = CDFelemSize((long)dataType) * numElems;
	    padValue = AllocateMemory ((size_t) nBytes, FatalError);
	    if (!ReadBytes(CDFfp,padValue,nBytes)) {
	      FreeMemory (padValue, FatalError);
	      return;
	    }
	    if (MOST(level)) {
	      int i;
	      strcpyX (text, "PadValue: ", MAX_SCREENLINE_LEN);
	      strcatX (text, " (0x", MAX_SCREENLINE_LEN);
	      for (i = (int) (nBytes - 1); i >= 0; i--) {
		 sprintf (EofS(text), "%02X", (int) ((Byte *)padValue)[i]);
	      }
	      strcatX (text, ") ", MAX_SCREENLINE_LEN);
	      ConvertBuffer ((long) encoding, HOST_DECODING, NEGtoPOSfp0off,
			     (long) dataType, (long) numElems, padValue,
			     padValue);
	      EncodeValuesFormat ((long) dataType, (long) numElems,
				  padValue, EofS(text), NULL, 0,
				  MAX_SCREENLINE_LEN - strlen(text),
				  EPOCH0_STYLE);
	      strcatX (text, "\n", MAX_SCREENLINE_LEN);
	      WriteOut (OUTfp, text);
	    }
	    FreeMemory (padValue, FatalError);
	  }
	  usedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * Variable indeX Record (VXR).
        **********************************************************************/
        case VXR_: {
	  Int32 nEntries, firstRec[MAX_VXR_ENTRIES]; int entryN;
	  Int32 lastRec[MAX_VXR_ENTRIES], VVRoffset[MAX_VXR_ENTRIES];
	  VXRcount++;
	  VXRbytes += recordSize;
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "VXRnext: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&nEntries)) return;
	  if (MOST(level)) {
	    sprintf (text, "Nentries: %ld\n", (long) nEntries);
	    WriteOut (OUTfp, text);
	  }
	  if (nEntries > MAX_VXR_ENTRIES) {
	    WriteOut (OUTfp, "MAX_VXR_ENTRIES exceeded.");
	    nEntries = MAX_VXR_ENTRIES;
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "NusedEntries: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4s(CDFfp,firstRec,(int)nEntries)) return;
	  if (!Read4s(CDFfp,lastRec,(int)nEntries)) return;
	  if (!Read4s(CDFfp,VVRoffset,(int)nEntries)) return;
	  if (MOST(level)) {
	    WriteOut (OUTfp, "\n  Entry  FirstRec  LastRec      Offset\n");
	    for (entryN = 0; entryN < nEntries; entryN++) {
	       sprintf (text, "     %2d   %7ld  %7ld  %s\n", entryN,
		        (long) firstRec[entryN], (long) lastRec[entryN],
		        FileOffset(VVRoffset[entryN]));
	       WriteOut (OUTfp, text);
	    }
	  }
	  usedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * Variable Values Record (VVR).
        **********************************************************************/
        case VVR_: {
	  uByte value; Int32 j;
	  VVRcount++;
	  VVRbytes += recordSize;
	  nBytes = recordSize - VVR_BASE_SIZE;
	  if (FULL(level)) {
	    for (j = 0; j < nBytes; j++) {
	       if (j % BYTESperLINE == 0) WriteOut (OUTfp, "\n  ");
	       if (!ReadBytes(CDFfp,&value,(Int32)1)) return;
	       sprintf (text, "%02X", (uInt) value);
	       WriteOut (OUTfp, text);
	    }
	    WriteOut (OUTfp, "\n");
	  }
	  else {
	    for (j = 0; j < nBytes; j++) {
	       if (!ReadBytes(CDFfp,&value,(Int32)1)) return;
	    }
	  }
	  usedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * Compressed Variable Values Record (CVVR).
        **********************************************************************/
        case CVVR_: {
	  uByte value; Int32 j;
	  CVVRcount++;
	  CVVRbytes += recordSize;
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "CPRoffset: %s\n", FileOffset(int32));
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "cSize: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  nBytes = recordSize - CVVR_BASE_SIZE;
	  if (FULL(level)) {
	    for (j = 0; j < nBytes; j++) {
	       if (j % BYTESperLINE == 0) WriteOut (OUTfp, "\n  ");
	       if (!ReadBytes(CDFfp,&value,(Int32)1)) return;
	       sprintf (text, "%02X", (uInt) value);
	       WriteOut (OUTfp, text);
	    }
	    WriteOut (OUTfp, "\n");
	  }
	  else {
	    for (j = 0; j < nBytes; j++) {
	       if (!ReadBytes(CDFfp,&value,(Int32)1)) return;
	    }
	  }
	  usedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * Compression Parameters Record (CPR).
        **********************************************************************/
        case CPR_: {
	  Int32 pCount; int parmN;
	  CPRcount++;
	  CPRbytes += recordSize;
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "cType: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuA: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&pCount)) return;
	  if (MOST(level)) {
	    sprintf (text, "pCount: %ld\n", (long) pCount);
	    WriteOut (OUTfp, text);
	  }
	  if (pCount > CDF_MAX_PARMS) {
	    WriteOut (OUTfp, "\nCDF_MAX_PARMS exceeded.\n");
	    pCount = CDF_MAX_PARMS;
	  }
	  for (parmN = 0; parmN < pCount; parmN++) {
	     if (!Read4(CDFfp,&int32)) return;
	     if (MOST(level)) {
	       sprintf (text, "  cParms[%d]: %ld\n", parmN, (long) int32);
	       WriteOut (OUTfp, text);
	     }
	  }
	  usedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * Sparseness Parameters Record (SPR).
        **********************************************************************/
        case SPR_: {
	  Int32 pCount; int parmN;
	  SPRcount++;
	  SPRbytes += recordSize;
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "sArraysType: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&int32)) return;
	  if (MOST(level)) {
	    sprintf (text, "rfuA: %ld\n", (long) int32);
	    WriteOut (OUTfp, text);
	  }
	  if (!Read4(CDFfp,&pCount)) return;
	  if (MOST(level)) {
	    sprintf (text, "pCount: %ld\n", (long) pCount);
	    WriteOut (OUTfp, text);
	  }
	  if (pCount > CDF_MAX_PARMS) {
	    WriteOut (OUTfp, "\nCDF_MAX_PARMS exceeded.\n");
	    pCount = CDF_MAX_PARMS;
	  }
	  for (parmN = 0; parmN < pCount; parmN++) {
	     if (!Read4(CDFfp,&int32)) return;
	     if (MOST(level)) {
	       sprintf (text, "  sArraysParms[%d]: %ld\n",
			parmN, (long) int32);
	       WriteOut (OUTfp, text);
	     }
	  }
	  usedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * Unused internal record
        **********************************************************************/
        case UIR_: {
	  UIRcount++;
	  UIRbytes += recordSize;
	  if (16 <= recordSize) {         /* Assumed a post-beta V2.5 CDF. */
	    if (!Read4(CDFfp,&int32)) return;
	    if (MOST(level)) {
	      sprintf (text, "Next: %s\n", FileOffset(int32));
	      WriteOut (OUTfp, text);
	    }
	    if (!Read4(CDFfp,&int32)) return;
	    if (MOST(level)) {
	      sprintf (text, "Prev: %s\n", FileOffset(int32));
	      WriteOut (OUTfp, text);
	    }
	    nBytes = recordSize - 16;
	    if (nBytes > 0) {
	      Byte byte; Int32 k;
	      for (k = 0; k < nBytes; k++) {
		 if (!ReadBytes(CDFfp,&byte,(Int32)1)) return;
	      }
	    }
	  }
	  else {		/* Must be a beta V2.5 CDF (or earlies). */
	    nBytes = recordSize - 8;
	    if (nBytes > 0) {
	      Byte byte; Int32 k;
	      for (k = 0; k < nBytes; k++) {
		 if (!ReadBytes(CDFfp,&byte,(Int32)1)) return;
	      }
	    }
	  }
	  wastedBytes += recordSize;
	  break;
        }
        /**********************************************************************
        * Illegal record type.
        **********************************************************************/
        default: {
	  sprintf (text, "Illegal record type detected (%ld)\n",
		   (long) recordType);
	  WriteOut (OUTfp, text);
	  return;
        }
     }
     CHECKforABORTso
  }
}

/******************************************************************************
* DisplaySummary.
******************************************************************************/

static void DisplaySummary () {
  char text[132+1]; int width1, width2; long largestBytes;
  long IRcount = CCRcount + CDRcount + GDRcount + ADRcount + AgrEDRcount +
		 AzEDRcount + rVDRcount + zVDRcount + VXRcount + VVRcount +
		 CVVRcount + CPRcount + SPRcount + UIRcount;
  long totalBytes = usedBytes + wastedBytes;
  double pctUsed = 100.0 * (((double) usedBytes) / ((double) totalBytes));
  double pctWasted = 100.0 * (((double) wastedBytes) / ((double) totalBytes));
  double pctCCR = 100.0 * (((double) CCRbytes) / ((double) totalBytes));
  double pctCDR = 100.0 * (((double) CDRbytes) / ((double) totalBytes));
  double pctGDR = 100.0 * (((double) GDRbytes) / ((double) totalBytes));
  double pctADR = 100.0 * (((double) ADRbytes) / ((double) totalBytes));
  double pctAgrEDR = 100.0 * (((double) AgrEDRbytes) / ((double) totalBytes));
  double pctAzEDR = 100.0 * (((double) AzEDRbytes) / ((double) totalBytes));
  double pct_rVDR = 100.0 * (((double) rVDRbytes) / ((double) totalBytes));
  double pct_zVDR = 100.0 * (((double) zVDRbytes) / ((double) totalBytes));
  double pctVXR = 100.0 * (((double) VXRbytes) / ((double) totalBytes));
  double pctVVR = 100.0 * (((double) VVRbytes) / ((double) totalBytes));
  double pctCVVR = 100.0 * (((double) CVVRbytes) / ((double) totalBytes));
  double pctCPR = 100.0 * (((double) CPRbytes) / ((double) totalBytes));
  double pctSPR = 100.0 * (((double) SPRbytes) / ((double) totalBytes));
  double pctUIR = 100.0 * (((double) UIRbytes) / ((double) totalBytes));
  width1 = LongWidth (totalBytes);
  largestBytes = 0;
  largestBytes = MAXIMUM(largestBytes,CCRbytes);
  largestBytes = MAXIMUM(largestBytes,CDRbytes);
  largestBytes = MAXIMUM(largestBytes,GDRbytes);
  largestBytes = MAXIMUM(largestBytes,ADRbytes);
  largestBytes = MAXIMUM(largestBytes,AgrEDRbytes);
  largestBytes = MAXIMUM(largestBytes,AzEDRbytes);
  largestBytes = MAXIMUM(largestBytes,rVDRbytes);
  largestBytes = MAXIMUM(largestBytes,zVDRbytes);
  largestBytes = MAXIMUM(largestBytes,VXRbytes);
  largestBytes = MAXIMUM(largestBytes,VVRbytes);
  largestBytes = MAXIMUM(largestBytes,CVVRbytes);
  largestBytes = MAXIMUM(largestBytes,CPRbytes);
  largestBytes = MAXIMUM(largestBytes,SPRbytes);
  largestBytes = MAXIMUM(largestBytes,UIRbytes);
  width2 = LongWidth (largestBytes);
  WriteOut (OUTfp, "\n\nSummary...");
  sprintf (text, "\n\n  Total bytes: %*ld", width1, totalBytes);
#if defined(vms)
  sprintf (EofS(text), " ,%ld blocks\n", (long) (((totalBytes-1)/512)+1));
#else
  strcatX (text, "\n", 132);
#endif
  WriteOut (OUTfp, text);
  sprintf (text, "   Used bytes: %*ld, %7.3f%%\n",
	   width1, usedBytes, pctUsed);
  WriteOut (OUTfp, text);
  sprintf (text, " Unused bytes: %*ld, %7.3f%%\n\n",
	   width1, wastedBytes, pctWasted);
  WriteOut (OUTfp, text);
  sprintf (text, "     IR count: %*ld\n\n",
	   width1, IRcount);
  WriteOut (OUTfp, text);
  sprintf (text, "    CCR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, CCRcount, width2, CCRbytes, pctCCR);
  WriteOut (OUTfp, text);
  sprintf (text, "    CDR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, CDRcount, width2, CDRbytes, pctCDR);
  WriteOut (OUTfp, text);
  sprintf (text, "    GDR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, GDRcount, width2, GDRbytes, pctGDR);
  WriteOut (OUTfp, text);
  sprintf (text, "    ADR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, ADRcount, width2, ADRbytes, pctADR);
  WriteOut (OUTfp, text);
  sprintf (text, " AgrEDR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, AgrEDRcount, width2, AgrEDRbytes, pctAgrEDR);
  WriteOut (OUTfp, text);
  sprintf (text, "  AzEDR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, AzEDRcount, width2, AzEDRbytes, pctAzEDR);
  WriteOut (OUTfp, text);
  sprintf (text, "   rVDR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, rVDRcount, width2, rVDRbytes, pct_rVDR);
  WriteOut (OUTfp, text);
  sprintf (text, "   zVDR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, zVDRcount, width2, zVDRbytes, pct_zVDR);
  WriteOut (OUTfp, text);
  sprintf (text, "    VXR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, VXRcount, width2, VXRbytes, pctVXR);
  WriteOut (OUTfp, text);
  sprintf (text, "    VVR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, VVRcount, width2, VVRbytes, pctVVR);
  WriteOut (OUTfp, text);
  sprintf (text, "   CVVR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, CVVRcount, width2, CVVRbytes, pctCVVR);
  WriteOut (OUTfp, text);
  sprintf (text, "    CPR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, CPRcount, width2, CPRbytes, pctCPR);
  WriteOut (OUTfp, text);
  sprintf (text, "    SPR count: %*ld, %*ld bytes, %6.3f%%\n",
	   width1, SPRcount, width2, SPRbytes, pctSPR);
  WriteOut (OUTfp, text);
  sprintf (text, "    UIR count: %*ld, %*ld bytes, %6.3f%%\n\n\n",
	   width1, UIRcount, width2, UIRbytes, pctUIR);
  WriteOut (OUTfp, text);
  return;
}

/******************************************************************************
* DumpVXR.
******************************************************************************/

static void DumpVXR (CDFfp, vxrOffset, indent)
vFILE *CDFfp;
Int32 vxrOffset;
int indent;
{
  char text[MAX_SCREENLINE_LEN+1];
  struct VXRstruct VXR; int entryN;
  Int32 irType;
  ReadVXR (CDFfp, vxrOffset,
	   VXR_RECORD, &VXR,
	   VXR_NULL);
  CpyNcharacters (text, indent, ' ');
  sprintf (EofS(text), "VXR @ %s (%ld/%ld)\n",
	   FileOffset(vxrOffset), VXR.NusedEntries, VXR.Nentries);
  WriteOut (OUTfp, text);
  for (entryN = 0; entryN < VXR.Nentries; entryN++) {
     CpyNcharacters (text, indent, ' ');
     if (VXR.First[entryN] == NO_RECORD)
       strcatX (text, "-------- ", MAX_SCREENLINE_LEN);
     else
       sprintf (EofS(text), "%08ld ", (long) VXR.First[entryN]);
     if (VXR.Last[entryN] == NO_RECORD)
       strcatX (text, "-------- ", MAX_SCREENLINE_LEN);
     else
       sprintf (EofS(text), "%08ld ", (long) VXR.Last[entryN]);
     if (VXR.Offset[entryN] == NO_OFFSET)
       strcatX (text, "----------", MAX_SCREENLINE_LEN);
     else
       strcatX (text, FileOffset(VXR.Offset[entryN]), MAX_SCREENLINE_LEN);
     strcatX (text, "\n", MAX_SCREENLINE_LEN);
     WriteOut (OUTfp, text);
     if (VXR.Offset[entryN] != NO_OFFSET) {
       ReadIrType (CDFfp, VXR.Offset[entryN], &irType);
       switch (irType) {
	 case VVR_:
	 case CVVR_:
	   break;
	 case VXR_:
	   DumpVXR (CDFfp, VXR.Offset[entryN], indent + 5);
	   break;
	 default:
	   break;
       }
     }
  }
  if (VXR.VXRnext != ZERO_OFFSET) DumpVXR (CDFfp, VXR.VXRnext, indent);
  return;
}

/******************************************************************************
* FileOffset.
******************************************************************************/

static char *FileOffset (offset)
Int32 offset;
{
  switch (radix) {
    case 10: return Deci32(offset);
    case 16: return Hex32(offset);
    default: return "?";
  }
}

/******************************************************************************
* Deci32.
******************************************************************************/

static char *Deci32 (value)
Int32 value;
{
  static char text[3][10+1];
  static int which = -1;
  which = (which + 1) % 3;
  sprintf (text[which], "%010ld", (long) value);
  return text[which];
}

/******************************************************************************
* Hex32.
******************************************************************************/

static char *Hex32 (value)
Int32 value;
{
  static char text[3][10+1];
  static int which = -1;
  which = (which + 1) % 3;
  sprintf (text[which], Int32hexFORMAT, value);
  return text[which];
}

/******************************************************************************
* SeekTo.
******************************************************************************/

static Logical SeekTo (fp, offset, how)
vFILE *fp;
long offset;
int how;
{
  if (!SEEKv(fp,offset,how)) {
    WriteOut (OUTfp, "\nSeek error.\n");
    return FALSE;
  }
  return TRUE;
}

/******************************************************************************
* Read4.
******************************************************************************/

static Logical Read4 (fp, value)
vFILE *fp;
Int32 *value;
{
  if (!Read32(fp,value)) {
    DisplayReadFailure (fp);
    return FALSE;
  }
  return TRUE;
}

/******************************************************************************
* Read4s.
******************************************************************************/

static Logical Read4s (fp, values, count)
vFILE *fp;
Int32 *values;
int count;
{
  int i;
  for (i = 0; i < count; i++) {
     if (!Read32(fp,&values[i])) return FALSE;
  }
  return TRUE;
}

/******************************************************************************
* ReadN.
******************************************************************************/

static Logical ReadBytes (fp, bytes, count)
vFILE *fp;
void *bytes;
Int32 count;
{
  if (!READv(bytes,(size_t)1,(size_t)count,fp)) {
    DisplayReadFailure (fp);
    return FALSE;
  }
  return TRUE;
}

/******************************************************************************
* DisplayReadFailure.
******************************************************************************/

static void DisplayReadFailure (fp)
vFILE *fp;
{
  if (V_error(fp)) {
    WriteOut (OUTfp, "\nRead failed - error from file system.\n");
    return;
  }
  if (V_eof(fp)) {
    WriteOut (OUTfp, BOO(EOFok,"\nEOF encountered.\n",
			       "\nUnexpected EOF encountered.\n"));
    return;
  }
  WriteOut (OUTfp, "\nUnknown read error - contact CDFsupport.\n");
  return;
}

/******************************************************************************
* DumpQOPs.
*    Returns TRUE if execution should continue.
******************************************************************************/

#if defined(mac)
Logical DumpQOPs (argC, argV)
int *argC;
char **argV[];
{
  DialogPtr dialogP;
  DialogRecord dRecord;
  WindowPtr behind = (WindowPtr) -1;
  ControlHandle controlHs[MAXIMUMin+1];
  Rect iRect;
  short itemN, iType;
  static Logical first = TRUE;
  char cancelTitle[] = "Cancel";
  static int dumpLevel = DEFAULTlevelDUMP;
  static Logical pageOutput = DEFAULTpageCVT;
  static Logical checkSizes = DEFAULTsizesDUMP;
  static Logical showSummary = DEFAULTsummaryDUMP;
  static Logical showIndexing = DEFAULTindexingDUMP;
  static Str255 CDFtext = "\p";
  static Str255 outText = "\p";
  /****************************************************************************
  * Create the dialog and get the control handles.
  ****************************************************************************/
  dialogP = GetNewDialog (QOPri, &dRecord, behind);
  for (itemN = 1; itemN <= MAXIMUMin; itemN++) {
     GetDItem (dialogP, itemN, &iType, (Handle *) &controlHs[itemN], &iRect);
  }
  /****************************************************************************
  * Set the control values.
  ****************************************************************************/
  SetIText ((Handle) controlHs[CDFTEXTin], CDFtext);
  SetIText ((Handle) controlHs[OUTTEXTin], outText);
  switch (dumpLevel) {
    case BRIEF_: SetCtlValue (controlHs[BRIEFin], 1); break;
    case MOST_: SetCtlValue (controlHs[MOSTin], 1); break;
    case FULL_: SetCtlValue (controlHs[FULLin], 1); break;
  }
  if (pageOutput) SetCtlValue (controlHs[PAGEin], 1);
  if (checkSizes) SetCtlValue (controlHs[SIZESin], 1);
  if (showSummary) SetCtlValue (controlHs[SUMMARYin], 1);
  if (showIndexing) SetCtlValue (controlHs[INDEXINGin], 1);
  SetDItem (dialogP, (short) ODBin, (short) userItem,
	    (Handle) OutlineDefaultButton, &iRect);
  /****************************************************************************
  * Change the "Quit" button to a "Cancel" button after the first time.
  ****************************************************************************/
  if (first)
    first = FALSE;
  else
    SetCTitle (controlHs[CANCELin], CtoPstr(cancelTitle));
  /****************************************************************************
  * Display the dialog and wait for user actions.
  ****************************************************************************/
  ShowWindow ((WindowPtr) dialogP);
  SetCursor (ARROW_CURSOR);
  for (;;) {
    ModalDialog (FilterDialogQOPso, &itemN);
    switch (itemN) {
      /************************************************************************
      * Ok.
      ************************************************************************/
      case OKin: {
	int n;
	/**********************************************************************
	* Get the value of each control.
	**********************************************************************/
	GetIText ((Handle) controlHs[CDFTEXTin], CDFtext);
	GetIText ((Handle) controlHs[OUTTEXTin], outText);
	if (GetCtlValue(controlHs[BRIEFin])) dumpLevel = BRIEF_;
	if (GetCtlValue(controlHs[MOSTin])) dumpLevel = MOST_;
	if (GetCtlValue(controlHs[FULLin])) dumpLevel = FULL_;
	pageOutput = GetCtlValue (controlHs[PAGEin]);
	checkSizes = GetCtlValue (controlHs[SIZESin]);
	showSummary = GetCtlValue (controlHs[SUMMARYin]);
	showIndexing = GetCtlValue (controlHs[INDEXINGin]);
	/**********************************************************************
	* Build argc/argv.
	**********************************************************************/
	*argC = 6 + BOO(NULpString(CDFtext),0,1) +
		    BOO(NULpString(outText),0,2);
	*argV = (char **) AllocateMemory (*argC * sizeof(char *),
					  FatalError);
	n = 0;
	MAKEstrARGv (argV, n, pgmName)
	if (!NULpString(CDFtext)) {
	  PtoCstr (CDFtext);
	  MAKEstrARGv (argV, n, (char *) CDFtext)
	  CtoPstr ((char *) CDFtext);
	}
	switch (dumpLevel) {
	  case BRIEF_:
	    MAKEstrARGv (argV, n, "-brief")
	    break;
	  case MOST_:
	    MAKEstrARGv (argV, n, "-most")
	    break;
	  case FULL_:
	    MAKEstrARGv (argV, n, "-full")
	    break;
	}
	MAKEbooARGv (argV, n, pageOutput, "-page", "-nopage")
	MAKEbooARGv (argV, n, checkSizes, "-sizes", "-nosizes")
	MAKEbooARGv (argV, n, showSummary, "-summary", "-nosummary")
	MAKEbooARGv (argV, n, showIndexing, "-indexing", "-noindexing")
	if (!NULpString(outText)) {
	  MAKEstrARGv (argV, n, "-output")
	  PtoCstr (outText);
	  MAKEstrARGv (argV, n, (char *) outText)
	  CtoPstr ((char *) outText);
	}
	/**********************************************************************
	* Close the dialog and return.
	**********************************************************************/
	CloseDialog (dialogP);
	return TRUE;
      }
      /************************************************************************
      * Help.
      ************************************************************************/
      case HELPin: {
	int n;
	*argC = 1;
	*argV = (char **) AllocateMemory (*argC * sizeof(char *),
					  FatalError);
	n = 0;
	MAKEstrARGv (argV, n, pgmName)
	CloseDialog (dialogP);
	return TRUE;
      }
      /************************************************************************
      * Cancel.
      ************************************************************************/
      case CANCELin:
	CloseDialog (dialogP);
	return FALSE;
      /************************************************************************
      * Select CDF specification.
      ************************************************************************/
      case CDFSELECTin: {
	StandardFileReply CDFreply;
	char CDFpath[DU_MAX_PATH_LEN+1];
	StandardGetFile (FilterForCDFs, -1, NULL, &CDFreply);
	if (CDFreply.sfGood && !CDFreply.sfIsFolder && !CDFreply.sfIsVolume) {
	  BuildMacPath (&CDFreply.sfFile, CDFpath, TRUE);
	  CDFtext[0] = strlen (CDFpath);
	  strcpyX ((char *) &CDFtext[1], CDFpath, 255);
	  SetIText ((Handle) controlHs[CDFTEXTin], CDFtext);
	}
	break;
      }
      /************************************************************************
      * Select output file specification.
      *     The cursor is set because `StandardPutFile' leaves the cursor as
      * an iBeam (instead of returning it to what it was).
      ************************************************************************/
      case OUTSELECTin: {
	StandardFileReply outReply;
	char outPath[DU_MAX_PATH_LEN+1], prompt[] = "Enter output file:";
	StandardPutFile (CtoPstr(prompt), CtoPstr(""), &outReply);
	if (outReply.sfGood && !outReply.sfIsFolder && !outReply.sfIsVolume) {
	  BuildMacPath (&outReply.sfFile, outPath, TRUE);
	  outText[0] = strlen (outPath);
	  strcpyX ((char *) &outText[1], outPath, 255);
	  SetIText ((Handle) controlHs[OUTTEXTin], outText);
	}
	SetCursor (&(qd.arrow));
	break;
      }
      /************************************************************************
      * Check boxes.
      ************************************************************************/
      case SIZESin:
      case PAGEin:
      case SUMMARYin:
      case INDEXINGin:
	SetCtlValue (controlHs[itemN], BOO(GetCtlValue(controlHs[itemN]),0,1));
	break;
      /************************************************************************
      * Radio buttons.
      ************************************************************************/
      case BRIEFin:
	SetCtlValue (controlHs[BRIEFin], 1);
	SetCtlValue (controlHs[MOSTin], 0);
	SetCtlValue (controlHs[FULLin], 0);
	break;
      case MOSTin:
	SetCtlValue (controlHs[BRIEFin], 0);
	SetCtlValue (controlHs[MOSTin], 1);
	SetCtlValue (controlHs[FULLin], 0);
	break;
      case FULLin:
	SetCtlValue (controlHs[BRIEFin], 0);
	SetCtlValue (controlHs[MOSTin], 0);
	SetCtlValue (controlHs[FULLin], 1);
	break;
    }
  }
}
#endif
