/******************************************************************************
*
*  NSSDC/CDF                                                    CDFexport/2.
*
*  Version 1.0b, 22-Sep-95, Hughes STX.
*
*  Modification history:
*
*   V1.0  15-Sep-95, J Love     Original version.
*   V1.0a 18-Sep-95, J Love	Macintosh event handling.
*   V1.0b 22-Sep-95, J Love	INITIALRECS/ALLOCATERECS in `AllocateRecords'.
*
******************************************************************************/

#include "cdfxp.h"

/******************************************************************************
* Local macros.
******************************************************************************/

#define SETTINGS_FILE   "export.set"
#define SETTINGS_DELIM  '"'

#define COUNTx(max,first,last) \
BOO(max < first,0,BOO(max < last,max-first+1,last-first+1));

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

static Logical SetFilter PROTOARGs((char *field, struct ItemStruct *Item));
static Logical SetOutput PROTOARGs((char *field, struct ItemStruct *Item));
static Logical SetWidth PROTOARGs((char *field, struct ItemStruct *Item));
static Logical SetMonotonic PROTOARGs((char *field, struct VarStruct *Var));
static Logical ScanValueField PROTOARGs((
  char *fieldPtr, long dataType, long numElems, char **commaPtr, void **value
));
static void WriteDelimitedString PROTOARGs((
  FILE *fp, int delim, char *string
));
static char *EndingDelimiter PROTOARGs((char *string));
static void RemoveDelimiters PROTOARGs((char *string));
static Logical AllocateRecords PROTOARGs((
  CDFid outID, struct ItemStruct *scalarHead, struct ItemStruct *hyperHead
));

/******************************************************************************
* SaveSettings.
******************************************************************************/

void SaveSettings () {
  FILE *fp; long version, release, increment; char subIncr;
  struct ItemStruct *Item; CDFstatus status;
  DisplayMessage ("Saving settings.", NOWAIT);
  status = CDFlib (GET_, LIB_VERSION_, &version,
			 LIB_RELEASE_, &release,
			 LIB_INCREMENT_, &increment,
			 LIB_subINCREMENT_, &subIncr,
		   NULL_);
  DisplayStatus (status, "inquiring CDF");
  if (StatusBAD(status)) return;
  fp = fopen (SETTINGS_FILE, "w");
  if (fp == NULL) {
    DisplayMessage ("Error opening settings file.", BEEPWAIT1);
    return;
  }
  fprintf (fp, "CDF V%ld.%ld.%ld%c\n", version, release, increment, subIncr);
  for (Item = itemHead; Item != NULL; Item = Item->nextItem) {
     switch (Item->type) {
       case RECORDt:
	 fprintf (fp, "ITEM=Record,");
	 fprintf (fp, "%s,", BOO(Item->outputSetting,"YES","no"));
	 if (Item->Record->min == NOminMax)
	   fprintf (fp, ",");
	 else
	   fprintf (fp, "%ld,", Item->Record->min + 1);
	 if (Item->Record->max == NOminMax)
	   fprintf (fp, ",");
	 else
	   fprintf (fp, "%ld,", Item->Record->max + 1);
	 fprintf (fp, "%s,", BOO(Item->filterSetting,
				 BOO(opt.exclusive,
				     BOO(Item->inclusive,"YeS","yEs"),
				     "YES"),
				 "no"));
	 fprintf (fp, "%d,", Item->width);
	 fprintf (fp, "\n");
	 break;
       case INDICESt:
	 fprintf (fp, "ITEM=Indices,");
	 fprintf (fp, "%s,", BOO(Item->outputSetting,"YES","no"));
	 if (Item->Indices->minNumDims == NOminMax)
	   fprintf (fp, ",");
	 else {
	   char indices[MAXitemFieldLEN+1];
	   EncodeIndicesJustify (indices, Item->Indices->minNumDims,
				 Item->Indices->minIndices, 0);
	   fprintf (fp, "%s,", indices);
	 }
	 if (Item->Indices->maxNumDims == NOminMax)
	   fprintf (fp, ",");
	 else {
	   char indices[MAXitemFieldLEN+1];
	   EncodeIndicesJustify (indices, Item->Indices->maxNumDims,
				 Item->Indices->maxIndices, 0);
	   fprintf (fp, "%s,", indices);
	 }
	 fprintf (fp, "%s,", BOO(Item->filterSetting,
				 BOO(opt.exclusive,
				     BOO(Item->inclusive,"YeS","yEs"),
				     "YES"),
				 "no"));
	 fprintf (fp, "%d,", Item->width);
	 fprintf (fp, "\n");
	 break;
       case VARIABLEt:
	 fprintf (fp, "ITEM=Variable,");
	 WriteDelimitedString (fp, (int) SETTINGS_DELIM, Item->Var->name);
	 fprintf (fp, ",");
	 fprintf (fp, "%s,", BOO(Item->outputSetting,"YES","no"));
	 if (Item->Var->min != NULL) {
	   if (STRINGdataType(Item->Var->dataType))
	     WriteDelimitedString (fp, (int) SETTINGS_DELIM,
				   (char *) Item->Var->min);
	   else {
	     char temp[MAX_nonSTRING_VALUE_LEN+1];
	     EncodeValueFormat (BOO(EPOCHdataType(Item->Var->dataType),
				    CDF_REAL8,Item->Var->dataType),
				Item->Var->min, temp,
				BOO(EPOCHdataType(Item->Var->dataType),
				    "%.13e",NULL),
				0, MAX_nonSTRING_VALUE_LEN, 0);
	     fprintf (fp, "[%s]", temp);
	   }
	 }
	 fprintf (fp, ",");
	 if (Item->Var->max != NULL) {
	   if (STRINGdataType(Item->Var->dataType))
	     WriteDelimitedString (fp, (int) SETTINGS_DELIM,
				   (char *) Item->Var->max);
	   else {
	     char temp[MAX_nonSTRING_VALUE_LEN+1];
	     EncodeValueFormat (BOO(EPOCHdataType(Item->Var->dataType),
				    CDF_REAL8,Item->Var->dataType),
				Item->Var->max, temp,
				BOO(EPOCHdataType(Item->Var->dataType),
				    "%.13e",NULL),
				0, MAX_nonSTRING_VALUE_LEN, 0);
	     fprintf (fp, "[%s]", temp);
	   }
	 }
	 fprintf (fp, ",");
	 fprintf (fp, "%s,", BOO(Item->filterSetting,
				 BOO(opt.exclusive,
				     BOO(Item->inclusive,"YeS","yEs"),
				     "YES"),
				 "no"));
	 if (Item->Var->fill != NULL) {
	   if (STRINGdataType(Item->Var->dataType))
	     WriteDelimitedString (fp, (int) SETTINGS_DELIM,
				   (char *) Item->Var->fill);
	   else {
	     char temp[MAX_nonSTRING_VALUE_LEN+1];
	     EncodeValueFormat (BOO(EPOCHdataType(Item->Var->dataType),
				    CDF_REAL8,Item->Var->dataType),
				Item->Var->fill, temp,
				BOO(EPOCHdataType(Item->Var->dataType),
				    "%.13e",NULL),
				0, MAX_nonSTRING_VALUE_LEN, 0);
	     fprintf (fp, "[%s]", temp);
	   }
	 }
	 fprintf (fp, ",");
	 fprintf (fp, "%s,", monos[Item->Var->monotonic+1]);
	 if (Item->Var->format != NULL) {
	   WriteDelimitedString (fp, (int) SETTINGS_DELIM, Item->Var->format);
	 }
	 fprintf (fp, ",");
	 fprintf (fp, "%d,", Item->width);
	 fprintf (fp, "\n");
	 break;
     }
  }
  fprintf (fp, "USE_FILTERS=%s\n", BOO(opt.overallFilter,"YES","no"));
  fprintf (fp, "USE_FILLS=%s\n", BOO(opt.useFills,"YES","no"));
  fprintf (fp, "CDF_FORMAT=%s\n", BOO(opt.singleFile,"single","multi"));
  fprintf (fp, "CDF_ENCODING=%s\n", encodings[(int)opt.encoding]);
  fprintf (fp, "EPOCH_STYLE=%s\n", epochStyles[opt.epochStyle]);
  fprintf (fp, "ORIENTATION=%s\n",
	   BOO(opt.horizontalMode,"horizontal","vertical"));
  fprintf (fp, "MAJORITY=%s\n", majorities[(int)opt.majority]);
  fprintf (fp, "SHOW_FILTERED=%s\n", BOO(opt.showFiltered,"YES","no"));
  fprintf (fp, "SPACING=%d\n", opt.spacing);
  fprintf (fp, "DELETE_EXISTING=%s\n", BOO(opt.deleteExisting,"YES","no"));
  fprintf (fp, "PREALLOCATE=%s\n", BOO(opt.preAllocate,"YES","no"));
  fclose (fp);
  DisplayMessage ("", NOWAIT);
  return;
}

