/******************************************************************************
*
*  NSSDC/CDF                                                       CDFexport.
*
*  Version 1.2, 6-Jun-96, Hughes STX.
*
*  Modification history:
*
*   V1.0  13-Sep-95, J Love     Original version.
*   V1.0a 28-Sep-95, J Love	Macintosh dialog filtering.  Outline default
*				button.
*   V1.1  21-Dec-95, J Love	OSF/1 V3.2.
*   V1.1a 20-May-96, J Love	`OptionMenu' fix for Alpha/OpenVMS.
*   V1.2   6-Jun-96, J Love	QNX port.
*
******************************************************************************/

#define CDFXP
#include "cdfxp.h"

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

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

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

MAIN {
#if defined(mac)
  strcpyX (pgmName, "CDFexport", MAX_PROGRAM_NAME_LEN);
  MacExecuteFSI (ExportCDFs, ExportQOPs);
  if (FreeMemory(NULL,FatalError) > 0) DisplayWarning ("Abandoned buffers.");
  return EXIT_SUCCESS_;
#else
  Logical success; int nBuffers;
  strcpy (pgmName, "CDFexport");
  success = ExportCDFs (argc, argv);
  nBuffers = FreeMemory(NULL,FatalError);
  if (nBuffers > 0) {
    char msg[MAX_MESSAGE_TEXT_LEN+1];
    sprintf (msg, "there %s %d abandoned buffer%s.",
	     BOO(nBuffers == 1,"was","were"), nBuffers,
	     BOO(nBuffers == 1,"","s"));
    DisplayWarning (msg);
  }
  return BOO(success,EXIT_SUCCESS_,EXIT_FAILURE_);
#endif
}

/******************************************************************************
* ExportCDFs.
******************************************************************************/

Logical ExportCDFs (argC, argV)
int argC;
char *argV[];
{
  char CDFspec[DU_MAX_PATH_LEN+1];
  long zMode; Logical prompt, negToPosFp0;
  static char *eLinesBeta[] = {
    "This is a beta release of CDFexport.  Please report any problems",
    "encountered or changes you would like to see to CDFsupport",
    "(cdfsupport@nssdca.gsfc.nasa.gov).  CDFexport should currently",
    "provide the same functionality as CDFlist with added functionality",
    "in the future.  CDFexport will eventually replace CDFlist in the",
    "CDF distribution."
  };
  static char *tLinesBeta[] = {
    "Enter any key to continue..."
  };
  static int exitCharsBeta[] = { NUL };
  STATIC_ struct EditWindowStruct EWbeta = {
    " CDFexport, beta release ", 7, 6, 68, 0, NULL, 6, 6, eLinesBeta, 6, 1,
    tLinesBeta, FALSE, TRUE, exitCharsBeta, REFRESHkey_FSI, NUL, NUL, NUL,
    NUL, NUL, NUL
  };
  /****************************************************************************
  * Scan parameters/options/qualifiers.
  ****************************************************************************/
  switch (argC) {
    case 1:
      create_pasteboard ();
      set_cursor_mode (CURSORoff);
      OnlineHelpWindow ("cdfxp.ilh", OLHhelpID);
      delete_pasteboard (ERASE);
      set_cursor_mode (CURSORon);
      return TRUE;
    default: {
      QOP *qop;
      static char *validQuals[] = {
	"initial", "prompt", "noprompt", "cache", "zmode", "neg2posfp0",
	"noneg2posfp0", "report", "statistics", "nostatistics", NULL
      };
      static int optRequired[] = {
	TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, 0
      };
      static char *reportTokens[] = { "errors", "warnings", "informationals" };
      Logical qopError = FALSE;

      qop = Qop (argC, argV, validQuals, optRequired);
      if (qop == NULL) return FALSE;

      /************************************************************************
      * Check for a parameter (the CDF[s]) and the `prompt' qualifier.
      ************************************************************************/
      qopError = qopError | !TFqualifier(qop,&prompt,PROMPTqual,NOPROMPTqual,
					 DEFAULTpromptEXPORT,"prompt");
      switch (qop->Nparms) {
	case 0:
	  if (prompt) {
	    strcpyX (CDFspec, CURRENTDIRECTORY, DU_MAX_PATH_LEN);
	    AppendToDir (CDFspec, "*");
	  }
	  else {
	    DisplayError ("Missing parameter.");
	    qopError = TRUE;
	  }
	  break;
	case 1:
	  strcpyX (CDFspec, qop->parms[CDFparm], DU_MAX_PATH_LEN);
	  break;
	default:
	  DisplayError ("Too many parameters.");
	  qopError = TRUE;
      }
      /************************************************************************
      * Check for `neg2posfp0' and `statistics' qualifiers.
      ************************************************************************/
      qopError = qopError | !TFqualifier(qop,&negToPosFp0,NEG2POSFP0qual,
					 NONEG2POSFP0qual,DEFAULT_NEGtoPOSfp0,
					 "neg2posfp0");
      qopError = qopError | !TFqualifier(qop,&dumpStats,STATSqual,NOSTATSqual,
					 DEFAULTstatsEXPORT,"statistics");
      /************************************************************************
      * Check for `cache' qualifier.
      ************************************************************************/
      if (qop->qualEntered[CACHEqual]) {
	if (sscanf(qop->qualOpt[CACHEqual],"%ld",&cacheSize) != 1) {
	  DisplayError ("Illegal cache size.");
	  qopError = TRUE;
	}
	if (cacheSize < 0) {
	  DisplayError ("Illegal cache size.");
	  qopError = TRUE;
	}
      }
      else
	cacheSize = 0;
      /***********************************************************************
      * 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 `zMode' qualifier.
      ************************************************************************/
      if (qop->qualEntered[ZMODEqual]) {
	if (!strcmp(qop->qualOpt[ZMODEqual],"0"))
	  zMode = zMODEoff;
	else
	  if (!strcmp(qop->qualOpt[ZMODEqual],"1"))
	    zMode = zMODEon1;
	  else
	    if (!strcmp(qop->qualOpt[ZMODEqual],"2"))
	      zMode = zMODEon2;
	    else {
	      DisplayError ("Illegal zMode.");
	      qopError = TRUE;
	    }
      }
      else
	zMode = DEFAULTzModeEXPORT;
      /************************************************************************
      * Set the special options to their default values...
      ************************************************************************/
      opt.eachFilter = DEFAULTeachFilterEXPORT;
      opt.overallFilter = DEFAULToverallFilterEXPORT;
      opt.useFills = DEFAULTfillvalEXPORT;
      opt.useFORMAT = DEFAULTformatEXPORT;
      opt.useFILLVAL = DEFAULTfillvalEXPORT;
      opt.useVALIDMIN = DEFAULTvalidminEXPORT;
      opt.useVALIDMAX = DEFAULTvalidmaxEXPORT;
      opt.useMONOTON = DEFAULTmonotonEXPORT;
      opt.singleFile = DEFAULTsingleEXPORT;
      opt.encoding = BOO(DEFAULTnetworkEXPORT,NETWORK_ENCODING,HOST_ENCODING);
      opt.epochStyle = DEFAULTepochEXPORT;
      opt.horizontalMode = DEFAULThorizontalEXPORT;
      opt.showRecord = DEFAULTrecordEXPORT;
      opt.showIndices = DEFAULTindicesEXPORT;
      opt.majority = DEFAULTmajorityEXPORT;
      opt.showFiltered = DEFAULTshowEXPORT;
      opt.exclusive = DEFAULTexclusiveEXPORT;
      opt.outputItem = DEFAULToutputEXPORT;
      opt.deleteExisting = DEFAULTdeleteEXPORT;
      opt.preAllocate = DEFAULTpreAllocateEXPORT;
      opt.spacing = DEFAULTspacingEXPORT;
      /************************************************************************
      * ...and then check for `initial' qualifier.
      ************************************************************************/
      if (qop->qualEntered[INITIALqual]) {
	char *option, *parenPtr, *commaPtr, *nextPtr; int which;
	static char *validOptions[] = {
	  "format", "noformat", "filter", "nofilter", "fillval", "nofillval",
	  "single", "multi", "host", "network", "epoch", "epoch1", "epoch2",
	  "epoch3", "epochf", "epochx", "horizontal", "vertical", "record",
	  "norecord", "indices", "noindices", "row", "column", "validmin",
	  "novalidmin", "monoton", "nomonoton", "validmax", "novalidmax",
	  "fills", "nofills", "exclusive", "noexclusive", "output", "nooutput",
	  "delete", "nodelete", "preallocate", "nopreallocate", NULL
	};
	/**********************************************************************
	* If VMS-style QOPs, "remove" beginning and ending parenthesis.  Also,
	* remove any white space in the list of options.
	**********************************************************************/
	if (qop->qualOpt[INITIALqual][0] == '(') {
	  option = &(qop->qualOpt[INITIALqual][1]);
	  parenPtr = strchr (option, ')');
	  if (parenPtr != NULL) *parenPtr = NUL;
	}
	else
	  option = qop->qualOpt[INITIALqual];
	RemoveWhiteSpace (option);
	/**********************************************************************
	* Scan comma-separated list of options.
	**********************************************************************/
	while (*option != NUL) {
	  /********************************************************************
	  * Location the end of the current option.  This will be either a
	  * comma or a NUL (if the last option).  Also set the pointer to the
	  * next option.
	  ********************************************************************/
	  commaPtr = strchr (option, ',');
	  if (commaPtr != NULL) {
	    *commaPtr = NUL;
	    nextPtr = commaPtr + 1;
	  }
	  else
	    nextPtr = option + strlen(option);
	  /********************************************************************
	  * Validate/set the current option.
	  ********************************************************************/
	  which = MatchOption (option, validOptions);
	  switch (which) {
	    case UNKNOWNopt: {
	      char msg[MAX_MESSAGE_TEXT_LEN+1];
	      sprintf (msg, "Unknown `initial' option: \"%s\"", option);
	      DisplayError (msg);
	      qopError = TRUE;
	      break;
	    }
	    case AMBIGUOUSopt: {
	      char msg[MAX_MESSAGE_TEXT_LEN+1];
	      sprintf (msg, "Ambiguous `initial' option: \"%s\"", option);
	      DisplayError (msg);
	      qopError = TRUE;
	      break;
	    }
	    case FILTERopt: opt.eachFilter = TRUE; break;
	    case NOFILTERopt: opt.eachFilter = FALSE; break;
	    case FILLSopt: opt.useFills = TRUE; break;
	    case NOFILLSopt: opt.useFills = FALSE; break;
	    case FORMATopt: opt.useFORMAT = TRUE; break;
	    case NOFORMATopt: opt.useFORMAT = FALSE; break;
	    case FILLVALopt: opt.useFILLVAL = TRUE; break;
	    case NOFILLVALopt: opt.useFILLVAL = FALSE; break;
	    case VALIDMINopt: opt.useVALIDMIN = TRUE; break;
	    case NOVALIDMINopt: opt.useVALIDMIN = FALSE; break;
	    case VALIDMAXopt: opt.useVALIDMAX = TRUE; break;
	    case NOVALIDMAXopt: opt.useVALIDMAX = FALSE; break;
	    case MONOTONopt: opt.useMONOTON = TRUE; break;
	    case NOMONOTONopt: opt.useMONOTON = FALSE; break;
	    case SINGLEopt: opt.singleFile = TRUE; break;
	    case MULTIopt: opt.singleFile = FALSE; break;
	    case HOSTopt: opt.encoding = HOST_ENCODING; break;
	    case NETWORKopt: opt.encoding = NETWORK_ENCODING; break;
	    case EPOCHopt: opt.epochStyle = EPOCH0_STYLE; break;
	    case EPOCH1opt: opt.epochStyle = EPOCH1_STYLE; break;
	    case EPOCH2opt: opt.epochStyle = EPOCH2_STYLE; break;
	    case EPOCH3opt: opt.epochStyle = EPOCH3_STYLE; break;
	    case EPOCHFopt: opt.epochStyle = EPOCHf_STYLE; break;
	    case EPOCHXopt: opt.epochStyle = EPOCHx_STYLE; break;
	    case HORIZONTALopt: opt.horizontalMode = TRUE; break;
	    case VERTICALopt: opt.horizontalMode = FALSE; break;
	    case RECORDopt: opt.showRecord = TRUE; break;
	    case NORECORDopt: opt.showRecord = FALSE; break;
	    case INDICESopt: opt.showIndices = TRUE; break;
	    case NOINDICESopt: opt.showIndices = FALSE; break;
	    case ROWopt: opt.majority = ROW_MAJOR; break;
	    case COLUMNopt: opt.majority = COL_MAJOR; break;
	    case EXCLUSIVEopt: opt.exclusive = TRUE; break;
	    case NOEXCLUSIVEopt: opt.exclusive = FALSE; break;
	    case OUTPUTopt: opt.outputItem = TRUE; break;
	    case NOOUTPUTopt: opt.outputItem = FALSE; break;
	    case DELETEopt: opt.deleteExisting = TRUE; break;
	    case NODELETEopt: opt.deleteExisting = FALSE; break;
	    case PREALLOCATEopt: opt.preAllocate = TRUE; break;
	    case NOPREALLOCATEopt: opt.preAllocate = FALSE; break;
	  }
	  /********************************************************************
	  * Move to the next option.
	  ********************************************************************/
	  option = nextPtr;
	}
      }
      FreeMemory (qop, FatalError);
      if (qopError) return FALSE;
      break;
    }
  }
  /****************************************************************************
  * Initialize screen.
  ****************************************************************************/
  create_pasteboard ();
  set_cursor_mode (CURSORoff);
  /****************************************************************************
  * Display `beta' message.
  ****************************************************************************/
  EditWindow (NEWew, &EWbeta, (Logical) TRUE);
  EditWindow (READew, &EWbeta);
  EditWindow (DELETEew, &EWbeta);
  /****************************************************************************
  * Export from CDFs until the cows come home.
  ****************************************************************************/
  CDFexportMenu (CDFspec, prompt, zMode, negToPosFp0);
  /****************************************************************************
  * Cleanup screen and return.
  ****************************************************************************/
  delete_pasteboard (ERASE);
  set_cursor_mode (CURSORon);
  return TRUE;
}

/******************************************************************************
* CDFexportMenu.
******************************************************************************/

