/******************************************************************************
*
*  NSSDC/CDF                                                    CDFskeleton.
*
*  Version 1.4h, 27-Sep-96, Hughes STX.
*
*  Modification history:
*
*   V1.0  18-May-92, J Love     Original version.
*   V1.1  18-Sep-92, J Love     CDF V2.3 (shareable/NeXT/zVar).
*   V1.2  16-Nov-92, J Love     Check for strings split across two lines.
*   V1.3  24-Jan-94, J Love     CDF V2.4.
*   V1.3a  6-Feb-94, J Love     DEC Alpha/OpenVMS port.
*   V1.4  13-Dec-94, J Love     CDF V2.5.
*   V1.4a 11-Jan-95, J Love	Uppercase file extensions.
*   V1.4b 28-Feb-95, J Love	Pass `char' as `int'.
*   V1.4c  6-Apr-95, J Love	POSIX.
*   V1.4d 11-May-95, J Love	EPOCH styles.
*   V1.4e  9-Jun-95, J Love	catchrX.
*   V1.4f 19-Sep-95, J Love	CHECKforABORTso.
*   V1.4g 28-Sep-95, J Love	Macintosh dialog filtering.  Outline default
*				button.
*   V1.4h 27-Sep-96, J Love	Fixed bug in `CATchr' (forgot to add 1 to the
*				string being reallocated).
*
******************************************************************************/

#include "skt2cdf.h"

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

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

/******************************************************************************
* Global variables.
******************************************************************************/

FILE *SKTfp;
FILE *NRVfp;
Logical done;                   /* Set TRUE when `#end' encountered. */
Logical deleteIFexists;
Logical useFillval;
Logical negToPosFp0;
Logical mLog;
char line[MAX_SKTLINE_LEN+1+1];         /* + NL + NUL */
int lineN;                              /* Line number in skeleton table. */
char CDFpath[DU_MAX_PATH_LEN+1];
char *stream, *token, *string, *eValue, *location;
size_t streamZ, tokenZ, stringZ, eValueZ, locationZ;
char epoch[EPOCH_STRING_LEN+1];
Logical inString;
char stringDelim;
int stringLineN;
Logical hyphenORendBraceNext;
Logical startBraceFound;
Logical locationStarted;
int epochCount;
int dimCount;
int varyCount;
enum searchForENUM searchFor;
enum nextItemENUM nextItem;
Logical zVar;
Logical report[3];

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

MAIN {
#if defined(mac)
  strcpyX (pgmName, "SkeletonCDF", MAX_PROGRAM_NAME_LEN);
  MacExecuteSO (CreateSkeletonCDF, CreateSkeletonQOPs);
  if (FreeMemory(NULL,FatalError) > 0) DisplayWarning ("Abandoned buffers.");
  return EXIT_SUCCESS_;
#else
  Logical success;
  strcpyX (pgmName, "SkeletonCDF", MAX_PROGRAM_NAME_LEN);
  success = CreateSkeletonCDF (argc, argv);
  if (FreeMemory(NULL,FatalError) > 0) DisplayWarning ("Abandoned buffers.");
  return BOO(success,EXIT_SUCCESS_,EXIT_FAILURE_);
#endif
}

/******************************************************************************
* CreateSkeletonCDF.
******************************************************************************/

Logical CreateSkeletonCDF (argC, argV)
int argC;
char *argV[];
{
  char SKTpath[DU_MAX_PATH_LEN+1];
  char SKTpathX[DU_MAX_PATH_LEN+1];
  char NRVpathX[DU_MAX_PATH_LEN+1];
  Logical comment;
  int i;
  QOP *qop;
  Logical qopError = FALSE;
  static char *validQuals[] = { "cdf*", "log", "nolog", "delete", "nodelete",
				"fillval", "nofillval", "report", "neg2posfp0",
				"noneg2posfp0", NULL };
  static int optRequired[] = { TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
			       TRUE, FALSE, FALSE, 0 };
  static char *reportTokens[] = { "errors", "warnings", "informationals" };

  /****************************************************************************
  * Parse qualifiers/options/parameters.
  ****************************************************************************/

  switch (argC) {
    case 1:
      PageOLH ("skt2cdf.olh", argV[0]);
      return TRUE;
    default:
      qop = Qop (argC, argV, validQuals, optRequired);
      if (qop == NULL) return FALSE;

      /************************************************************************
      * Skeleton table pathname.
      ************************************************************************/

      if (qop->Nparms < 1) {
	DisplayError ("Missing parameter.");
	qopError = TRUE;
      }
      else
	strcpyX (SKTpath, qop->parms[SKTPATHparm], DU_MAX_PATH_LEN);

      /************************************************************************
      * Check for CDF pathname qualifier.
      ************************************************************************/

      if (qop->qualEntered[CDFPATHqual])
	strcpyX (CDFpath, qop->qualOpt[CDFPATHqual], DU_MAX_PATH_LEN);
      else
	CDFpath[0] = NUL;

      /************************************************************************
      * Check for `report' qualifier.  If absent, use defaults.
      ************************************************************************/

      if (qop->qualEntered[REPORTqual]) {
	if (!ParseOptionList(3,reportTokens,qop->qualOpt[REPORTqual],report)) {
	  DisplayError ("Illegal list of `report' options.");
	  qopError = TRUE;
	}
      }
      else {
	report[ERRORs] = REPORTerrorsDEFAULT;
	report[WARNs] = REPORTwarningsDEFAULT;
	report[INFOs] = REPORTinfosDEFAULT;
      }

      /************************************************************************
      * Check for `log', `delete', `fillval', and `neg2posfp0' qualifiers.
      ************************************************************************/

      qopError = qopError | !TFqualifier(qop,&mLog,LOGqual,NOLOGqual,
					 DEFAULTlogSKT2CDF,"log");
      qopError = qopError | !TFqualifier(qop,&deleteIFexists,DELqual,NODELqual,
					 DEFAULTdeleteSKT2CDF,"delete");
      qopError = qopError | !TFqualifier(qop,&useFillval,FILLVALqual,
					 NOFILLVALqual,DEFAULTfillvalSKT2CDF,
					 "fillval");
      qopError = qopError | !TFqualifier(qop,&negToPosFp0,NEG2POSqual,
					 NONEG2POSqual,DEFAULT_NEGtoPOSfp0,
					 "neg2posfp0");

      /************************************************************************
      * Free QOP memory and check for an error.
      ************************************************************************/

      FreeMemory (qop, FatalError);
      if (qopError) return FALSE;

      break;
  }

  /****************************************************************************
  * Initialize.
  ****************************************************************************/

  done = FALSE;
  lineN = 0;
  inString = FALSE;

  /****************************************************************************
  * Open skeleton table.
  ****************************************************************************/

  ExpandPath (SKTpath, SKTpathX);
  strcatX (SKTpathX, ".skt", DU_MAX_PATH_LEN);
  SKTfp = fopen (SKTpathX, "r");
  if (SKTfp == NULL) {
    ExpandPath (SKTpath, SKTpathX);
    strcatX (SKTpathX, ".skeleton_table", DU_MAX_PATH_LEN);
    SKTfp = fopen (SKTpathX, "r");
    if (SKTfp == NULL) {
      ExpandPath (SKTpath, SKTpathX);
      strcatX (SKTpathX, ".SKT", DU_MAX_PATH_LEN);
      SKTfp = fopen (SKTpathX, "r");
      if (SKTfp == NULL) {
	ExpandPath (SKTpath, SKTpathX);
	strcatX (SKTpathX, ".skt;1", DU_MAX_PATH_LEN);
	SKTfp = fopen (SKTpathX, "r");
	if (SKTfp == NULL) {
	  ExpandPath (SKTpath, SKTpathX);
	  strcatX (SKTpathX, ".SKT;1", DU_MAX_PATH_LEN);
	  SKTfp = fopen (SKTpathX, "r");
	  if (SKTfp == NULL) {
	    WriteOut (stdout, "Unable to open skeleton table \"");
	    WriteOut (stdout, SKTpath);
	    WriteOut (stdout, "\".\n");
	    return FALSE;
	  }
	}
      }
    }
  }

  /****************************************************************************
  * Open NRV file (may not exist).
  ****************************************************************************/

  ExpandPath (SKTpath, NRVpathX);
  strcatX (NRVpathX, ".nrv", DU_MAX_PATH_LEN);
  NRVfp = fopen (NRVpathX, "r");
  if (NRVfp == NULL) {
    ExpandPath (SKTpath, NRVpathX);
    strcatX (NRVpathX, ".nrv_file", DU_MAX_PATH_LEN);
    NRVfp = fopen (NRVpathX, "r");
    if (NRVfp == NULL) {
      ExpandPath (SKTpath, NRVpathX);
      strcatX (NRVpathX, ".NRV", DU_MAX_PATH_LEN);
      NRVfp = fopen (NRVpathX, "r");
      if (NRVfp == NULL) {
	ExpandPath (SKTpath, NRVpathX);
	strcatX (NRVpathX, ".nrv;1", DU_MAX_PATH_LEN);
	NRVfp = fopen (NRVpathX, "r");
	if (NRVfp == NULL) {
	  ExpandPath (SKTpath, NRVpathX);
	  strcatX (NRVpathX, ".NRV;1", DU_MAX_PATH_LEN);
	  NRVfp = fopen (NRVpathX, "r");
	}
      }
    }
  }

  if (NRVfp != NULL) {
    WriteOut (stdout, "\n");
    WriteOut (stdout,
	      "***********************************************************\n");
    WriteOut (stdout,
	      "* NRV files are no longer supported (no one seemed to use *\n");
    WriteOut (stdout,
	      "* them).  If this is a problem, contact CDFSUPPORT (we'll *\n");
    WriteOut (stdout,
	      "* work out something).  The NRV file is being ignored.    *\n");
    WriteOut (stdout,
	      "***********************************************************\n");
    WriteOut (stdout, "\n");
    fclose (NRVfp);
  }

  /****************************************************************************
  * Initialize character strings which may increase in size.
  ****************************************************************************/

  AllocateGrowingStrings ();

  /****************************************************************************
  * Scan skeleton table (creating the specified CDF).
  ****************************************************************************/

  NextSearchItem (TOKEN, HEADERmark);

  while (fgets(line,MAX_SKTLINE_LEN,SKTfp) != NULL) {
    lineN++;
    for (i = 0, comment = FALSE; line[i] != NUL && !comment; i++) {
       switch (line[i]) {
	 case Nl:
	   if (!CharStream((int)' ')) {
	     FreeGrowingStrings ();
	     return done;
	   }
	   break;
	 case '!':
	   if (inString) {
	     if (!CharStream((int)line[i])) {
	       FreeGrowingStrings ();
	       return done;
	     }
	   }
	   else {
	     if (!CharStream((int)' ')) {
	       FreeGrowingStrings ();
	       return done;
	     }
	     comment = TRUE;
	   }
	   break;
	 default:
	   if (!CharStream((int)line[i])) {
	     FreeGrowingStrings ();
	     return done;
	   }
	   break;
       }
    }
    CHECKforABORTso
  }

  /****************************************************************************
  * Call `CharStream' with a blank just in case the last line of the skeleton
  * table doesn't have a terminating newline character.  This will cause the
  * `#end' token to be processed.
  ****************************************************************************/

  if (!CharStream((int)' ')) {
    FreeGrowingStrings ();
    return done;
  }

  /****************************************************************************
  * Check if an unexpected EOF occurred.
  ****************************************************************************/

  if (!done) ParseError ("Unexpected end-of-file.");

  FreeGrowingStrings ();
  return done;
}