/******************************************************************************
* RestoreSettings.
******************************************************************************/

void RestoreSettings () {
  char *equalPtr, *newlinePtr, *commaPtr, *fieldPtr, *linePtr, *delimPtr;
  FILE *fp; long valueL; struct ItemStruct *Item;
  char line[MAX_SETTINGS_LEN+1+1];              /* +1+1 for newline+NUL. */
  DisplayMessage ("Restoring settings.", NOWAIT);
  if (!IsReg(SETTINGS_FILE)) {
    DisplayMessage ("A settings file does not exist.", BEEPWAIT1);
    return;
  }
  fp = fopen (SETTINGS_FILE, "r");
  if (fp == NULL) {
    DisplayMessage ("Error opening settings file.", BEEPWAIT1);
    return;
  }
  linePtr = fgets (line, MAX_SETTINGS_LEN+1+1, fp);
  if (linePtr == NULL) {
    DisplayMessage ("Error reading settings file.", BEEPWAIT1);
    return;
  }
  while ((linePtr = fgets(line,MAX_SETTINGS_LEN+1+1,fp)) != NULL) {
    newlinePtr = strchr (line, '\n');
    if (newlinePtr != NULL) *newlinePtr = NUL;
    equalPtr = strchr (line, '=');
    if (equalPtr == NULL) break;
    *equalPtr = NUL;
    /**************************************************************************
    * Check for item/variable.
    **************************************************************************/
    if (!strcmp(line,"ITEM")) {
      fieldPtr = equalPtr + 1;
      commaPtr = strchr (fieldPtr, ',');
      if (commaPtr == NULL) break;
      *commaPtr = NUL;
      /************************************************************************
      * Check for `Record' item.
      ************************************************************************/
      if (!strcmp(fieldPtr,"Record")) {
	/**********************************************************************
	* Check if `Record' is present in SelectionWindow.
	**********************************************************************/
	for (Item = itemHead; Item != NULL; Item = Item->nextItem) {
	   if (Item->type == RECORDt) break;
	}
	if (Item == NULL) continue;
	/**********************************************************************
	* Check for `Output' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (!SetOutput(fieldPtr,Item)) break;
	/**********************************************************************
	* Check for `Minimum' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (NULstring(fieldPtr))
	  Item->Record->min = NOminMax;
	else
	  if (sscanf(fieldPtr,"%ld",&valueL) == 1)
	    if (valueL > 0)
	      Item->Record->min = valueL - 1;
	    else
	      break;
	  else
	    break;
	/**********************************************************************
	* Check for `Maximum' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (NULstring(fieldPtr))
	  Item->Record->max = NOminMax;
	else
	  if (sscanf(fieldPtr,"%ld",&valueL) == 1)
	    if (valueL > 0)
	      Item->Record->max = valueL - 1;
	    else
	      break;
	  else
	    break;
	/**********************************************************************
	* Check that Minimum/Maximum fields are legal.
	**********************************************************************/
	if (Item->Record->min != NOminMax && Item->Record->max != NOminMax) {
	  if (Item->Record->min > Item->Record->max) {
	    Item->Record->min = NOminMax;
	    Item->Record->max = NOminMax;
	    break;
	  }
	}
	/**********************************************************************
	* Check for `Filter' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (!SetFilter(fieldPtr,Item)) break;
	/**********************************************************************
	* Check for `Width' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (!SetWidth(fieldPtr,Item)) break;
	continue;
      }
      /************************************************************************
      * Check for `Indices' item.
      ************************************************************************/
      if (!strcmp(fieldPtr,"Indices")) {
	/**********************************************************************
	* Check if `Indices' is present in SelectionWindow.
	**********************************************************************/
	for (Item = itemHead; Item != NULL; Item = Item->nextItem) {
	   if (Item->type == INDICESt) break;
	}
	if (Item == NULL) continue;
	/**********************************************************************
	* Check for `Output' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (!SetOutput(fieldPtr,Item)) break;
	/**********************************************************************
	* Check for `Minimum' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	if (*fieldPtr == '[') {
	  long numDims, indices[CDF_MAX_DIMS]; int dimN;
	  commaPtr = strchr (fieldPtr + 1, ']');
	  if (commaPtr == NULL) break;
	  commaPtr = strchr (commaPtr + 1, ',');
	  if (commaPtr == NULL) break;
	  *commaPtr = NUL;
	  if (DecodeRecordAndIndices(fieldPtr,NULL,&numDims,indices)) {
	    Item->Indices->minNumDims = numDims;
	    for (dimN = 0; dimN < numDims; dimN++) {
	       Item->Indices->minIndices[dimN] = indices[dimN];
	    }
	  }
	  else
	    break;
	}
	else
	  if (*fieldPtr == ',') {
	    Item->Indices->minNumDims = NOminMax;
	    commaPtr = fieldPtr;
	  }
	  else
	    break;
	/**********************************************************************
	* Check for `Maximum' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	if (*fieldPtr == '[') {
	  long numDims, indices[CDF_MAX_DIMS]; int dimN;
	  commaPtr = strchr (fieldPtr + 1, ']');
	  if (commaPtr == NULL) break;
	  commaPtr = strchr (commaPtr + 1, ',');
	  if (commaPtr == NULL) break;
	  *commaPtr = NUL;
	  if (DecodeRecordAndIndices(fieldPtr,NULL,&numDims,indices)) {
	    Item->Indices->maxNumDims = numDims;
	    for (dimN = 0; dimN < numDims; dimN++) {
	       Item->Indices->maxIndices[dimN] = indices[dimN];
	    }
	  }
	  else
	    break;
	}
	else
	  if (*fieldPtr == ',') {
	    Item->Indices->maxNumDims = NOminMax;
	    commaPtr = fieldPtr;
	  }
	  else
	    break;
	/**********************************************************************
	* Check for `Filter' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (!SetFilter(fieldPtr,Item)) break;
	/**********************************************************************
	* Check for `Width' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (!SetWidth(fieldPtr,Item)) break;
	continue;
      }
      /************************************************************************
      * Check for `Variable' item.
      ************************************************************************/
      if (!strcmp(fieldPtr,"Variable")) {
	/**********************************************************************
	* Parse variable name.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	if (*fieldPtr != SETTINGS_DELIM) break;
	delimPtr = EndingDelimiter (fieldPtr);
	if (delimPtr == NULL) break;
	commaPtr = delimPtr + 1;
	if (*commaPtr != ',') break;
	*commaPtr = NUL;
	RemoveDelimiters (fieldPtr);
	/**********************************************************************
	* Check if the variable is in the SelectionWindow.
	**********************************************************************/
	for (Item = itemHead; Item != NULL; Item = Item->nextItem) {
	   if (Item->type == VARIABLEt) {
	     if (!strcmp(fieldPtr,Item->Var->name)) break;
	   }
	}
	if (Item == NULL) continue;
	/**********************************************************************
	* Check for `Output' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (!SetOutput(fieldPtr,Item)) break;
	/**********************************************************************
	* Check for `Minimum' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	if (!ScanValueField(fieldPtr,Item->Var->dataType,
			    Item->Var->numElems,&commaPtr,
			    &(Item->Var->min))) break;
	/**********************************************************************
	* Check for `Maximum' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	if (!ScanValueField(fieldPtr,Item->Var->dataType,
			    Item->Var->numElems,&commaPtr,
			    &(Item->Var->max))) break;
	/**********************************************************************
	* Check for `Filter' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (!SetFilter(fieldPtr,Item)) break;
	/**********************************************************************
	* Check for `Fill' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	if (!ScanValueField(fieldPtr,Item->Var->dataType,
			    Item->Var->numElems,&commaPtr,
			    &(Item->Var->fill))) break;
	/**********************************************************************
	* Check for `Monotonicity' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (!SetMonotonic(fieldPtr,Item->Var)) break;
	/**********************************************************************
	* Check for `Format' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	if (*fieldPtr == SETTINGS_DELIM) {
	  delimPtr = EndingDelimiter (fieldPtr);
	  if (delimPtr == NULL) break;
	  commaPtr = delimPtr + 1;
	  if (*commaPtr != ',') break;
	  *commaPtr = NUL;
	  RemoveDelimiters (fieldPtr);
	  if (Item->Var->format != NULL) {
	    FreeMemory (Item->Var->format, FatalError);
	  }
	  Item->Var->format = (char *) AllocateMemory (strlen(fieldPtr) + 1,
						       FatalError);
	  strcpyX (Item->Var->format, fieldPtr, 0);
	}
	else {
	  commaPtr = fieldPtr;
	  if (Item->Var->format != NULL) {
	    FreeMemory (Item->Var->format, FatalError);
	    Item->Var->format = NULL;
	  }
	}
	/**********************************************************************
	* Check for `Width' field.
	**********************************************************************/
	fieldPtr = commaPtr + 1;
	commaPtr = strchr (fieldPtr, ',');
	if (commaPtr == NULL) break;
	*commaPtr = NUL;
	if (!SetWidth(fieldPtr,Item)) break;
	continue;
      }
      break;
    }
    /**************************************************************************
    * Check for `Use filters'.
    **************************************************************************/
    if (!strcmp(line,"USE_FILTERS")) {
      if (!strcmp(equalPtr+1,"YES")) {
	opt.overallFilter = TRUE;
	continue;
      }
      if (!strcmp(equalPtr+1,"no")) {
	opt.overallFilter = FALSE;
	continue;
      }
      break;
    }
    /**************************************************************************
    * Check for `Use fills'.
    **************************************************************************/
    if (!strcmp(line,"USE_FILLS")) {
      if (!strcmp(equalPtr+1,"YES")) {
	opt.useFills = TRUE;
	continue;
      }
      if (!strcmp(equalPtr+1,"no")) {
	opt.useFills = FALSE;
	continue;
      }
      break;
    }
    /**************************************************************************
    * Check for `CDF Format'.
    **************************************************************************/
    if (!strcmp(line,"CDF_FORMAT")) {
      if (!strcmp(equalPtr+1,"single")) {
	opt.singleFile = TRUE;
	continue;
      }
      if (!strcmp(equalPtr+1,"multi")) {
	opt.singleFile = FALSE;
	continue;
      }
      break;
    }
    /**************************************************************************
    * Check for `CDF Encoding'.
    **************************************************************************/
    if (!strcmp(line,"CDF_ENCODING")) {
      int encoding; Logical found;
      for (encoding = 0, found = FALSE; encoding <= MAX_ENCODING; encoding++) {
	 if (encodings[encoding] != NULL) {
	   if (!strcmp(equalPtr+1,encodings[encoding])) {
	     opt.encoding = encoding;
	     found = TRUE;
	     break;
	   }
	 }
      }
      if (found) continue;
      break;
    }
    /**************************************************************************
    * Check for `EPOCH Style'.
    **************************************************************************/
    if (!strcmp(line,"EPOCH_STYLE")) {
      int style; Logical found;
      for (style = EPOCH0_STYLE, found = FALSE;
	   style <= EPOCHx_STYLE; style++) {
	 if (!strcmp(equalPtr+1,epochStyles[style])) {
	   opt.epochStyle = style;
	   found = TRUE;
	   break;
	 }
      }
      if (found) continue;
      break;
    }
    /**************************************************************************
    * Check for `Listing Orientation'.
    **************************************************************************/
    if (!strcmp(line,"ORIENTATION")) {
      if (!strcmp(equalPtr+1,"horizontal")) {
	opt.horizontalMode = TRUE;
	continue;
      }
      if (!strcmp(equalPtr+1,"vertical")) {
	opt.horizontalMode = FALSE;
	continue;
      }
      break;
    }
    /**************************************************************************
    * Check for `Listing/CDF Majority'.
    **************************************************************************/
    if (!strcmp(line,"MAJORITY")) {
      if (!strcmp(equalPtr+1,"row")) {
	opt.majority = ROW_MAJOR;
	continue;
      }
      if (!strcmp(equalPtr+1,"column")) {
	opt.majority = COL_MAJOR;
	continue;
      }
      if (!strcmp(equalPtr+1,"input")) {
	opt.majority = INPUT_MAJOR;
	continue;
      }
      break;
    }
    /**************************************************************************
    * Check for `Show Filtered Lines'.
    **************************************************************************/
    if (!strcmp(line,"SHOW_FILTERED")) {
      if (!strcmp(equalPtr+1,"YES")) {
	opt.showFiltered = TRUE;
	continue;
      }
      if (!strcmp(equalPtr+1,"no")) {
	opt.showFiltered = FALSE;
	continue;
      }
      break;
    }
    /**************************************************************************
    * Check for `Listing Spacing'.
    **************************************************************************/
    if (!strcmp(line,"SPACING")) {
      int spacing;
      if (sscanf(equalPtr+1,"%d",&spacing) == 1) {
	if (0 <= spacing) {
	  opt.spacing = spacing;
	  continue;
	}
	else
	  break;
      }
      else
	break;
    }
    /**************************************************************************
    * Check for `Delete Existing CDF'.
    **************************************************************************/
    if (!strcmp(line,"DELETE_EXISTING")) {
      if (!strcmp(equalPtr+1,"YES")) {
	opt.deleteExisting = TRUE;
	continue;
      }
      if (!strcmp(equalPtr+1,"no")) {
	opt.deleteExisting = FALSE;
	continue;
      }
      break;
    }
    /**************************************************************************
    * Check for `Preallocate Variable Records'.
    **************************************************************************/
    if (!strcmp(line,"PREALLOCATE")) {
      if (!strcmp(equalPtr+1,"YES")) {
	opt.preAllocate = TRUE;
	continue;
      }
      if (!strcmp(equalPtr+1,"no")) {
	opt.preAllocate = FALSE;
	continue;
      }
      break;
    }
    break;
  }
  fclose (fp);
  if (linePtr != NULL) {
    DisplayMessage ("Error parsing settings file.", BEEPWAIT1);
    return;
  }
  DisplayMessage ("", NOWAIT);
  return;
}

/******************************************************************************
* SetFilter.
******************************************************************************/

static Logical SetFilter (field, Item)
char *field;
struct ItemStruct *Item;
{
  if (!strcmp(field,"YES")) {
    Item->filterSetting = TRUE;
    Item->inclusive = TRUE;
    return TRUE;
  }
  if (!strcmp(field,"YeS")) {
    Item->filterSetting = TRUE;
    Item->inclusive = TRUE;
    return TRUE;
  }
  if (!strcmp(field,"yEs")) {
    Item->filterSetting = TRUE;
    Item->filterSetting = FALSE;
    return TRUE;
  }
  if (!strcmp(field,"no")) {
    Item->filterSetting = FALSE;
    Item->inclusive = TRUE;
    return TRUE;
  }
  return FALSE;
}

/******************************************************************************
* SetOutput.
******************************************************************************/

static Logical SetOutput (field, Item)
char *field;
struct ItemStruct *Item;
{
  if (!strcmp(field,"YES")) {
    Item->outputSetting = TRUE;
    return TRUE;
  }
  if (!strcmp(field,"no")) {
    Item->outputSetting = FALSE;
    return TRUE;
  }
  return FALSE;
}

/******************************************************************************
* SetWidth.
******************************************************************************/

static Logical SetWidth (field, Item)
char *field;
struct ItemStruct *Item;
{
  int width;
  if (sscanf(field,"%d",&width) != 1) return FALSE;
  if (width < 0) return FALSE;
  Item->width = width;
  return TRUE;
}

/******************************************************************************
* SetMonotonic.
******************************************************************************/

static Logical SetMonotonic (field, Var)
char *field;
struct VarStruct *Var;
{
  if (Var->monotonic != NAmono) {
    if (!strcmp(field,"Increase")) {
      Var->monotonic = INCREASEmono;
      return TRUE;
    }
    if (!strcmp(field,"Decrease")) {
      Var->monotonic = DECREASEmono;
      return TRUE;
    }
    if (!strcmp(field,"False")) {
      Var->monotonic = FALSEmono;
      return TRUE;
    }
    if (!strcmp(field,"Unknown")) {
      Var->monotonic = UNKNOWNmono;
      return TRUE;
    }
    return FALSE;
  }
  return TRUE;
}

/******************************************************************************
* ScanValueField.
******************************************************************************/

static Logical ScanValueField (fieldPtr, dataType, numElems, commaPtr, value)
char *fieldPtr;         /* In: Pointer to beginning of field. */
long dataType;          /* In: Data type of variable. */
long numElems;          /* In: Number of elements of variable. */
char **commaPtr;        /* Out: Pointer (to pointer) to ending comma. */
void **value;           /* Out: Pointer (to pointer) to value. */
{
  if (*fieldPtr == ',') {
    if (*value != NULL) {
      FreeMemory (*value, FatalError);
      *value = NULL;
    }
    *commaPtr = fieldPtr;
    return TRUE;
  }
  if (*fieldPtr == SETTINGS_DELIM) {
    char *delimPtr = EndingDelimiter (fieldPtr);
    if (delimPtr == NULL) return FALSE;
    *commaPtr = delimPtr + 1;
    if (**commaPtr != ',') return FALSE;
    **commaPtr = NUL;
    if (STRINGdataType(dataType)) {
      RemoveDelimiters (fieldPtr);
      if (strlen(fieldPtr) == numElems) {
	if (*value == NULL) {
	  size_t nBytes = (size_t) (numElems + 1);
	  *value = (char *) AllocateMemory (nBytes, FatalError);
	}
	strcpyX (*value, fieldPtr, 0);
      }
    }
    return TRUE;
  }
  if (*fieldPtr == '[') {
    void *newValue; long newNumElems;
    char *endPtr = strchr (fieldPtr + 1, ']');
    if (endPtr == NULL) return FALSE;
    *commaPtr = endPtr + 1;
    if (**commaPtr != ',') return FALSE;
    fieldPtr++;
    *endPtr = NUL;
    if (!STRINGdataType(dataType)) {
      if (!DecodeValues(fieldPtr,BOO(EPOCHdataType(dataType),
				     CDF_REAL8,dataType),
			&newNumElems,&newValue,0)) return FALSE;
      if (newNumElems != numElems) {
	FreeMemory (newValue, FatalError);
	return FALSE;
      }
      if (*value != NULL) FreeMemory (*value, FatalError);
      *value = newValue;
    }
    return TRUE;
  }
  return FALSE;
}

/******************************************************************************
* WriteDelimitedString.
******************************************************************************/

static void WriteDelimitedString (fp, delim, string)
FILE *fp;
int delim;
char *string;
{
  int i;
  fprintf (fp, "%c", (char) delim);
  for (i = 0; string[i] != NUL; i++) {
     if (string[i] == (char) delim) fprintf (fp, "%c", (char) delim);
     fprintf (fp, "%c", string[i]);
  }
  fprintf (fp, "%c", (char) delim);
  return;
}

/******************************************************************************
* EndingDelimiter.
******************************************************************************/

static char *EndingDelimiter (string)
char *string;
{
  int i; char delim;
  for (i = 1, delim = string[0]; string[i] != NUL; i++) {
     if (string[i] == delim) {
       if (string[i+1] != delim)
	 return &(string[i]);
       else
	 i++;
     }
  }
  return NULL;
}

/******************************************************************************
* RemoveDelimiters.
******************************************************************************/

static void RemoveDelimiters (string)
char *string;
{
  int to = 0, from = 1; char delim = string[0];
  for (;;) {
     if (string[from] == NUL) {
       string[to] = NUL;
       return;
     }
     if (string[from] == delim)
       if (string[from+1] == delim) {
	 string[to++] = delim;
	 from += 2;
       }
       else
	 from++;
     else
       string[to++] = string[from++];
  }
}

/******************************************************************************
* SetItemMonotonicities.
******************************************************************************/

void SetItemMonotonicities () {
  struct ItemStruct *Item;
  DisplayMessage ("Setting variable monotonicities.", NOWAIT);
  for (Item = itemHead; Item != NULL; Item = Item->nextItem) {
     if (Item->type == VARIABLEt) {
       if (Item->Var->monotonic != NAmono) {
	 if (!SetVarMonotonicity(Item->Var)) return;
       }
     }
  }
  DisplayMessage ("", NOWAIT);
  return;
}

/******************************************************************************
* SetVarMonotonicity.
******************************************************************************/

Logical SetVarMonotonicity (Var)
struct VarStruct *Var;
{
  static long indices[CDF_MAX_DIMS] = { 0,0,0,0,0,0,0,0,0,0 };
  void *prevValue = NULL; CDFstatus status;
  Var->monotonic = UNKNOWNmono;
  status = CDFlib (SELECT_, BOO(Var->zVar,zVAR_,rVAR_), Var->varN,
			    BOO(Var->zVar,zVAR_SEQPOS_,
					  rVAR_SEQPOS_), 0L, indices,
		   NULL_);
  DisplayStatus (status, "setting sequential position");
  if (StatusBAD(status)) return FALSE;
  status = CDFlib (GET_, BOO(Var->zVar,zVAR_SEQDATA_,
				       rVAR_SEQDATA_), Var->value,
		   NULL_);
  while (StatusOK(status)) {
    if (prevValue == NULL) {
      prevValue = AllocateMemory (Var->nValueBytes, FatalError);
      ASSIGNx (prevValue, Var->value, Var->dataType, Var->numElems);
    }
    else {
      switch (Var->monotonic) {
	case UNKNOWNmono:
	  if (GTx(Var->value,prevValue,Var->dataType,Var->numElems)) {
	    Var->monotonic = INCREASEmono;
	    break;
	  }
	  if (LTx(Var->value,prevValue,Var->dataType,Var->numElems)) {
	    Var->monotonic = DECREASEmono;
	    break;
	  }
	  Var->monotonic = FALSEmono;
	  FreeMemory (prevValue, FatalError);
	  return TRUE;
	case INCREASEmono:
	  if (LTx(Var->value,prevValue,Var->dataType,Var->numElems)) {
	    Var->monotonic = FALSEmono;
	    FreeMemory (prevValue, FatalError);
	    return TRUE;
	  }
	  break;
	case DECREASEmono:
	  if (GTx(Var->value,prevValue,Var->dataType,Var->numElems)) {
	    Var->monotonic = FALSEmono;
	    FreeMemory (prevValue, FatalError);
	    return TRUE;
	  }
	  break;
      }
      ASSIGNx (prevValue, Var->value, Var->dataType, Var->numElems);
    }
    status = CDFlib (GET_, BOO(Var->zVar,zVAR_SEQDATA_,
					 rVAR_SEQDATA_), Var->value,
		     NULL_);
  }
  if (prevValue != NULL) FreeMemory (prevValue, FatalError);
  if (status != END_OF_VAR) {
    DisplayStatus (status, "reading variable value");
    if (StatusBAD(status)) return FALSE;
  }
  return TRUE;
}

/******************************************************************************
* BuildExportList.
******************************************************************************/

void BuildExportList (exportHead)
struct ItemStruct **exportHead;
{
  struct ItemStruct *Item, *exportTail;
  for (Item = itemHead, *exportHead = NULL, exportTail = NULL;
       Item != NULL; Item = Item->nextItem) {
     if (Item->outputSetting || (opt.overallFilter && Item->filterSetting)) {
       if (*exportHead == NULL)
	 *exportHead = exportTail = Item;
       else
	 exportTail = exportTail->nextExport = Item;
       Item->nextExport = NULL;
       Item->output = Item->outputSetting;
       Item->filter = BOO(opt.overallFilter,Item->filterSetting,FALSE);
     }
  }
  return;
}

/******************************************************************************
* RemoveExportItems.
******************************************************************************/

void RemoveExportItems (exportHead)
struct ItemStruct **exportHead;
{
  struct ItemStruct *Item = *exportHead, *prevItem = NULL;
  while (Item != NULL) {
    if (!Item->output && !Item->filter) {
      if (prevItem == NULL)
	*exportHead = Item->nextExport;
      else
	prevItem->nextExport = Item->nextExport;
    }
    else
      prevItem = Item;
    Item = Item->nextExport;
  }
  return;
}

/******************************************************************************
* FreeExportBuffers.
******************************************************************************/

void FreeExportBuffers (exportHead)
struct ItemStruct *exportHead;
{
  struct ItemStruct *Item;
  for (Item = exportHead; Item != NULL; Item = Item->nextExport) {
     if (Item->Var->buffer != NULL) FreeMemory (Item->Var->buffer, FatalError);
  }
  return;
}

/******************************************************************************
* FirstRecord.
******************************************************************************/

long FirstRecord (recX, scalarHead, filteringScalars, recCount)
long recX;
struct ItemStruct *scalarHead;
Logical filteringScalars;
long recCount;
{
  struct ItemStruct *Item; long firstX;
  if (!filteringScalars) return recX;
  for (firstX = recX; firstX < recCount; firstX++) {
     Logical passes = TRUE;
     for (Item = scalarHead; Item != NULL; Item = Item->nextScalar) {
	if (Item->filter) {
	  Byte *value = Item->Var->buffer +
		        (size_t) (firstX * Item->Var->nValueBytes);
	  if (!VarPassesMin(Item,value) || !VarPassesMax(Item,value)) {
	    passes = FALSE;
	    break;
	  }
	}
     }
     if (passes) return firstX;
  }
  return NO_RECORD;
}

/******************************************************************************
* LastRecord.
******************************************************************************/

long LastRecord (firstX, scalarHead, filteringScalars, recCount)
long firstX;
struct ItemStruct *scalarHead;
Logical filteringScalars;
long recCount;
{
  struct ItemStruct *Item; long recX;
  if (!filteringScalars) return (recCount - 1);
  for (recX = firstX + 1; recX < recCount; recX++) {
     for (Item = scalarHead; Item != NULL; Item = Item->nextScalar) {
	if (Item->filter) {
	  Byte *value = Item->Var->buffer +
		        (size_t) (recX * Item->Var->nValueBytes);
	  if (!VarPassesMin(Item,value) ||
	      !VarPassesMax(Item,value)) return (recX - 1);
	}
     }
  }
  return (recCount - 1);
}

/******************************************************************************
* FilterBuffer.
******************************************************************************/

void FilterBuffer (Item, buffer, nValues)
struct ItemStruct *Item;
Byte *buffer;
long nValues;
{
  long valueN; Byte *value;
  for (valueN = 0, value = buffer; valueN < nValues;
       valueN++, value += Item->Var->nValueBytes) {
     if (!VarPassesMin(Item,value) || !VarPassesMax(Item,value)) {
       if (USEFILL(Item->Var,opt))
	 ASSIGNx (value, Item->Var->fill,
		  Item->Var->dataType, Item->Var->numElems);
       else
	 ASSIGNx (value, Item->Var->pad,
		  Item->Var->dataType, Item->Var->numElems);
     }
  }
  return;
}

/******************************************************************************
* FilterHypers.
******************************************************************************/

void FilterHypers (hyperHead, nValues)
struct ItemStruct *hyperHead;
long nValues;
{
  long valueN; Byte *value; struct ItemStruct *Item;
  for (valueN = 0; valueN < nValues; valueN++) {
     for (Item = hyperHead; Item != NULL; Item = Item->nextHyper) {
	if (Item->filter) {
	  value = VALUEinBUFFER (Item->Var, valueN);
	  if (!VarPassesMin(Item,value) || !VarPassesMax(Item,value)) {
	    for (Item = hyperHead; Item != NULL; Item = Item->nextHyper) {
	       if (Item->output) {
		 value = VALUEinBUFFER (Item->Var, valueN);
		 if (USEFILL(Item->Var,opt))
		   ASSIGNx (value, Item->Var->fill, Item->Var->dataType,
			    Item->Var->numElems);
		 else
		   ASSIGNx (value, Item->Var->pad, Item->Var->dataType,
			    Item->Var->numElems);
	       }
	    }
	    break;
	  }
	}
     }
  }
  return;
}

/******************************************************************************
* AbortListing.
******************************************************************************/

Logical AbortListing (oFp, line)
FILE *oFp;
char *line;
{
  int key; AOSs2A (keyDefsBlank, "", "")
  if (read_input(
#if defined(CURSESui)
		 EWmsg->wid,
#endif
			    &key,PASSTHRUri,FALSE)) {
    if (key == ABORTkey_EXPORT) {
      EWkey->eLines = keyDefsBlank;
      EditWindow (UPDATEew, EWkey, (Logical) TRUE);
      DisplayMessage ("Aborting at user's request.", NOBEEPWAIT1);
      DisplayMessage ("Listing file is not complete.", BEEPWAIT);
      if (fclose(oFp) == EOF) {
	DisplayMessage ("Error closing listing file.", BEEPWAIT);
      }
      FreeMemory (line, FatalError);
      return TRUE;
    }
    else
      EditWindow (BEEPew, EWmsg);
  }
  return FALSE;
}

/******************************************************************************
* AbortCDF.
******************************************************************************/

Logical AbortCDF (exportHead)
struct ItemStruct *exportHead;
{
  int key; AOSs2A (keyDefsBlank, "", "")
  if (read_input(
#if defined(CURSESui)
		 EWmsg->wid,
#endif
			    &key,PASSTHRUri,FALSE)) {
    if (key == ABORTkey_EXPORT) {
      EWkey->eLines = keyDefsBlank;
      EditWindow (UPDATEew, EWkey, (Logical) TRUE);
      DisplayMessage ("Aborting at user's request.", NOBEEPWAIT1);
      DisplayMessage ("CDF is not complete.", BEEPWAIT);
      FreeExportBuffers (exportHead);
      return TRUE;
    }
    else
      EditWindow (BEEPew, EWmsg);
  }
  return FALSE;
}

/******************************************************************************
* PreAllocateRecords.
******************************************************************************/

Logical PreAllocateRecords (inID, outID, scalarHead, hyperHead, firstRec,
			    lastRec)
CDFid inID;
CDFid outID;
struct ItemStruct *scalarHead;
struct ItemStruct *hyperHead;
long *firstRec;
long *lastRec;
{
  long nRecords = *lastRec - *firstRec + 1, newFirstRec, newLastRec, nHypers;
  long nValues, hyperN, recX, firstX, lastX, recF, recL;
  CDFstatus status; struct ItemStruct *Item; struct HyperStruct hyper;
  size_t nBytes, *nValueBytes; Byte ***handles; struct GroupStruct groups;
  int filterCount, filterX;
  AOSs1A (allocMsg,"Allocating variable records.")
  /****************************************************************************
  * Count number of scalars still being filtered.  Note that monotonic
  * scalars will have already been considered when the first/last record was
  * determined (and filtering turned off for them).
  ****************************************************************************/
  DisplayPctComplete (NO_PCT, allocMsg[0]);
  for (Item = scalarHead, filterCount = 0;
       Item != NULL; Item = Item->nextScalar) if (Item->filter) filterCount++;
  /****************************************************************************
  * If no scalars are being filtered, allocate records based on the current
  * first/last records.
  ****************************************************************************/
  if (filterCount == 0) {
    for (Item = scalarHead; Item != NULL; Item = Item->nextScalar) {
       if (Item->output) {
	 Item->Var->oRecords = COUNTx(Item->Var->maxRec,*firstRec,*lastRec);
       }
    }
    for (Item = hyperHead; Item != NULL; Item = Item->nextHyper) {
       if (Item->output) {
	 Item->Var->oRecords = COUNTx(Item->Var->maxRec,*firstRec,*lastRec);
       }
    }
    if (!AllocateRecords(outID,scalarHead,hyperHead)) return FALSE;
    return TRUE;
  }
  /****************************************************************************
  * Allocate buffers for the scalars.
  ****************************************************************************/
  nBytes = filterCount * sizeof(Byte **);
  handles = (Byte ***) AllocateMemory (nBytes, FatalError);
  nBytes = filterCount * sizeof(size_t);
  nValueBytes = (size_t *) AllocateMemory (nBytes, FatalError);
  for (Item = scalarHead, filterX = 0;
       Item != NULL; Item = Item->nextScalar) {
     if (Item->filter) {
       handles[filterX] = &(Item->Var->buffer);
       nValueBytes[filterX++] = Item->Var->nValueBytes;
     }
  }
  AllocateBuffers (nRecords, 0L, NULL, &groups, filterCount, 0, handles,
		   nValueBytes, ROWmajor(inMajority), 1, FatalError);
  FreeMemory (handles, FatalError);
  FreeMemory (nValueBytes, FatalError);
  /****************************************************************************
  * Initialize new first/last records and output record counts (for those
  * variables being output).
  ****************************************************************************/
  newFirstRec = *lastRec + 1;
  newLastRec = *firstRec - 1;
  for (Item = scalarHead; Item != NULL; Item = Item->nextScalar) {
     if (Item->output) Item->Var->oRecords = 0;
  }
  for (Item = hyperHead; Item != NULL; Item = Item->nextHyper) {
     if (Item->output) Item->Var->oRecords = 0;
  }
  /****************************************************************************
  * Read values for scalars being filtered and count the number of records
  * that pass the filters.  Also update the first/last records if needed.
  ****************************************************************************/
  InitHyperParms (&hyper, &groups, 0L, &nHypers, &nValues);
  for (hyperN = 0; hyperN < nHypers; hyperN++) {
     /*************************************************************************
     * Hyper read scalar values.
     *************************************************************************/
     for (Item = scalarHead; Item != NULL; Item = Item->nextScalar) {
	if (Item->filter) {
	  status = HYPERget (inID, Item->Var->varN, Item->Var->zVar,
			     *firstRec + hyper.recNumber, hyper.recCount,
			     dimIndices_0, dimCounts_1, Item->Var->buffer);
	  DisplayStatus (status, readingCDF);
	  if (StatusBAD(status)) {
	    for (Item = scalarHead; Item != NULL; Item = Item->nextScalar) {
	       FreeMemory (Item->Var->buffer, FatalError);
	    }
	    return FALSE;
	  }
	}
     }
     /*************************************************************************
     * Until no more records...
     *************************************************************************/
     for (recX = 0;;) {
	/**********************************************************************
	* Determine next range of records in this hyper read that pass the
	* filters.
	**********************************************************************/
	firstX = FirstRecord (recX, scalarHead, TRUE, hyper.recCount);
	if (firstX == NO_RECORD) break;
	lastX = LastRecord (firstX, scalarHead, TRUE, hyper.recCount);
	/**********************************************************************
	* Calculate the absolute record number of this range and set the new
	* first/last records accordingly.
	**********************************************************************/
	recF = *firstRec + hyper.recNumber + firstX;
	recL = *firstRec + hyper.recNumber + lastX;
	newFirstRec = MINIMUM(newFirstRec,recF);
	newLastRec = MAXIMUM(newLastRec,recL);
	/**********************************************************************
	* Add to the output record count of those variables being output.
	**********************************************************************/
	for (Item = scalarHead; Item != NULL; Item = Item->nextScalar) {
	   if (Item->output) {
	     Item->Var->oRecords += COUNTx(Item->Var->maxRec,recF,recL);
	   }
	}
	for (Item = hyperHead; Item != NULL; Item = Item->nextHyper) {
	   if (Item->output) {
	     Item->Var->oRecords += COUNTx(Item->Var->maxRec,recF,recL);
	   }
	}
	/**********************************************************************
	* Increment to check for the next range of records.
	**********************************************************************/
	recX = lastX + 1;
	if (recX == hyper.recCount) break;
     }
     /*************************************************************************
     * Increment to next hyper.
     *************************************************************************/
     IncrHyperParms (&hyper, &groups, 0L, ROWmajor(inMajority), &nValues);
  }
  /****************************************************************************
  * Free memory used.
  ****************************************************************************/
  for (Item = scalarHead; Item != NULL; Item = Item->nextScalar) {
     FreeMemory (Item->Var->buffer, FatalError);
  }
  /****************************************************************************
  * Check/set the new first/last records.
  ****************************************************************************/
  if (newLastRec < newFirstRec) {
    DisplayMessage ("No records in valid range.", BEEPWAIT1);
    return FALSE;
  }
  *firstRec = newFirstRec;
  *lastRec = newLastRec;
  /****************************************************************************
  * Allocate the records.
  ****************************************************************************/
  if (!AllocateRecords(outID,scalarHead,hyperHead)) return FALSE;
  return TRUE;
}

/******************************************************************************
* AllocateRecords.
******************************************************************************/

static Logical AllocateRecords (outID, scalarHead, hyperHead)
CDFid outID;
struct ItemStruct *scalarHead;
struct ItemStruct *hyperHead;
{
  CDFstatus status; struct ItemStruct *Item; int varCount = 0, varNum = 0;
  AOSs1A (allocMsg,"Allocating variable records.")
#if defined(vms)
  long rItem = rVAR_INITIALRECS_;
  long zItem = zVAR_INITIALRECS_;
#else
  long rItem = rVAR_ALLOCATERECS_;
  long zItem = zVAR_ALLOCATERECS_;
#endif
  /****************************************************************************
  * Count number of variables being allocated.
  ****************************************************************************/
  for (Item = scalarHead; Item != NULL; Item = Item->nextScalar) {
     if (Item->output && Item->Var->oRecords > 0) varCount++;
  }
  for (Item = hyperHead; Item != NULL; Item = Item->nextHyper) {
     if (Item->output && Item->Var->oRecords > 0) varCount++;
  }
  /****************************************************************************
  * Select output CDF.
  ****************************************************************************/
  status = CDFlib (SELECT_, CDF_, outID,
		   NULL_);
  DisplayStatus (status, "selecting output CDF");
  if (StatusBAD(status)) return FALSE;
  /****************************************************************************
  * Allocate records for scalar variables being output.  Also check that one
  * or more records will be written to the variable.
  ****************************************************************************/
  for (Item = scalarHead; Item != NULL; Item = Item->nextScalar) {
     if (Item->output && Item->Var->oRecords > 0) {
       struct VarStruct *Var = Item->Var;
       status = CDFlib (SELECT_, BOO(Var->zVar,zVAR_,rVAR_), Var->varNo,
			PUT_, BOO(Var->zVar,zItem,rItem), Var->oRecords,
			NULL_);
       DisplayStatus (status, "allocating output records");
       if (StatusBAD(status)) return FALSE;
       DisplayPctComplete (PCT(varNum,varCount,1,1), allocMsg[0]);
       varNum++;
     }
  }
  /****************************************************************************
  * Allocate records for hyper variables being output.  Also check that one
  * or more records will be written to the variable.
  ****************************************************************************/
  for (Item = hyperHead; Item != NULL; Item = Item->nextHyper) {
     if (Item->output && Item->Var->oRecords > 0) {
       struct VarStruct *Var = Item->Var;
       status = CDFlib (SELECT_, BOO(Var->zVar,zVAR_,rVAR_), Var->varNo,
			PUT_, BOO(Var->zVar,zItem,rItem), Var->oRecords,
			NULL_);
       DisplayStatus (status, "allocating output records");
       if (StatusBAD(status)) return FALSE;
       DisplayPctComplete (PCT(varNum,varCount,1,1), allocMsg[0]);
       varNum++;
     }
  }
  return TRUE;
}