void CDFexportMenu (iniSpec, prompt, zMode, negToPosFp0)
char *iniSpec;          /* Initial specification. */
Logical prompt;         /* Was the `prompt' qualifier specified? */
long zMode;             /* zMode to use. */
Logical negToPosFp0;    /* TRUE if -0.0 should be converted to 0.0. */
{
  char path[CDFfieldLEN+1];
  static Logical first = TRUE;
  /****************************************************************************
  * CDFwindow definitions.
  ****************************************************************************/
  AOSs2A (cdfLines,
    "CDF(s):                                                                       ",
    BLANKSforHEADER)
  static int cdfLineNs[] = { CDFfieldLINEn };
  static int cdfCols[] = { CDFfieldCOLn };
  static int cdfLens[] = { CDFfieldLEN };
  static int cdfExits[] = { ENTERkey_EXPORT, HELPkey_FSI, EXITkey_FSI,
			    NUL };
  STATIC_ struct FieldWindowStruct FWcdf_ = {
    0, 0, SCREEN_WIDTH, " CDFexport ", 0, NULL, 2, cdfLines, 1, cdfLineNs,
    cdfCols, cdfLens, 2, 0, NULL, cdfExits, REFRESHkey_FSI, FALSE, NUL, NUL,
    INSERTorOVERkey_FSI
  };
  /****************************************************************************
  * SelectionWindow definitions.
  ****************************************************************************/
  static char varLabel[] = " SelectionWindow, part 0 ";
  static char *varHeader[] = { "" };
  STATIC_ struct ItemWindowStruct IWsel_ = {
    4, 0, SCREEN_WIDTH, varLabel, 1, varHeader, 0, NULL, 0, NULL, NULL, NULL,
    9, 0, NULL, NULL, REFRESHkey_FSI, TRUE, NSkey_FSI, PSkey_FSI
  };
  /****************************************************************************
  * KeyWindow definitions.
  ****************************************************************************/
  AOSs2B (keyDefsBlank, "", "")
  AOSs2C (keyDefsPrompt, "Open: ________  Help: ________  Exit: ________", "")
  STATIC_ struct EditWindowStruct EWkey_ = {
    " KeyDefinitions ", 17, 0, SCREEN_WIDTH, 0, NULL, 2, 2, keyDefsBlank, 2,
    0, NULL, FALSE, TRUE, NULL, NUL, NUL, NUL, NUL, NUL, NUL, NUL
  };
  /****************************************************************************
  * MessageWindow definitions.
  ****************************************************************************/
  AOSs1 (msgLines, BLANKSforHEADER)
  static int msgExits[] = { NUL };
  STATIC_ struct EditWindowStruct EWmsg_ = {
    " MessageBuffer ", 21, 0, SCREEN_WIDTH, 0, NULL, 1, 1, msgLines, 1, 0,
    NULL, FALSE, TRUE, msgExits, REFRESHkey_FSI, NUL, NUL, NUL, NUL, NUL,
    NUL
  };
  /****************************************************************************
  * Initialize global variables.
  ****************************************************************************/
  itemHead = NULL;
  FWcdf = &FWcdf_;
  IWsel = &IWsel_;
  EWkey = &EWkey_;
  EWmsg = &EWmsg_;
  /****************************************************************************
  * Initialize CDFwindow and encode key definitions.
  ****************************************************************************/
  CopyToField (iniSpec, &(FWcdf->fLines[CDFfieldLINEn][CDFfieldCOLn]),
	       CDFfieldLEN);
  if (first) {
    EncodeKeyDefinitions (2, keyDefsPrompt, ENTERkey_EXPORT, HELPkey_FSI,
			  EXITkey_FSI);
    first = FALSE;
  }
  /****************************************************************************
  * Display menu widgets.
  ****************************************************************************/
  FieldWindow (NEWfw, FWcdf, 0, (Logical) TRUE);
  ItemWindow (NEWiw, IWsel, 0);
  EditWindow (NEWew, EWkey, (Logical) TRUE);
  EditWindow (NEWew, EWmsg, (Logical) TRUE);
  /****************************************************************************
  * Determine/export CDF.
  ****************************************************************************/
  if (prompt) {
    EWkey->eLines = keyDefsPrompt;
    EditWindow (UPDATEew, EWkey, (Logical) TRUE);
    FieldWindow (READfw, FWcdf);
  }
  else
    FWcdf->key = ENTERkey_EXPORT;
  for (;;) {
     switch (FWcdf->key) {
       case ENTERkey_EXPORT:
	 CopyFromField (&(FWcdf->fLines[CDFfieldLINEn][CDFfieldCOLn]),
			CDFfieldLEN, path);
	 if (IsCDF(path)) {
	   ExportCDF (path, zMode, negToPosFp0);
	   MakeNUL (FWcdf->fLines[CDFvarsRecsLINEn]);
	   FieldWindow (UPDATEfw, FWcdf, 0, (Logical) TRUE);
	   IWsel->label = varLabel;
	   IWsel->hLines = varHeader;
	   ItemWindow (UPDATEiw, IWsel, 0);
	 }
	 else
	   if (IsDir(path) || IsWild(path))
	     ExportCDFsSpec (path, zMode, negToPosFp0, varLabel, varHeader);
	   else
	     DisplayMessage ("No CDFs found for specification.", BEEPWAIT1);
	 break;
       case HELPkey_FSI:
	 OnlineHelpWindow ("cdfxp.ilh", CDFhelpID);
	 break;
       case EXITkey_FSI:
	 FieldWindow (DELETEfw, FWcdf);
	 ItemWindow (DELETEiw, IWsel);
	 EditWindow (DELETEew, EWkey);
	 EditWindow (DELETEew, EWmsg);
	 return;
     }
     EWkey->eLines = keyDefsPrompt;
     EditWindow (UPDATEew, EWkey, (Logical) TRUE);
     FieldWindow (READfw, FWcdf);
  }
}

/******************************************************************************
* ExportCDFsSpec.
******************************************************************************/

void ExportCDFsSpec (path, zMode, negToPosFp0, varLabel0, varHeader0)
char *path;
long zMode;
Logical negToPosFp0;
char *varLabel0;
char *varHeader0[];
{
  Logical first = TRUE;
  int nCDFs, CDFn;
  char **dirS, **CDFs;
  static int exitCDFs[] = { ENTERkey_EXPORT, HELPkey_FSI, EXITkey_FSI,
			    NUL };
  STATIC_ struct ItemWindowStruct IWcdfs = {
    1, 79 - CDFnameLEN - 2, CDFnameLEN + 2, " Select a CDF ", 0, NULL, 0,
    NULL, 0, NULL, NULL, NULL, 13, 0, NULL, exitCDFs, REFRESHkey_FSI,
    TRUE, NUL, NUL
  };
  AOSs2 (keyDefs, "Open: ________  Help: ________  Exit: ________", "")
  /****************************************************************************
  * Encode key definitions first time.
  ****************************************************************************/
  if (first) {
    EncodeKeyDefinitions (2, keyDefs, ENTERkey_EXPORT, HELPkey_FSI,
			  EXITkey_FSI);
    first = FALSE;
  }
  /****************************************************************************
  * Get directory listing of CDFs.
  ****************************************************************************/
  nCDFs = CDFdirList (path, &dirS, &CDFs);
  if (nCDFs < 1) {
    DisplayMessage ("No CDFs found for specification.", BEEPWAIT1);
    return;
  }
  /****************************************************************************
  * Allocate/load CDFs.
  ****************************************************************************/
  AllocIW (&IWcdfs, nCDFs, nCDFs, IWcdfs.nColsTotal - 2, FatalError);
  for (CDFn = 0; CDFn < nCDFs; CDFn++) {
     strcpyX (IWcdfs.iLines[CDFn], CDFs[CDFn], IWcdfs.nColsTotal - 2);
     IWcdfs.iLineNs[CDFn] = CDFn;
     IWcdfs.iCols[CDFn] = 0;
     IWcdfs.iLens[CDFn] = CDFnameLEN;
  }
  /****************************************************************************
  * Select a CDF.
  ****************************************************************************/
  ItemWindow (NEWiw, &IWcdfs, 0);
  EWkey->eLines = keyDefs;
  EditWindow (UPDATEew, EWkey, (Logical) TRUE);
  for (;;) {
     ItemWindow (READiw, &IWcdfs);
     switch (IWcdfs.key) {
       case ENTERkey_EXPORT: {
	 char pathX[DU_MAX_PATH_LEN+1];
	 strcpyX (pathX, dirS[IWcdfs.itemN], DU_MAX_PATH_LEN);
	 AppendToDir (pathX, CDFs[IWcdfs.itemN]);
	 ItemWindow (UNDISPLAYiw, &IWcdfs);
	 ExportCDF (pathX, zMode, negToPosFp0);
	 CopyToField (path, &(FWcdf->fLines[CDFfieldLINEn][CDFfieldCOLn]),
		      CDFfieldLEN);
	 MakeNUL (FWcdf->fLines[CDFvarsRecsLINEn]);
	 FieldWindow (UPDATEfw, FWcdf, 0, (Logical) TRUE);
	 IWsel->label = varLabel0;
	 IWsel->hLines = varHeader0;
	 ItemWindow (UPDATEiw, IWsel, 0);
	 EWkey->eLines = keyDefs;
	 EditWindow (UPDATEew, EWkey, (Logical) TRUE);
	 ItemWindow (REDISPLAYiw, &IWcdfs);
	 break;
       }
       case HELPkey_FSI:
	 OnlineHelpWindow ("cdfxp.ilh", CDFShelpID);
	 break;
       case EXITkey_FSI:
	 ItemWindow (DELETEiw, &IWcdfs);
	 FreeIW (&IWcdfs, FatalError);
	 FreeMemory (CDFs, FatalError);
	 FreeMemory (dirS, FatalError);
	 return;
     }
  }
}

/******************************************************************************
* ExportCDF.
******************************************************************************/

void ExportCDF (path, zMode, negToPosFp0)
char *path;
long zMode;
Logical negToPosFp0;
{
  CDFid id;
  CDFstatus status;
  AOSs2 (keyDefsBlank, "", "")
  /****************************************************************************
  * Erase key definitions while loading.
  ****************************************************************************/
  EWkey->eLines = keyDefsBlank;
  EditWindow (UPDATEew, EWkey, (Logical) TRUE);
  /****************************************************************************
  * Open CDF.
  ****************************************************************************/
  status = CDFlib (OPEN_, CDF_, path, &id,
		   NULL_);
  DisplayStatus (status, "opening CDF");
  if (StatusBAD(status)) return;
  /****************************************************************************
  * Select CDF modes, etc.
  ****************************************************************************/
  status = CDFlib (SELECT_, CDF_READONLY_MODE_, READONLYon,
			    CDF_zMODE_, zMode,
			    CDF_CACHESIZE_, cacheSize,
			    CDF_NEGtoPOSfp0_MODE_, BOO(negToPosFp0,
						       NEGtoPOSfp0on,
						       NEGtoPOSfp0off),
		   NULL_);
  DisplayStatus (status, "setting CDF modes");
  if (StatusBAD(status)) {
    CDFlib (CLOSE_, CDF_,
	    NULL_);
    return;
  }
  /****************************************************************************
  * Inquire CDF and load/update widgets.
  ****************************************************************************/
  if (!LoadCDF(path)) {
    CDFlib (CLOSE_, CDF_,
	    NULL_);
    FreeItems ();
    return;
  }
  FieldWindow (UPDATEfw, FWcdf, 0, (Logical) TRUE);
  /****************************************************************************
  * Process keystrokes.
  ****************************************************************************/
  for (;;) {
     if (!SelectionWindow1()) break;
     if (!SelectionWindow2()) break;
     if (!SelectionWindow3()) break;
  }
  /****************************************************************************
  * Exit this CDF.
  ****************************************************************************/
  FreeItems ();
  FreeIW (IWsel, FatalError);
  if (dumpStats) {
    vSTATS vStats;
    char temp1[MAX_SCREENLINE_LEN+1],
	 temp2[MAX_SCREENLINE_LEN+1],
	 temp3[MAX_SCREENLINE_LEN+1];
    status = CDFlib (CLOSE_, CDFwithSTATS_, &vStats,
		     NULL_);
    DisplayStatus (status, "closing CDF");
    BuildStatistics ("Input CDF", &vStats, temp1, temp2, temp3);
    InfoWindow (temp1, temp2, temp3, FALSE);
  }
  else{
    status = CDFlib (CLOSE_, CDF_,
		     NULL_);
    DisplayStatus (status, "closing CDF");
  }
  return;
}

/******************************************************************************
* LoadCDF.
******************************************************************************/