/******************************************************************************
* CharStream.
*    Returns TRUE if the scanning of the skeleton table should continue.
* Returns FALSE if scanning is complete (either because of an error detected
* or the end of the skeleton table has been reached).
******************************************************************************/

Logical CharStream (chr)
int chr;
{
  CATchr (&stream, chr, &streamZ, BASE_STREAM_SIZE);
  switch (searchFor) {
    case TOKEN:
      if (IsSpaceChr(chr)) {
	if (token[0] != NUL)
	  if (!ItemStream(token)) return FALSE;
      }
      else
	CATchr (&token, chr, &tokenZ, BASE_TOKEN_SIZE);
      break;

    case DELIMSTRING:
      if (!IsSpaceChr(chr))
	if (!inString) {
	  stringDelim = (char) chr;
	  stringLineN = lineN;
	  inString = TRUE;
	}
	else {
	  if (lineN != stringLineN) {
	    ParseError ("String/substring split across lines.");
	    return FALSE;
	  }
	  if ((char) chr == stringDelim) {
	    inString = FALSE;
	    if (!ItemStream(string)) return FALSE;
	  }
	  else
	    CATchr (&string, chr, &stringZ, BASE_STRING_SIZE);
	}
      else
	if (inString) {
	  if (lineN != stringLineN) {
	    ParseError ("String/substring split across lines.");
	    return FALSE;
	  }
	  CATchr (&string, chr, &stringZ, BASE_STRING_SIZE);
	}
      break;

    case BRACEDnonChar:
      if (!IsSpaceChr(chr))
	if (startBraceFound)
	  if ((char) chr == '}') {
	    if (!ItemStream(eValue)) return FALSE;
	  }
	  else
	    CATchr (&eValue, chr, &eValueZ, BASE_EVALUE_SIZE);
	else
	  if ((char) chr == '{')
	    startBraceFound = TRUE;
	  else {
	    ItemError1 ("{", TempCharStr(chr));
	    return FALSE;
	  }
      else
	if (startBraceFound) CATchr (&eValue, chr, &eValueZ, BASE_EVALUE_SIZE);
      break;

    case BRACEDchar:
      if (!IsSpaceChr(chr))
	if (!startBraceFound)
	  if ((char) chr == '{')
	    startBraceFound = TRUE;
	  else {
	    ItemError1 ("{", TempCharStr(chr));
	    return FALSE;
	  }
	else
	  if (inString) {
	    if (lineN != stringLineN) {
	      ParseError ("String/substring split across lines.");
	      return FALSE;
	    }
	    if ((char) chr == stringDelim) {
	      inString = FALSE;
	      hyphenORendBraceNext = TRUE;
	    }
	    else
	      CATchr (&eValue, chr, &eValueZ, BASE_EVALUE_SIZE);
	  }
	  else
	    switch ((char)chr) {
	      case '}':
		if (!hyphenORendBraceNext) {
		  ParseError ("Unexpected closing brace (\"}\").");
		  return FALSE;
		}
		if (!ItemStream(eValue)) return FALSE;
		break;
	      case '-':
		if (hyphenORendBraceNext)
		  hyphenORendBraceNext = FALSE;
		else {
		  ItemError1 ("<delimiter>", "-");
		  return FALSE;
		}
		break;
	      default:
		if (hyphenORendBraceNext) {
		  ItemError2 ("-", "}", TempCharStr(chr));
		  return FALSE;
		}
		stringDelim = (char) chr;
		stringLineN = lineN;
		inString = TRUE;
		break;
	    }
      else
	if (startBraceFound)
	  if (inString) {
	    if (lineN != stringLineN) {
	      ParseError ("String/substring split across lines.");
	      return FALSE;
	    }
	    CATchr (&eValue, chr, &eValueZ, BASE_EVALUE_SIZE);
	  }
      break;

    case VALLOCATION:
      if (!IsSpaceChr(chr))
	if (locationStarted) {
	  CATchr (&location, chr, &locationZ, BASE_LOCATION_SIZE);
	  if ((char) chr == ']') {
	    if (!ItemStream(location)) return FALSE;
	  }
	}
	else
	  if ((char) chr == '[' || IsDigitChr(chr)) {
	    CATchr (&location, chr, &locationZ, BASE_LOCATION_SIZE);
	    locationStarted = TRUE;
	  }
	  else {
	    ItemError2 ("[", "<record-number", TempCharStr(chr));
	    return FALSE;
	  }
      else
	if (locationStarted) CATchr (&location, chr, &locationZ,
				       BASE_LOCATION_SIZE);
      break;

    case VAREPOCH:
      if (epochCount == 0) {
	if (!IsSpaceChr(chr)) {
	  catchrX (epoch, chr, EPOCH_STRING_LEN);
	  epochCount = 1;
	}
      }
      else {
	catchrX (epoch, chr, EPOCH_STRING_LEN);
	epochCount++;
	if (epochCount == EPOCH_STRING_LEN)
	  if (!ItemStream(epoch)) return FALSE;
      }
      break;

    default:
      ParseError ("** Internal error, contact CDFSUPPORT **");
      return FALSE;
  }

  return TRUE;
}

/******************************************************************************
* ItemStream.
*    Returns TRUE if the scanning of the skeleton table should continue.
* Returns FALSE if scanning is complete (either because of an error detected
* or the end of the skeleton table has been reached).
******************************************************************************/

Logical ItemStream (item)
char *item;
{
  static long format;
  static long majority;
  static long encoding;
  static long zNumDims;
  static long rNumDims;
  static long entryNum;
  static long vDataType;
  static long eDataType;
  static long vNumElems;
  static long recVary;
  static long dimVarys[CDF_MAX_DIMS];
  static long zDimSizes[CDF_MAX_DIMS];
  static long rDimSizes[CDF_MAX_DIMS];
  static char gAttrName[CDF_ATTR_NAME_LEN+1];
  static char vAttrName[CDF_ATTR_NAME_LEN+1];
  static char varName[CDF_VAR_NAME_LEN+1];
  CDFstatus status;

  switch (nextItem) {
    case HEADERmark:
      if (!strcmp(item,"#header")) {
	if (mLog) WriteOut (stdout, "Reading header section...\n");
	NextSearchItem (TOKEN, CDForOUTPUTmark);
      }
      else {
	ItemError1 ("#header", item);
	return FALSE;
      }
      break;

    case CDForOUTPUTmark:
      if (!strcmp(item,"CDF"))
	NextSearchItem (TOKEN, NAMEmark);
      else
	if (!strcmp(item,"OUTPUT"))
	  NextSearchItem (TOKEN, FILEmark);
	else {
	  ItemError2 ("CDF", "OUTPUT", item);
	  return FALSE;
	}
      break;

    case NAMEmark:
      if (!strcmp(item,"NAME:"))
	NextSearchItem (TOKEN, CDFPATHfield);
      else
	if (!strcmp(item,"NAME"))
	  NextSearchItem (TOKEN, NAMECOLONmark);
	else {
	  ItemError2 ("NAME:", "NAME", item);
	  return FALSE;
	}
      break;

    case NAMECOLONmark:
      if (!strcmp(item,":"))
	NextSearchItem (TOKEN, CDFPATHfield);
      else {
	ItemError1 (":", item);
	return FALSE;
      }
      break;

    case FILEmark:
      if (!strcmp(item,"FILE"))
	NextSearchItem (TOKEN, NAMEmark);
      else {
	ItemError1 ("FILE", item);
	return FALSE;
      }
      break;

    case CDFPATHfield:
      if (CDFpath[0] == NUL) strcpyX (CDFpath, item, DU_MAX_PATH_LEN);
      NextSearchItem (TOKEN, DATAmark);
      break;

    case DATAmark:
      if (!strcmp(item,"DATA"))
	NextSearchItem (TOKEN, ENCODINGmark);
      else {
	ItemError1 ("DATA", item);
	return FALSE;
      }
      break;

    case ENCODINGmark:
      if (!strcmp(item,"ENCODING:"))
	NextSearchItem (TOKEN, ENCODINGfield);
      else
	if (!strcmp(item,"ENCODING"))
	  NextSearchItem (TOKEN, ENCODINGCOLONmark);
	else {
	  ItemError2 ("ENCODING:", "ENCODING", item);
	  return FALSE;
	}
      break;

    case ENCODINGCOLONmark:
      if (!strcmp(item,":"))
	NextSearchItem (TOKEN, ENCODINGfield);
      else {
	ItemError1 (":", item);
	return FALSE;
      }
      break;

    case ENCODINGfield:
      encoding = WhichEncoding (item);
      if (encoding == -1) {
	ItemError1 ("<encoding>", item);
	return FALSE;
      }
      else
	NextSearchItem (TOKEN, MAJORITYmark);
      break;

    case MAJORITYmark:
      if (!strcmp(item,"MAJORITY:"))
	NextSearchItem (TOKEN, MAJORITYfield);
      else
	if (!strcmp(item,"MAJORITY"))
	  NextSearchItem (TOKEN, MAJORITYCOLONmark);
	else {
	  ItemError2 ("MAJORITY:", "MAJORITY", item);
	  return FALSE;
	}
      break;

    case MAJORITYCOLONmark:
      if (!strcmp(item,":"))
	NextSearchItem (TOKEN, MAJORITYfield);
      else {
	ItemError1 (":", item);
	return FALSE;
      }
      break;

    case MAJORITYfield:
      majority = WhichMajority (item);
      if (majority == -1) {
	ItemError1 ("<majority>", item);
	return FALSE;
      }
      else
	NextSearchItem (TOKEN, FORMATmarkOrNUMVARSfield);
      break;

    case FORMATmarkOrNUMVARSfield:
      if (!strcmp(item,"FORMAT:"))
	NextSearchItem (TOKEN, FORMATfield);
      else
	if (!strcmp(item,"FORMAT"))
	  NextSearchItem (TOKEN, FORMATCOLONmark);
	else {
#if DEFAULT_TO_SINGLE_FILE
	  format = SINGLE_FILE;
#else
	  format = MULTI_FILE;
#endif
	  NextSearchItem (TOKEN, NUMGATTRSfield);  /* Assumed to be number of
						      variables/zVariables. */
	}
      break;

    case FORMATCOLONmark:
      if (!strcmp(item,":"))
	NextSearchItem (TOKEN, FORMATfield);
      else {
	ItemError1 (":", item);
	return FALSE;
      }
      break;

    case FORMATfield:
      format = WhichFormat (item);
      if (format == -1) {
	ItemError1 ("<format>", item);
	return FALSE;
      }
      else
	NextSearchItem (TOKEN, NUMVARSfield);
      break;

    case NUMVARSfield:
      NextSearchItem (TOKEN, NUMGATTRSfield);
      break;

    case NUMGATTRSfield:
      NextSearchItem (TOKEN, NUMVATTRSfield);
      break;

    case NUMVATTRSfield:
      NextSearchItem (TOKEN, NUMRECSfield);
      break;

    case NUMRECSfield:
      NextSearchItem (TOKEN, NUMDIMSfield);
      break;

    case NUMDIMSfield:
      if (sscanf(item,"%ld",&rNumDims) == 1) {
	if (rNumDims < 0 || rNumDims > CDF_MAX_DIMS) {
	  ItemError1 ("<num-dims>", item);
	  return FALSE;
	}
	else
	  if (rNumDims > 0) {
	    dimCount = 0;
	    NextSearchItem (TOKEN, DIMSIZEfield);
	  }
	  else {
	    CDFid id;
	    if (deleteIFexists) CDFlib (OPEN_, CDF_, CDFpath, &id,
					DELETE_, CDF_,
					NULL_);
	    status = CDFlib (CREATE_, CDF_, CDFpath, rNumDims,
					    rDimSizes, &id,
			     SELECT_, CDF_NEGtoPOSfp0_MODE_,
					    BOO(negToPosFp0,NEGtoPOSfp0on,
							    NEGtoPOSfp0off),
			     PUT_, CDF_FORMAT_, format,
				   CDF_MAJORITY_, majority,
				   CDF_ENCODING_, encoding,
			     NULL_);
	    if (!StatusHandler(status,"creating the CDF")) return FALSE;
	    NextSearchItem (TOKEN, GATTRmark);
	  }
      }
      else {
	ItemError1 ("<num-dims>", item);
	return FALSE;
      }
      break;

    case DIMSIZEfield:
      if (sscanf(item,"%ld",&rDimSizes[dimCount]) == 1) {
	dimCount++;
	if (dimCount == rNumDims) {
	  CDFid id;
	  if (deleteIFexists) CDFlib (OPEN_, CDF_, CDFpath, &id,
				      DELETE_, CDF_,
				      NULL_);
	  status = CDFlib (CREATE_, CDF_, CDFpath, rNumDims,
					  rDimSizes, &id,
			   SELECT_, CDF_NEGtoPOSfp0_MODE_,
					    BOO(negToPosFp0,NEGtoPOSfp0on,
							    NEGtoPOSfp0off),
			   PUT_, CDF_FORMAT_, format,
				 CDF_MAJORITY_, majority,
				 CDF_ENCODING_, encoding,
			   NULL_);
	  if (!StatusHandler(status,"creating the CDF")) return FALSE;
	  NextSearchItem (TOKEN, GATTRmark);
	}
	else
	  NextSearchItem (TOKEN, DIMSIZEfield);
      }
      else {
	ItemError1 ("<dim-size>", item);
	return FALSE;
      }
      break;

    case GATTRmark:
      if (!strcmp(item,"#GLOBALattributes")) {
	if (mLog)
	  WriteOut (stdout, "Reading global scope attributes section...\n");
	NextSearchItem (TOKEN, GaNAMEfieldOrVATTRmark);
      }
      else {
	ItemError1 ("#GLOBALattributes", item);
	return FALSE;
      }
      break;

    case GaNAMEfieldOrVATTRmark:
      if (!strcmp(item,"#VARIABLEattributes")) {
	if (mLog)
	  WriteOut (stdout,
		    "Reading variable scope attributes section...\n");
	NextSearchItem (TOKEN, VaNAMEfieldOrVARmark);
      }
      else
	if (!RecurCharStream(stream,DELIMSTRING,GaNAMEfield)) return FALSE;
      break;

    case GaNAMEfield: {
      long gAttrNum;
      strcpyX (gAttrName, item, CDF_ATTR_NAME_LEN);
      status = CDFlib (CREATE_, ATTR_, gAttrName, GLOBAL_SCOPE, &gAttrNum,
		       NULL_);
      if (!StatusHandler(status,"creating a gAttribute")) return FALSE;
      NextSearchItem (TOKEN, PERIODmarkOrGa1stENTRYNUMfield);
      break;
    }

    case PERIODmarkOrGa1stENTRYNUMfield:
      if (!strcmp(item,"."))
	NextSearchItem (TOKEN, GaNAMEfieldOrVATTRmark);
      else
	if (item[strlen(item)-1] != ':') {
	  ItemError1 ("<entry-num>:", item);
	  return FALSE;
	}
	else
	  if (sscanf(item,"%ld",&entryNum) == 1) {
	    entryNum--;                         /* Reference from zero (0). */
	    NextSearchItem (TOKEN, GaDATATYPEfield);
	  }
	  else {
	    ItemError1 ("<entry-num>:", item);
	    return FALSE;
	  }
      break;

    case GaDATATYPEfield:
      eDataType = WhichDataType(item);
      if (eDataType == -1) {
	ItemError1 ("<data-type>", item);
	return FALSE;
      }
      else {
	if (STRINGdataType(eDataType))
	  NextSearchItem (BRACEDchar, GaVALUEfield);
	else
	  NextSearchItem (BRACEDnonChar, GaVALUEfield);
      }
      break;

    case GaVALUEfield: {
      void *valuesB;
      long eNumElems;
      if (!STRINGdataType(eDataType)) {
	if (!DecodeValues(item,eDataType,&eNumElems,&valuesB,EPOCH0_STYLE)) {
	  ParseError ("Illegal entry value(s).");
	  return FALSE;
	}
      }
      else {
	eNumElems = strlen (item);
	valuesB = item;
      }
      status = CDFlib (SELECT_, gENTRY_, entryNum,
		       PUT_, gENTRY_DATA_, eDataType, eNumElems, valuesB,
		       NULL_);
      if (!StatusHandler(status,"writing a gAttribute gEntry")) return FALSE;
      if (valuesB != item) FreeMemory (valuesB, FatalError);
      NextSearchItem (TOKEN, PERIODmarkOrGaENTRYNUMfield);
      break;
    }

    case PERIODmarkOrGaENTRYNUMfield:
      if (!strcmp(item,"."))
	NextSearchItem (TOKEN, GaNAMEfieldOrVATTRmark);
      else
	if (!RecurCharStream(stream,TOKEN,GaENTRYNUMfield)) return FALSE;
      break;

    case GaENTRYNUMfield:
      if (item[strlen(item)-1] != ':') {
	ItemError1 ("<entry-num>:", item);
	return FALSE;
      }
      else
	if (sscanf(item,"%ld",&entryNum) == 1) {
	  entryNum--;                           /* Reference from zero (0). */
	  NextSearchItem (TOKEN, GaDATATYPEfieldOrBRACEmark);
	}
	else {
	  ItemError1 ("<entry-num>:", item);
	  return FALSE;
	}
      break;

    case GaDATATYPEfieldOrBRACEmark:
      if (item[0] == '{')
	if (STRINGdataType(eDataType)) {
	  if (!RecurCharStream(stream,BRACEDchar,GaVALUEfield)) return FALSE;
	}
	else {
	  if (!RecurCharStream(stream,BRACEDnonChar,GaVALUEfield)) return
								   FALSE;
	}
      else
	if (!RecurCharStream(stream,TOKEN,GaDATATYPEfield)) return FALSE;
      break;

    case VaNAMEfieldOrVARmark:
      if (!strcmp(item,"#variables")) {
	if (mLog) WriteOut (stdout, "Reading rVariables section...\n");
	NextSearchItem (TOKEN, VARNAMEfieldORzVARmarkOrENDmark);
	zVar = FALSE;
      }
      else
	if (!RecurCharStream(stream,DELIMSTRING,VaNAMEfield)) return FALSE;
      break;

    case VaNAMEfield: {
      long vAttrNum;
      strcpyX (vAttrName, item, CDF_ATTR_NAME_LEN);
      status = CDFlib (CREATE_, ATTR_, vAttrName, VARIABLE_SCOPE, &vAttrNum,
		       NULL_);
      if (!StatusHandler(status,"creating a vAttribute")) return FALSE;
      NextSearchItem (TOKEN, VaNAMEfieldOrVARmark);
      break;
    }

    case VARNAMEfieldORzVARmarkOrENDmark:
      if (!strcmp(item,"#end")) {
	if (mLog) WriteOut (stdout, "Reading end section...");
	fclose (SKTfp);
	status = CDFlib (CLOSE_, CDF_,
			 NULL_);
	if (!StatusHandler(status,"closing the CDF")) return FALSE;
	if (mLog) WriteOut (stdout, "success.\n");
	done = TRUE;
	return FALSE;
      }
      else
	if (!strcmp(item,"#zVariables")) {
	  if (mLog) WriteOut (stdout, "Reading zVariables section...\n");
	  NextSearchItem (TOKEN, VARNAMEfieldOrENDmark);
	  zVar = TRUE;
	}
	else
	  if (!RecurCharStream(stream,DELIMSTRING,VARNAMEfield)) return FALSE;
      break;

    case VARNAMEfieldOrENDmark:
      if (!strcmp(item,"#end")) {
	if (mLog) WriteOut (stdout, "Reading end section...");
	fclose (SKTfp);
	status = CDFlib (CLOSE_, CDF_,
			 NULL_);
	if (!StatusHandler(status,"closing the CDF")) return FALSE;
	if (mLog) WriteOut (stdout, "success.\n");
	done = TRUE;
	return FALSE;
      }
      else
	if (!RecurCharStream(stream,DELIMSTRING,VARNAMEfield)) return FALSE;
      break;

    case VARNAMEfield:
      strcpyX (varName, item, CDF_VAR_NAME_LEN);
      NextSearchItem (TOKEN, VARDATATYPEfield);
      break;

    case VARDATATYPEfield:
      vDataType = WhichDataType(item);
      if (vDataType == -1) {
	ItemError1 ("<data-type>", item);
	return FALSE;
      }
      else {
	NextSearchItem (TOKEN, VARNUMELEMSfield);
      }
      break;

    case VARNUMELEMSfield:
      if (sscanf(item,"%ld",&vNumElems) == 1)
	if (zVar)
	  NextSearchItem (TOKEN, zVARNUMDIMSfield);
	else
	  NextSearchItem (TOKEN, VARRECVARYfield);
      else {
	ItemError1 ("<num-elems>", item);
	return FALSE;
      }
      break;

    case zVARNUMDIMSfield:
      if (sscanf(item,"%ld",&zNumDims) == 1)
	if (zNumDims < 0 || zNumDims > CDF_MAX_DIMS) {
	  ItemError1 ("<num-dims>", item);
	  return FALSE;
	}
	else
	  if (zNumDims > 0) {
	    dimCount = 0;
	    NextSearchItem (TOKEN, zVARDIMSIZEfield);
	  }
	  else
	    NextSearchItem (TOKEN, VARRECVARYfield);
      else {
	ItemError1 ("<num-dims>", item);
	return FALSE;
      }
      break;

    case zVARDIMSIZEfield:
      if (sscanf(item,"%ld",&zDimSizes[dimCount]) == 1) {
	dimCount++;
	if (dimCount == zNumDims)
	  NextSearchItem (TOKEN, VARRECVARYfield);
	else
	  NextSearchItem (TOKEN, zVARDIMSIZEfield);
      }
      else {
	ItemError1 ("<dim-size>", item);
	return FALSE;
      }
      break;

    case VARRECVARYfield:
      if (strlen(item) > (size_t) 1) {
	char msg[MAX_MSG_LEN+1];
	sprintf (msg, "Extra character(s) after record variance (`%s').",
		 &item[1]);
	ParseError (msg);
	return FALSE;
      }
      else {
	switch (item[0]) {
	  case 't':
	  case 'T':
	    recVary = VARY;
	    break;
	  case 'f':
	  case 'F':
	    recVary = NOVARY;
	    break;
	  default:
	    ItemError1 ("<rec-vary>", item);
	    return FALSE;
	}
	if (Zq(zVar,zNumDims,rNumDims) > 0) {
	  varyCount = 0;
	  NextSearchItem (TOKEN, VARDIMVARYfield);
	}
	else {
	  long varNum;
	  if (zVar)
	    status = CDFlib (CREATE_, zVAR_, varName, vDataType, vNumElems,
					     zNumDims, zDimSizes, recVary,
					     dimVarys, &varNum,
			     NULL_);
	  else
	    status = CDFlib (CREATE_, rVAR_, varName, vDataType, vNumElems,
					     recVary, dimVarys, &varNum,
			     NULL_);
	  if (!StatusHandler(status,Zq(zVar,"creating a zVariable",
					    "creating an rVariable"))) return
								       FALSE;
	  /********************************************************************
	  * Check if a pad value may possibly be written with the value of the
	  * FILLVAL attribute.  If so, we want to allocate space for it now so
	  * that the `.cdf' file is not fragmented when the FILLVAL attribute
	  * entry is encountered.  [There will have been intervening attribute
	  * entries written which would force this rVDR/zVDR to be wasted and a
	  * new one created when the pad value is written.]  To allocated space
	  * we simply inquired the default pad value and then immediately write
	  * it back for the variable.
	  ********************************************************************/
	  if (useFillval) {
	    void *padValue = AllocateMemory ((size_t) (vNumElems *
						       CDFelemSize(vDataType)),
					     FatalError);
	    status = CDFlib (GET_, Zq(zVar,zVAR_PADVALUE_,
					   rVAR_PADVALUE_), padValue,
			     PUT_, Zq(zVar,zVAR_PADVALUE_,
					   rVAR_PADVALUE_), padValue,
			     NULL_);
	    if (!StatusHandler(status,
			       Zq(zVar,"writing zVariable pad value",
				       "writing rVariable pad value"))) {
	      FreeMemory (padValue, FatalError);
	      return FALSE;
	    }
	    FreeMemory (padValue, FatalError);
	  }
	  status = CDFlib (SELECT_, Zq(zVar,zENTRY_,rENTRY_), varNum,
			   NULL_);
	  if (!StatusHandler(status,
			     Zq(zVar,"selecting the current zEntry number",
				     "selecting the current rEntry number")))
									return
									FALSE;
	  NextSearchItem (TOKEN, VaNAMEfieldOrPERIODmark);
	}
      }
      break;

    case VARDIMVARYfield:
      if (strlen(item) > (size_t) 1) {
	char msg[MAX_MSG_LEN+1];
	sprintf (msg, "Extra character(s) after dimension variance (`%s').",
		 &item[1]);
	ParseError (msg);
	return FALSE;
      }
      else {
	switch (item[0]) {
	  case 't':
	  case 'T':
	    dimVarys[varyCount] = VARY;
	    break;
	  case 'f':
	  case 'F':
	    dimVarys[varyCount] = NOVARY;
	    break;
	  default:
	    ItemError1 ("<dim-vary>", item);
	    return FALSE;
	}
	varyCount++;
	if (varyCount == Zq(zVar,zNumDims,rNumDims)) {
	  long varNum;
	  if (zVar)
	    status = CDFlib (CREATE_, zVAR_, varName, vDataType, vNumElems,
					     zNumDims, zDimSizes, recVary,
					     dimVarys, &varNum,
			     NULL_);
	  else
	    status = CDFlib (CREATE_, rVAR_, varName, vDataType, vNumElems,
					     recVary, dimVarys, &varNum,
			     NULL_);
	  if (!StatusHandler(status,Zq(zVar,"creating a zVariable",
					    "creating an rVariable"))) return
								       FALSE;
	  /********************************************************************
	  * Check if a pad value may possibly be written with the value of the
	  * FILLVAL attribute.  If so, we want to allocate space for it now so
	  * that the `.cdf' file is not fragmented when the FILLVAL attribute
	  * entry is encountered.  [There will have been intervening attribute
	  * entries written which would force this rVDR/zVDR to be wasted and a
	  * new one created when the pad value is written.]  To allocated space
	  * we simply inquired the default pad value and then immediately write
	  * it back for the variable.
	  ********************************************************************/
	  if (useFillval) {
	    void *padValue = AllocateMemory ((size_t) (vNumElems *
						       CDFelemSize(vDataType)),
					     FatalError);
	    status = CDFlib (GET_, Zq(zVar,zVAR_PADVALUE_,
					   rVAR_PADVALUE_), padValue,
			     PUT_, Zq(zVar,zVAR_PADVALUE_,
					   rVAR_PADVALUE_), padValue,
			     NULL_);
	    if (!StatusHandler(status,
			       Zq(zVar,"writing zVariable pad value",
				       "writing rVariable pad value"))) {
	       FreeMemory (padValue, FatalError);
	       return FALSE;
	    }
	    FreeMemory (padValue, FatalError);
	  }
	  status = CDFlib (SELECT_, Zq(zVar,zENTRY_,rENTRY_), varNum,
			   NULL_);
	  if (!StatusHandler(status,
			     Zq(zVar,"selecting the current zEntry number",
				     "selecting the current rEntry number")))
									return
									FALSE;
	  NextSearchItem (TOKEN, VaNAMEfieldOrPERIODmark);
	}
	else
	  NextSearchItem (TOKEN, VARDIMVARYfield);
      }
      break;

    case VaNAMEfieldOrPERIODmark:
      if (!strcmp(item,"."))
	if (zVar)
	  NextSearchItem (TOKEN, LOCfieldOrVARNAMEfieldOrENDmark);
	else
	  NextSearchItem (TOKEN, LOCfieldOrVARNAMEfieldORzVARmarkOrENDmark);
      else
	if (!RecurCharStream(stream,DELIMSTRING,VaeNAMEfield)) return FALSE;
      break;

    case VaeNAMEfield:
      strcpyX (vAttrName, item, CDF_ATTR_NAME_LEN);
      status = CDFlib (SELECT_, ATTR_NAME_, vAttrName,
		       NULL_);
      if (!StatusHandler(status,"selecting the current vAttribute")) return
								     FALSE;
      NextSearchItem (TOKEN, VaeDATATYPEfield);
      break;

    case VaeDATATYPEfield:
      eDataType = WhichDataType(item);
      if (eDataType == -1) {
	ItemError1 ("<data-type>", item);
	return FALSE;
      }
      else {
	if (STRINGdataType(eDataType))
	  NextSearchItem (BRACEDchar, VaeVALUEfield);
	else
	  NextSearchItem (BRACEDnonChar, VaeVALUEfield);
      }
      break;

    case VaeVALUEfield: {
      void *eValueB;
      long eNumElems;
      if (!STRINGdataType(eDataType)) {
	if (!DecodeValues(item,eDataType,&eNumElems,&eValueB,EPOCH0_STYLE)) {
	  ParseError ("Illegal entry value(s).");
	  return FALSE;
	}
      }
      else {
	eNumElems = strlen (item);
	eValueB = item;
      }
      status = CDFlib (PUT_, Zq(zVar,zENTRY_DATA_,
				     rENTRY_DATA_), eDataType, eNumElems,
						    eValueB,
		       NULL_);
      if (!StatusHandler(status,
			 Zq(zVar,"writing a vAttribute zEntry",
				 "writing a vAttribute rEntry"))) return FALSE;
      if (useFillval) {
	if (!strcmpITB(vAttrName,"FILLVAL")) {
	  void *padValue;
	  padValue = AllocateMemory ((size_t) (CDFelemSize(vDataType) *
					       vNumElems),
				     FatalError);
	  ConvertDataType (eDataType, eNumElems, eValueB, vDataType, vNumElems,
			   padValue);
	  status = CDFlib (PUT_, Zq(zVar,zVAR_PADVALUE_,
					 rVAR_PADVALUE_), padValue,
			   NULL_);
	  if (!StatusHandler(status,
			     Zq(zVar,"writing a zVariable pad value",
				     "writing an rVariable pad value"))) return
									 FALSE;
	  FreeMemory (padValue, FatalError);
	}
      }
      if (eValueB != item) FreeMemory (eValueB, FatalError);
      NextSearchItem (TOKEN, VaNAMEfieldOrPERIODmark);
      break;
    }

    case LOCfieldOrVARNAMEfieldORzVARmarkOrENDmark:
      if (!strcmp(item,"#end")) {
	fclose (SKTfp);
	status = CDFlib (CLOSE_, CDF_,
			 NULL_);
	if (!StatusHandler(status,"closing the CDF")) return FALSE;
	done = TRUE;
	return FALSE;
      }
      else
	if (!strcmp(item,"#zVariables")) {
	  if (mLog) WriteOut (stdout, "Reading zVariables section...\n");
	  NextSearchItem (TOKEN, VARNAMEfieldOrENDmark);
	  zVar = TRUE;
	}
	else
	  if (item[0] == '[' || IsDigitChr(item[0])) {
	    if (!RecurCharStream(stream,VALLOCATION,LOCfield)) return FALSE;
	  }
	  else {
	    if (!RecurCharStream(stream,DELIMSTRING,VARNAMEfield)) return
								     FALSE;
	  }
      break;

    case LOCfieldOrVARNAMEfieldOrENDmark:
      if (!strcmp(item,"#end")) {
	if (mLog) WriteOut (stdout, "Reading end section...");
	fclose (SKTfp);
	status = CDFlib (CLOSE_, CDF_,
			 NULL_);
	if (!StatusHandler(status,"closing the CDF")) return FALSE;
	if (mLog) WriteOut (stdout, "success.\n");
	done = TRUE;
	return FALSE;
      }
      else
	if (item[0] == '[' || IsDigitChr(item[0])) {
	  if (!RecurCharStream(stream,VALLOCATION,LOCfield)) return FALSE;
	}
	else {
	  if (!RecurCharStream(stream,DELIMSTRING,VARNAMEfield)) return FALSE;
	}
      break;

    case LOCfield: {
      long indices[CDF_MAX_DIMS], nIndices, recNum;
      if (!DecodeRecordAndIndices(item,&recNum,&nIndices,indices)) {
	ParseError ("Illegal record number and/or indices.");
	return FALSE;
      }
      if (nIndices != BOO(zVar,zNumDims,rNumDims)) {
	ParseError ("Wrong number of indices.");
	return FALSE;
      }
      status = CDFlib (SELECT_, Zq(zVar,zVAR_RECNUMBER_,
					rVARs_RECNUMBER_), recNum,
				Zq(zVar,zVAR_DIMINDICES_,
					rVARs_DIMINDICES_), indices,
		       NULL_);
      if (!StatusHandler
	     (status,
	      Zq(zVar,"selecting the current record/indices for a zVariable",
		      "selecting the current record/indices for the rVariables")))
									return FALSE;
      NextSearchItem (TOKEN, EQUALmark);
     break;
    }

    case EQUALmark:
      if (!strcmp(item,"="))
	if (STRINGdataType(vDataType))
	  NextSearchItem (BRACEDchar, VARVALUEfield);
	else
	  if (vDataType == CDF_EPOCH)
	    NextSearchItem (VAREPOCH, VARVALUEfield);
	  else
	    NextSearchItem (TOKEN, VARVALUEfield);
      else {
	ItemError1 ("=", item);
	return FALSE;
      }
      break;

    case VARVALUEfield: {
      void *value;
      long numElems;
      if (!STRINGdataType(vDataType)) {
	if (!DecodeValues(item,vDataType,&numElems,&value,EPOCH0_STYLE)) {
	  ParseError ("Illegal variable value.\n");
	  return FALSE;
	}
	if (numElems != 1) {
	  ParseError ("Illegal number of elements for a variable value.");
	  FreeMemory (value, FatalError);
	  return FALSE;
	}
      }
      else {
	int len;
	len = strlen (item);
	if (len > vNumElems) {
	  char tempS[MAX_MSG_LEN+1];
	  WriteOut (stdout, "ERROR: Truncating value for variable `");
	  WriteOut (stdout, varName);
	  WriteOut (stdout, "' at line\n");
	  sprintf (tempS, "       number %d (this is a non-fatal error).\n",
		   lineN);
	  WriteOut (stdout, tempS);
	  value = item;
	}
	else
	  if (len < vNumElems) {
	    if (mLog) {
	      char tempS[MAX_MSG_LEN+1];
	      WriteOut (stdout,
			"WARNING: Blank padding value for variable `");
	      WriteOut (stdout, varName);
	      WriteOut (stdout, "'\n");
	      sprintf (tempS, "         at line number %d.\n", lineN);
	      WriteOut (stdout, tempS);
	    }
	    value = AllocateMemory ((size_t) (vNumElems + 1), FatalError);
	    strcpyX (value, item, (size_t) vNumElems);
	    BlankPadRight (value, (int) (vNumElems - len));
	  }
	  else
	    value = item;
      }
      status = CDFlib (PUT_, Zq(zVar,zVAR_DATA_,rVAR_DATA_), value,
		       NULL_);
      if (!StatusHandler(status,
			 Zq(zVar,"writing a zVariable value",
				 "writing an rVariable value"))) return FALSE;
      if (value != item) FreeMemory (value, FatalError);
      if (zVar)
	NextSearchItem (TOKEN, LOCfieldOrVARNAMEfieldOrENDmark);
      else
	NextSearchItem (TOKEN, LOCfieldOrVARNAMEfieldORzVARmarkOrENDmark);
      break;
    }

    default:
      ParseError ("** Internal error, contact CDFSUPPORT **");
      return FALSE;
  }
  return TRUE;
}