Logical LoadCDF (path)
char *path;
{
  CDFstatus status;
  char varName[CDF_VAR_NAME_LEN+1];
  long NrVars, NzVars, eDataType, eNumElems, rMaxRec, zMaxRec;
  int varX, lineL = SCREEN_WIDTH - 2, nVars, dimN;
  struct ItemStruct *Item, *itemTail = NULL;
  static char readingCDF[] = "reading input CDF";
  /****************************************************************************
  * Initialize.
  ****************************************************************************/
  nItems = 0;
  /****************************************************************************
  * Inquire/check number of variables.
  ****************************************************************************/
  status = CDFlib (GET_, CDF_NUMrVARS_, &NrVars,
			 CDF_NUMzVARS_, &NzVars,
		   NULL_);
  DisplayStatus (status, readingCDF);
  if (StatusBAD(status)) return FALSE;
  nVars = (int) (NrVars + NzVars);
  if (nVars == 0) {
    DisplayMessage ("Error loading CDF.", BEEPWAIT1);
    DisplayMessage ("CDF contains no variables.", BEEPWAIT1);
    return FALSE;
  }
  /****************************************************************************
  * Inquire/check number of records.
  ****************************************************************************/
  status = CDFlib (GET_, rVARs_MAXREC_, &rMaxRec,
			 zVARs_MAXREC_, &zMaxRec,
		   NULL_);
  DisplayStatus (status, readingCDF);
  if (StatusBAD(status)) return FALSE;
  if (rMaxRec == -1 && zMaxRec == -1) {
    DisplayMessage ("Error loading CDF.", BEEPWAIT1);
    DisplayMessage ("CDF contains no records.", BEEPWAIT1);
    return FALSE;
  }
  /****************************************************************************
  * Display `loading' message.
  ****************************************************************************/
  DisplayMessage ("Loading CDF...", NOWAIT);
  /****************************************************************************
  * Load CDF window.
  ****************************************************************************/
  CopyToField (path,&(FWcdf->fLines[CDFfieldLINEn][CDFfieldCOLn]),CDFfieldLEN);
  sprintf (FWcdf->fLines[CDFvarsRecsLINEn],
	   "Variables: %ldr/%ldz, Records: %ldr/%ldz",
	   NrVars, NzVars, rMaxRec + 1, zMaxRec + 1);
  /****************************************************************************
  * Load records/indices.
  ****************************************************************************/
  if (opt.showRecord) {
    Item = (struct ItemStruct *)
	 AllocateMemory (sizeof(struct ItemStruct), FatalError);
    Item->type = RECORDt;
    Item->width = MIN_RECORD_WIDTH;
    Item->outputSetting = opt.outputItem;
    Item->filterSetting = opt.eachFilter;
    Item->inclusive = TRUE;
    Item->nextItem = NULL;
    Item->Record = (struct RecordStruct *)
		 AllocateMemory (sizeof(struct RecordStruct), FatalError);
    Item->Record->min = NOminMax;
    Item->Record->max = NOminMax;
    itemHead = Item;
    itemTail = Item;
    nItems++;
  }
  if (opt.showIndices) {
    Item = (struct ItemStruct *)
	 AllocateMemory (sizeof(struct ItemStruct), FatalError);
    Item->type = INDICESt;
    Item->width = MIN_INDICES_WIDTH;
    Item->outputSetting = opt.outputItem;
    Item->filterSetting = opt.eachFilter;
    Item->inclusive = TRUE;
    Item->nextItem = NULL;
    Item->Indices = (struct IndicesStruct *)
		  AllocateMemory (sizeof(struct IndicesStruct), FatalError);
    Item->Indices->minNumDims = NOminMax;
    Item->Indices->maxNumDims = NOminMax;
    if (itemHead == NULL)
      itemHead = Item;
    else
      itemTail->nextItem = Item;
    itemTail = Item;
    nItems++;
  }
  /****************************************************************************
  * Load variables.
  ****************************************************************************/
  for (varX = 0; varX < nVars; varX++) {
     /*************************************************************************
     * Allocate a variable structure and an Item structure and place on linked
     * list.
     *************************************************************************/
     Item = (struct ItemStruct *)
	  AllocateMemory (sizeof(struct ItemStruct), FatalError);
     Item->type = VARIABLEt;
     Item->outputSetting = opt.outputItem;
     Item->filterSetting = FALSE;		/* Reset below... */
     Item->inclusive = TRUE;
     Item->Var = (struct VarStruct *)
	         AllocateMemory (sizeof(struct VarStruct), FatalError);
     Item->nextItem = NULL;
     if (itemHead == NULL)
       itemHead = Item;
     else
       itemTail->nextItem = Item;
     itemTail = Item;
     /*************************************************************************
     * Initialize variable structure.
     *************************************************************************/
     Item->Var->zVar = (varX >= NrVars);
     Item->Var->varN = BOO(varX >= NrVars,varX - NrVars,varX);
     Item->Var->min = NULL;
     Item->Var->max = NULL;
     Item->Var->format = NULL;
     Item->Var->fill = NULL;
     Item->Var->pad = NULL;
     Item->Var->value = NULL;
     /*************************************************************************
     * Load variable dimensionality, variances, data type/number of elements,
     * and maximum record written.
     *************************************************************************/
     status = CDFlib (SELECT_, BOO(Item->Var->zVar,
				   zVAR_,rVAR_), Item->Var->varN,
		      GET_, BOO(Item->Var->zVar,
				zVAR_NUMDIMS_,
				rVARs_NUMDIMS_), &(Item->Var->numDims),
			    BOO(Item->Var->zVar,
				zVAR_DIMSIZES_,
				rVARs_DIMSIZES_), Item->Var->dimSizes,
			    BOO(Item->Var->zVar,
				zVAR_DATATYPE_,
				rVAR_DATATYPE_), &(Item->Var->dataType),
			    BOO(Item->Var->zVar,
				zVAR_NUMELEMS_,
				rVAR_NUMELEMS_), &(Item->Var->numElems),
			    BOO(Item->Var->zVar,
				zVAR_MAXREC_,
				rVAR_MAXREC_), &(Item->Var->maxRec),
			    BOO(Item->Var->zVar,
				zVAR_RECVARY_,
				rVAR_RECVARY_), &(Item->Var->recVary),
			    BOO(Item->Var->zVar,
				zVAR_DIMVARYS_,
				rVAR_DIMVARYS_), Item->Var->dimVarys,
		      NULL_);
     DisplayStatus (status, readingCDF);
     if (StatusBAD(status)) return FALSE;
     /*************************************************************************
     * Calculate the number of values per variable record (conceptual) and
     * the number of bytes per value.
     *************************************************************************/
     Item->Var->nRecordValues = 1;
     for (dimN = 0; dimN < Item->Var->numDims; dimN++) {
	Item->Var->nRecordValues *= Item->Var->dimSizes[dimN];
     }
     Item->Var->nValueBytes =
	    (size_t) (CDFelemSize(Item->Var->dataType) * Item->Var->numElems);
     /*************************************************************************
     * Load variable name.
     *************************************************************************/
     status = CDFlib (GET_, BOO(Item->Var->zVar,
				zVAR_NAME_,rVAR_NAME_), varName,
		      NULL_);
     DisplayStatus (status, readingCDF);
     if (StatusBAD(status)) return FALSE;
     RemoveTrailingBlanks (varName);
     Item->Var->name = (char *) AllocateMemory (strlen(varName)+1, FatalError);
     strcpyX (Item->Var->name, varName, 0);
     /*************************************************************************
     * Load VALIDMIN entry.
     *************************************************************************/
     if (opt.useVALIDMIN) {
       status = CDFlib (SELECT_, ATTR_NAME_, "VALIDMIN",
				 BOO(Item->Var->zVar,
				     zENTRY_,rENTRY_), Item->Var->varN,
			GET_, BOO(Item->Var->zVar,
				  zENTRY_DATATYPE_,
				  rENTRY_DATATYPE_), &eDataType,
			      BOO(Item->Var->zVar,
				  zENTRY_NUMELEMS_,
				  rENTRY_NUMELEMS_), &eNumElems,
			NULL_);
       switch (status) {
	 case CDF_OK: {
	   size_t nBytesE = (size_t) (CDFelemSize(eDataType) * eNumElems);
	   void *buffer = AllocateMemory (nBytesE, FatalError);
	   status = CDFlib (GET_, BOO(Item->Var->zVar,
				      zENTRY_DATA_,rENTRY_DATA_), buffer,
			    NULL_);
	   DisplayStatus (status, readingCDF);
	   if (StatusBAD(status)) {
	     FreeMemory (buffer, FatalError);
	     return FALSE;
	   }
	   Item->Var->min = AllocateMemory (Item->Var->nValueBytes,FatalError);
	   ConvertDataType (eDataType, eNumElems, buffer, Item->Var->dataType,
			    Item->Var->numElems, Item->Var->min);
	   FreeMemory (buffer, FatalError);
	   Item->filterSetting = opt.eachFilter;
	   break;
	 }
	 case NO_SUCH_ATTR:
	 case NO_SUCH_ENTRY:
	   break;
	 default:
	   DisplayStatus (status, readingCDF);
	   return FALSE;
       }
     }
     /*************************************************************************
     * Load VALIDMAX.
     *************************************************************************/
     if (opt.useVALIDMAX) {
       status = CDFlib (SELECT_, ATTR_NAME_, "VALIDMAX",
				 BOO(Item->Var->zVar,
				     zENTRY_,rENTRY_), Item->Var->varN,
			GET_, BOO(Item->Var->zVar,
				  zENTRY_DATATYPE_,
				  rENTRY_DATATYPE_), &eDataType,
			      BOO(Item->Var->zVar,
				  zENTRY_NUMELEMS_,
				  rENTRY_NUMELEMS_), &eNumElems,
			NULL_);
       switch (status) {
	 case CDF_OK: {
	   size_t nBytesE = (size_t) (CDFelemSize(eDataType) * eNumElems);
	   void *buffer = AllocateMemory (nBytesE, FatalError);
	   status = CDFlib (GET_, BOO(Item->Var->zVar,
				      zENTRY_DATA_,rENTRY_DATA_), buffer,
			    NULL_);
	   DisplayStatus (status, readingCDF);
	   if (StatusBAD(status)) {
	     FreeMemory (buffer, FatalError);
	     return FALSE;
	   }
	   Item->Var->max = AllocateMemory (Item->Var->nValueBytes,FatalError);
	   ConvertDataType (eDataType, eNumElems, buffer, Item->Var->dataType,
			    Item->Var->numElems, Item->Var->max);
	   FreeMemory (buffer, FatalError);
	   Item->filterSetting = opt.eachFilter;
	   break;
	 }
	 case NO_SUCH_ATTR:
	 case NO_SUCH_ENTRY:
	   break;
	 default:
	   DisplayStatus (status, readingCDF);
	   return FALSE;
       }
     }
     /*************************************************************************
     * Validate VALIDMIN/VALIDMAX entries.
     *************************************************************************/
     if (Item->Var->min != NULL && Item->Var->max != NULL) {
       if (GTx(Item->Var->min,Item->Var->max,
	       Item->Var->dataType,Item->Var->numElems)) {
	 static char msg[SCREEN_WIDTH+CDF_VAR_NAME_LEN+1];
	 sprintf (msg, "VALIDMIN > VALIDMAX for variable `%s' (both ignored).",
		  Item->Var->name);
	 DisplayMessage (msg, NOBEEPWAIT1);
	 FreeMemory (Item->Var->min, FatalError);
	 FreeMemory (Item->Var->max, FatalError);
	 Item->Var->min = NULL;
	 Item->Var->max = NULL;
	 Item->filterSetting = FALSE;
       }
     }
     /*************************************************************************
     * Load FILLVAL.
     *************************************************************************/
     if (opt.useFILLVAL) {
       status = CDFlib (SELECT_, ATTR_NAME_, "FILLVAL",
				 BOO(Item->Var->zVar,
				     zENTRY_,rENTRY_), Item->Var->varN,
			GET_, BOO(Item->Var->zVar,
				  zENTRY_DATATYPE_,
				  rENTRY_DATATYPE_), &eDataType,
			      BOO(Item->Var->zVar,
				  zENTRY_NUMELEMS_,
				  rENTRY_NUMELEMS_), &eNumElems,
			NULL_);
       switch (status) {
	 case CDF_OK: {
	   size_t nBytesE = (size_t) (CDFelemSize(eDataType) * eNumElems);
	   void *buffer = AllocateMemory (nBytesE, FatalError);
	   status = CDFlib (GET_, BOO(Item->Var->zVar,
				      zENTRY_DATA_,rENTRY_DATA_), buffer,
			    NULL_);
	   DisplayStatus (status, readingCDF);
	   if (StatusBAD(status)) {
	     FreeMemory (buffer, FatalError);
	     return FALSE;
	   }
	   Item->Var->fill = AllocateMemory (Item->Var->nValueBytes,
					     FatalError);
	   ConvertDataType (eDataType, eNumElems, buffer, Item->Var->dataType,
			    Item->Var->numElems, Item->Var->fill);
	   FreeMemory (buffer, FatalError);
	   break;
	 }
	 case NO_SUCH_ATTR:
	 case NO_SUCH_ENTRY:
	   break;
	 default:
	   DisplayStatus (status, readingCDF);
	   return FALSE;
       }
     }
     /*************************************************************************
     * Load FORMAT entry.
     *************************************************************************/
     if (opt.useFORMAT) {
       status = CDFlib (SELECT_, ATTR_NAME_, "FORMAT",
				 BOO(Item->Var->zVar,
				     zENTRY_,rENTRY_), Item->Var->varN,
			GET_, BOO(Item->Var->zVar,
				  zENTRY_DATATYPE_,
				  rENTRY_DATATYPE_), &eDataType,
			      BOO(Item->Var->zVar,
				  zENTRY_NUMELEMS_,
				  rENTRY_NUMELEMS_), &eNumElems,
			NULL_);
       switch (status) {
	 case CDF_OK: {
	   if (STRINGdataType(eDataType)) {
	     Item->Var->format = (char *)
				 AllocateMemory ((size_t) eNumElems + 1,
						 FatalError);
	     status = CDFlib (GET_, BOO(Item->Var->zVar,
					zENTRY_DATA_,
					rENTRY_DATA_), Item->Var->format,
			      NULL_);
	     DisplayStatus (status, readingCDF);
	     if (StatusBAD(status)) return FALSE;
	     (Item->Var->format)[(int)eNumElems] = NUL;
	     RemoveWhiteSpace (Item->Var->format);
	   }
	   break;
	 }
	 case NO_SUCH_ATTR:
	 case NO_SUCH_ENTRY:
	   break;
	 default:
	   DisplayStatus (status, readingCDF);
	   return FALSE;
       }
     }
     Item->width = VariableWidth(Item->Var);
     /*************************************************************************
     * Load monotonicity.
     *************************************************************************/
     if (OneDimensionVaries(Item->Var)) {
       Item->Var->monotonic = UNKNOWNmono;
       if (opt.useMONOTON) {
         status = CDFlib (SELECT_, ATTR_NAME_, "MONOTON",
				   BOO(Item->Var->zVar,
				       zENTRY_,rENTRY_), Item->Var->varN,
			  GET_, BOO(Item->Var->zVar,
				    zENTRY_DATATYPE_,
				    rENTRY_DATATYPE_), &eDataType,
			        BOO(Item->Var->zVar,
				    zENTRY_NUMELEMS_,
				    rENTRY_NUMELEMS_), &eNumElems,
			  NULL_);
         switch (status) {
	   case CDF_OK: {
	     size_t nBytes = (size_t) (CDFelemSize(eDataType) * eNumElems);
	     char *buffer = (char *) AllocateMemory (nBytes + 1, FatalError);
	     status = CDFlib (GET_, BOO(Item->Var->zVar,
					zENTRY_DATA_,rENTRY_DATA_), buffer,
			      NULL_);
	     DisplayStatus (status, readingCDF);
	     if (StatusBAD(status)) {
	       FreeMemory (buffer, FatalError);
	       return FALSE;
	     }
	     if (STRINGdataType(eDataType)) {
	       buffer[(int)eNumElems] = NUL;
	       if (!strcmpITB(buffer,"INCREASE"))
	         Item->Var->monotonic = INCREASEmono;
	       else
	         if (!strcmpITB(buffer,"DECREASE"))
		   Item->Var->monotonic = DECREASEmono;
	         else
		   if (!strcmpITB(buffer,"FALSE")) {
		     Item->Var->monotonic=FALSEmono;
		   }
	     }
	     FreeMemory (buffer, FatalError);
	     break;
	   }
	   case NO_SUCH_ATTR:
	   case NO_SUCH_ENTRY:
	     break;
	   default:
	     DisplayStatus (status, readingCDF);
	     return FALSE;
         }
       }
     }
     else
       Item->Var->monotonic = NAmono;
     /*************************************************************************
     * Load pad value.
     *************************************************************************/
     Item->Var->pad = AllocateMemory (Item->Var->nValueBytes, FatalError);
     status = CDFlib (GET_, BOO(Item->Var->zVar,
				zVAR_PADVALUE_,
				rVAR_PADVALUE_), Item->Var->pad,
		      NULL_);
     if (status != NO_PADVALUE_SPECIFIED) DisplayStatus (status, readingCDF);
     if (StatusBAD(status)) return FALSE;
     /*************************************************************************
     * Allocate buffer for value(s) and set miscellaneous flags.
     *************************************************************************/
     Item->Var->value = AllocateMemory (Item->Var->nValueBytes, FatalError);
     Item->Var->scalar = ScalarVariable (Item->Var);
     /*************************************************************************
     * Tally another line.
     *************************************************************************/
     nItems++;
  }
  /****************************************************************************
  * Inquire majority.
  ****************************************************************************/
  status = CDFlib (GET_, CDF_MAJORITY_, &inMajority,
		   NULL_);
  DisplayStatus (status, "inquiring CDF majority");
  if (StatusBAD(status)) return FALSE;
  /****************************************************************************
  * Allocate SelectionWindow.
  ****************************************************************************/
  AllocIW (IWsel, nSELWINDOWcolsMAX * nItems, nItems, lineL, FatalError);
  /****************************************************************************
  * Clear `loading' message and return success.
  ****************************************************************************/
  DisplayMessage ("", NOWAIT);
  return TRUE;
}

/******************************************************************************
* LoadSelectionWindow.
******************************************************************************/