/******************************************************************************
* NextSearchItem.
******************************************************************************/

void NextSearchItem (type, item)
enum searchForENUM type;
enum nextItemENUM item;
{
  searchFor = type;
  switch (type) {
    case TOKEN:
      token[0] = NUL;
      break;
    case DELIMSTRING:
      stringDelim = NUL;
      stringLineN = -1;
      string[0] = NUL;
      break;
    case BRACEDchar:
      startBraceFound = FALSE;
      stringDelim = NUL;
      stringLineN = -1;
      hyphenORendBraceNext = FALSE;
      eValue[0] = NUL;
      break;
    case BRACEDnonChar:
      startBraceFound = FALSE;
      eValue[0] = NUL;
      break;
    case VALLOCATION:
      locationStarted = FALSE;
      location[0] = NUL;
      break;
    case VAREPOCH:
      epochCount = 0;
      epoch[0] = NUL;
      break;
  }
  nextItem = item;
  stream[0] = NUL;
  return;
}

/******************************************************************************
* ItemError1.  (A specific item was expected).
******************************************************************************/

void ItemError1 (expected, found)
char *expected;
char *found;
{
  char msg[MAX_MSG_LEN+1];
  sprintf (msg, "Expecting `%s', found `%s' instead.", expected, found);
  ParseError (msg);
  return;
}

/******************************************************************************
* ItemError2.  (One of two items were expected).
******************************************************************************/

void ItemError2 (expected1, expected2, found)
char *expected1;
char *expected2;
char *found;
{
  char msg[MAX_MSG_LEN+1];
  sprintf (msg, "Expecting `%s' or `%s', found `%s' instead.",
	   expected1, expected2, found);
  ParseError (msg);
  return;
}

/******************************************************************************
* ItemError3.  (One of three items were expected).
******************************************************************************/

void ItemError3 (expected1, expected2, expected3, found)
char *expected1;
char *expected2;
char *expected3;
char *found;
{
  char msg[MAX_MSG_LEN+1];
  sprintf (msg, "Expecting `%s', `%s', or `%s', found `%s' instead.",
	   expected1, expected2, expected3, found);
  ParseError (msg);
  return;
}

/******************************************************************************
* ParseError.
******************************************************************************/

void ParseError (msg)
char *msg;
{
  char tempS[MAX_MSG_LEN+1];
  WriteOut (stdout, msg);
  WriteOut (stdout, "\n\n");
  sprintf (tempS, "Error at (or before) line number %d which follows...\n\n",
	   lineN);
  WriteOut (stdout, tempS);
  WriteOut (stdout, line);
  WriteOut (stdout, "\n");      /* `line' has a NL at the end already. */
  CDFlib (DELETE_, CDF_,
	  NULL_);
  fclose (SKTfp);
}