Logical LoadSelectionWindow (part)
int part;
{
  int itemN, lineN;
  char temp[MAXitemFieldLEN+1];
  struct ItemStruct *Item;
  /****************************************************************************
  * Load the requested part.
  ****************************************************************************/
  switch (part) {
    /**************************************************************************
    * Part 1.  Name, data specification, dimensionality, variances, output.
    **************************************************************************/
    case PART1:
      for (Item = itemHead, lineN = 0;
	   Item != NULL; Item = Item->nextItem, lineN++) {
	 /*********************************************************************
	 * Load item/variable name.
	 *********************************************************************/
	 switch (Item->type) {
	   case RECORDt:
	     MakeNUL (IWsel->iLines[lineN]);
	     CatToString (IWsel->iLines[lineN], "<Record>", NAMEitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	   case INDICESt:
	     MakeNUL (IWsel->iLines[lineN]);
	     CatToString (IWsel->iLines[lineN], "<Indices>", NAMEitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	   case VARIABLEt:
	     EncodeString ((long) strlen(Item->Var->name), Item->Var->name,
			   IWsel->iLines[lineN], -NAMEitemLEN, NAMEitemLEN);
	     break;
	 }
	 itemN = nSELWINDOWcols1 * lineN;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = NAMEitemCOLn;
	 IWsel->iLens[itemN] = NAMEitemLEN;
	 /*********************************************************************
	 * Load data specification.
	 *********************************************************************/
	 strcatX (IWsel->iLines[lineN], " ", 0);
	 switch (Item->type) {
	   case RECORDt:
	   case INDICESt:
	     CatToString (IWsel->iLines[lineN], na, SPECitemLEN, LEFT_JUSTIFY,
			  dots);
	     break;
	   case VARIABLEt:
	     sprintf (temp, "%s/%ld", DataTypeToken(Item->Var->dataType),
		      Item->Var->numElems);
	     CatToString (IWsel->iLines[lineN], temp, SPECitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	 }
	 itemN++;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = SPECitemCOLn;
	 IWsel->iLens[itemN] = SPECitemLEN;
	 /*********************************************************************
	 * Load dimensionality.
	 *********************************************************************/
	 strcatX (IWsel->iLines[lineN], " ", 0);
	 switch (Item->type) {
	   case RECORDt:
	   case INDICESt:
	     CatToString (IWsel->iLines[lineN], na, DIMENitemLEN, LEFT_JUSTIFY,
			  dots);
	     break;
	   case VARIABLEt:
	     EncodeDimensionality (temp, Item->Var->numDims,
				   Item->Var->dimSizes);
	     CatToString (IWsel->iLines[lineN], temp, DIMENitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	 }
	 itemN++;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = DIMENitemCOLn;
	 IWsel->iLens[itemN] = DIMENitemLEN;
	 /*********************************************************************
	 * Load variances.
	 *********************************************************************/
	 strcatX (IWsel->iLines[lineN], " ", 0);
	 switch (Item->type) {
	   case RECORDt:
	   case INDICESt:
	     CatToString (IWsel->iLines[lineN], na, VARYSitemLEN, LEFT_JUSTIFY,
			  dots);
	     break;
	   case VARIABLEt:
	     EncodeVariances (temp, Item->Var->recVary, Item->Var->numDims,
			      Item->Var->dimVarys);
	     CatToString (IWsel->iLines[lineN], temp, VARYSitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	 }
	 itemN++;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = VARYSitemCOLn;
	 IWsel->iLens[itemN] = VARYSitemLEN;
	 /*********************************************************************
	 * Load output.
	 *********************************************************************/
	 strcatX (IWsel->iLines[lineN], " ", 0);
	 CatToString (IWsel->iLines[lineN],
		      BOO(Item->outputSetting,"YES","no"),
		      OUTPUTitemLEN, LEFT_JUSTIFY, dots);
	 itemN++;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = OUTPUTitemCOLn;
	 IWsel->iLens[itemN] = OUTPUTitemLEN;
      }
      IWsel->nItems = itemN + 1;
      break;
    /**************************************************************************
    * Part 2.  Name, minimum, maximum, filter.
    **************************************************************************/
    case PART2:
      for (Item = itemHead, lineN = 0;
	   Item != NULL; Item = Item->nextItem, lineN++) {
	 /*********************************************************************
	 * Load item/variable name.
	 *********************************************************************/
	 switch (Item->type) {
	   case RECORDt:
	     MakeNUL (IWsel->iLines[lineN]);
	     CatToString (IWsel->iLines[lineN], "<Record>", NAMEitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	   case INDICESt:
	     MakeNUL (IWsel->iLines[lineN]);
	     CatToString (IWsel->iLines[lineN], "<Indices>", NAMEitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	   case VARIABLEt:
	     EncodeString ((long) strlen(Item->Var->name), Item->Var->name,
			   IWsel->iLines[lineN], -NAMEitemLEN, NAMEitemLEN);
	     break;
	 }
	 itemN = nSELWINDOWcols2 * lineN;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = NAMEitemCOLn;
	 IWsel->iLens[itemN] = NAMEitemLEN;
	 /*********************************************************************
	 * Load minimum value.
	 *********************************************************************/
	 strcatX (IWsel->iLines[lineN], " ", 0);
	 switch (Item->type) {
	   case RECORDt:
	     if (Item->Record->min == NOminMax)
	       MakeNUL (temp);
	     else
	       sprintf (temp, "%ld", Item->Record->min + 1);
	     CatToString (IWsel->iLines[lineN], temp, MINitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	   case INDICESt:
	     if (Item->Indices->minNumDims == NOminMax)
	       CatNcharacters (IWsel->iLines[lineN], MINitemLEN, ' ');
	     else {
	       EncodeIndicesJustify (temp, Item->Indices->minNumDims,
				     Item->Indices->minIndices, 0);
	       CatToString (IWsel->iLines[lineN], temp, MINitemLEN,
			    LEFT_JUSTIFY, dots);
	     }
	     break;
	   case VARIABLEt:
	     if (Item->Var->min != NULL)
	       EncodeValuesFormat (Item->Var->dataType, Item->Var->numElems,
				   Item->Var->min, EofS(IWsel->iLines[lineN]),
				   NULL, -MINitemLEN, MINitemLEN,
				   opt.epochStyle);
	     else
	       CatNcharacters (IWsel->iLines[lineN], MINitemLEN, ' ');
	     break;
	 }
	 itemN++;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = MINitemCOLn;
	 IWsel->iLens[itemN] = MINitemLEN;
	 /*********************************************************************
	 * Load maximum value.
	 *********************************************************************/
	 strcatX (IWsel->iLines[lineN], " ", 0);
	 switch (Item->type) {
	   case RECORDt:
	     if (Item->Record->max == NOminMax)
	       MakeNUL (temp);
	     else
	       sprintf (temp, "%ld", Item->Record->max + 1);
	     CatToString (IWsel->iLines[lineN], temp, MAXitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	   case INDICESt:
	     if (Item->Indices->maxNumDims == NOminMax)
	       CatNcharacters (IWsel->iLines[lineN], MAXitemLEN, ' ');
	     else {
	       EncodeIndicesJustify (temp, Item->Indices->maxNumDims,
				     Item->Indices->maxIndices, 0);
	       CatToString (IWsel->iLines[lineN], temp, MAXitemLEN,
			    LEFT_JUSTIFY, dots);
	     }
	     break;
	   case VARIABLEt:
	     if (Item->Var->max != NULL)
	       EncodeValuesFormat (Item->Var->dataType, Item->Var->numElems,
				   Item->Var->max, EofS(IWsel->iLines[lineN]),
				   NULL, -MAXitemLEN, MAXitemLEN,
				   opt.epochStyle);
	     else
	       CatNcharacters (IWsel->iLines[lineN], MAXitemLEN, ' ');
	     break;
	 }
	 itemN++;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = MAXitemCOLn;
	 IWsel->iLens[itemN] = MAXitemLEN;
	 /*********************************************************************
	 * Load filter.
	 *********************************************************************/
	 strcatX (IWsel->iLines[lineN], " ", 0);
	 CatToString (IWsel->iLines[lineN],
		      BOO(Item->filterSetting,
			  BOO(opt.overallFilter,
			      BOO(opt.exclusive,
				  BOO(Item->inclusive,"YeS","yEs"),"YES"),
			      BOO(opt.exclusive,
				  BOO(Item->inclusive,
				      "(YeS)","(yEs)"),"(YES)")),
			  BOO(opt.overallFilter,"no","(no)")),
		      FILTERitemLEN, LEFT_JUSTIFY, dots);
	 itemN++;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = FILTERitemCOLn;
	 IWsel->iLens[itemN] = FILTERitemLEN;
      }
      IWsel->nItems = itemN + 1;
      break;
    /**************************************************************************
    * Part 3.  Name, fill value, monotonicity, format, width.
    **************************************************************************/
    case PART3:
      for (Item = itemHead, lineN = 0;
	   Item != NULL; Item = Item->nextItem, lineN++) {
	 /*********************************************************************
	 * Load item/variable name.
	 *********************************************************************/
	 switch (Item->type) {
	   case RECORDt:
	     MakeNUL (IWsel->iLines[lineN]);
	     CatToString (IWsel->iLines[lineN], "<Record>", NAMEitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	   case INDICESt:
	     MakeNUL (IWsel->iLines[lineN]);
	     CatToString (IWsel->iLines[lineN], "<Indices>", NAMEitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	   case VARIABLEt:
	     EncodeString ((long) strlen(Item->Var->name), Item->Var->name,
			   IWsel->iLines[lineN], -NAMEitemLEN, NAMEitemLEN);
	     break;
	 }
	 itemN = nSELWINDOWcols3 * lineN;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = NAMEitemCOLn;
	 IWsel->iLens[itemN] = NAMEitemLEN;
	 /*********************************************************************
	 * Load fill value.
	 *********************************************************************/
	 strcatX (IWsel->iLines[lineN], " ", 0);
	 switch (Item->type) {
	   case RECORDt:
	   case INDICESt:
	     CatToString (IWsel->iLines[lineN], na, FILLitemLEN, LEFT_JUSTIFY,
			  dots);
	     break;
	   case VARIABLEt:
	     if (Item->Var->fill != NULL) {
	       char temp[FILLitemLEN+1];
	       MakeNUL (temp);
	       if (!opt.useFills) strcatX (temp, "(", 0);
	       EncodeValuesFormat (Item->Var->dataType, Item->Var->numElems,
				   Item->Var->fill, EofS(temp), NULL, 0,
				   FILLitemLEN - 2, opt.epochStyle);
	       if (!opt.useFills) strcatX (temp, ")", 0);
	       CatToString (IWsel->iLines[lineN], temp, FILLitemLEN,
			    LEFT_JUSTIFY, dots);
	     }
	     else
	       CatNcharacters (IWsel->iLines[lineN], FILLitemLEN, ' ');
	     break;
	 }
	 itemN++;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = FILLitemCOLn;
	 IWsel->iLens[itemN] = FILLitemLEN;
	 /*********************************************************************
	 * Load monotonicity.
	 *********************************************************************/
	 strcatX (IWsel->iLines[lineN], " ", 0);
	 switch (Item->type) {
	   case RECORDt:
	   case INDICESt:
	     CatToString (IWsel->iLines[lineN], na, MONOitemLEN, LEFT_JUSTIFY,
			  dots);
	     break;
	   case VARIABLEt:
	     CatToString (IWsel->iLines[lineN], monos[Item->Var->monotonic+1],
			  MONOitemLEN, LEFT_JUSTIFY, dots);
	     break;
	 }
	 itemN++;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = MONOitemCOLn;
	 IWsel->iLens[itemN] = MONOitemLEN;
	 /*********************************************************************
	 * Load format.
	 *********************************************************************/
	 strcatX (IWsel->iLines[lineN], " ", 0);
	 switch (Item->type) {
	   case RECORDt:
	   case INDICESt:
	     CatToString (IWsel->iLines[lineN], na, FORMATitemLEN,
			  LEFT_JUSTIFY, dots);
	     break;
	   case VARIABLEt:
	     if (Item->Var->format != NULL)
	       if ((EPOCHdataType(Item->Var->dataType) &&
		    (opt.epochStyle == EPOCH0_STYLE ||
		     opt.epochStyle == EPOCH1_STYLE ||
		     opt.epochStyle == EPOCH2_STYLE ||
		     opt.epochStyle == EPOCH3_STYLE))
		   || STRINGdataType(Item->Var->dataType)) {
		 size_t nBytes = 1 + strlen(Item->Var->format) + 1;
		 char *temp = (char *) AllocateMemory (nBytes + 1, FatalError);
		 strcpyX (temp, "(", 0);
		 strcatX (temp, Item->Var->format, 0);
		 strcatX (temp, ")", 0);
		 CatToString (IWsel->iLines[lineN], temp, FORMATitemLEN,
			      LEFT_JUSTIFY, dots);
		 FreeMemory (temp, FatalError);
	       }
	       else
		 CatToString (IWsel->iLines[lineN], Item->Var->format,
			      FORMATitemLEN, LEFT_JUSTIFY, dots);
	     else
	       CatNcharacters (IWsel->iLines[lineN], FORMATitemLEN, ' ');
	     break;
	 }
	 itemN++;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = FORMATitemCOLn;
	 IWsel->iLens[itemN] = FORMATitemLEN;
	 /*********************************************************************
	 * Load width.
	 *********************************************************************/
	 strcatX (IWsel->iLines[lineN], " ", 0);
	 sprintf (temp, "%d", Item->width);
	 CatToString (IWsel->iLines[lineN], temp, WIDTHitemLEN, LEFT_JUSTIFY,
		      dots);
	 itemN++;
	 IWsel->iLineNs[itemN] = lineN;
	 IWsel->iCols[itemN] = WIDTHitemCOLn;
	 IWsel->iLens[itemN] = WIDTHitemLEN;
      }
      IWsel->nItems = itemN + 1;
      break;
  }
  /****************************************************************************
  * Success, return.
  ****************************************************************************/
  return TRUE;
}

/******************************************************************************
* SelectionWindow1.
******************************************************************************/

Logical SelectionWindow1 () {
  static Logical first = TRUE;
  int rX, cX, rXt, itemN; struct ItemStruct *Item;
  static char label[] = " SelectionWindow, part 1 ";
  static char *header[] = {
    "Item/Variable         DataSpec  Dimensionality              Variances   Output"
  };
  static int exits[] = {
    PART2key_EXPORT, ENTERkey_EXPORT, EXITkey_FSI, ACTIONSkey_EXPORT,
    FLIPkey_EXPORT, DELETEkey_FSI, HELPkey_FSI, OPTIONSkey_EXPORT, NUL
  };
  AOSs2 (keyDefs,
   "Modify: ________  Actions: ________  Reverse: ________  Help: ________",
   "Part 2: ________  Options: ________  Delete:  ________  Exit: ________")
  /****************************************************************************
  * Encode key definitions first time.
  ****************************************************************************/
  if (first) {
    EncodeKeyDefinitions (2, keyDefs,
			  ENTERkey_EXPORT, ACTIONSkey_EXPORT, FLIPkey_EXPORT,
			  HELPkey_FSI, PART2key_EXPORT, OPTIONSkey_EXPORT,
			  DELETEkey_FSI, EXITkey_FSI);
    first = FALSE;
  }
  /****************************************************************************
  * Load SelectionWindow structure.
  ****************************************************************************/
  IWsel->label = label;
  IWsel->hLines = header;
  IWsel->exitChars = exits;
  LoadSelectionWindow (PART1);
  /****************************************************************************
  * Load KeyWindow structure.
  ****************************************************************************/
  EWkey->eLines = keyDefs;
  /****************************************************************************
  * Determine initial item number.
  ****************************************************************************/
  itemN = (IWsel->itemN / nSELWINDOWcols3) * nSELWINDOWcols1;
  switch (IWsel->itemN % nSELWINDOWcols3) {
    case NAMEcx: break;
    case FILLcx: itemN += SPECcx; break;
    case MONOcx: itemN += VARYScx; break;
    case FORMATcx: itemN += VARYScx; break;
    case WIDTHcx: itemN += OUTPUTcx; break;
  }
  /****************************************************************************
  * Update SelectionWindow and KeyWindow.
  ****************************************************************************/
  ItemWindow (UPDATEiw, IWsel, itemN);
  EditWindow (UPDATEew, EWkey, (Logical) TRUE);
  /****************************************************************************
  * Process keystrokes.
  ****************************************************************************/
  for (;;) {
     ItemWindow (READiw, IWsel);
     rX = IWsel->itemN / nSELWINDOWcols1;
     cX = IWsel->itemN % nSELWINDOWcols1;
     for (Item = itemHead, rXt = 0; rXt < rX; rXt++) Item = Item->nextItem;
     switch (IWsel->key) {
       case ENTERkey_EXPORT:
	 switch (cX) {
	   case NAMEcx:
	   case SPECcx:
	   case DIMENcx:
	   case VARYScx:
	     ItemWindow (BEEPiw, IWsel);
	     break;
	   case OUTPUTcx:
	     Item->outputSetting = BOO(Item->outputSetting,FALSE,TRUE);
	     LoadSelectionWindow (PART1);
	     ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	     break;
	 }
	 break;
       case DELETEkey_FSI:
	 switch (cX) {
	   case NAMEcx:
	     ItemWindow (BEEPiw, IWsel);
	     break;
	   case SPECcx:
	   case DIMENcx:
	   case VARYScx:
	   case OUTPUTcx:
	     ItemWindow (BEEPiw, IWsel);
	     break;
	 }
	 break;
       case FLIPkey_EXPORT:
	 if (FlipItems(rX)) {
	   int itemN = (IWsel->itemN + nSELWINDOWcols1) % IWsel->nItems;
	   LoadSelectionWindow (PART1);
	   ItemWindow (UPDATEiw, IWsel, itemN);
	 }
	 else
	   ItemWindow (BEEPiw, IWsel);
	 break;
       case ACTIONSkey_EXPORT:
	 ActionMenu (PART1);
	 break;
       case OPTIONSkey_EXPORT:
	 OptionMenu (PART1);
	 break;
       case HELPkey_FSI:
	 OnlineHelpWindow ("cdfxp.ilh", VAR1helpID);
	 break;
       case PART2key_EXPORT:
	 return TRUE;
       case EXITkey_FSI:
	 if (ConfirmExit()) return FALSE;
	 break;
     }
     /*************************************************************************
     * Update key definitions in case they were changed.
     *************************************************************************/
     EWkey->eLines = keyDefs;
     EditWindow (UPDATEew, EWkey, (Logical) TRUE);
  }
}

/******************************************************************************
* SelectionWindow2.
******************************************************************************/

Logical SelectionWindow2 () {
  static Logical first = TRUE;
  int rX, cX, rXt, itemN; struct ItemStruct *Item;
  static char label[] = " SelectionWindow, part 2 ";
  static char *header[] = {
    "Item/Variable         Minimum                  Maximum                  Filter"
  };
  static int exits[] = {
    PART3key_EXPORT, ENTERkey_EXPORT, EXITkey_FSI, ACTIONSkey_EXPORT,
    FLIPkey_EXPORT, DELETEkey_FSI, HELPkey_FSI, OPTIONSkey_EXPORT, NUL
  };
  AOSs2 (keyDefs,
   "Modify: ________   Actions: ________   Reverse: ________   Help: ________",
   "Part 3: ________   Options: ________   Delete:  ________   Exit: ________")
  /****************************************************************************
  * Encode key definitions first time.
  ****************************************************************************/
  if (first) {
    EncodeKeyDefinitions (2, keyDefs,
			  ENTERkey_EXPORT, ACTIONSkey_EXPORT, FLIPkey_EXPORT,
			  HELPkey_FSI, PART2key_EXPORT, OPTIONSkey_EXPORT,
			  DELETEkey_FSI, EXITkey_FSI);
    first = FALSE;
  }
  /****************************************************************************
  * Load SelectionWindow structure.
  ****************************************************************************/
  IWsel->label = label;
  IWsel->hLines = header;
  IWsel->exitChars = exits;
  LoadSelectionWindow (PART2);
  /****************************************************************************
  * Load KeyWindow structure.
  ****************************************************************************/
  EWkey->eLines = keyDefs;
  /****************************************************************************
  * Determine initial item number.
  ****************************************************************************/
  itemN = (IWsel->itemN / nSELWINDOWcols1) * nSELWINDOWcols2;
  switch (IWsel->itemN % nSELWINDOWcols1) {
    case NAMEcx: break;
    case SPECcx: itemN += MINcx; break;
    case DIMENcx: itemN += MINcx; break;
    case VARYScx: itemN += MAXcx; break;
    case OUTPUTcx: itemN += FILTERcx; break;
  }
  /****************************************************************************
  * Update SelectionWindow and KeyWindow.
  ****************************************************************************/
  ItemWindow (UPDATEiw, IWsel, itemN);
  EditWindow (UPDATEew, EWkey, (Logical) TRUE);
  /****************************************************************************
  * Process keystrokes.
  ****************************************************************************/
  for (;;) {
     ItemWindow (READiw, IWsel);
     rX = IWsel->itemN / nSELWINDOWcols2;
     cX = IWsel->itemN % nSELWINDOWcols2;
     for (Item = itemHead, rXt = 0; rXt < rX; rXt++) Item = Item->nextItem;
     switch (IWsel->key) {
       case ENTERkey_EXPORT:
	 switch (cX) {
	   case NAMEcx:
	     ItemWindow (BEEPiw, IWsel);
	     break;
	   case MINcx:
	   case MAXcx: {
	     switch (Item->type) {
	       case RECORDt:
	       case INDICESt:
	       case VARIABLEt:
		 if (PromptForMinMax(Item,(cX == MINcx))) {
		   LoadSelectionWindow (PART2);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 break;
	     }
	     break;
	   case FILTERcx:
	     if (MINorMAXexists(Item)) {
	       if (opt.exclusive) {
		 if (Item->filterSetting) {
		   if (Item->inclusive)
		     Item->inclusive = FALSE;
		   else
		     Item->filterSetting = FALSE;
		 }
		 else {
		   Item->filterSetting = TRUE;
		   Item->inclusive = TRUE;
		 }
	       }
	       else
		 Item->filterSetting = BOO(Item->filterSetting,FALSE,TRUE);
	       opt.overallFilter = TRUE;
	       LoadSelectionWindow (PART2);
	       ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	     }
	     else
	       ItemWindow (BEEPiw, IWsel);
	     break;
	   }
	 }
	 break;
       case DELETEkey_FSI:
	 switch (cX) {
	   case NAMEcx:
	     ItemWindow (BEEPiw, IWsel);
	     break;
	   case MINcx:
	     switch (Item->type) {
	       case RECORDt:
		 if (Item->Record->min != NOminMax) {
		   Item->Record->min = NOminMax;
		   if (Item->Record->max == NOminMax) {
		     Item->filterSetting = FALSE;
		   }
		   LoadSelectionWindow (PART2);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 else
		   ItemWindow (BEEPiw, IWsel);
		 break;
	       case INDICESt:
		 if (Item->Indices->minNumDims != NOminMax) {
		   Item->Indices->minNumDims = NOminMax;
		   if (Item->Indices->maxNumDims == NOminMax) {
		     Item->filterSetting = FALSE;
		   }
		   LoadSelectionWindow (PART2);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 else
		   ItemWindow (BEEPiw, IWsel);
		 break;
	       case VARIABLEt:
		 if (Item->Var->min != NULL) {
		   FreeMemory (Item->Var->min, FatalError);
		   Item->Var->min = NULL;
		   if (Item->Var->max == NULL) Item->filterSetting = FALSE;
		   LoadSelectionWindow (PART2);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 else
		   ItemWindow (BEEPiw, IWsel);
		 break;
	     }
	     break;
	   case MAXcx:
	     switch (Item->type) {
	       case RECORDt:
		 if (Item->Record->max != NOminMax) {
		   Item->Record->max = NOminMax;
		   if (Item->Record->min == NOminMax) {
		     Item->filterSetting = FALSE;
		   }
		   LoadSelectionWindow (PART2);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 else
		   ItemWindow (BEEPiw, IWsel);
		 break;
	       case INDICESt:
		 if (Item->Indices->maxNumDims != NOminMax) {
		   Item->Indices->maxNumDims = NOminMax;
		   if (Item->Indices->minNumDims == NOminMax) {
		     Item->filterSetting = FALSE;
		   }
		   LoadSelectionWindow (PART2);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 else
		   ItemWindow (BEEPiw, IWsel);
		 break;
	       case VARIABLEt:
		 if (Item->Var->max != NULL) {
		   FreeMemory (Item->Var->max, FatalError);
		   Item->Var->max = NULL;
		   if (Item->Var->min == NULL) Item->filterSetting = FALSE;
		   LoadSelectionWindow (PART2);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 else
		   ItemWindow (BEEPiw, IWsel);
		 break;
	     }
	     break;
	   case FILTERcx:
	     ItemWindow (BEEPiw, IWsel);
	     break;
	 }
	 break;
       case FLIPkey_EXPORT:
	 if (FlipItems(rX)) {
	   int itemN = (IWsel->itemN + nSELWINDOWcols2) % IWsel->nItems;
	   LoadSelectionWindow (PART2);
	   ItemWindow (UPDATEiw, IWsel, itemN);
	 }
	 else
	   ItemWindow (BEEPiw, IWsel);
	 break;
       case ACTIONSkey_EXPORT:
	 ActionMenu (PART2);
	 break;
       case OPTIONSkey_EXPORT:
	 OptionMenu (PART2);
	 break;
       case HELPkey_FSI:
	 OnlineHelpWindow ("cdfxp.ilh", VAR2helpID);
	 break;
       case PART3key_EXPORT:
	 return TRUE;
       case EXITkey_FSI:
	 if (ConfirmExit()) return FALSE;
	 break;
     }
     /*************************************************************************
     * Update key definitions in case they were changed.
     *************************************************************************/
     EWkey->eLines = keyDefs;
     EditWindow (UPDATEew, EWkey, (Logical) TRUE);
  }
}

/******************************************************************************
* SelectionWindow3.
******************************************************************************/

Logical SelectionWindow3 () {
  static Logical first = TRUE;
  int rX, cX, rXt, itemN; struct ItemStruct *Item;
  static char label[] = " SelectionWindow, part 3 ";
  static char *header[] = {
    "Item/Variable         FillValue                  Monotonicity  Format    Width"
  };
  static int exits[] = {
    PART1key_EXPORT, ENTERkey_EXPORT, EXITkey_FSI, ACTIONSkey_EXPORT,
    FLIPkey_EXPORT, DELETEkey_FSI, HELPkey_FSI, OPTIONSkey_EXPORT, NUL
  };
  AOSs2 (keyDefs,
   "Modify: ________   Actions: ________   Reverse: ________   Help: ________",
   "Part 1: ________   Options: ________   Delete:  ________   Exit: ________")
  /****************************************************************************
  * Encode key definitions first time.
  ****************************************************************************/
  if (first) {
    EncodeKeyDefinitions (2, keyDefs,
			  ENTERkey_EXPORT, ACTIONSkey_EXPORT, FLIPkey_EXPORT,
			  HELPkey_FSI, PART2key_EXPORT, OPTIONSkey_EXPORT,
			  DELETEkey_FSI, EXITkey_FSI);
    first = FALSE;
  }
  /****************************************************************************
  * Load SelectionWindow structure.
  ****************************************************************************/
  IWsel->label = label;
  IWsel->hLines = header;
  IWsel->exitChars = exits;
  LoadSelectionWindow (PART3);
  /****************************************************************************
  * Load KeyWindow structure.
  ****************************************************************************/
  EWkey->eLines = keyDefs;
  /****************************************************************************
  * Determine initial item number.
  ****************************************************************************/
  itemN = (IWsel->itemN / nSELWINDOWcols2) * nSELWINDOWcols3;
  switch (IWsel->itemN % nSELWINDOWcols2) {
    case NAMEcx: break;
    case MINcx: itemN += FILLcx; break;
    case MAXcx: itemN += MONOcx; break;
    case FILTERcx: itemN += WIDTHcx; break;
  }
  /****************************************************************************
  * Update SelectionWindow and KeyWindow.
  ****************************************************************************/
  ItemWindow (UPDATEiw, IWsel, itemN);
  EditWindow (UPDATEew, EWkey, (Logical) TRUE);
  /****************************************************************************
  * Process keystrokes.
  ****************************************************************************/
  for (;;) {
     ItemWindow (READiw, IWsel);
     rX = IWsel->itemN / nSELWINDOWcols3;
     cX = IWsel->itemN % nSELWINDOWcols3;
     for (Item = itemHead, rXt = 0; rXt < rX; rXt++) Item = Item->nextItem;
     switch (IWsel->key) {
       case ENTERkey_EXPORT:
	 switch (cX) {
	   case NAMEcx:
	     ItemWindow (BEEPiw, IWsel);
	     break;
	   case FILLcx:
	     switch (Item->type) {
	       case RECORDt:
	       case INDICESt:
		 ItemWindow (BEEPiw, IWsel);
		 break;
	       case VARIABLEt:
		 if (PromptForFill(Item->Var)) {
		   LoadSelectionWindow (PART3);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 break;
	     }
	     break;
	   case WIDTHcx: {
	     if (PromptForWidth(Item)) {
	       LoadSelectionWindow (PART3);
	       ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	     }
	     break;
	   }
	   case MONOcx:
	     switch (Item->type) {
	       case RECORDt:
	       case INDICESt:
		 ItemWindow (BEEPiw, IWsel);
		 break;
	       case VARIABLEt:
		 if (Item->Var->monotonic != NAmono) {
		   Item->Var->monotonic = (Item->Var->monotonic + 1) % 4;
		   LoadSelectionWindow (PART3);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 else
		   ItemWindow (BEEPiw, IWsel);
		 break;
	     }
	     break;
	   case FORMATcx:
	     switch (Item->type) {
	       case RECORDt:
	       case INDICESt:
		 ItemWindow (BEEPiw, IWsel);
		 break;
	       case VARIABLEt:
		 if (PromptForFormat(Item)) {
		   LoadSelectionWindow (PART3);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 break;
	     }
	     break;
	 }
	 break;
       case DELETEkey_FSI:
	 switch (cX) {
	   case NAMEcx:
	     ItemWindow (BEEPiw, IWsel);
	     break;
	   case FILLcx:
	     switch (Item->type) {
	       case RECORDt:
	       case INDICESt:
		 ItemWindow (BEEPiw, IWsel);
		 break;
	       case VARIABLEt:
		 if (Item->Var->fill != NULL) {
		   FreeMemory (Item->Var->fill, FatalError);
		   Item->Var->fill = NULL;
		   LoadSelectionWindow (PART3);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 else
		   ItemWindow (BEEPiw, IWsel);
		 break;
	     }
	     break;
	   case WIDTHcx:
	   case MONOcx:
	     ItemWindow (BEEPiw, IWsel);
	     break;
	   case FORMATcx:
	     switch (Item->type) {
	       case RECORDt:
	       case INDICESt:
		 ItemWindow (BEEPiw, IWsel);
		 break;
	       case VARIABLEt:
		 if (Item->Var->format != NULL) {
		   FreeMemory (Item->Var->format, FatalError);
		   Item->Var->format = NULL;
		   LoadSelectionWindow (PART3);
		   ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
		 }
		 else
		   ItemWindow (BEEPiw, IWsel);
		 break;
	     }
	     break;
	 }
	 break;
       case FLIPkey_EXPORT:
	 if (FlipItems(rX)) {
	   int itemN = (IWsel->itemN + nSELWINDOWcols3) % IWsel->nItems;
	   LoadSelectionWindow (PART3);
	   ItemWindow (UPDATEiw, IWsel, itemN);
	 }
	 else
	   ItemWindow (BEEPiw, IWsel);
	 break;
       case ACTIONSkey_EXPORT:
	 ActionMenu (PART3);
	 break;
       case OPTIONSkey_EXPORT:
	 OptionMenu (PART3);
	 break;
       case HELPkey_FSI:
	 OnlineHelpWindow ("cdfxp.ilh", VAR3helpID);
	 break;
       case PART1key_EXPORT:
	 return TRUE;
       case EXITkey_FSI:
	 if (ConfirmExit()) return FALSE;
	 break;
     }
     /*************************************************************************
     * Update key definitions in case they were changed.
     *************************************************************************/
     EWkey->eLines = keyDefs;
     EditWindow (UPDATEew, EWkey, (Logical) TRUE);
  }
}

/******************************************************************************
* ActionMenu.
******************************************************************************/

void ActionMenu (part)
int part;
{
  static Logical first = TRUE;
  static int actExits[] = { ENTERkey_EXPORT, HELPkey_FSI, EXITkey_FSI,
			    NUL };
  static char *actLines[] = {
    "Export to ASCII/screen",
    "Export to ASCII/file...",
    "Export to CDF...",
    "Output all items/variables",
    "Output no items/variables",
    "Filter all items/variables",
    "Filter no items/variables",
    "Save settings",
    "Restore settings",
    "Set monotonicities",
    "OptionMenu..."
  };
  static int actLineNs[] = { 0,1,2,3,4,5,6,7,8,9,10 };
  static int actCols[] = { 0,0,0,0,0,0,0,0,0,0,0 };
  static int actLens[] = { 33,33,33,33,33,33,33,33,33,33,33 };
  STATIC_ struct ItemWindowStruct IWact = {
    1, 44, 35, " ActionMenu ", 0, NULL, 11, actLines, 11, actLineNs,
    actCols, actLens, 13, 0, NULL, actExits, REFRESHkey_FSI, FALSE, NUL, NUL
  };
  struct ItemStruct *Item;
  AOSs2 (keyDefs, "Select: ________  Help: ________  Exit: ________", "")
  /****************************************************************************
  * Create ActionMenu and process keystrokes.
  ****************************************************************************/
  if (first) {
    EncodeKeyDefinitions (2, keyDefs, ENTERkey_EXPORT, HELPkey_FSI,
			  EXITkey_FSI);
    first = FALSE;
  }
  ItemWindow (NEWiw, &IWact, 0);
  for (;;) {
     EWkey->eLines = keyDefs;
     EditWindow (UPDATEew, EWkey, (Logical) TRUE);
     ItemWindow (READiw, &IWact);
     switch (IWact.key) {
       case ENTERkey_EXPORT: {
	 switch (IWact.itemN) {
	   case TOSCREENact:
	     ItemWindow (DELETEiw, &IWact);
	     if (opt.horizontalMode)
	       ToScreenHori ();
	     else
	       ToScreenVert ();
	     LoadSelectionWindow (part);
	     ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	     return;
	   case TOFILEact:
	     ItemWindow (DELETEiw, &IWact);
	     if (opt.horizontalMode)
	       ToFileHori ();
	     else
	       ToFileVert ();
	     LoadSelectionWindow (part);
	     ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	     return;
	   case TOCDFact: {
	     CDFstatus status; CDFid inID;
	     ItemWindow (DELETEiw, &IWact);
	     status = CDFlib (CONFIRM_, CDF_, &inID,
			      NULL_);
	     DisplayStatus (status, "confirming input CDF");
	     if (StatusBAD(status)) break;
	     ToCDF (inID);
	     status = CDFlib (SELECT_, CDF_, inID,
			      NULL_);
	     DisplayStatus (status, "selecting input CDF");
	     if (StatusBAD(status)) {
	       /* Do what? */
	     }
	     LoadSelectionWindow (part);
	     ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	     return;
	   }
	   case OUTPUTallITEMSact:
	   case OUTPUTnoITEMSact: {
	     Logical state = (IWact.itemN == OUTPUTallITEMSact);
	     DisplayMessage (BOO(state,"Setting `outputs' to `YES'.",
				        "Setting `outputs' to `no'"), NOWAIT);
	     for (Item = itemHead; Item != NULL; Item = Item->nextItem) {
		Item->outputSetting = state;
	     }
	     LoadSelectionWindow (part);
	     ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	     DisplayMessage ("", NOWAIT);
	     break;
	   }
	   case FILTERallITEMSact:
	     DisplayMessage ("Setting `filters' to `YES'.", NOWAIT);
	     for (Item = itemHead; Item != NULL; Item = Item->nextItem) {
		switch (Item->type) {
		  case RECORDt:
		    if (Item->Record->min != NOminMax ||
		        Item->Record->max != NOminMax) {
		      Item->filterSetting = TRUE;
		    }
		    break;
		  case INDICESt:
		    if (Item->Indices->minNumDims != NOminMax ||
		        Item->Indices->maxNumDims != NOminMax) {
		      Item->filterSetting = TRUE;
		    }
		    break;
		  case VARIABLEt:
		    if (Item->Var->min != NULL ||
		        Item->Var->max != NULL) Item->filterSetting = TRUE;
		    break;
		}
	     }
	     LoadSelectionWindow (part);
	     ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	     DisplayMessage ("", NOWAIT);
	     break;
	   case FILTERnoITEMSact:
	     DisplayMessage ("Setting `filters' to `no'.", NOWAIT);
	     for (Item = itemHead; Item != NULL; Item = Item->nextItem) {
		Item->filterSetting = FALSE;
	     }
	     LoadSelectionWindow (part);
	     ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	     DisplayMessage ("", NOWAIT);
	     break;
	   case SAVEact:
	     SaveSettings ();
	     break;
	   case RESTOREact:
	     RestoreSettings ();
	     LoadSelectionWindow (part);
	     ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	     break;
	   case SETMONOact:
	     SetItemMonotonicities ();
	     LoadSelectionWindow (part);
	     ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	     break;
	   case OPTIONact:
	     OptionMenu (part);
	     break;
	 }
	 break;
      }
      case HELPkey_FSI:
	OnlineHelpWindow ("cdfxp.ilh", ACTIONhelpID);
	break;
      case EXITkey_FSI:
	ItemWindow (DELETEiw, &IWact);
	return;
    }
  }
}

/******************************************************************************
* OptionMenu.
******************************************************************************/

void OptionMenu (part)
int part;
{
  Logical first = TRUE;
  static long nextEncoding[] = {
    0L, SUN_ENCODING, VAX_ENCODING, DECSTATION_ENCODING, SGi_ENCODING,
    IBMPC_ENCODING, IBMRS_ENCODING, HOST_ENCODING, MAC_ENCODING, HP_ENCODING,
    0L, NeXT_ENCODING, ALPHAOSF1_ENCODING, ALPHAVMSd_ENCODING,
    ALPHAVMSg_ENCODING, NETWORK_ENCODING
  };
  static long prevEncoding[] = {
    0L, ALPHAVMSg_ENCODING, NETWORK_ENCODING, SUN_ENCODING, VAX_ENCODING,
    DECSTATION_ENCODING, SGi_ENCODING, IBMPC_ENCODING, IBMRS_ENCODING,
    HOST_ENCODING, 0L, MAC_ENCODING, HP_ENCODING, NeXT_ENCODING,
    ALPHAOSF1_ENCODING, ALPHAVMSd_ENCODING
  };
  static int optExits[] = { ENTERkey_EXPORT, HELPkey_FSI, EXITkey_FSI,
			    INCREMENTkey_EXPORT, DECREMENTkey_EXPORT, NUL };
  AOSs11 (optLines, BLANKs40, BLANKs40, BLANKs40, BLANKs40, BLANKs40,
	  BLANKs40, BLANKs40, BLANKs40, BLANKs40, BLANKs40, BLANKs40)
  static int optLineNs[] = { 0,1,2,3,4,5,6,7,8,9,10 };
  static int optCols[] = { 0,0,0,0,0,0,0,0,0,0,0 };
  static int optLens[] = { 33,33,33,33,33,33,33,33,33,33,33 };
  STATIC_ struct ItemWindowStruct IWopt = {
    1, 44, 35, " OptionMenu ", 0, NULL, 11, optLines, 11, optLineNs, optCols,
    optLens, 13, 0, NULL, optExits, REFRESHkey_FSI, FALSE, NUL, NUL
  };
  AOSs2 (keyDefs,
	 "Toggle: ________  Increment: ____________  Exit: ________",
	 "Help:   ________  Decrement: ____________")
  /****************************************************************************
  * Display OptionMenu and process keystrokes until exit requested.
  ****************************************************************************/
  for (;;) {
     /*************************************************************************
     * Initialize/update OptionMenu lines.
     *************************************************************************/
     sprintf (IWopt.iLines[FILTERrx], "Use filters: %s",
	      BOO(opt.overallFilter,"YES","no"));
     sprintf (IWopt.iLines[FILLSrx], "Use fill values: %s",
	      BOO(opt.useFills,"YES","no"));
     sprintf (IWopt.iLines[CDFFORMATrx], "Create %s-file CDFs",
	      BOO(opt.singleFile,"single","multi"));
     sprintf (IWopt.iLines[ENCODINGrx], "Create %s-encoded CDFs",
	      encodings[(int)opt.encoding]);
     sprintf (IWopt.iLines[EPOCHrx], "EPOCH style: %s",
	      epochStyles[opt.epochStyle]);
     sprintf (IWopt.iLines[hvMODErx], "%s listings",
	      BOO(opt.horizontalMode,"Horizontal","Vertical"));
     sprintf (IWopt.iLines[MAJORrx], "CDF/listing majority: %s",
	      majorities[(int)opt.majority]);
     sprintf (IWopt.iLines[SHOWrx], "Show filtered lines: %s",
	      BOO(opt.showFiltered,"YES","no"));
     sprintf (IWopt.iLines[SPACINGrx], "Item/variable spacing: %d",
	      opt.spacing);
     sprintf (IWopt.iLines[DELETErx], "Delete existing CDF: %s",
	      BOO(opt.deleteExisting,"YES, beware!","no"));
     sprintf (IWopt.iLines[PREALLOCrx], "Preallocate records: %s",
	      BOO(opt.preAllocate,"YES","no"));
     /*************************************************************************
     * Create OptionMenu and encode key definitions first time.  Just update
     * the OptionMenu all the following times.
     *************************************************************************/
     if (first) {
       ItemWindow (NEWiw, &IWopt, 0);
       EncodeKeyDefinitions (2, keyDefs, ENTERkey_EXPORT, INCREMENTkey_EXPORT,
			     EXITkey_FSI, HELPkey_FSI,
			     DECREMENTkey_EXPORT);
       first = FALSE;
     }
     else
       ItemWindow (UPDATEiw, &IWopt, IWopt.itemN);
     /*************************************************************************
     * Update key definitions (in case there were changed).
     *************************************************************************/
     EWkey->eLines = keyDefs;
     EditWindow (UPDATEew, EWkey, (Logical) TRUE);
     /*************************************************************************
     * Process keystroke.
     *************************************************************************/
     ItemWindow (READiw, &IWopt);
     switch (IWopt.key) {
       case ENTERkey_EXPORT:
	 switch (IWopt.itemN) {
	   case FILTERrx:
	     opt.overallFilter = BOO(opt.overallFilter,FALSE,TRUE);
	     break;
	   case FILLSrx:
	     opt.useFills = BOO(opt.useFills,FALSE,TRUE);
	     break;
	   case CDFFORMATrx:
	     opt.singleFile = BOO(opt.singleFile,FALSE,TRUE);
	     break;
	   case ENCODINGrx:
	     opt.encoding = nextEncoding[(int)opt.encoding];
	     break;
	   case EPOCHrx:
	     opt.epochStyle = BOO(opt.epochStyle == EPOCHx_STYLE,
				  EPOCH0_STYLE,opt.epochStyle + 1);
	     break;
	   case hvMODErx:
	     opt.horizontalMode = BOO(opt.horizontalMode,FALSE,TRUE);
	     break;
	   case MAJORrx:
	     opt.majority = BOO(opt.majority == COL_MAJOR,
				INPUT_MAJOR,opt.majority + 1);
	     break;
	   case SHOWrx:
	     opt.showFiltered = BOO(opt.showFiltered,FALSE,TRUE);
	     break;
	   case SPACINGrx:
	     opt.spacing++;
	     break;
	   case DELETErx:
	     opt.deleteExisting = BOO(opt.deleteExisting,FALSE,TRUE);
	     break;
	   case PREALLOCrx:
	     opt.preAllocate = BOO(opt.preAllocate,FALSE,TRUE);
	     break;
	 }
	 LoadSelectionWindow (part);
	 ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	 break;
       case INCREMENTkey_EXPORT:
	 switch (IWopt.itemN) {
	   case FILTERrx:
	   case FILLSrx:
	   case CDFFORMATrx:
	   case hvMODErx:
	   case SHOWrx:
	   case DELETErx:
	   case PREALLOCrx:
	     ItemWindow (BEEPiw, &IWopt);
	     break;
	   case MAJORrx:
	     opt.majority = BOO(opt.majority == COL_MAJOR,
				INPUT_MAJOR,opt.majority + 1);
	     break;
	   case ENCODINGrx:
	     opt.encoding = nextEncoding[(int)opt.encoding];
	     break;
	   case EPOCHrx:
	     opt.epochStyle = BOO(opt.epochStyle == EPOCHx_STYLE,
				  EPOCH0_STYLE,opt.epochStyle + 1);
	     break;
	   case SPACINGrx:
	     opt.spacing++;
	     break;
	 }
	 LoadSelectionWindow (part);
	 ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	 break;
       case DECREMENTkey_EXPORT:
	 switch (IWopt.itemN) {
	   case FILTERrx:
	   case FILLSrx:
	   case CDFFORMATrx:
	   case hvMODErx:
	   case SHOWrx:
	   case DELETErx:
	   case PREALLOCrx:
	     ItemWindow (BEEPiw, &IWopt);
	     break;
	   case MAJORrx:
	     opt.majority = BOO(opt.majority == INPUT_MAJOR,
				COL_MAJOR,opt.majority - 1);
	     break;
	   case ENCODINGrx:
	     opt.encoding = prevEncoding[(int)opt.encoding];
	     break;
	   case EPOCHrx:
	     opt.epochStyle = BOO(opt.epochStyle == EPOCH0_STYLE,
				  EPOCHx_STYLE,opt.epochStyle - 1);
	     break;
	   case SPACINGrx:
	     if (opt.spacing > 0)
	       opt.spacing--;
	     else
	       ItemWindow (BEEPiw, &IWopt);
	     break;
	 }
	 LoadSelectionWindow (part);
	 ItemWindow (UPDATEiw, IWsel, IWsel->itemN);
	 break;
       case HELPkey_FSI:
	 OnlineHelpWindow ("cdfxp.ilh", OPTIONhelpID);
	 break;
       case EXITkey_FSI:
	 ItemWindow (DELETEiw, &IWopt);
	 return;
     }
  }
}

/******************************************************************************
* ConfirmExit.
*   Returns TRUE if exit should proceed.  FALSE if exit should be aborted.
******************************************************************************/

Logical ConfirmExit () {
  return TRUE;
}

/******************************************************************************
* DisplayStatus.
******************************************************************************/

void DisplayStatus (status, text)
CDFstatus status;
char *text;
{
  if (status == CDF_OK) return;
  if (StatusINFO(status)) {
    if (report[INFOs]) {
      char explanation[CDF_STATUSTEXT_LEN+1], msg[MAX_MESSAGE_TEXT_LEN+1];
      CDFlib (SELECT_, CDF_STATUS_, status,
	      GET_, STATUS_TEXT_, explanation,
	      NULL_);
      strcpyX (msg, "Informational...while ", MAX_MESSAGE_TEXT_LEN);
      strcatX (msg, text, MAX_MESSAGE_TEXT_LEN);
      strcatX (msg, ".", MAX_MESSAGE_TEXT_LEN);
      DisplayMessage (msg, NOBEEPWAIT1);
      DisplayMessage (explanation, NOBEEPWAIT1);
    }
    return;
  }
  if (StatusWARN(status)) {
    if (report[WARNs]) {
      char explanation[CDF_STATUSTEXT_LEN+1], msg[MAX_MESSAGE_TEXT_LEN+1];
      CDFlib (SELECT_, CDF_STATUS_, status,
	      GET_, STATUS_TEXT_, explanation,
	      NULL_);
      strcpyX (msg, "Warning...while ", MAX_MESSAGE_TEXT_LEN);
      strcatX (msg, text, MAX_MESSAGE_TEXT_LEN);
      strcatX (msg, ".", MAX_MESSAGE_TEXT_LEN);
      DisplayMessage (msg, BEEPWAIT1);
      DisplayMessage (explanation, BEEPWAIT1);
    }
    return;
  }
  if (report[ERRORs]) {
    char explanation[CDF_STATUSTEXT_LEN+1], msg[MAX_MESSAGE_TEXT_LEN+1];
    CDFlib (SELECT_, CDF_STATUS_, status,
	    GET_, STATUS_TEXT_, explanation,
	    NULL_);
    strcpyX (msg, "ERROR...while ", MAX_MESSAGE_TEXT_LEN);
    strcatX (msg, text, MAX_MESSAGE_TEXT_LEN);
    strcatX (msg, ".", MAX_MESSAGE_TEXT_LEN);
    DisplayMessage (msg, BEEPWAIT1);
    DisplayMessage (explanation, BEEPWAIT);
  }
  return;
}

/******************************************************************************
* DisplayMessage.
*   wait = NOWAIT.      Return immediately, do not clear message.  NUL is
*                       returned.
*   wait = NOBEEPWAIT.  Wait for user input then clear message.  The key
*                       entered is returned.
*   wait = NOBEEPWAIT1. Wait one second then clear message.  NUL is returned.
*   wait = NOBEEPWAIT2. Wait two seconds then clear message.  NUL is returned.
*   wait = BEEPWAIT.    Beep, wait for user input then clear message.  The key
*                       entered is returned.
*   wait = BEEPWAIT1.   Beep, wait one second then clear message.  NUL is
*                       returned.
*   wait = BEEPWAIT2.   Beep, wait two seconds then clear message.  NUL is
*                       returned.
******************************************************************************/

int DisplayMessage (message, wait)
char *message;
int wait;
{
  strcpyX (EWmsg->eLines[0], message, EWmsg->nColsTotal - 2);
  switch (wait) {
    case NOWAIT:
      EditWindow (UPDATEew, EWmsg, (Logical) TRUE);
      return 0;
    case NOBEEPWAIT:
    case BEEPWAIT: {
      static char newLabel[] = { " Enter any key to acknowledge. " };
      char *saveLabel = EWmsg->label;
      EWmsg->label = newLabel;
      if (wait == BEEPWAIT) EditWindow (BEEPew, EWmsg);
      EditWindow (UPDATEew, EWmsg, (Logical) TRUE);
      EditWindow (READew, EWmsg);
      MakeNUL (EWmsg->eLines[0]);
      EWmsg->label = saveLabel;
      EditWindow (UPDATEew, EWmsg, (Logical) TRUE);
      return EWmsg->key;
    }
    case NOBEEPWAIT1:
    case NOBEEPWAIT2:
    case BEEPWAIT1:
    case BEEPWAIT2: {
      double seconds = BOO((wait == NOBEEPWAIT1 || wait == BEEPWAIT1),1.0,2.0);
      if (wait == BEEPWAIT1 || wait == BEEPWAIT2) EditWindow (BEEPew, EWmsg);
      EditWindow (UPDATEew, EWmsg, (Logical) TRUE);
      zzzzz (seconds);
      MakeNUL (EWmsg->eLines[0]);
      EditWindow (UPDATEew, EWmsg, (Logical) TRUE);
      return 0;
    }
  }
  return 0;
}

/******************************************************************************
* MatchOption.
*   Returns `AMBIGUOUSopt' if ambiguous, `UNKNOWNopt' if not found.
******************************************************************************/

int MatchOption (option, validOptions)
char *option;
char *validOptions[];
{
  int i, j;
  /****************************************************************************
  * First try for an exact match.
  ****************************************************************************/
  for (i = 0; validOptions[i] != NULL; i++) {
     if (!strcmpIgCase(option,validOptions[i])) return i;
  }
  /****************************************************************************
  * Check if the option matches the first part of one of the valid options
  * (ie. it has been abbreviated by truncation).  If so, make sure that it
  * cannot be any of the other valid options (ie. ambiguous).
  ****************************************************************************/
  for (i = 0; validOptions[i] != NULL; i++) {
     if (!strncmpIgCase(option,validOptions[i],strlen(option))) {
       for (j = i + 1; validOptions[j] != NULL; j++) {
	  if (!strncmpIgCase(option,validOptions[j],strlen(option))) {
	    return AMBIGUOUSopt;
	  }
       }
       return i;
     }
  }
  /****************************************************************************
  * The option was not found.
  ****************************************************************************/
  return UNKNOWNopt;
}

/******************************************************************************
* FreeItems.
******************************************************************************/

void FreeItems () {
  struct ItemStruct *Item = itemHead, *tItem;
  while (Item != NULL) {
    tItem = Item->nextItem;
    switch (Item->type) {
      case RECORDt:
	FreeMemory (Item->Record, FatalError);
	break;
      case INDICESt:
	FreeMemory (Item->Indices, FatalError);
	break;
      case VARIABLEt:
	if (Item->Var->name != NULL) FreeMemory (Item->Var->name, FatalError);
	if (Item->Var->min != NULL) FreeMemory (Item->Var->min, FatalError);
	if (Item->Var->max != NULL) FreeMemory (Item->Var->max, FatalError);
	if (Item->Var->format != NULL) FreeMemory (Item->Var->format,
						   FatalError);
	if (Item->Var->fill != NULL) FreeMemory (Item->Var->fill, FatalError);
	if (Item->Var->pad != NULL) FreeMemory (Item->Var->pad, FatalError);
	if (Item->Var->value != NULL) FreeMemory (Item->Var->value,
						  FatalError);
	FreeMemory (Item->Var, FatalError);
	break;
    }
    FreeMemory (Item, FatalError);
    Item = tItem;
  }
  itemHead = NULL;
  return;
}
/******************************************************************************
* LongValueWidth.
******************************************************************************/

int LongValueWidth (value)
long value;
{
  int width = 1;
  long tmp = value / 10;
  while (tmp != 0) {
    width++;
    tmp = tmp / 10;
  }
  return width;
}

/******************************************************************************
* RecordIndicesWidth.
******************************************************************************/

int RecordIndicesWidth (lastRec, numDims, dimSizes, type)
long lastRec;
long numDims;
long dimSizes[];
int type;
{
  switch (type) {
    case RECORDt:
      return MAXIMUM(LongValueWidth(lastRec),MIN_RECORD_WIDTH);
    case INDICESt: {
      int n, indicesW = (int) (1 + BOO(numDims > 1,numDims - 1,0) + 1);
      for (n = 0; n < numDims; n++) indicesW += LongValueWidth(dimSizes[n]);
      return MAXIMUM(indicesW,MIN_INDICES_WIDTH);
    }
  }
  return 0;
}

/******************************************************************************
* FreeToScreen.
******************************************************************************/

void FreeToScreen (header, scrLines, trailer)
char *header[];
char *scrLines[];
char *trailer[];
{
  int lineN;
  FreeMemory (header[0], FatalError);
  for (lineN = 0; lineN < NscrLINES; lineN++) {
     FreeMemory (scrLines[lineN], FatalError);
  }
  FreeMemory (trailer[0], FatalError);
  return;
}

/******************************************************************************
* PromptFor.
******************************************************************************/

Logical PromptFor (value, valueL, cursorAt, label, helpID)
char *value;
int valueL;
int cursorAt;
char *label;
int helpID;
{
  static Logical first = TRUE;
  static int exits[] = {
    ENTERkey_EXPORT, HELPkey_FSI, EXITkey_FSI, NUL
  };
  static struct PromptWindowStruct PW = {
    NULL, 21, 0, SCREEN_WIDTH, 0, NULL, 0, NULL, 0, NULL, exits,
    REFRESHkey_FSI, SOLkey_FSI, EOLkey_FSI, INSERTorOVERkey_FSI
  };
  AOSs2A (keyDefsBlank, "", "")
  AOSs2B (keyDefsPrompt, "Enter: ________  Help: ________  Exit: ________", "")
  if (first) {
    EncodeKeyDefinitions (2, keyDefsPrompt, ENTERkey_EXPORT, HELPkey_FSI,
			  EXITkey_FSI);
    first = FALSE;
  }
  PW.label = label;
  PW.maxChars = valueL;
  PW.value = value;
  PromptWindow (NEWpw, &PW, cursorAt, (Logical) TRUE);
  EWkey->eLines = keyDefsPrompt;
  EditWindow (UPDATEew, EWkey, (Logical) TRUE);
  for (;;) {
     PromptWindow (READpw, &PW);
     switch (PW.key) {
       case ENTERkey_EXPORT:
	 PromptWindow (DELETEpw, &PW);
	 EWkey->eLines = keyDefsBlank;
	 EditWindow (UPDATEew, EWkey, (Logical) TRUE);
	 return TRUE;
       case HELPkey_FSI:
	 OnlineHelpWindow ("cdfxp.ilh", helpID);
	 break;
       case EXITkey_FSI:
	 PromptWindow (DELETEpw, &PW);
	 EWkey->eLines = keyDefsBlank;
	 EditWindow (UPDATEew, EWkey, (Logical) TRUE);
	 return FALSE;
     }
  }
}

/******************************************************************************
* PromptForMinMax.
******************************************************************************/

Logical PromptForMinMax (Item, min)
struct ItemStruct *Item;
Logical min;
{
  static char value[MAXminMaxLEN+1];
  static char minGTmaxMsg[] = {
    "That minimum would be greater than the current maximum."
  };
  static char maxLTminMsg[] = {
    "That maximum would be less than the current minimum."
  };
  switch (Item->type) {
    /**************************************************************************
    * Record.
    **************************************************************************/
    case RECORDt: {
      long *mmPtr = BOO(min,&(Item->Record->min),&(Item->Record->max));
      long *mmPtrX = BOO(min,&(Item->Record->max),&(Item->Record->min));
      long newValue;
      /************************************************************************
      * Set initial value.
      ************************************************************************/
      if (*mmPtr == NOminMax)
	MakeNUL (value);
      else
	sprintf (value, "%ld", *mmPtr + 1);
      /************************************************************************
      * Prompt for new value.
      ************************************************************************/
      if (!PromptFor(value,MAXminMaxLEN,strlen(value),
		     BOO(min,"Enter new minimum record...",
			     "Enter new maximum record..."),
		     MINmaxRECORDhelpID)) {
	return FALSE;
      }
      /************************************************************************
      * Entering nothing erases the current value.
      ************************************************************************/
      if (NULstring(value)) {
	if (*mmPtr != NOminMax) {
	  *mmPtr = NOminMax;
	  if (*mmPtrX == NOminMax) Item->filterSetting = FALSE;
	  return TRUE;
	}
	else
	  return FALSE;
      }
      /************************************************************************
      * Decode the value entered.
      ************************************************************************/
      if (sscanf(value,"%ld",&newValue) != 1) {
	DisplayMessage ("Error decoding value.", BEEPWAIT1);
	return FALSE;
      }
      else
	newValue--;
      /************************************************************************
      * Validate the value entered.
      ************************************************************************/
      if (newValue < 0) {
	DisplayMessage ("Illegal value.", BEEPWAIT1);
	return FALSE;
      }
      /************************************************************************
      * Make sure the new minimum is not greater than the current maximum.
      ************************************************************************/
      if (min && Item->Record->max != NOminMax) {
	if (newValue > Item->Record->max) {
	  DisplayMessage (minGTmaxMsg, BEEPWAIT1);
	  return FALSE;
	}
      }
      /************************************************************************
      * Make sure the new maximum is not less than the current minimum.
      ************************************************************************/
      if (!min && Item->Record->min != NOminMax) {
	if (newValue < Item->Record->min) {
	  DisplayMessage (maxLTminMsg, BEEPWAIT1);
	  return FALSE;
	}
      }
      /************************************************************************
      * Everything's OK, save the new value entered.
      ************************************************************************/
      *mmPtr = newValue;
      Item->filterSetting = TRUE;
      opt.overallFilter = TRUE;
      break;
    }
    /**************************************************************************
    * Indices.
    **************************************************************************/
    case INDICESt: {
      long *mmNumDims = BOO(min,&(Item->Indices->minNumDims),
				&(Item->Indices->maxNumDims)),
	   *mmNumDimsX = BOO(min,&(Item->Indices->maxNumDims),
				 &(Item->Indices->minNumDims)),
	   *mmIndices = BOO(min,Item->Indices->minIndices,
				Item->Indices->maxIndices),
	   newNumDims, newIndices[CDF_MAX_DIMS];
      int dimN;
      /************************************************************************
      * Set initial indices.
      ************************************************************************/
      if (*mmNumDims == NOminMax)
	strcpyX (value, "[]", MAXminMaxLEN);
      else
	EncodeIndicesJustify (value, *mmNumDims, mmIndices, 0);
      /************************************************************************
      * Prompt for new indices.
      ************************************************************************/
      if (!PromptFor(value,MAXminMaxLEN,strlen(value)-1,
		     BOO(min,"Enter new minimum indices...",
			     "Enter new maximum indices..."),
		     MINmaxINDICEShelpID)) {
	return FALSE;
      }
      /************************************************************************
      * Entering nothing erases the current indices.
      ************************************************************************/
      if (NULstring(value)) {
	if (*mmNumDims != NOminMax) {
	  *mmNumDims = NOminMax;
	  if (*mmNumDimsX == NOminMax) Item->filterSetting = FALSE;
	  return TRUE;
	}
	else
	  return FALSE;
      }
      /************************************************************************
      * Decode the indices entered.
      ************************************************************************/
      if (!DecodeRecordAndIndices(value,NULL,&newNumDims,newIndices)) {
	DisplayMessage ("Error decoding indices.", BEEPWAIT1);
	return FALSE;
      }
      /************************************************************************
      * Validate the indices entered.
      ************************************************************************/
      if (newNumDims == 0) {
	DisplayMessage ("Zero dimensions not allowed.", BEEPWAIT1);
	return FALSE;
      }
      for (dimN = 0; dimN < newNumDims; dimN++) {
	 if (newIndices[dimN] < 0) {
	   DisplayMessage ("Illegal indices.", BEEPWAIT1);
	   return FALSE;
	 }
      }
      /************************************************************************
      * Everything's OK, save the new value entered.
      ************************************************************************/
      *mmNumDims = newNumDims;
      for (dimN = 0; dimN < newNumDims; dimN++) {
	 mmIndices[dimN] = newIndices[dimN];
      }
      Item->filterSetting = TRUE;
      opt.overallFilter = TRUE;
      break;
    }
    /**************************************************************************
    * Variable.
    **************************************************************************/
    case VARIABLEt: {
      void *newValue; long numElems;
      void **mmPtr = BOO(min,&(Item->Var->min),&(Item->Var->max));
      void **mmPtrX = BOO(min,&(Item->Var->max),&(Item->Var->min));
      /************************************************************************
      * Set initial value.
      ************************************************************************/
      if (*mmPtr == NULL)
	strcpyX (value, BOO(STRINGdataType(Item->Var->dataType),"\"\"",""), 0);
      else
	EncodeValuesFormat (Item->Var->dataType, Item->Var->numElems, *mmPtr,
			    value, NULL, 0, MAXminMaxLEN, opt.epochStyle);
      /************************************************************************
      * Prompt for new value.
      ************************************************************************/
      if (!PromptFor(value,MAXminMaxLEN,
		     strlen(value) -
		      BOO(STRINGdataType(Item->Var->dataType),1,0),
		     BOO(min,"Enter new minimum value...",
			     "Enter new maximum value..."),
		     MINmaxVALUEhelpID)) {
	return FALSE;
      }
      /************************************************************************
      * Entering nothing erases the current value.
      ************************************************************************/
      if (NULstring(value)) {
	if (*mmPtr != NULL) {
	  FreeMemory (*mmPtr, FatalError);
	  *mmPtr = NULL;
	  if (*mmPtrX == NULL) Item->filterSetting = FALSE;
	  return TRUE;
	}
	else
	  return FALSE;
      }
      /************************************************************************
      * Decode/validate the value entered.
      ************************************************************************/
      if (!DecodeValues(value,Item->Var->dataType,
			&numElems,&newValue,opt.epochStyle)) {
	DisplayMessage ("Error decoding value.", BEEPWAIT1);
	return FALSE;
      }
      if (numElems != Item->Var->numElems) {
	DisplayMessage ("Illegal number of elements.", BEEPWAIT1);
	FreeMemory (newValue, FatalError);
	return FALSE;
      }
      /************************************************************************
      * Make sure the new minimum is not greater than the current maximum.
      ************************************************************************/
      if (min && Item->Var->max != NULL) {
	if (GTx(newValue,Item->Var->max,
		Item->Var->dataType,Item->Var->numElems)) {
	  DisplayMessage (minGTmaxMsg, BEEPWAIT1);
	  FreeMemory (newValue, FatalError);
	  return FALSE;
	}
      }
      /************************************************************************
      * Make sure the new maximum is not less than the current minimum.
      ************************************************************************/
      if (!min && Item->Var->min != NULL) {
	if (LTx(newValue,Item->Var->min,
		Item->Var->dataType,Item->Var->numElems)) {
	  DisplayMessage (maxLTminMsg, BEEPWAIT1);
	  FreeMemory (newValue, FatalError);
	  return FALSE;
	}
      }
      /************************************************************************
      * Everything's OK, save the new value entered.
      ************************************************************************/
      if (*mmPtr == NULL) {
	*mmPtr = AllocateMemory (Item->Var->nValueBytes, FatalError);
      }
      memmove (*mmPtr, newValue, Item->Var->nValueBytes);
      FreeMemory (newValue, FatalError);
      Item->filterSetting = TRUE;
      opt.overallFilter = TRUE;
      break;
    }
  }
  return TRUE;
}

/******************************************************************************
* PromptForFill.
******************************************************************************/

Logical PromptForFill (Var)
struct VarStruct *Var;
{
  static char value[MAXfillLEN+1];
  void *newValue; long numElems;
  /****************************************************************************
  * Set initial value.
  ****************************************************************************/
  if (Var->fill == NULL)
    strcpyX (value, BOO(STRINGdataType(Var->dataType),"\"\"",""), 0);
  else
    EncodeValuesFormat (Var->dataType, Var->numElems, Var->fill, value, NULL,
			0, MAXfillLEN, opt.epochStyle);
  /****************************************************************************
  * Prompt for new value.
  ****************************************************************************/
  if (!PromptFor(value,MAXfillLEN,
		 strlen(value) - BOO(STRINGdataType(Var->dataType),1,0),
		 "Enter new fill value...",FILLhelpID)) {
    return FALSE;
  }
  /****************************************************************************
  * Check if nothing entered.
  ****************************************************************************/
  if (NULstring(value)) {
    if (Var->fill != NULL) {
      FreeMemory (Var->fill, FatalError);
      Var->fill = NULL;
      return TRUE;
    }
    else
      return FALSE;
  }
  /****************************************************************************
  * Decode/validate value.
  ****************************************************************************/
  if (!DecodeValues(value,Var->dataType,
		    &numElems,&newValue,opt.epochStyle)) {
    DisplayMessage ("Error decoding value.", BEEPWAIT1);
    return FALSE;
  }
  if (numElems != Var->numElems) {
    DisplayMessage ("Illegal number of elements.", BEEPWAIT1);
    FreeMemory (newValue, FatalError);
    return FALSE;
  }
  /****************************************************************************
  * Save new value.
  ****************************************************************************/
  if (Var->fill == NULL) {
    Var->fill = AllocateMemory (Var->nValueBytes, FatalError);
  }
  memmove (Var->fill, newValue, Var->nValueBytes);
  FreeMemory (newValue, FatalError);
  opt.useFills = TRUE;
  return TRUE;
}

/******************************************************************************
* PromptForWidth.
******************************************************************************/

Logical PromptForWidth (Item)
struct ItemStruct *Item;
{
  static char value[MAXwidthLEN+1]; int newWidth;
  /****************************************************************************
  * Set initial value.
  ****************************************************************************/
  sprintf (value, "%d", Item->width);
  /****************************************************************************
  * Prompt for new value.
  ****************************************************************************/
  if (!PromptFor(value,MAXwidthLEN,strlen(value),
		 "Enter new width...",WIDTHhelpID)) return FALSE;
  /****************************************************************************
  * Check if nothing entered.
  ****************************************************************************/
  if (NULstring(value)) return FALSE;
  /****************************************************************************
  * Decode/validate value.
  ****************************************************************************/
  if (sscanf(value,"%d",&newWidth) != 1) {
    DisplayMessage ("Error decoding width.", BEEPWAIT1);
    return FALSE;
  }
  if (newWidth < 0) {
    DisplayMessage ("Illegal width.", BEEPWAIT1);
    return FALSE;
  }
  /****************************************************************************
  * Save new width.
  ****************************************************************************/
  Item->width = newWidth;
  return TRUE;
}

/******************************************************************************
* PromptForFormat.
******************************************************************************/

Logical PromptForFormat (Item)
struct ItemStruct *Item;
{
  char format[MAXitemFieldLEN+1];
  /****************************************************************************
  * Set initial format.
  ****************************************************************************/
  strcpyX (format, BOO(Item->Var->format == NULL,"",Item->Var->format),
	   MAXitemFieldLEN);
  /****************************************************************************
  * Prompt for new format.
  ****************************************************************************/
  if (!PromptFor(format,MAXitemFieldLEN,strlen(format),
		 "Enter new format...",FORMAThelpID)) return FALSE;
  /****************************************************************************
  * Check if nothing entered.
  ****************************************************************************/
  if (NULstring(format)) {
    if (Item->Var->format != NULL) {
      FreeMemory (Item->Var->format, FatalError);
      Item->Var->format = NULL;
      return TRUE;
    }
    else
      return FALSE;
  }
  /****************************************************************************
  * Decode/validate new format?
  ****************************************************************************/
  if (!ValidFormat(format)) {
    DisplayMessage ("Illegal format specification.", BEEPWAIT1);
    return FALSE;
  }
  /****************************************************************************
  * Save new format.
  ****************************************************************************/
  if (Item->Var->format != NULL) FreeMemory (Item->Var->format, FatalError);
  Item->Var->format = (char *) AllocateMemory (strlen(format) + 1, FatalError);
  strcpyX (Item->Var->format, format, 0);
  /****************************************************************************
  * Possibly increase width based on new format.
  ****************************************************************************/
  Item->width = MAXIMUM(Item->width,VariableWidth(Item->Var));
  /****************************************************************************
  * Return success.
  ****************************************************************************/
  return TRUE;
}

/******************************************************************************
* FlipItems.
******************************************************************************/

Logical FlipItems (itemX)
int itemX;
{
  struct ItemStruct *Item = itemHead, *prevItem = NULL, *tItem; int itemXt;
  /****************************************************************************
  * Locate Item (and previous Item) to be flipped.
  ****************************************************************************/
  for (itemXt = 0; itemXt < itemX; itemXt++) {
     prevItem = Item;
     Item = Item->nextItem;
  }
  /****************************************************************************
  * Check if only one Item.
  ****************************************************************************/
  if (Item == itemHead && Item->nextItem == NULL) return FALSE;
  /****************************************************************************
  * Check if last Item.
  ****************************************************************************/
  if (Item->nextItem == NULL) {
    prevItem->nextItem = NULL;
    Item->nextItem = itemHead;
    itemHead = Item;
    return TRUE;
  }
  /****************************************************************************
  * Check if first Item.
  ****************************************************************************/
  if (Item == itemHead) {
    tItem = Item->nextItem;
    Item->nextItem = tItem->nextItem;
    tItem->nextItem = Item;
    itemHead = tItem;
    return TRUE;
  }
  /****************************************************************************
  * In the middle of the Item's.
  ****************************************************************************/
  tItem = Item->nextItem;
  Item->nextItem = tItem->nextItem;
  tItem->nextItem = Item;
  prevItem->nextItem = tItem;
  return TRUE;
}

/******************************************************************************
* VariableWidth.
******************************************************************************/

int VariableWidth (Var)
struct VarStruct *Var;
{
  int width;
  /****************************************************************************
  * Check for a string data type.  In this case the format is ignored.
  ****************************************************************************/
  if (STRINGdataType(Var->dataType)) {
    return MAXIMUM(MIN_VARIABLE_WIDTH,(int)(Var->numElems+2));
  }
  /****************************************************************************
  * Check for an EPOCH data type.  Depending on the style the format may be
  * ignored.
  ****************************************************************************/
  if (EPOCHdataType(Var->dataType)) {
    switch (opt.epochStyle) {
      case EPOCH0_STYLE:
	return MAXIMUM(EPOCH_STRING_LEN,MIN_VARIABLE_WIDTH);
      case EPOCH1_STYLE:
	return MAXIMUM(EPOCH1_STRING_LEN,MIN_VARIABLE_WIDTH);
      case EPOCH2_STYLE:
	return MAXIMUM(EPOCH2_STRING_LEN,MIN_VARIABLE_WIDTH);
      case EPOCH3_STYLE:
	return MAXIMUM(EPOCH3_STRING_LEN,MIN_VARIABLE_WIDTH);
      case EPOCHf_STYLE:
	break;			/* Continue on... */
      case EPOCHx_STYLE:
	return MAXIMUM(EPOCHx_STRING_MAX,MIN_VARIABLE_WIDTH);
    }
  }
  /****************************************************************************
  * Check if a format exists.
  ****************************************************************************/
  if (Var->format != NULL) {
    width = FormatWidth(Var->format);
    if (width != 0) return MAXIMUM(MIN_VARIABLE_WIDTH,width);
  }
  /****************************************************************************
  * Either a format doesn't exist or is illegal.  Use the default width for
  * the data type.
  ****************************************************************************/
  switch (Var->dataType) {
    case CDF_BYTE:
    case CDF_INT1:
      width = 4;
      break;
    case CDF_UINT1:
      width = 3;
      break;
    case CDF_INT2:
      width = 6;
      break;
    case CDF_UINT2:
      width = 5;
      break;
    case CDF_INT4:
      width = 11;
      break;
    case CDF_UINT4:
      width = 10;
      break;
    case CDF_REAL4:
    case CDF_FLOAT:
    case CDF_REAL8:
    case CDF_DOUBLE:
      width = 12;
      break;
    case CDF_EPOCH:	/* Format (C/Fortran) style. */
      width = 12;
      break;
    default:            /* Unknown data type. */
      width = 0;
  }
  return MAXIMUM(MIN_VARIABLE_WIDTH,width);
}

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

#if defined(mac)
Logical ExportQOPs (argC, argV)
int *argC;
char **argV[];
{
  DialogPtr dialogP;
  DialogRecord dRecord;
  WindowPtr behind = (WindowPtr) -1;
  ControlHandle controlHs[MAXIMUMin+1];
  Rect iRect;
  short itemN, iType, i;

  static Logical negToPos = DEFAULT_NEGtoPOSfp0;
  static Logical useFilters = DEFAULTeachFilterEXPORT;
  static Logical useFills = DEFAULTfillsEXPORT;
  static Logical useFormat = DEFAULTformatEXPORT;
  static Logical useFillval = DEFAULTfillvalEXPORT;
  static Logical useValidmin = DEFAULTvalidminEXPORT;
  static Logical useValidmax = DEFAULTvalidmaxEXPORT;
  static Logical useMonoton = DEFAULTmonotonEXPORT;
  static Logical showRecord = DEFAULTrecordEXPORT;
  static Logical showIndices = DEFAULTindicesEXPORT;
  static Logical exclusiveFilters = DEFAULTexclusiveEXPORT;
  static Logical showStatistics = DEFAULTstatsEXPORT;
  static Logical outputItems = DEFAULToutputEXPORT;
  static Logical deleteExisting = DEFAULTdeleteEXPORT;
  static Logical preAllocate = DEFAULTpreAllocateEXPORT;
  static int zMode = DEFAULTzModeEXPORT;
  static int newFormat = BOO(DEFAULTsingleEXPORT,0,1);
  static int newEncoding = BOO(DEFAULTnetworkEXPORT,1,0);
  static int majority = (int) DEFAULTmajorityEXPORT;
  static int orientation = BOO(DEFAULThorizontalEXPORT,0,1);
  static int epochStyle = DEFAULTepochEXPORT;
  static Str255 cacheText = "\p";
  static Str255 CDFtext = "\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.
  ****************************************************************************/

  if (negToPos) SetCtlValue (controlHs[NEGZin], 1);
  if (useFilters) SetCtlValue (controlHs[FILTERin], 1);
  if (useFills) SetCtlValue (controlHs[FILLSin], 1);
  if (useFormat) SetCtlValue (controlHs[FORMATin], 1);
  if (useFillval) SetCtlValue (controlHs[FILLVALin], 1);
  if (useValidmin) SetCtlValue (controlHs[VALIDMINin], 1);
  if (useValidmax) SetCtlValue (controlHs[VALIDMAXin], 1);
  if (useMonoton) SetCtlValue (controlHs[MONOTONin], 1);
  if (showRecord) SetCtlValue (controlHs[RECORDin], 1);
  if (showIndices) SetCtlValue (controlHs[INDICESin], 1);
  if (exclusiveFilters) SetCtlValue (controlHs[EXCLUSIVEin], 1);
  if (showStatistics) SetCtlValue (controlHs[STATSin], 1);
  if (outputItems) SetCtlValue (controlHs[OUTPUTin], 1);
  if (deleteExisting) SetCtlValue (controlHs[DELETEin], 1);
  if (preAllocate) SetCtlValue (controlHs[PREALLOCATEin], 1);
  SetCtlValue (controlHs[zMODEinBASE+zMode], 1);
  SetCtlValue (controlHs[FORMATinBASE+newFormat], 1);
  SetCtlValue (controlHs[ENCODINGinBASE+newEncoding], 1);
  SetCtlValue (controlHs[MAJORITYinBASE+majority], 1);
  SetCtlValue (controlHs[EPOCHinBASE+epochStyle], 1);
  SetCtlValue (controlHs[ORIENTinBASE+orientation], 1);
  SetIText ((Handle) controlHs[CDFTEXTin], CDFtext);
  SetIText ((Handle) controlHs[CACHEin], cacheText);

  SetDItem (dialogP, (short) ODBin, (short) userItem,
	    (Handle) OutlineDefaultButton, &iRect);

  /****************************************************************************
  * Display the dialog and wait for user actions.
  ****************************************************************************/
    
  ShowWindow ((WindowPtr) dialogP);
  SetCursor (ARROW_CURSOR);

  for (;;) {
    ModalDialog (FilterDialogQOPfsi, &itemN);
    switch (itemN) {
      /************************************************************************
      * Ok.
      ************************************************************************/
      case OKin: {
	int n;
	char tempS[8+8+9+10+11+11+10+9+10+12+9+9+14+7+8+7+7+11+1];
	char *zModesMAC[3] = { "0", "1", "2" };
	char *formatsMAC[2] = { ",single", ",multi" };
	char *encodingsMAC[2] = { ",host", ",network" };
	char *majoritiesMAC[3] = { "", ",row", ",column" };
	char *orientationsMAC[2] = { ",horizontal", ",vertical" };
	char *epochStylesMAC[6] = {
	  ",epoch", ",epoch1", ",epoch2", ",epoch3", ",epochf", ",epochx"
	};
	/**********************************************************************
	* Get the value of each control.
	**********************************************************************/

	negToPos = GetCtlValue (controlHs[NEGZin]);
	useFilters = GetCtlValue (controlHs[FILTERin]);
	useFills = GetCtlValue (controlHs[FILLSin]);
	useFormat = GetCtlValue (controlHs[FORMATin]);
	useFillval = GetCtlValue (controlHs[FILLVALin]);
	useValidmin = GetCtlValue (controlHs[VALIDMINin]);
	useValidmax = GetCtlValue (controlHs[VALIDMAXin]);
	useMonoton = GetCtlValue (controlHs[MONOTONin]);
	showRecord = GetCtlValue (controlHs[RECORDin]);
	showIndices = GetCtlValue (controlHs[INDICESin]);
	exclusiveFilters = GetCtlValue (controlHs[EXCLUSIVEin]);
	showStatistics = GetCtlValue (controlHs[STATSin]);
	outputItems = GetCtlValue (controlHs[OUTPUTin]);
	deleteExisting = GetCtlValue (controlHs[DELETEin]);
	preAllocate = GetCtlValue (controlHs[PREALLOCATEin]);

	for (zMode = 0; zMode < 3; zMode++) {
	   if (GetCtlValue(controlHs[zMODEinBASE+zMode])) break;
	}
	for (newFormat = 0; newFormat < 2; newFormat++) {
	   if (GetCtlValue(controlHs[FORMATinBASE+newFormat])) break;
	}
	for (newEncoding = 0; newEncoding < 2; newEncoding++) {
	   if (GetCtlValue(controlHs[ENCODINGinBASE+newEncoding])) break;
	}
	for (majority = 0; majority < 3; majority++) {
	   if (GetCtlValue(controlHs[MAJORITYinBASE+majority])) break;
	}
	for (epochStyle = 0; epochStyle < 5; epochStyle++) {
	   if (GetCtlValue(controlHs[EPOCHinBASE+epochStyle])) break;
	}
	for (orientation = 0; orientation < 2; orientation++) {
	   if (GetCtlValue(controlHs[ORIENTinBASE+orientation])) break;
	}

	GetIText ((Handle) controlHs[CDFTEXTin], CDFtext);
	GetIText ((Handle) controlHs[CACHEin], cacheText);

	/**********************************************************************
	* Build argc/argv.
	**********************************************************************/

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

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

	MAKEstrARGv (argV, n, "-initial");
	strcpyX (tempS, BOO(useFilters,"filter","nofilter"), 0);
	strcatX (tempS, BOO(useFills,",fills",",nofills"), 0);
	strcatX (tempS, BOO(useFormat,",format",",noformat"), 0);
	strcatX (tempS, BOO(useFillval,",fillval",",nofillval"), 0);
	strcatX (tempS, BOO(useValidmin,",validmin",",novalidmin"), 0);
	strcatX (tempS, BOO(useValidmax,",validmax",",novalidmax"), 0);
	strcatX (tempS, BOO(useMonoton,",monoton",",nomonoton"), 0);
	strcatX (tempS, BOO(showRecord,",record",",norecord"), 0);
	strcatX (tempS, BOO(showIndices,",indices",",noindices"), 0);
	strcatX (tempS, BOO(exclusiveFilters,",exclusive",",noexclusive"), 0);
	strcatX (tempS, BOO(exclusiveFilters,",exclusive",",noexclusive"), 0);
	strcatX (tempS, BOO(outputItems,",output",",nooutput"), 0);
	strcatX (tempS, BOO(deleteExisting,",delete",",nodelete"), 0);
	strcatX (tempS, BOO(preAllocate,",preallocate",",nopreallocate"), 0);
	strcatX (tempS, formatsMAC[newFormat], 0);
	strcatX (tempS, encodingsMAC[newEncoding], 0);
	strcatX (tempS, majoritiesMAC[majority], 0);
	strcatX (tempS, epochStylesMAC[epochStyle], 0);
	strcatX (tempS, orientationsMAC[orientation], 0);
	MAKEstrARGv (argV, n, tempS);

	MAKEstrARGv (argV, n, BOO(negToPos,"-neg2posfp0","-noneg2posfp0"));
	MAKEstrARGv (argV, n, BOO(showStatistics,"-statistics",
						 "-nostatistics"));

	MAKEstrARGv (argV, n, "-zmode");
	MAKEstrARGv (argV, n, zModesMAC[zMode]);

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

	/**********************************************************************
	* Close the dialog and return.
	**********************************************************************/
        CloseDialog (dialogP);
        return TRUE;
      }
      /************************************************************************
      * Help.
      ************************************************************************/
      case HELPin: {
	int n;
	*argC = 1;
	*argV = (char **) AllocateMemory (*argC * sizeof(char *), FatalError);
	n = 0;
	MAKEstrARGv (argV, n, pgmName)
	CloseDialog (dialogP);
	return TRUE;
      }
      /************************************************************************
      * Cancel.
      ************************************************************************/
      case CANCELin:
        CloseDialog (dialogP);
        return FALSE;
      /************************************************************************
      * Select CDF specification.
      ************************************************************************/
      case CDFSELECTin: {
	StandardFileReply CDFreply;
	char CDFpath[DU_MAX_PATH_LEN+1];
	StandardGetFile (FilterForCDFs, -1, NULL, &CDFreply);
	if (CDFreply.sfGood && !CDFreply.sfIsFolder && !CDFreply.sfIsVolume) {
	  BuildMacPath (&CDFreply.sfFile, CDFpath, TRUE);
	  CDFtext[0] = strlen (CDFpath);
	  strcpyX ((char *) &CDFtext[1], CDFpath, 255);
	  SetIText ((Handle) controlHs[CDFTEXTin], CDFtext);
	}
	break;
      }
      /************************************************************************
      * Check boxes.
      ************************************************************************/
      case NEGZin:
      case FILTERin:
      case FILLSin:
      case FORMATin:
      case FILLVALin:
      case VALIDMINin:
      case VALIDMAXin:
      case MONOTONin:
      case RECORDin:
      case INDICESin:
      case EXCLUSIVEin:
      case STATSin:
      case OUTPUTin:
      case DELETEin:
      case PREALLOCATEin:
        SetCtlValue (controlHs[itemN], BOO(GetCtlValue(controlHs[itemN]),0,1));
        break;
      /************************************************************************
      * Radio buttons.
      ************************************************************************/
      case zMODEinBASE+0:
      case zMODEinBASE+1:
      case zMODEinBASE+2:
        for (i = 0; i < 3; i++) SetCtlValue (controlHs[zMODEinBASE+i], 0);
        SetCtlValue (controlHs[itemN], 1);
        break;
      case FORMATinBASE+0:
      case FORMATinBASE+1:
        for (i = 0; i < 2; i++) SetCtlValue (controlHs[FORMATinBASE+i], 0);
        SetCtlValue (controlHs[itemN], 1);
        break;
      case ENCODINGinBASE+0:
      case ENCODINGinBASE+1:
        for (i = 0; i < 2; i++) SetCtlValue (controlHs[ENCODINGinBASE+i], 0);
        SetCtlValue (controlHs[itemN], 1);
        break;
      case MAJORITYinBASE+0:
      case MAJORITYinBASE+1:
      case MAJORITYinBASE+2:
        for (i = 0; i < 3; i++) SetCtlValue (controlHs[MAJORITYinBASE+i], 0);
        SetCtlValue (controlHs[itemN], 1);
        break;
      case EPOCHinBASE+0:
      case EPOCHinBASE+1:
      case EPOCHinBASE+2:
      case EPOCHinBASE+3:
      case EPOCHinBASE+4:
      case EPOCHinBASE+5:
        for (i = 0; i < 6; i++) SetCtlValue (controlHs[EPOCHinBASE+i], 0);
        SetCtlValue (controlHs[itemN], 1);
        break;
      case ORIENTinBASE+0:
      case ORIENTinBASE+1:
        for (i = 0; i < 2; i++) SetCtlValue (controlHs[ORIENTinBASE+i], 0);
        SetCtlValue (controlHs[itemN], 1);
        break;
    }
  }
}
#endif