/******************************************************************************
* CATchr.
* Concatenate a character to a string.  If the string is already at its
* maximum length, reallocate the string with a larger size.
******************************************************************************/

void CATchr (str, chr, size, base)
char **str;
int chr;
size_t *size;
int base;
{
  size_t nChars = (size_t) (*size + base);
  if (strlen(*str) == *size) {
    *str = (char *) ReallocateMemory (*str, (size_t) (nChars + 1), FatalError);
    *size += base;
  }
  catchrX (*str, chr, nChars);
  return;
}

/******************************************************************************
* RecurCharStream.
******************************************************************************/

Logical RecurCharStream (stream, type, next)
char *stream;
enum searchForENUM type;
enum nextItemENUM next;
{
  char *streamT; int i;
  size_t nChars = strlen (stream);
  streamT = (char *) AllocateMemory (nChars + 1, FatalError);
  strcpyX (streamT, stream, nChars);
  NextSearchItem (type, next);
  for (i = 0; streamT[i] != NUL; i++)
     if (!CharStream(streamT[i])) {
       FreeMemory (streamT, FatalError);
       return FALSE;
     }
  FreeMemory (streamT, FatalError);
  return TRUE;
}

/******************************************************************************
* AllocateGrowingStrings.
******************************************************************************/

void AllocateGrowingStrings () {
  streamZ = BASE_STREAM_SIZE;
  stream = (char *) AllocateMemory ((size_t) (streamZ + 1), FatalError);
  stringZ = BASE_STRING_SIZE;
  string = (char *) AllocateMemory ((size_t) (stringZ + 1), FatalError);
  tokenZ = BASE_TOKEN_SIZE;
  token = (char *) AllocateMemory ((size_t) (tokenZ + 1), FatalError);
  eValueZ = BASE_EVALUE_SIZE;
  eValue = (char *) AllocateMemory ((size_t) (eValueZ + 1), FatalError);
  locationZ = BASE_LOCATION_SIZE;
  location = (char *) AllocateMemory ((size_t) (locationZ + 1), FatalError);
  return;
}

/******************************************************************************
* FreeGrowingStrings.
******************************************************************************/

void FreeGrowingStrings () {
  FreeMemory (stream, FatalError);
  FreeMemory (string, FatalError);
  FreeMemory (token, FatalError);
  FreeMemory (eValue, FatalError);
  FreeMemory (location, FatalError);
  return;
}

/******************************************************************************
* StatusHandler.
******************************************************************************/

Logical StatusHandler (status, msg)
CDFstatus status;
char *msg;
{
  char text[CDF_STATUSTEXT_LEN+1];
  static char infoT[] =
    "The following informational condition was encountered while";
  static char warnT[] =
    "The following warning condition was encountered while";
  static char errorT[] =
    "The following ERROR condition was encountered while";
  char tempS[2*(MAX_SCREENLINE_LEN+1)];

  if (StatusINFO(status) && report[INFOs]) {
    CDFlib (SELECT_, CDF_STATUS_, status,
	    GET_, STATUS_TEXT_, text,
	    NULL_);
    sprintf (tempS, "\n%s%s%s.\n\n%s\n", infoT,
	     ((strlen(infoT) + strlen(msg) + 2 >
	      (size_t) MAX_SCREENLINE_LEN) ? "\n" : " "), msg, text);
    WriteOut (stdout, tempS);
    return TRUE;
  }

  if (StatusWARN(status) && report[WARNs]) {
    CDFlib (SELECT_, CDF_STATUS_, status,
	    GET_, STATUS_TEXT_, text,
	    NULL_);
    sprintf (tempS, "\n%s%s%s.\n\n%s\n", warnT,
	     ((strlen(warnT) + strlen(msg) + 2 >
	      (size_t) MAX_SCREENLINE_LEN) ? "\n" : " "), msg, text);
    WriteOut (stdout, tempS);
    return TRUE;
  }

  if (StatusERROR(status)) {
    if (report[ERRORs]) {
      CDFlib (SELECT_, CDF_STATUS_, status,
	      GET_, STATUS_TEXT_, text,
	      NULL_);
      sprintf (tempS, "\n%s%s%s.\n\n%s\n", errorT,
	       ((strlen(errorT) + strlen(msg) + 2 >
		(size_t) MAX_SCREENLINE_LEN) ? "\n" : " "), msg, text);
      WriteOut (stdout, tempS);
    }
    ParseError ("");
    return FALSE;
  }

  return TRUE;  /* CDF_OK */
}

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

#if defined(mac)
Logical CreateSkeletonQOPs (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 Logical logMsg = DEFAULTlogSKT2CDF;
  static Logical deleteCDF = DEFAULTdeleteSKT2CDF;
  static Logical reportInfos = REPORTinfosDEFAULT;
  static Logical reportWarns = REPORTwarningsDEFAULT;
  static Logical reportErrors = REPORTerrorsDEFAULT;
  static Logical negToPos = DEFAULT_NEGtoPOSfp0;
  static Logical useFillval = DEFAULTfillvalSKT2CDF;
  static Str255 CDFtext = "\p";
  static Str255 sktText = "\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[SKTTEXTin], sktText);

  if (logMsg) SetCtlValue (controlHs[LOGin], 1);
  if (deleteCDF) SetCtlValue (controlHs[DELETEin], 1);
  if (reportInfos) SetCtlValue (controlHs[INFOin], 1);
  if (reportWarns) SetCtlValue (controlHs[WARNin], 1);
  if (reportErrors) SetCtlValue (controlHs[ERRORin], 1);
  if (negToPos) SetCtlValue (controlHs[NEGZin], 1);
  if (useFillval) SetCtlValue (controlHs[FILLin], 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[SKTTEXTin], sktText);

	logMsg = GetCtlValue (controlHs[LOGin]);
	deleteCDF = GetCtlValue (controlHs[DELETEin]);
	reportInfos = GetCtlValue (controlHs[INFOin]);
	reportWarns = GetCtlValue (controlHs[WARNin]);
	reportErrors = GetCtlValue (controlHs[ERRORin]);
	negToPos = GetCtlValue (controlHs[NEGZin]);
	useFillval = GetCtlValue (controlHs[FILLin]);
	
	/**********************************************************************
	* Build argc/argv.
	**********************************************************************/

	*argC = 7 + BOO(NULpString(CDFtext),0,2) +
		    BOO(NULpString(sktText),0,1);
	*argV = (char **) AllocateMemory (*argC * sizeof(char *), FatalError);
	
	n = 0;
	MAKEstrARGv (argV, n, pgmName)

	if (!NULpString(sktText)) {
	  PtoCstr (sktText);
	  MAKEstrARGv (argV, n, (char *) sktText)
	  CtoPstr ((char *) sktText);
	}

	if (!NULpString(CDFtext)) {
	  MAKEstrARGv (argV, n, "-cdf")
	  PtoCstr (CDFtext);
	  MAKEstrARGv (argV, n, (char *) CDFtext)
	  CtoPstr ((char *) CDFtext);
	}

	MAKEbooARGv (argV, n, logMsg, "-log", "-nolog")
	MAKEbooARGv (argV, n, deleteCDF, "-delete", "-nodelete")
	MAKEbooARGv (argV, n, negToPos, "-neg2posfp0", "-noneg2posfp0")
	MAKEbooARGv (argV, n, useFillval, "-fillval", "-nofillval")
	
	MAKEstrARGv (argV, n, "-report")
	MAKEstrARGv (argV, n, StatusCodeReportOptions(reportErrors,
						      reportWarns,
						      reportInfos))

	/**********************************************************************
	* 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 skeleton CDF specification.
      *     The cursor is set because `StandardPutFile' leaves the cursor as
      * an iBeam (instead of returning it to what it was).
      ************************************************************************/
      case CDFSELECTin: {
	StandardFileReply CDFreply;
	char CDFpath[DU_MAX_PATH_LEN+1], prompt[] = "Enter CDF name:";
	StandardPutFile (CtoPstr(prompt), CtoPstr(""), &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);
	}
	SetCursor (&(qd.arrow));
	break;
      }
      /************************************************************************
      * Select skeleton table.
      ************************************************************************/
      case SKTSELECTin: {
	StandardFileReply sktReply;
	char sktPath[DU_MAX_PATH_LEN+1];
	StandardGetFile (FilterForSKTs, -1, NULL, &sktReply);
	if (sktReply.sfGood && !sktReply.sfIsFolder && !sktReply.sfIsVolume) {
	  BuildMacPath (&sktReply.sfFile, sktPath, TRUE);
	  sktText[0] = strlen (sktPath);
	  strcpyX ((char *) &sktText[1], sktPath, 255);
	  SetIText ((Handle) controlHs[SKTTEXTin], sktText);
	}
	break;
      }
      /************************************************************************
      * Check boxes.
      ************************************************************************/
      case LOGin:
      case DELETEin:
      case INFOin:
      case WARNin:
      case ERRORin:
      case NEGZin:
      case FILLin:
	SetCtlValue (controlHs[itemN], BOO(GetCtlValue(controlHs[itemN]),0,1));
	break;
    }
  }
}
#endif
