/******************************************************************************
*
*  NSSDC/CDF                            CDFedit, part 1 (attributes/entries).
*
*  Version 1.2d, 20-May-96, Hughes STX.
*
*  Modification history:
*
*   V1.0  24-Jan-94, J Love     Original version.
*   V1.0a  4-Feb-94, J Love     DEC Alpha/OpenVMS port.
*   V1.0b 22-Feb-94, J Love     Spelling lesson.
*   V1.1  15-Dec-94, J Love     CDF V2.5.
*   V1.1a 23-Jan-95, J Love	IRIX 6.x (64-bit).
*   V1.1b 28-Feb-95, J Love	Solaris 2.3.  Pass `char' as `int'.
*   V1.2  11-Apr-95, J Love	POSIX.
*   V1.2a 11-May-95, J Love	EPOCH styles.
*   V1.2b  9-Jun-95, J Love	catchrX.
*   V1.2c  6-Sep-95, J Love	CDFexport-related changes.  FSI key
*				definitions.
*   V1.2d 20-May-96, J Love	Fixed a trailer in `EditVarEntries'.
*
******************************************************************************/

#include "cdfedit.h"

/******************************************************************************
* EditAttrs.
******************************************************************************/

Logical EditAttrs (G, CDFname)
Logical G;                      /* If TRUE, global scoped attributes. */
char *CDFname;                  /* Name of the CDF being edited. */
{
   CDFstatus status;
   long nAttrs;                 /* Number of attributes of the proper scope. */
   long attrN, entryN;
   long fieldN;
   AOSs2 (header, BLANKSforHEADER, BLANKSforHEADER)
   AOSs3 (trailer,
"Modify: ________ Create attribute: ________ Delete attribute/entry: ________",
"Help:   ________ Create entry:     ________",
"Exit:   ________ Toggle scope:     ________")
   static int exitChars[] = { MODIFYkey_EDIT, CREATEATTRkey_EDIT, EXITkey_FSI,
			      HELPkey_FSI, CREATEENTRYkey_EDIT,
			      TOGGLESCOPEkey_EDIT, DELETEATTRorENTRYkey_EDIT,
			      NUL };
   static char label[6+DU_MAX_NAME_LEN+14+1];
   STATIC_ struct ItemWindowStruct IW = {
      0, 0, 80, label, 2, header, 0, NULL, 0, NULL, NULL, NULL, 12, 3, trailer,
      exitChars, REFRESHkey_FSI, TRUE, NSkey_FSI, PSkey_FSI
   };
   long *nEntries;
   long *attrNs;
   long **entryNs;
   int attrX, entryX;
   int entryType;
   Logical change;

   /***************************************************************************
   * Build attribute/entry lines and display menu.
   ***************************************************************************/

   EncodeKeyDefinitions (3, trailer,
			 MODIFYkey_EDIT, CREATEATTRkey_EDIT,
			 DELETEATTRorENTRYkey_EDIT, HELPkey_FSI,
			 CREATEENTRYkey_EDIT, EXITkey_FSI,
			 TOGGLESCOPEkey_EDIT);
   if (!BuildAttrMenu(G,CDFname,&nAttrs,&attrNs,
		      &nEntries,&entryNs,&IW)) return FALSE;
   ItemWindow (NEWiw, &IW, 0);

   /***************************************************************************
   * Read/process keystrokes until request to exit menu.
   ***************************************************************************/

   for (;;) {
     ItemWindow (READiw, &IW);
     switch (IW.key) {
       /***********************************************************************
       * Item selected.
       ***********************************************************************/
       case SELECTkey_EDIT: {
	 int nameItemN;

	 /*********************************************************************
	 * Check if any attributes/entries exist.
	 *********************************************************************/

	 if (IW.nItems == 0) {
	   InputErrorWindow (Gq(G,"Nothing selected (no gAttributes exist).",
				  "Nothing selected (no vAttributes exist)."),
			     FALSE);
	   break;
	 }

	 /*********************************************************************
	 * Calculate attribute, entry, and field numbers.
	 *********************************************************************/

	 for (attrX = 0, nameItemN = 0; attrX < nAttrs; attrX++) {
	    int nItems = (int) (1 + (4 * nEntries[attrX]));
	    if (IW.itemN < nameItemN + nItems) {
	      int itemNt = IW.itemN - nameItemN;
	      attrN = attrNs[attrX];
	      if (itemNt == 0)
		fieldN = 0;
	      else {
		fieldN = ((itemNt - 1) % 4) + 1;
		entryX = (itemNt - 1) / 4;
		entryN = entryNs[attrX][entryX];
		if (G)
		  entryType = gENTRYt;
		else {
		  long NrEntries;
		  status = CDFlib (SELECT_, ATTR_, attrN,
				   GET_, ATTR_NUMrENTRIES_, &NrEntries,
				   NULL_);
		  ReportStatus (status);
		  if (StatusBAD(status)) return FALSE;
		  entryType = (entryX < NrEntries ? rENTRYt : zENTRYt);
		}
	      }
	      break;
	    }
	    nameItemN += nItems;
	 }

	 /*********************************************************************
	 * Perform desired function (based on field number).
	 *********************************************************************/

	 switch (fieldN) {
	   /*******************************************************************
	   * Modify attribute name.
	   *******************************************************************/
	   case 0: {
	     static char *header[] = {
	       "Enter new name (with delimiters)...",
	       "Syntax: <delim><char1><char2>...<charN><delim>",
	       "Example: \"FILTERs\""
	     };
	     AOSs1 (trailer,
		    "Enter: ________   Exit: ________  Help: ________")
	     static int exitChars[] = { ENTERkey_EDIT, EXITkey_FSI,
					HELPkey_FSI, NUL };
	     static char value[1+CDF_ATTR_NAME_LEN+1+1];
	     static char label[15+CDF_ATTR_NAME_LEN+1];
	     STATIC_ struct PromptWindowStruct PWt = {
		label, 7, 10, 60, 3, header, CDF_ATTR_NAME_LEN+2, value,
		1, trailer, exitChars, REFRESHkey_FSI, SOLkey_FSI,
		EOLkey_FSI, INSERTorOVERkey_FSI
	     };
	     char attrName[CDF_ATTR_NAME_LEN+1], delim;
	     EncodeKeyDefinitions (1, trailer, ENTERkey_EDIT, EXITkey_FSI,
				   HELPkey_FSI);
	     status = CDFlib (SELECT_, ATTR_, attrN,
			      GET_, ATTR_NAME_, attrName,
			      NULL_);
	     ReportStatus (status);
	     if (StatusBAD(status)) return FALSE;
	     delim = PickDelimiter (attrName, strlen(attrName));
	     sprintf (value, "%c%s%c", delim, attrName, delim);
	     sprintf (label, " %cAttribute %c%s%c ", Gq(G,'g','v'),
		      delim, attrName, delim);
	     PromptWindow (NEWpw, &PWt, (int) (strlen(PWt.value) - 1),
			   (Logical) TRUE);
	     for (;;) {
		if (EnterPW(&PWt,ATTRRENAMEhelpID)) {
		  char attrName[CDF_ATTR_NAME_LEN+1];
		  if (DecodeDelimitedString(PWt.value,attrName)) {
		    status = CDFlib (PUT_, ATTR_NAME_, attrName,
				     NULL_);
		    ReportStatus (status);
		    if (StatusOK(status)) {
		      FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, &IW);
		      BuildAttrMenu (G, CDFname, &nAttrs, &attrNs,
				     &nEntries, &entryNs, &IW);
		      ItemWindow (UPDATEiw, &IW, IW.itemN);
		      break;
		    }
		  }
		  else
		    InputErrorWindow ("Illegal name (check delimiters).",
				      FALSE);
		}
		else
		  break;
	     }
	     PromptWindow (DELETEpw, &PWt);
	     break;
	   }

	   /*******************************************************************
	   * Modify variable name/global entry number.
	   *******************************************************************/
	   case 1: {
	     if (G)
	       InputErrorWindow ("Not supported.", FALSE);
	     else {
	       static char *header[] = {
		 "Enter new name (with delimiters)...",
		 "Syntax: <delim><char1><char2>...<charN><delim>",
		 "Example: \"Humidity\""
	       };
	       AOSs1 (trailer,
		      "Enter: ________   Exit: ________  Help: ________")
	       static int exitChars[] = { ENTERkey_EDIT, EXITkey_FSI,
					  HELPkey_FSI, NUL };
	       static char value[1+CDF_VAR_NAME_LEN+1+1];
	       static char label[14+CDF_VAR_NAME_LEN+1];
	       STATIC_ struct PromptWindowStruct PWt = {
		  label, 7, 10, 60, 3, header, CDF_VAR_NAME_LEN+2, value,
		  1, trailer, exitChars, REFRESHkey_FSI, SOLkey_FSI,
		  EOLkey_FSI, INSERTorOVERkey_FSI
	       };
	       char varName[CDF_VAR_NAME_LEN+1], delim;
	       Logical Z = (entryType == zENTRYt);
	       long nVars;

	       status = CDFlib (GET_, Zq(Z,CDF_NUMzVARS_,
					   CDF_NUMrVARS_), &nVars,
				NULL_);
	       ReportStatus (status);
	       if (StatusBAD(status)) return FALSE;
	       if (entryN >= nVars) {
		 InputErrorWindow (Zq(Z,"No corresponding zVariable.",
					"No corresponding rVariable."), FALSE);
		 break;
	       }
	       EncodeKeyDefinitions (1, trailer, ENTERkey_EDIT, EXITkey_FSI,
				     HELPkey_FSI);

	       status = CDFlib (SELECT_, Zq(Z,zVAR_,rVAR_), entryN,
				GET_, Zq(Z,zVAR_NAME_,
					   rVAR_NAME_), varName,
				NULL_);
	       ReportStatus (status);
	       if (StatusBAD(status)) return FALSE;
	       delim = PickDelimiter (varName, strlen(varName));
	       sprintf (value, "%c%s%c", delim, varName, delim);
	       sprintf (label, " %cVariable %c%s%c ", Zq(Z,'z','r'),
			delim, varName, delim);
	       PromptWindow (NEWpw, &PWt, (int) (strlen(PWt.value) - 1),
			     (Logical) TRUE);
	       for (;;) {
		  if (EnterPW(&PWt,VARRENAMEhelpID)) {
		    char varName[CDF_VAR_NAME_LEN+1];
		    if (DecodeDelimitedString(PWt.value,varName)) {
		      status = CDFlib (PUT_, Zq(Z,zVAR_NAME_,
						  rVAR_NAME_), varName,
				       NULL_);
		      ReportStatus (status);
		      if (StatusOK(status)) {
			FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, &IW);
			BuildAttrMenu (G, CDFname, &nAttrs, &attrNs,
				       &nEntries, &entryNs, &IW);
			ItemWindow (UPDATEiw, &IW, IW.itemN);
			break;
		      }
		    }
		    else
		      InputErrorWindow ("Illegal name (check delimiters).",
					FALSE);
		  }
		  else
		    break;
	       }
	       PromptWindow (DELETEpw, &PWt);
	     }
	     break;
	   }

	   /*******************************************************************
	   * Modify data specification.
	   *******************************************************************/
	   case 2: {
	     long dataType, numElems;
	     static char *header[] = { "Select new data type..." };
	     static char *items[] = { DTiwSYMBOLS };
	     AOSs2 (trailer, "Select: ________   Exit: ________",
			     "Help:   ________")
	     static int iLineNs[] = { DTiwLINEnS };
	     static int iCols[] = { DTiwCOLs };
	     static int iLens[] = { DTiwLENs };
	     static int exitChars[] = { SELECTkey_EDIT, EXITkey_FSI,
					HELPkey_FSI, NUL };
	     static char label[15+CDF_VAR_NAME_LEN+1];
	     STATIC_ struct ItemWindowStruct IWdt = {
		5, 22, 35, label, 1, header, 7, items, 14, iLineNs, iCols,
		iLens, 7, 2, trailer, exitChars, REFRESHkey_FSI, FALSE
	     };
	     EncodeKeyDefinitions (2, trailer, SELECTkey_EDIT, EXITkey_FSI,
				   HELPkey_FSI);
	     if (G)
	       sprintf (label, " gEntry %ld ", entryN + 1);
	     else {
	       Logical Z = (entryType == zENTRYt);
	       char varName[CDF_VAR_NAME_LEN+1], delim;
	       status = CDFlib (SELECT_, Zq(Z,zVAR_,rVAR_), entryN,
				GET_, Zq(Z,zVAR_NAME_,rVAR_NAME_), varName,
				NULL_);
	       switch (status) {
		 case CDF_OK:
		   delim = PickDelimiter (varName, strlen(varName));
		   sprintf (label, " %cEntry for %c%s%c ", Zq(Z,'z','r'),
			    delim, varName, delim);
		   break;
		 case NO_SUCH_VAR:
		   sprintf (label, " %cEntry %ld ", Zq(Z,'z','r'), entryN + 1);
		   break;
		 default:
		   ReportStatus (status);
		   return FALSE;
	       }
	     }
	     status = CDFlib (SELECT_, ATTR_, attrN,
				       E3(entryType,
					  gENTRY_,rENTRY_,zENTRY_), entryN,
			      GET_, E3(entryType,
				       gENTRY_DATATYPE_,
				       rENTRY_DATATYPE_,
				       zENTRY_DATATYPE_), &dataType,
			      NULL_);
	     ReportStatus (status);
	     if (StatusBAD(status)) return FALSE;
	     ItemWindow (NEWiw, &IWdt, DataTypeItemN(dataType));
	     for (;;) {
		if (EnterIW(&IWdt,ENTRYDATATYPEhelpID)) {
		  dataType = WhichDataType(&(items[iLineNs[IWdt.itemN]]
						  [iCols[IWdt.itemN]]));
		  status = CDFlib (GET_, E3(entryType,
					    gENTRY_NUMELEMS_,
					    rENTRY_NUMELEMS_,
					    zENTRY_NUMELEMS_), &numElems,
				   NULL_);
		  ReportStatus (status);
		  if (StatusBAD(status)) return FALSE;
		  status = CDFlib (PUT_, E3(entryType,
					    gENTRY_DATASPEC_,
					    rENTRY_DATASPEC_,
					    zENTRY_DATASPEC_), dataType,
							       numElems,
				   NULL_);
		  ReportStatus (status);
		  if (StatusOK(status)) {
		    FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, &IW);
		    BuildAttrMenu (G, CDFname, &nAttrs, &attrNs, &nEntries,
				   &entryNs, &IW);
		    ItemWindow (UPDATEiw, &IW, IW.itemN);
		    break;
		  }
		}
		else
		  break;
	     }
	     ItemWindow (DELETEiw, &IWdt);
	     break;
	   }

	   /*******************************************************************
	   * Modify data specification/value(s).
	   *******************************************************************/
	   case 3: {
	     if (!EditEntry(attrN,entryN,entryType,TRUE,&change)) return FALSE;
	     if (change) {
	       FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, &IW);
	       BuildAttrMenu (G, CDFname, &nAttrs, &attrNs, &nEntries,
			      &entryNs, &IW);
	       ItemWindow (UPDATEiw, &IW, IW.itemN);
	     }
	     break;
	   }

	   /*******************************************************************
	   * Modify value(s).
	   *******************************************************************/
	   case 4: {
	     if (!EditEntry(attrN,entryN,entryType,
			    FALSE,&change)) return FALSE;
	     if (change) {
	       FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, &IW);
	       BuildAttrMenu (G, CDFname, &nAttrs, &attrNs, &nEntries,
			      &entryNs, &IW);
	       ItemWindow (UPDATEiw, &IW, IW.itemN);
	     }
	     break;
	   }
	 }

	 break;
       }

       /***********************************************************************
       * Create new attribute.
       ***********************************************************************/
       case CREATEATTRkey_EDIT: {
	 static char *header[] = {
	   "Enter name (with delimiters)...",
	   "Syntax: <delim><char1><char2>...<charN><delim>",
	   "Example: \"Resolution\""
	 };
	 AOSs1 (trailer, "Enter: ________   Exit: ________  Help: ________")
	 static int exitChars[] = { ENTERkey_EDIT, EXITkey_FSI, HELPkey_FSI,
				    NUL };
	 static char value[DELIMed_ATTR_NAME_LEN+1];
	 static char label[16+1];
	 STATIC_ struct PromptWindowStruct PWt = {
	    label, 7, 10, 60, 3, header, CDF_ATTR_NAME_LEN+2, value, 1,
	    trailer, exitChars, REFRESHkey_FSI, SOLkey_FSI, EOLkey_FSI,
	    INSERTorOVERkey_FSI
	 };
	 EncodeKeyDefinitions (1, trailer, ENTERkey_EDIT, EXITkey_FSI,
			       HELPkey_FSI);
	 strcpyX (value, "\"\"", DELIMed_ATTR_NAME_LEN);
	 sprintf (label, " New %cAttribute ", Gq(G,'g','v'));
	 PromptWindow (NEWpw, &PWt, 1, (Logical) TRUE);
	 for (;;) {
	    if (EnterPW(&PWt,ATTRNAMEhelpID)) {
	      char attrName[CDF_ATTR_NAME_LEN+1];
	      long attrNum;
	      if (DecodeDelimitedString(PWt.value,attrName)) {
		status = CDFlib (CREATE_, ATTR_, attrName,
						 Gq(G,GLOBAL_SCOPE,
						      VARIABLE_SCOPE),
						 &attrNum,
				 NULL_);
		ReportStatus (status);
		if (StatusOK(status)) {
		  FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, &IW);
		  BuildAttrMenu (G, CDFname, &nAttrs, &attrNs, &nEntries,
				 &entryNs, &IW);
		  ItemWindow (UPDATEiw, &IW, IW.nItems - 1);
		  break;
		}
	      }
	      else
		InputErrorWindow ("Illegal name (check delimiters).", FALSE);
	    }
	    else
	      break;
	 }
	 PromptWindow (DELETEpw, &PWt);
	 break;
       }

       /***********************************************************************
       * Create new entry.
       ***********************************************************************/
       case CREATEENTRYkey_EDIT: {
	 int nameItemN;
	 if (IW.nItems == 0) {
	   InputErrorWindow(Gq(G,"A gAttribute isn't selected (none exist).",
				 "A vAttribute isn't selected (none exist)."),
			    FALSE);
	   break;
	 }
	 for (attrX = 0, nameItemN = 0; attrX < nAttrs; attrX++) {
	    int nItems = (int) (1 + (4 * nEntries[attrX]));
	    if (IW.itemN < nameItemN + nItems) {
	      attrN = attrNs[attrX];
	      break;
	    }
	    nameItemN += nItems;
	 }
	 if (!CreateEntry(G,attrN,&change)) return FALSE;
	 if (change) {
	   FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, &IW);
	   BuildAttrMenu (G, CDFname, &nAttrs, &attrNs, &nEntries, &entryNs,
			  &IW);
	   ItemWindow (UPDATEiw, &IW, nameItemN);
	 }
	 break;
       }

       /***********************************************************************
       * Delete attribute/entry.
       ***********************************************************************/
       case DELETEATTRorENTRYkey_EDIT: {
	 int nameItemN;
	 Logical deleteAttr;
	 if (IW.nItems == 0) {
	   InputErrorWindow
	       (Gq(G,"A gAttribute/gEntry isn't selected (none exist).",
		     "A vAttribute/entry isn't selected (none exist)."),FALSE);
	   break;
	 }
	 for (attrX = 0, nameItemN = 0; attrX < nAttrs; attrX++) {
	    int nItems = (int) (1 + (4 * nEntries[attrX]));
	    if (IW.itemN < nameItemN + nItems) {
	      int itemNt = IW.itemN - nameItemN;
	      attrN = attrNs[attrX];
	      if (itemNt == 0)
		deleteAttr = TRUE;
	      else {
		deleteAttr = FALSE;
		entryX = (itemNt - 1) / 4;
		entryN = entryNs[attrX][entryX];
		if (G)
		  entryType = gENTRYt;
		else {
		  long NrEntries;
		  status = CDFlib (SELECT_, ATTR_, attrN,
				   GET_, ATTR_NUMrENTRIES_, &NrEntries,
				   NULL_);
		  ReportStatus (status);
		  if (StatusBAD(status)) return FALSE;
		  entryType = (entryX < NrEntries ? rENTRYt : zENTRYt);
		}
	      }
	      break;
	    }
	    nameItemN += nItems;
	 }
	 if (deleteAttr) {
	   char attrName[CDF_ATTR_NAME_LEN+1],
		question[22+CDF_ATTR_NAME_LEN+1], delim;
	   status = CDFlib (SELECT_, ATTR_, attrN,
			    GET_, ATTR_NAME_, attrName,
			    NULL_);
	   ReportStatus (status);
	   if (StatusBAD(status)) return FALSE;
	   delim = PickDelimiter (attrName, strlen(attrName));
	   sprintf (question, "Delete %sAttribute %c%s%c ?",
		    BOO(G,"g","v"), delim, attrName, delim);
	   if (ConfirmWindow(7,question,NULL,FALSE,DELETEATTRhelpID)) {
	     status = CDFlib (DELETE_, ATTR_,
			      NULL_);
	     ReportStatus (status);
	     if (StatusOK(status)) {
	       FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, &IW);
	       BuildAttrMenu (G, CDFname, &nAttrs, &attrNs, &nEntries,
			      &entryNs, &IW);
	       ItemWindow (UPDATEiw, &IW, 0);
	     }
	   }
	 }
	 else {
	   char attrName[CDF_ATTR_NAME_LEN+1], delim,
		question1[40+ENTRYNUM_FIELD_LEN+CDF_ATTR_NAME_LEN+1],
		question2[31+CDF_VAR_NAME_LEN+1];
	   status = CDFlib (SELECT_, ATTR_, attrN,
			    GET_, ATTR_NAME_, attrName,
			    NULL_);
	   ReportStatus (status);
	   if (StatusBAD(status)) return FALSE;
	   if (entryType == gENTRYt) {
	     delim = PickDelimiter (attrName, strlen(attrName));
	     sprintf (question1,
		      "Delete gEntry number %ld of gAttribute %c%s%c ?",
		      entryN + 1, delim, attrName, delim);
	     question2[0] = NUL;
	   }
	   else {
	     char varName[CDF_VAR_NAME_LEN+1], attrName[CDF_ATTR_NAME_LEN+1];
	     status = CDFlib (SELECT_, BOO(entryType==zENTRYt,zVAR_,
							      rVAR_), entryN,
			      GET_, BOO(entryType==zENTRYt,
					zVAR_NAME_,rVAR_NAME_), varName,
			      NULL_);
	     switch (status) {
	       case CDF_OK:
		 delim = PickDelimiter (attrName, strlen(attrName));
		 sprintf (question1, "Delete %sEntry of vAttribute %c%s%c",
			  BOO(entryType==zENTRYt,"z","r"), delim, attrName,
			  delim);
		 delim = PickDelimiter (varName, strlen(varName));
		 sprintf (question2, "corresponding to %sVariable %c%s%c ?",
			  BOO(entryType==zENTRYt,"z","r"), delim, varName,
			  delim);
		 break;
	       case NO_SUCH_VAR:
		 sprintf (question1,
			  "Delete %sEntry number %ld of vAttribute %c%s%c ?",
			  BOO(entryType==zENTRYt,"z","r"), entryN + 1, delim,
			  attrName, delim);
		 question2[0] = NUL;
		 break;
	       default:
		 ReportStatus (status);
		 return FALSE;
	     }
	   }
	   if (ConfirmWindow(7,question1,
			     BOO(NULstring(question2),NULL,question2),
			     FALSE,DELETEENTRYhelpID)) {
	     status = CDFlib (SELECT_, E3(entryType,
					  gENTRY_,rENTRY_,zENTRY_), entryN,
			      DELETE_, E3(entryType,gENTRY_,rENTRY_,zENTRY_),
			      NULL_);
	     ReportStatus (status);
	     if (StatusOK(status)) {
	       FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, &IW);
	       BuildAttrMenu (G, CDFname, &nAttrs, &attrNs, &nEntries,
			      &entryNs, &IW);
	       ItemWindow (UPDATEiw, &IW, 0);
	     }
	   }
	 }
	 break;
       }

       /***********************************************************************
       * Toggle scope.
       ***********************************************************************/
       case TOGGLESCOPEkey_EDIT: {
	 int nameItemN;
	 if (IW.nItems == 0) {
	   InputErrorWindow(Gq(G,"A gAttribute isn't selected (none exist).",
				 "A vAttribute isn't selected (none exist)."),
			    FALSE);
	   break;
	 }
	 for (attrX = 0, nameItemN = 0; attrX < nAttrs; attrX++) {
	    int nItems = (int) (1 + (4 * nEntries[attrX]));
	    if (IW.itemN < nameItemN + nItems) {
	      attrN = attrNs[attrX];
	      break;
	    }
	    nameItemN += nItems;
	 }
	 status = CDFlib (SELECT_, ATTR_, attrN,
			  PUT_, ATTR_SCOPE_, Gq(G,VARIABLE_SCOPE,GLOBAL_SCOPE),
			  NULL_);
	 ReportStatus (status);
	 if (StatusOK(status)) {
	   FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, &IW);
	   BuildAttrMenu (G, CDFname, &nAttrs, &attrNs, &nEntries,
			  &entryNs, &IW);
	   ItemWindow (UPDATEiw, &IW, 0);
	 }
	 break;
       }

       /***********************************************************************
       * Display online help.
       ***********************************************************************/
       case HELPkey_FSI:
	 OnlineHelpWindow (ilhFile, Gq(G,GATTRShelpID,VATTRShelpID));
	 break;

       /***********************************************************************
       * Exit menu.
       ***********************************************************************/
       case EXITkey_FSI:
	 ItemWindow (DELETEiw, &IW);
	 FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, &IW);
	 return TRUE;
     }
   }
}

/******************************************************************************
* BuildAttrMenu.
******************************************************************************/

Logical BuildAttrMenu (G, CDFname, nAttrs, attrNs, nEntries, entryNs, IW)
Logical G;
char *CDFname;
long *nAttrs;
long **attrNs;
long **nEntries;
long ***entryNs;
struct ItemWindowStruct *IW;
{
   CDFstatus status;
   int attrX, entryX;
   long dataType, numElems;
   long NgEntries, NrEntries, NzEntries;
   char attrName[CDF_ATTR_NAME_LEN+1];
   int curItem;
   int curCol;
   int lineN;
   int n;
   int dataSpecCol;
   void *binary;
   char field[80+1];
   long TnAttrs;                /* Total number of attributes (global and
				   variable scoped). */
   int entryType;
   int entryXt;
   int nLines, nItems;
   long attrN;
   long estLines;               /* Estimated number of lines for the menu. */

   static char *eLines[] = { "Loading menu..." };
   static int exitChars[] = { NUL };
   STATIC_ struct EditWindowStruct EW = {
     NULL, 10, 30, 17, 0, NULL, 1, 1, eLines, 1, 0, NULL, FALSE, TRUE,
     exitChars, NUL, NUL, NUL, NUL, NUL, NUL, NUL
   };

   /***************************************************************************
   * Determine number of attributes (total and of proper scope).
   ***************************************************************************/

   status = CDFlib (GET_, CDF_NUMATTRS_, &TnAttrs,
			  Gq(G,CDF_NUMgATTRS_,CDF_NUMvATTRS_), nAttrs,
		    NULL_);
   ReportStatus (status);
   if (StatusBAD(status)) return FALSE;

   if (!G) {
     long NrVars, NzVars;
     status = CDFlib (GET_, CDF_NUMrVARS_, &NrVars,
			    CDF_NUMzVARS_, &NzVars,
		      NULL_);
     ReportStatus (status);
     if (StatusBAD(status)) return FALSE;
     estLines = (*nAttrs) * (NrVars + NzVars);
     if (estLines > MANY_ATTRs_AND_ENTRYs) {
       EditWindow (NEWew, &EW, (Logical) TRUE);
     }
   }

   /***************************************************************************
   * Count/save attribute and entry numbers.  Determine attribute name item
   * numbers.
   ***************************************************************************/

   *nEntries = (long *) AllocateMemory ((size_t) (*nAttrs * sizeof(long)),
					FatalError);
   *attrNs = (long *) AllocateMemory ((size_t) (*nAttrs * sizeof(long)),
				      FatalError);
   *entryNs = (long **) AllocateMemory ((size_t) (*nAttrs * sizeof(long *)),
					FatalError);

   for (attrN=0, attrX=0, nItems=0, nLines=0; attrN < TnAttrs; attrN++) {
      long scope;

      /************************************************************************
      * Determine scope.
      ************************************************************************/

      status = CDFlib (SELECT_, ATTR_, attrN,
		       GET_, ATTR_SCOPE_, &scope,
		       NULL_);
      ReportStatus (status);
      if (StatusBAD(status)) return FALSE;

      /************************************************************************
      * If proper scope, save attribute number and determine which entries
      * exist.
      ************************************************************************/

      if ((G && (scope == GLOBAL_SCOPE)) ||
	  (!G && (scope == VARIABLE_SCOPE))) {
	int i;
	long entryN;

	/**********************************************************************
	* Tally an attribute of the proper scope.
	**********************************************************************/

	(*attrNs)[attrX] = attrN;
	nItems++;
	nLines++;

	/**********************************************************************
	* Determine number of entries.
	**********************************************************************/

	if (G) {
	  status = CDFlib (GET_, ATTR_NUMgENTRIES_, &NgEntries,
			   NULL_);
	  ReportStatus (status);
	  if (StatusBAD(status)) return FALSE;
	  (*nEntries)[attrX] = NgEntries;
	}
	else {
	  status = CDFlib (GET_, ATTR_NUMrENTRIES_, &NrEntries,
				 ATTR_NUMzENTRIES_, &NzEntries,
			   NULL_);
	  ReportStatus (status);
	  if (StatusBAD(status)) return FALSE;
	  (*nEntries)[attrX] = NrEntries + NzEntries;
	}

	(*entryNs)[attrX] = (long *)
			    AllocateMemory ((size_t) ((*nEntries)[attrX] *
						       sizeof(long)),
					    FatalError);

	for (i = 0, entryX = 0, entryType = Gq(G,gENTRYt,rENTRYt);
	     i < Gq(G,1,2); i++, entryType = zENTRYt) {
	  long nEntries = E3(entryType,NgEntries,NrEntries,NzEntries);
	  for (entryN = 0, entryXt = 0; entryXt < nEntries; entryN++) {
	     status = CDFlib (SELECT_, E3(entryType,
					  gENTRY_,rENTRY_,zENTRY_), entryN,
			      CONFIRM_, E3(entryType,
					   CURgENTRY_EXISTENCE_,
					   CURrENTRY_EXISTENCE_,
					   CURzENTRY_EXISTENCE_),
			      NULL_);
	     switch (status) {
	       case CDF_OK:
		 nItems += 4;
		 if (entryX > 0) nLines++;
		 (*entryNs)[attrX][entryX] = entryN;
		 entryX++;
		 entryXt++;
		 break;
	       case NO_SUCH_ENTRY:
		 break;
	       default:
		 ReportStatus (status);
		 return FALSE;
	     }
	  }
	}
	attrX++;
      }
   }

   /***************************************************************************
   * Allocate item section control/lines.
   ***************************************************************************/

   AllocIW (IW, nItems, nLines, 80, FatalError);

   /***************************************************************************
   * Encode label/header.
   ***************************************************************************/

   sprintf (IW->label, " CDF \"%s\" %cAttributes ", CDFname, Gq(G,'g','v'));

   if (G) {
     sprintf (IW->hLines[0], "%ld gAttribute%s", *nAttrs,
	      (*nAttrs == 1 ? "" : "s"));
     strcpyX (IW->hLines[1],
	      "AttrName         Entry  DataSpec  Value(s)", 0);
   }
   else {
     sprintf (IW->hLines[0], "%ld vAttribute%s", *nAttrs,
	      (*nAttrs == 1 ? "" : "s"));
     strcpyX (IW->hLines[1],
	      "AttrName         VarName          DataSpec  Value(s)", 0);
   }

   /***************************************************************************
   * Build each attribute/entry line.
   ***************************************************************************/

   lineN = 0;
   curItem = 0;

   for (attrX = 0; attrX < *nAttrs; attrX++) {
      /************************************************************************
      * Inquire attribute.
      ************************************************************************/

      status = CDFlib (SELECT_, ATTR_, (*attrNs)[attrX],
		       GET_, ATTR_NAME_, attrName,
		       NULL_);
      ReportStatus (status);
      if (StatusBAD(status)) return FALSE;

      /************************************************************************
      * Setup attribute name.
      ************************************************************************/

      curCol = 0;

      EncodeString (strlen(attrName), attrName, field, 0, ATTRNAME_FIELD_LEN);
      strcpyX (IW->iLines[lineN], field, 0);
      if (strlen(field) < (size_t) ATTRNAME_FIELD_LEN) {
	CatNcharacters (IW->iLines[lineN], ATTRNAME_FIELD_LEN - strlen(field),
			(int) ' ');
      }
      CatNcharacters (IW->iLines[lineN], ATTRNAME_FIELD_BLANKS, (int) ' ');

      IW->iLineNs[curItem] = lineN;
      IW->iCols[curItem] = curCol;
      IW->iLens[curItem] = strlen(field);

      curCol += ATTRNAME_FIELD_LEN + ATTRNAME_FIELD_BLANKS;
      curItem++;

      /************************************************************************
      * Setup each entry.
      ************************************************************************/

      if (!G) {
	status = CDFlib (GET_, ATTR_NUMrENTRIES_, &NrEntries,
			 NULL_);
	ReportStatus (status);
	if (StatusBAD(status)) return FALSE;
      }

      if ((*nEntries)[attrX] > 0) {
	for (entryX = 0; entryX < (*nEntries)[attrX]; entryX++) {
	   int entryType = Gq(G,gENTRYt,
				(entryX < NrEntries ? rENTRYt : zENTRYt));

	   /*******************************************************************
	   * Inquire entry.
	   *******************************************************************/

	   status = CDFlib (SELECT_, E3(entryType,
					gENTRY_,
					rENTRY_,
					zENTRY_), (*entryNs)[attrX][entryX],
			    GET_, E3(entryType,gENTRY_DATATYPE_,
					       rENTRY_DATATYPE_,
					       zENTRY_DATATYPE_), &dataType,
				  E3(entryType,gENTRY_NUMELEMS_,
					       rENTRY_NUMELEMS_,
					       zENTRY_NUMELEMS_), &numElems,
			    NULL_);
	   ReportStatus (status);
	   if (StatusBAD(status)) return FALSE;

	   /*******************************************************************
	   * Pad with blanks the attribute name field if this isn't the first
	   * entry.
	   *******************************************************************/

	   if (entryX > 0) {
	     strcpyX (IW->iLines[lineN], "", 0);
	     CatNcharacters (IW->iLines[lineN], ATTRNAME_FIELD_LEN, (int) ' ');
	     CatNcharacters (IW->iLines[lineN], ATTRNAME_FIELD_BLANKS,
			     (int) ' ');
	     curCol = ATTRNAME_FIELD_LEN + ATTRNAME_FIELD_BLANKS;
	   }

	   /*******************************************************************
	   * Setup entry number field if global scope, variable name if
	   * variable scope.
	   *******************************************************************/

	   if (G) {
	     sprintf (field, "%ld", (*entryNs)[attrX][entryX] + 1);
	     if (strlen(field) > (size_t) ENTRYNUM_FIELD_LEN)
	       strcpyX (field + ENTRYNUM_FIELD_LEN - 3, "...", 0);
	   }
	   else {
	     char varName[CDF_VAR_NAME_LEN+1];
	     status = CDFlib (SELECT_, E3(entryType,0,rVAR_,
					  zVAR_), (*entryNs)[attrX][entryX],
			      GET_, E3(entryType,0,
				       rVAR_NAME_,zVAR_NAME_), varName,
			      NULL_);
	     switch (status) {
	       case CDF_OK:
		 EncodeString (strlen(varName), varName, field, 0,
			       VARNAME_FIELD_LEN);
		 break;
	       case NO_SUCH_VAR: {
		 Logical Z = E3(entryType,FALSE,FALSE,TRUE);
		 sprintf (field, "<%sEntry.%ld>", Zq(Z,"z","r"),
			  (*entryNs)[attrX][entryX] + 1);
		 break;
	       }
	       default:
		 ReportStatus (status);
		 return FALSE;
	     }
	   }

	   strcatX (IW->iLines[lineN], field, 0);
	   if (strlen(field) < (size_t) Gq(G,ENTRYNUM_FIELD_LEN,
					     VARNAME_FIELD_LEN)) {
	     CatNcharacters (IW->iLines[lineN],
			     Gq(G,ENTRYNUM_FIELD_LEN,
				  VARNAME_FIELD_LEN) - strlen(field),
			     (int) ' ');
	   }
	   CatNcharacters (IW->iLines[lineN],
			   Gq(G,ENTRYNUM_FIELD_BLANKS,VARNAME_FIELD_BLANKS),
			   (int) ' ');

	   IW->iLineNs[curItem] = lineN;
	   IW->iCols[curItem] = curCol;
	   IW->iLens[curItem] = strlen(field);

	   curCol += Gq(G,ENTRYNUM_FIELD_LEN,VARNAME_FIELD_LEN) +
		     Gq(G,ENTRYNUM_FIELD_BLANKS,VARNAME_FIELD_BLANKS);
	   curItem++;

	   /*******************************************************************
	   * Setup data type & number of elements.
	   *******************************************************************/

	   strcpyX (field, DataTypeToken(dataType), 0);
	   catchrX (field, (int) '/', 0);
	   sprintf (&field[strlen(field)], "%ld", numElems);
	   if (strlen(field) > (size_t) DATASPEC_FIELD_LEN) {
	     strcpyX (field + DATASPEC_FIELD_LEN - 3, "...", 0);
	   }
	   strcatX (IW->iLines[lineN], field, 0);
	   if (strlen(field) < (size_t) DATASPEC_FIELD_LEN) {
	     CatNcharacters (IW->iLines[lineN],
			     DATASPEC_FIELD_LEN - strlen(field), (int) ' ');
	   }
	   CatNcharacters (IW->iLines[lineN], DATASPEC_FIELD_BLANKS, (int)' ');

	   IW->iLineNs[curItem] = lineN;
	   IW->iCols[curItem] = curCol;
	   IW->iLens[curItem] = strlen(field);

	   dataSpecCol = curCol;

	   curCol += DATASPEC_FIELD_LEN + DATASPEC_FIELD_BLANKS;
	   curItem++;

	   /*******************************************************************
	   * Setup value(s) field.
	   *******************************************************************/

	   binary = AllocateMemory ((size_t) (numElems *
					      CDFelemSize(dataType)),
				    FatalError);

	   status = CDFlib (GET_, E3(entryType,gENTRY_DATA_,
					       rENTRY_DATA_,
					       zENTRY_DATA_), binary,
			    NULL_);
	   ReportStatus (status);
	   if (StatusBAD(status)) return FALSE;

	   n = EncodeValuesFormat (dataType, numElems, binary,
				   &(IW->iLines[lineN][curCol]), NULL, 0,
				   Gq(G,gAttrENTRYVALUE_FIELD_LEN,
					vAttrENTRYVALUE_FIELD_LEN),
				   EPOCH0_STYLE);
	   FreeMemory (binary, FatalError);

	   /*******************************************************************
	   * Set 'dataSpec/value' item control information.
	   *******************************************************************/

	   IW->iLineNs[curItem] = lineN;
	   IW->iCols[curItem] = dataSpecCol;
	   IW->iLens[curItem] = strlen(&(IW->iLines[lineN][dataSpecCol]));
	   curItem++;

	   /*******************************************************************
	   * Set `value' item control information.
	   *******************************************************************/

	   IW->iLineNs[curItem] = lineN;
	   IW->iCols[curItem] = curCol;
	   IW->iLens[curItem] = n;
	   curItem++;

	   lineN++;
	}
      }
      else {
	lineN++;        /* No entries, just increment to next line. */
      }
   }

   if (!G) {
     if (estLines > MANY_ATTRs_AND_ENTRYs) EditWindow (DELETEew, &EW, TRUE);
   }

   return TRUE;
}

/******************************************************************************
* FreeAttrMenu.
******************************************************************************/

void FreeAttrMenu (nAttrs, attrNs, nEntries, entryNs, IW)
long nAttrs;
long *attrNs;
long *nEntries;
long **entryNs;
struct ItemWindowStruct *IW;
{
  int attrX;
  FreeIW (IW, FatalError);
  for (attrX = (int) (nAttrs - 1); attrX >= 0; attrX--) {
     if (entryNs[attrX] != NULL) FreeMemory (entryNs[attrX], FatalError);
  }
  if (entryNs != NULL) FreeMemory (entryNs, FatalError);
  if (attrNs != NULL) FreeMemory (attrNs, FatalError);
  if (nEntries != NULL) FreeMemory (nEntries, FatalError);
  return;
}

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

Logical EditEntry (attrN, entryN, entryType, newDataSpec, change)
long attrN;
long entryN;
int entryType;
Logical newDataSpec;
Logical *change;
{
  long dataType, numElems;
  CDFstatus status;
  static char value[MAX_ENTRYSTRING_LEN+1];
  static char label[15+CDF_VAR_NAME_LEN+1];
  /****************************************************************************
  * No change yet.  Set to TRUE when a change to the CDF is made.
  ****************************************************************************/
  *change = FALSE;
  /****************************************************************************
  * Encode label.
  ****************************************************************************/
  switch (entryType) {
    case gENTRYt:
      sprintf (label, " gEntry %ld ", entryN + 1);
      break;
    case rENTRYt:
    case zENTRYt: {
      Logical Z = (entryType == zENTRYt);
      char varName[CDF_VAR_NAME_LEN+1], delim;
      status = CDFlib (SELECT_, Zq(Z,zVAR_,rVAR_), entryN,
		       GET_, Zq(Z,zVAR_NAME_,rVAR_NAME_), varName,
		       NULL_);
      switch (status) {
	case CDF_OK:
	  delim = PickDelimiter (varName, strlen(varName));
	  sprintf (label, " %cEntry for %c%s%c ", Zq(Z,'z','r'),
		   delim, varName, delim);
	  break;
	case NO_SUCH_VAR:
	  sprintf (label, " %cEntry %ld ", Zq(Z,'z','r'), entryN + 1);
	  break;
	default:
	  ReportStatus (status);
	  return FALSE;
      }
      break;
    }
  }
  /****************************************************************************
  * Either prompt for a new data type (the number of elements will be implied
  * by the value entered) or read the current data specification and value.
  ****************************************************************************/
  if (newDataSpec) {
    static char *header[] = { "Select data type..." };
    static char *items[] = { DTiwSYMBOLS };
    AOSs2 (trailer, "Select: ________   Exit: ________",
		    "Help:   ________")
    static int iLineNs[] = { DTiwLINEnS };
    static int iCols[14] = { DTiwCOLs };
    static int iLens[14] = { DTiwLENs };
    static int exitChars[] = { SELECTkey_EDIT, EXITkey_FSI, HELPkey_FSI,
			       NUL };
    STATIC_ struct ItemWindowStruct IWt = {
	5, 23, 35, label, 1, header, 7, items, 14, iLineNs, iCols, iLens, 7,
	2, trailer, exitChars, REFRESHkey_FSI, FALSE
    };
    EncodeKeyDefinitions (2, trailer, SELECTkey_EDIT, EXITkey_FSI,
			  HELPkey_FSI);
    ItemWindow (NEWiw, &IWt, 0);
    if (EnterIW(&IWt,ENTRYDATATYPEhelpID))
      dataType = WhichDataType(&items[iLineNs[IWt.itemN]][iCols[IWt.itemN]]);
    else
      dataType = -1;
    ItemWindow (DELETEiw, &IWt);
    if (STRINGdataType(dataType))
      strcpyX (value, "\"\"", MAX_ENTRYSTRING_LEN);
    else
      strcpyX (value, "", MAX_ENTRYSTRING_LEN);
  }
  else {
    void *binary;
    status = CDFlib (SELECT_, ATTR_, attrN,
			      E3(entryType,gENTRY_,
					   rENTRY_,
					   zENTRY_), entryN,
		     GET_, E3(entryType,gENTRY_DATATYPE_,
					rENTRY_DATATYPE_,
					zENTRY_DATATYPE_), &dataType,
			   E3(entryType,gENTRY_NUMELEMS_,
					rENTRY_NUMELEMS_,
					zENTRY_NUMELEMS_), &numElems,
		     NULL_);
    ReportStatus (status);
    if (StatusBAD(status)) return FALSE;
    binary = AllocateMemory ((size_t) (numElems * CDFelemSize(dataType)),
			     FatalError);
    status = CDFlib (GET_, E3(entryType,gENTRY_DATA_,
					rENTRY_DATA_,
					zENTRY_DATA_), binary,
		     NULL_);
    ReportStatus (status);
    if (StatusBAD(status)) return FALSE;
    EncodeValuesFormat (dataType, numElems, binary, value, NULL, 0,
			MAX_ENTRYSTRING_LEN, EPOCH0_STYLE);
    FreeMemory (binary, FatalError);
  }
  /****************************************************************************
  * Prompt for the new value.
  ****************************************************************************/
  if (dataType == -1)
    return TRUE;
  else {
    static char *headerChar[] = {
      "Enter character string (with delimiters)...",
      "Syntax: <delim><char1><char2>...<charN><delim>"
    };
    static char *headerNoChar[] = {
      "Enter value(s)...",
      "Syntax: <value1>,<value2>,...,<valueN>"
    };
    AOSs1 (trailer, "Enter: ________   Exit: ________  Help: ________")
    static int exitChars[] = { ENTERkey_EDIT, EXITkey_FSI, HELPkey_FSI,
			       NUL };
    STATIC_ struct PromptWindowStruct PW = {
      label, 7, 5, 70, 2, NULL, MAX_ENTRYSTRING_LEN, value, 1, trailer,
      exitChars, REFRESHkey_FSI, SOLkey_FSI, EOLkey_FSI,
      INSERTorOVERkey_FSI
    };
    PW.hLines = (STRINGdataType(dataType) ? headerChar : headerNoChar);
    EncodeKeyDefinitions (1, trailer, ENTERkey_EDIT, EXITkey_FSI,
			  HELPkey_FSI);
    PromptWindow (NEWpw, &PW, (int) BOO(STRINGdataType(dataType),
					strlen(PW.value)-1,
					strlen(PW.value)), (Logical) TRUE);
    for (;;) {
       if (EnterPW(&PW,ENTRYVALUEhelpID)) {
	 void *newBinary;
	 long newNumElems;
	 if (DecodeValues(PW.value,dataType,&newNumElems,
			  &newBinary,EPOCH0_STYLE)) {
	   status = CDFlib (SELECT_, ATTR_, attrN,
				     E3(entryType,gENTRY_,
						  rENTRY_,
						  zENTRY_), entryN,
			    PUT_, E3(entryType,gENTRY_DATA_,
					       rENTRY_DATA_,
					       zENTRY_DATA_), dataType,
							      newNumElems,
							      newBinary,
			    NULL_);
	   ReportStatus (status);
	   FreeMemory (newBinary, FatalError);
	   if (StatusOK(status)) {
	     *change = TRUE;
	     break;
	   }
	 }
	 else
	   InputErrorWindow ("Error decoding values.", FALSE);
       }
       else
	 break;
    }
    PromptWindow (DELETEpw, &PW);
  }
  return TRUE;
}

/******************************************************************************
* EditVarEntries.
******************************************************************************/

Logical EditVarEntries (Z, varN)
Logical Z;
long varN;
{
   CDFstatus status;
   long nAttrs;                 /* Number of variable scoped attributes. */
   long attrN;
   AOSs2 (header, BLANKSforHEADER, BLANKSforHEADER)
   AOSs2A (trailer,
     "Modify: ________    Create attribute: ________   Help:         ________",
     "Exit:   ________    Create entry:     ________   Delete entry: ________")
   static int exitChars[] = {
     MODIFYkey_EDIT, EXITkey_FSI, CREATEATTRkey_EDIT, HELPkey_FSI,
     CREATEENTRYkey_EDIT, DELETEATTRorENTRYkey_EDIT, NUL
   };
   static char label[12+CDF_VAR_NAME_LEN+11+1];
   STATIC_ struct ItemWindowStruct IW = {
      0, 0, 80, label, 2, header, 0, NULL, 0, NULL, NULL, NULL, 13, 2, trailer,
      exitChars, REFRESHkey_FSI, TRUE, NSkey_FSI, PSkey_FSI
   };
   long *attrNs;                /* Attribute numbers. */
   Logical *entryIs;            /* TRUE if there is an entry. */
   int attrX;                   /* Attribute index (which variable scoped
				   attribute). */
   Logical change;

   /***************************************************************************
   * Build attribute/entry lines.
   ***************************************************************************/

   EncodeKeyDefinitions (2, trailer, MODIFYkey_EDIT, CREATEATTRkey_EDIT,
			 HELPkey_FSI, EXITkey_FSI, CREATEENTRYkey_EDIT,
			 DELETEATTRorENTRYkey_EDIT);
   if (!BuildVarEntryMenu(Z,varN,&nAttrs,&attrNs,&entryIs,&IW)) return FALSE;

   /***************************************************************************
   * Display window.
   ***************************************************************************/

   ItemWindow (NEWiw, &IW, 0);

   /***************************************************************************
   * Read/process keystrokes until request to exit menu.
   ***************************************************************************/

   for (;;) {
     ItemWindow (READiw, &IW);
     switch (IW.key) {
       /***********************************************************************
       * Item selected.
       ***********************************************************************/
       case MODIFYkey_EDIT: {
	 int fieldN, nameItemN;

	 /*********************************************************************
	 * Do any items exist?
	 *********************************************************************/

	 if (IW.nItems == 0) {
	   InputErrorWindow ("Nothing selected (no vAttributes exist).",
			     FALSE);
	   break;
	 }

	 /*********************************************************************
	 * Calculate attribute and field numbers.
	 *********************************************************************/

	 for (attrX = 0, nameItemN = 0; attrX < nAttrs; attrX++) {
	    int nItems = (int) (entryIs[attrX] ? 4 : 1);
	    if (IW.itemN < nameItemN + nItems) {
	      attrN = attrNs[attrX];
	      fieldN = IW.itemN - nameItemN;
	      break;
	    }
	    nameItemN += nItems;
	 }

	 /*********************************************************************
	 * Perform desired function (based on field number).
	 *********************************************************************/

	 switch (fieldN) {
	   /*******************************************************************
	   * Modify attribute name.
	   *******************************************************************/
	   case 0: {
	     static char *header[] = {
		"Enter new name (with delimiters)...",
		"Syntax: <delim><char1><char2>...<charN><delim>",
		"Example: \"RANGE\""
	     };
	     AOSs1 (trailer,
		    "Enter: ________   Exit: ________  Help: ________")
	     static int exitChars[] = { ENTERkey_EDIT, EXITkey_FSI,
					HELPkey_FSI, NUL };
	     static char value[1+CDF_ATTR_NAME_LEN+1+1];
	     static char label[15+CDF_ATTR_NAME_LEN+1];
	     STATIC_ struct PromptWindowStruct PW = {
		label, 7, 10, 60, 3, header, CDF_ATTR_NAME_LEN+2, value, 1,
		trailer, exitChars, REFRESHkey_FSI, SOLkey_FSI, EOLkey_FSI,
		INSERTorOVERkey_FSI
	     };
	     char attrName[CDF_ATTR_NAME_LEN+1], delim;
	     EncodeKeyDefinitions (1, trailer, ENTERkey_EDIT, EXITkey_FSI,
				   HELPkey_FSI);
	     status = CDFlib (SELECT_, ATTR_, attrN,
			      GET_, ATTR_NAME_, attrName,
			      NULL_);
	     ReportStatus (status);
	     if (StatusBAD(status)) return FALSE;
	     delim = PickDelimiter (attrName, strlen(attrName));
	     sprintf (value, "%c%s%c", delim, attrName, delim);
	     sprintf (label, " vAttribute %c%s%c ", delim, attrName, delim);
	     PromptWindow (NEWpw, &PW, (int) (strlen(PW.value) - 1),
			   (Logical) TRUE);
	     for (;;) {
		if (EnterPW(&PW,ATTRRENAMEhelpID)) {
		  char attrName[CDF_ATTR_NAME_LEN+1];
		  if (DecodeDelimitedString(PW.value,attrName)) {
		    status = CDFlib (PUT_, ATTR_NAME_, attrName,
				     NULL_);
		    ReportStatus (status);
		    if (StatusOK(status)) {
		      FreeVarEntryMenu (attrNs, entryIs, &IW);
		      if (!BuildVarEntryMenu(Z,varN,&nAttrs,&attrNs,
					     &entryIs, &IW)) return FALSE;
		      ItemWindow (UPDATEiw, &IW, IW.itemN);
		      break;
		    }
		  }
		  else
		    InputErrorWindow ("Illegal name (check delimiters).",
				      FALSE);
		}
		else
		  break;
	     }
	     PromptWindow (DELETEpw, &PW);
	     break;
	   }

	   /*******************************************************************
	   * Modify data specification.
	   *******************************************************************/
	   case 1: {
	     long dataType, numElems;
	     static char *header[] = { "Select new data type..." };
	     AOSs2 (trailer, "Select: ________   Exit: ________",
			     "Help:   ________")
	     static char *items[] = { DTiwSYMBOLS };
	     static int iLineNs[] = { DTiwLINEnS };
	     static int iCols[] = { DTiwCOLs };
	     static int iLens[] = { DTiwLENs };
	     static int exitChars[] = { SELECTkey_EDIT, EXITkey_FSI,
					HELPkey_FSI, NUL };
	     static char label[15+CDF_VAR_NAME_LEN+1];
	     STATIC_ struct ItemWindowStruct IWdt = {
		5, 23, 35, label, 1, header, 7, items, 14, iLineNs, iCols,
		iLens, 7, 2, trailer, exitChars, REFRESHkey_FSI, FALSE
	     };
	     char varName[CDF_VAR_NAME_LEN+1], delim;
	     EncodeKeyDefinitions (2, trailer, SELECTkey_EDIT, EXITkey_FSI,
				   HELPkey_FSI);
	     status = CDFlib (SELECT_, Zq(Z,zVAR_,rVAR_), varN,
				       ATTR_, attrN,
				       Zq(Z,zENTRY_,rENTRY_), varN,
			      GET_, Zq(Z,zVAR_NAME_,rVAR_NAME_), varName,
				    Zq(Z,zENTRY_DATATYPE_,
					 rENTRY_DATATYPE_), &dataType,
			      NULL_);
	     ReportStatus (status);
	     if (StatusBAD(status)) return FALSE;
	     delim = PickDelimiter (varName, strlen(varName));
	     sprintf (label, " %cEntry for %c%s%c ", Zq(Z,'z','r'),
		      delim, varName, delim);
	     ItemWindow (NEWiw, &IWdt, DataTypeItemN(dataType));
	     for (;;) {
		if (EnterIW(&IWdt,ENTRYDATATYPEhelpID)) {
		  dataType = WhichDataType(&items[iLineNs[IWdt.itemN]]
						 [iCols[IWdt.itemN]]);
		  status = CDFlib (GET_, Zq(Z,zENTRY_NUMELEMS_,
					      rENTRY_NUMELEMS_), &numElems,
				   NULL_);
		  ReportStatus (status);
		  if (StatusBAD(status)) return FALSE;
		  status = CDFlib (PUT_, Zq(Z,zENTRY_DATASPEC_,
					      rENTRY_DATASPEC_), dataType,
								 numElems,
				   NULL_);
		  ReportStatus (status);
		  if (StatusOK(status)) {
		    FreeVarEntryMenu (attrNs, entryIs, &IW);
		    if (!BuildVarEntryMenu(Z,varN,&nAttrs,&attrNs,
					   &entryIs,&IW)) return FALSE;
		    ItemWindow (UPDATEiw, &IW, IW.itemN);
		    break;
		  }
		}
		else
		  break;
	     }
	     ItemWindow (DELETEiw, &IWdt);
	     break;
	   }

	   /*******************************************************************
	   * Modify data specification/value(s).
	   *******************************************************************/
	   case 2: {
	     if (!EditEntry(attrN,varN,
			    Zq(Z,zENTRYt,rENTRYt),TRUE,&change)) return FALSE;
	     if (&change) {
	       FreeVarEntryMenu (attrNs, entryIs, &IW);
	       if (!BuildVarEntryMenu(Z,varN,&nAttrs,
				      &attrNs,&entryIs,&IW)) return FALSE;
	       ItemWindow (UPDATEiw, &IW, IW.itemN);
	     }
	     break;
	   }

	   /*******************************************************************
	   * Modify value(s).
	   *******************************************************************/
	   case 3: {
	     if (!EditEntry(attrN,varN,
			    Zq(Z,zENTRYt,rENTRYt),FALSE,&change)) return FALSE;
	     if (&change) {
	       FreeVarEntryMenu (attrNs, entryIs, &IW);
	       if (!BuildVarEntryMenu(Z,varN,&nAttrs,
				      &attrNs,&entryIs,&IW)) return FALSE;
	       ItemWindow (UPDATEiw, &IW, IW.itemN);
	     }
	     break;
	   }
	 }

	 break;
       }

       /***********************************************************************
       * Create attribute.
       ***********************************************************************/
       case CREATEATTRkey_EDIT: {
	 static char *header[] = {
	   "Enter name (with delimiters)...",
	   "Syntax: <delim><char1><char2>...<charN><delim>",
	   "Example: \"MetricUnits\""
	 };
	 AOSs1 (trailer, "Enter: ________   Exit: ________  Help: _______")
	 static int exitChars[] = { ENTERkey_EDIT, EXITkey_FSI, HELPkey_FSI,
				    NUL };
	 static char value[DELIMed_ATTR_NAME_LEN+1];
	 STATIC_ struct PromptWindowStruct PWt = {
	    " New vAttribute ", 7, 10, 60, 3, header, CDF_ATTR_NAME_LEN+2,
	    value, 1, trailer, exitChars, REFRESHkey_FSI, SOLkey_FSI,
	    EOLkey_FSI, INSERTorOVERkey_FSI
	 };
	 EncodeKeyDefinitions (1, trailer, ENTERkey_EDIT, EXITkey_FSI,
			       HELPkey_FSI);
	 strcpyX (PWt.value, "\"\"", DELIMed_ATTR_NAME_LEN);
	 PromptWindow (NEWpw, &PWt, 1, (Logical) TRUE);
	 for (;;) {
	    if (EnterPW(&PWt,ATTRNAMEhelpID)) {
	      char attrName[CDF_ATTR_NAME_LEN+1];
	      long attrNum;
	      if (DecodeDelimitedString(PWt.value,attrName)) {
		status = CDFlib (CREATE_, ATTR_, attrName, VARIABLE_SCOPE,
						 &attrNum,
				 NULL_);
		ReportStatus (status);
		if (StatusOK(status)) {
		  FreeVarEntryMenu (attrNs, entryIs, &IW);
		  if (!BuildVarEntryMenu(Z,varN,&nAttrs,
					 &attrNs,&entryIs,&IW)) return FALSE;
		  ItemWindow (UPDATEiw, &IW, IW.nItems - 1);
		  break;
		}
	      }
	      else
		InputErrorWindow ("Illegal name (check delimiters).", FALSE);
	    }
	    else
	      break;
	 }
	 PromptWindow (DELETEpw, &PWt);
	 break;
       }

       /***********************************************************************
       * Create entry.
       ***********************************************************************/
       case CREATEENTRYkey_EDIT: {
	 int nameItemN;
	 /*********************************************************************
	 * Do any items exist?
	 *********************************************************************/
	 if (IW.nItems == 0) {
	   InputErrorWindow ("A vAttribute isn't selected (none exist).",
			     FALSE);
	   break;
	 }
	 /*********************************************************************
	 * Calculate attribute number.
	 *********************************************************************/
	 for (attrX = 0, nameItemN = 0; attrX < nAttrs; attrX++) {
	    int nItems = (int) (entryIs[attrX] ? 4 : 1);
	    if (IW.itemN < nameItemN + nItems) {
	      attrN = attrNs[attrX];
	      break;
	    }
	    nameItemN += nItems;
	 }
	 /*********************************************************************
	 * Create entry (unless it already exists).
	 *********************************************************************/

         if (entryIs[attrX])
           InputErrorWindow ("An entry already exists.", FALSE);
         else {
           if (!EditEntry(attrN,varN,
			  Zq(Z,zENTRYt,rENTRYt),TRUE,&change)) return FALSE;
	   if (change) {
             FreeVarEntryMenu (attrNs, entryIs, &IW);
             BuildVarEntryMenu (Z, varN, &nAttrs, &attrNs, &entryIs, &IW);
             ItemWindow (UPDATEiw, &IW, nameItemN);
           }
	 }
	 break;
       }

       /***********************************************************************
       * Delete entry.
       ***********************************************************************/
       case DELETEATTRorENTRYkey_EDIT: {
	 int nameItemN;
	 /*********************************************************************
	 * Do any items exist?
	 *********************************************************************/
	 if (IW.nItems == 0) {
	   InputErrorWindow ("A vAttribute isn't selected (none exist).",
			     FALSE);
	   break;
	 }
	 /*********************************************************************
	 * Calculate attribute number.
	 *********************************************************************/
	 for (attrX = 0, nameItemN = 0; attrX < nAttrs; attrX++) {
	    int nItems = (int) (entryIs[attrX] ? 4 : 1);
	    if (IW.itemN < nameItemN + nItems) {
	      attrN = attrNs[attrX];
	      break;
	    }
	    nameItemN += nItems;
	 }
	 /*********************************************************************
	 * Delete entry (unless it doesn't exist).
	 *********************************************************************/
	 if (entryIs[attrX]) {
	   char attrName[CDF_ATTR_NAME_LEN+1], varName[CDF_VAR_NAME_LEN+1],
		question1[28+1+CDF_ATTR_NAME_LEN+1+1],
		question2[29+1+CDF_VAR_NAME_LEN+1+1], delim;
	   status = CDFlib (SELECT_, ATTR_, attrN,
			    GET_, ATTR_NAME_, attrName,
			    SELECT_, BOO(Z,zVAR_,rVAR_), varN,
			    GET_, BOO(Z,zVAR_NAME_,rVAR_NAME_), varName,
			    NULL_);
	   ReportStatus (status);
	   if (StatusBAD(status)) return FALSE;
	   delim = PickDelimiter (attrName, strlen(attrName));
	   sprintf (question1, "Delete %sEntry of vAttribute %c%s%c",
		    BOO(Z,"z","r"), delim, attrName, delim);
	   delim = PickDelimiter (varName, strlen(varName));
	   sprintf (question2, "corresponding to %sVariable %c%s%c ?",
		    BOO(Z,"z","r"), delim, varName, delim);
	   if (ConfirmWindow(7,question1,question2,FALSE,DELETEENTRYhelpID)) {
	     status = CDFlib (SELECT_, BOO(Z,zENTRY_,rENTRY_), varN,
			      DELETE_, BOO(Z,zENTRY_,rENTRY_),
			      NULL_);
	     ReportStatus (status);
	     if (StatusOK(status)) {
	       FreeVarEntryMenu (attrNs, entryIs, &IW);
	       if (!BuildVarEntryMenu(Z,varN,&nAttrs,&attrNs,
				      &entryIs,&IW)) return FALSE;
	       ItemWindow (UPDATEiw, &IW, nameItemN);
	     }
	   }
	 }
	 else
	   InputErrorWindow ("An entry does not exist.", FALSE);
	 break;
       }

       /***********************************************************************
       * Display online help.
       ***********************************************************************/
       case HELPkey_FSI:
	 OnlineHelpWindow (ilhFile, ENTRIEShelpID);
	 break;

       /***********************************************************************
       * Exit menu.
       ***********************************************************************/
       case EXITkey_FSI:
	 ItemWindow (DELETEiw, &IW);
	 FreeVarEntryMenu (attrNs, entryIs, &IW);
	 return TRUE;
     }
   }
}

/******************************************************************************
* BuildVarEntryMenu.
******************************************************************************/

Logical BuildVarEntryMenu (Z, varN, nAttrs, attrNs, entryIs, IW)
Logical Z;
long varN;
long *nAttrs;
long **attrNs;
Logical **entryIs;
struct ItemWindowStruct *IW;
{
   CDFstatus status;
   int attrX, curItem, curCol, lineN, n, nItems, dataSpecCol;
   long dataType, numElems, attrN;
   char attrName[CDF_ATTR_NAME_LEN+1], varName[CDF_VAR_NAME_LEN+1], delim,
	field[80+1];
   void *binary;

   /***************************************************************************
   * Determine number of attributes (of variable scope).
   ***************************************************************************/

   status = CDFlib (GET_, CDF_NUMvATTRS_, nAttrs,
		    NULL_);
   ReportStatus (status);
   if (StatusBAD(status)) return FALSE;

   /***************************************************************************
   * Count/save attribute numbers.
   ***************************************************************************/

   *attrNs = (long *) AllocateMemory ((size_t) (*nAttrs * sizeof(long)),
				      FatalError);
   *entryIs = (Logical *) AllocateMemory ((size_t) (*nAttrs * sizeof(Logical)),
					  FatalError);

   for (attrN = 0, attrX = 0, nItems = 0; attrX < *nAttrs; attrN++) {
      long scope;

      /************************************************************************
      * Determine scope.
      ************************************************************************/

      status = CDFlib (SELECT_, ATTR_, attrN,
		       GET_, ATTR_SCOPE_, &scope,
		       NULL_);
      ReportStatus (status);
      if (StatusBAD(status)) return FALSE;

      /************************************************************************
      * If proper scope, save attribute number and determine if entry exists.
      ************************************************************************/

      if (scope == VARIABLE_SCOPE) {
	(*attrNs)[attrX] = attrN;
	status = CDFlib (SELECT_, Zq(Z,zENTRY_,rENTRY_), varN,
			 GET_, Zq(Z,zENTRY_DATATYPE_,
				    rENTRY_DATATYPE_), &dataType,
			 NULL_);
	switch (status) {
	  case CDF_OK:
	    (*entryIs)[attrX] = TRUE;
	    nItems += 4;
	    break;
	  case NO_SUCH_ENTRY:
	    (*entryIs)[attrX] = FALSE;
	    nItems += 1;
	    break;
	  default:
	    ReportStatus (status);
	    return FALSE;
	}
	attrX++;
      }
   }

   /***************************************************************************
   * Allocate item section control/lines.
   ***************************************************************************/

   AllocIW (IW, nItems, (int) *nAttrs, 80, FatalError);

   /***************************************************************************
   * Encode label/header.
   ***************************************************************************/

   status = CDFlib (SELECT_, Zq(Z,zVAR_,rVAR_), varN,
		    GET_, Zq(Z,zVAR_NAME_,rVAR_NAME_), varName,
		    NULL_);
   ReportStatus (status);
   if (StatusBAD(status)) return FALSE;

   delim = PickDelimiter (varName, strlen(varName));
   sprintf (IW->label, " %cVariable %c%s%c %cEntries ", Zq(Z,'z','r'), delim,
	    varName, delim, Zq(Z,'z','r'));

   sprintf (IW->hLines[0],"%ld vAttributes", *nAttrs);
   strcpyX (IW->hLines[1], "AttrName         DataSpec  Value(s)", 0);

   /***************************************************************************
   * Build each attribute/entry line.
   ***************************************************************************/

   lineN = 0;
   curItem = 0;

   for (attrX = 0; attrX < *nAttrs; attrX++) {
      /************************************************************************
      * Inquire attribute/entry.
      ************************************************************************/

      status = CDFlib (SELECT_, ATTR_, (*attrNs)[attrX],
		       GET_, ATTR_NAME_, attrName,
		       NULL_);
      ReportStatus (status);
      if (StatusBAD(status)) return FALSE;

      /************************************************************************
      * Setup attribute name.
      ************************************************************************/

      curCol = 0;

      EncodeString (strlen(attrName), attrName, field, 0, ATTRNAME_FIELD_LEN);
      strcpyX (IW->iLines[lineN], field, 0);
      if (strlen(field) < (size_t) ATTRNAME_FIELD_LEN) {
	CatNcharacters (IW->iLines[lineN], ATTRNAME_FIELD_LEN - strlen(field),
			(int) ' ');
      }
      CatNcharacters (IW->iLines[lineN], ATTRNAME_FIELD_BLANKS, (int) ' ');

      IW->iLineNs[curItem] = lineN;
      IW->iCols[curItem] = curCol;
      IW->iLens[curItem] = strlen(field);

      curCol += ATTRNAME_FIELD_LEN + ATTRNAME_FIELD_BLANKS;
      curItem++;

      /************************************************************************
      * Setup entry data specification/value(s).
      ************************************************************************/

      if ((*entryIs)[attrX]) {
	status = CDFlib (SELECT_, Zq(Z,zENTRY_,rENTRY_), varN,
			 GET_, Zq(Z,zENTRY_DATATYPE_,
				    rENTRY_DATATYPE_), &dataType,
			       Zq(Z,zENTRY_NUMELEMS_,
				    rENTRY_NUMELEMS_), &numElems,
			 NULL_);
	ReportStatus (status);
	if (StatusBAD(status)) return FALSE;

	strcpyX (field, DataTypeToken(dataType), 0);
	catchrX (field, (int) '/', 0);
	sprintf (&field[strlen(field)], "%ld", numElems);
	if (strlen(field) > (size_t) DATASPEC_FIELD_LEN) {
	  strcpyX (field + DATASPEC_FIELD_LEN - 3, "...", 0);
	}
	strcatX (IW->iLines[lineN], field, 0);
	if (strlen(field) < (size_t) DATASPEC_FIELD_LEN) {
	  CatNcharacters (IW->iLines[lineN],
			  DATASPEC_FIELD_LEN - strlen(field), (int) ' ');
	}
	CatNcharacters (IW->iLines[lineN], DATASPEC_FIELD_BLANKS, (int) ' ');

	IW->iLineNs[curItem] = lineN;
	IW->iCols[curItem] = curCol;
	IW->iLens[curItem] = strlen(field);

	dataSpecCol = curCol;

	curCol += DATASPEC_FIELD_LEN + DATASPEC_FIELD_BLANKS;
	curItem++;

	binary = AllocateMemory ((size_t) (numElems * CDFelemSize(dataType)),
				 FatalError);

	status = CDFlib (GET_, Zq(Z,zENTRY_DATA_,rENTRY_DATA_), binary,
			 NULL_);
	ReportStatus (status);
	if (StatusBAD(status)) return FALSE;

	n = EncodeValuesFormat (dataType, numElems, binary,
				&(IW->iLines[lineN][curCol]), NULL, 0,
				varENTRYVALUE_FIELD_LEN, EPOCH0_STYLE);
	FreeMemory (binary, FatalError);

	IW->iLineNs[curItem] = lineN;
	IW->iCols[curItem] = dataSpecCol;
	IW->iLens[curItem] = strlen(&(IW->iLines[lineN][dataSpecCol]));
	curItem++;

	IW->iLineNs[curItem] = lineN;
	IW->iCols[curItem] = curCol;
	IW->iLens[curItem] = n;
	curItem++;
      }
      lineN++;
   }
   return TRUE;
}

/******************************************************************************
* FreeVarEntryMenu.
******************************************************************************/

void FreeVarEntryMenu (attrNs, entryIs, IW)
long *attrNs;
Logical *entryIs;
struct ItemWindowStruct *IW;
{
  FreeIW (IW, FatalError);
  if (attrNs != NULL) FreeMemory (attrNs, FatalError);
  if (entryIs != NULL) FreeMemory (entryIs, FatalError);
  return;
}

/******************************************************************************
* CreateEntry.
******************************************************************************/

Logical CreateEntry (G, attrN, change)
Logical G;
long attrN;
Logical *change;
{
  long entryN;
  int entryType;
  char attrName[CDF_ATTR_NAME_LEN+1], delim;
  CDFstatus status;

  /****************************************************************************
  * No change yet.  Set to TRUE when the CDF is modified.
  ****************************************************************************/

  *change = FALSE;

  /****************************************************************************
  * Inquire attribute name and delimiter to be used.
  ****************************************************************************/

  status = CDFlib (SELECT_, ATTR_, attrN,
		   GET_, ATTR_NAME_, attrName,
		   NULL_);
  ReportStatus (status);
  if (StatusBAD(status)) return FALSE;
  delim = PickDelimiter (attrName, strlen(attrName));

  if (G) {
    /**************************************************************************
    * Global scope attribute, prompt for entry number.
    **************************************************************************/
    static char *header[] = { "Enter gEntry number..." };
    AOSs2 (trailer, "Enter: ________   Exit: ________", "Help:  ________")
    static char value[MAX_ENTRYNUM_LEN+1];
    static int exitChars[] = { ENTERkey_EDIT, EXITkey_FSI, HELPkey_FSI,
			       NUL };
    static char label[15+CDF_ATTR_NAME_LEN+1];
    STATIC_ struct PromptWindowStruct PW = {
      label, 4, 23, 34, 1, header, MAX_ENTRYNUM_LEN, value, 2, trailer,
      exitChars, REFRESHkey_FSI, SOLkey_FSI, EOLkey_FSI,
      INSERTorOVERkey_FSI
    };
    entryType = gENTRYt;
    EncodeKeyDefinitions (2, trailer, ENTERkey_EDIT, EXITkey_FSI,
			  HELPkey_FSI);
    strcpyX (PW.value, "", MAX_ENTRYNUM_LEN);
    sprintf (label, " gAttribute %c%s%c ", delim, attrName, delim);
    PromptWindow (NEWpw, &PW, 0, (Logical) TRUE);
    for (;;) {
       if (EnterPW(&PW,ENTRYNUMBERhelpID)) {
	 long entryNt;
	 if (sscanf(PW.value,"%ld",&entryNt) == 1)
	   if (entryNt > 0)
	     if (!EntryExists(attrN,entryNt-1,entryType)) {
	       entryN = entryNt - 1;
	       break;
	     }
	     else
	       InputErrorWindow (E3(entryType,"That gEntry already exists.",
					      "That rEntry already exists.",
					      "That zEntry already exists."),
				 FALSE);
	   else
	     InputErrorWindow ("Illegal gEntry number.", FALSE);
	 else
	   InputErrorWindow ("Error decoding gEntry number.", FALSE);
       }
       else {
	 entryN = -1;
	 break;
       }
    }
    PromptWindow (DELETEpw, &PW);
  }
  else {
    /**************************************************************************
    * Variable scope attribute, select corresponding variable (or enter entry
    * number).
    **************************************************************************/
    static char *header[] =  { "Select the corresponding variable..." };
    AOSs3 (trailer, "Select: ________   Numbered rEntry: ________",
		    "Exit:   ________   Numbered zEntry: ________",
		    "Help:   ________")
    static int exitChars[] = { SELECTkey_EDIT, rENTRYbyNUMBERkey_EDIT,
			       EXITkey_FSI, zENTRYbyNUMBERkey_EDIT,
			       HELPkey_FSI, NUL };
    static char label[15+CDF_ATTR_NAME_LEN+1];
    STATIC_ struct ItemWindowStruct IW = {
	4, 16, 48, label, 1, header, 0, NULL, 0, NULL, NULL, NULL, 7,
	3, trailer, exitChars, REFRESHkey_FSI, TRUE, NSkey_FSI, PSkey_FSI
    };
    CDFstatus status;
    long NrVars, NzVars;
    int nVars, varX;

    EncodeKeyDefinitions (3, trailer, SELECTkey_EDIT, rENTRYbyNUMBERkey_EDIT,
			  EXITkey_FSI, zENTRYbyNUMBERkey_EDIT, HELPkey_FSI);
    sprintf (label, " vAttribute %c%s%c ", delim, attrName, delim);

    status = CDFlib (GET_, CDF_NUMrVARS_, &NrVars,
			   CDF_NUMzVARS_, &NzVars,
		     NULL_);
    ReportStatus (status);
    if (StatusBAD(status)) return FALSE;
    nVars = (int) (NrVars + NzVars);

    AllocIW (&IW, nVars, nVars, 1+CDF_VAR_NAME_LEN+1+1, FatalError);
    for (varX = 0; varX < nVars; varX++) {
       char varName[CDF_VAR_NAME_LEN+1], delim;
       Logical Z = (varX < NrVars ? FALSE : TRUE);
       long varN = Zq(Z,varX-NrVars,varX);
       status = CDFlib (SELECT_, Zq(Z,zVAR_,rVAR_), varN,
			GET_, Zq(Z,zVAR_NAME_,rVAR_NAME_), varName,
			NULL_);
       ReportStatus (status);
       if (StatusBAD(status)) return FALSE;
       delim = PickDelimiter (varName, strlen(varName));
       sprintf (IW.iLines[varX], "%c%s%c", delim, varName, delim);
       IW.iLineNs[varX] = varX;
       IW.iCols[varX] = 0;
       IW.iLens[varX] = MINIMUM((size_t)40,strlen(IW.iLines[varX]));
    }

    ItemWindow (NEWiw, &IW, 0);
    for (entryN = -2; entryN == -2; ) {
       ItemWindow (READiw, &IW);
       switch (IW.key) {
	 case SELECTkey_EDIT:
	   if (IW.nItems == 0)
	     InputErrorWindow ("No variables exist.", FALSE);
	   else {
	     Logical Z = (IW.itemN < NrVars ? FALSE : TRUE);
	     long entryNt = Zq(Z,IW.itemN-NrVars,IW.itemN);
	     entryType = Zq(Z,zENTRYt,rENTRYt);
	     if (!EntryExists(attrN,entryNt,entryType))
	       entryN = entryNt;
	     else
	       InputErrorWindow (E3(entryType,"That gEntry already exists.",
					      "That rEntry already exists.",
					      "That zEntry already exists."),
				  FALSE);
	   }
	   break;
	 case zENTRYbyNUMBERkey_EDIT:
	 case rENTRYbyNUMBERkey_EDIT: {
	   static char *rHeader[] = { "Enter rEntry number..." };
	   static char *zHeader[] = { "Enter zEntry number..." };
	   AOSs2 (trailer, "Enter: ________   Exit: ________",
			   "Help:  ________")
	   static char value[MAX_ENTRYNUM_LEN+1];
	   static int exitChars[] = { ENTERkey_EDIT, EXITkey_FSI,
				      HELPkey_FSI, NUL };
	   static char label[15+CDF_ATTR_NAME_LEN+1];
	   STATIC_ struct PromptWindowStruct PW = {
	     label, 7, 23, 34, 1, NULL, MAX_ENTRYNUM_LEN, value, 2, trailer,
	     exitChars, REFRESHkey_FSI, SOLkey_FSI, EOLkey_FSI,
	     INSERTorOVERkey_FSI
	   };
	   Logical Z = (IW.key == zENTRYbyNUMBERkey_EDIT);
	   entryType = Zq(Z,zENTRYt,rENTRYt);
	   EncodeKeyDefinitions (2, trailer, ENTERkey_EDIT, EXITkey_FSI,
				 HELPkey_FSI);
	   PW.hLines = Zq(Z,zHeader,rHeader);
	   strcpyX (PW.value, "", MAX_ENTRYNUM_LEN);
	   sprintf (label, " vAttribute %c%s%c ", delim, attrName, delim);
	   PromptWindow (NEWpw, &PW, 0, (Logical) TRUE);
	   for (;;) {
	      if (EnterPW(&PW,ENTRYNUMBERhelpID)) {
		long entryNt;
		if (sscanf(PW.value,"%ld",&entryNt) == 1)
		  if (entryNt > 0)
		    if (!EntryExists(attrN,entryNt-1,entryType)) {
		      entryN = entryNt - 1;
		      break;
		    }
		    else
		      InputErrorWindow (E3(entryType,
					   "That gEntry already exists.",
					   "That rEntry already exists.",
					   "That zEntry already exists."),
					FALSE);
		  else
		    InputErrorWindow (Zq(Z,"Illegal zEntry number.",
					   "Illegal rEntry number."),
				      FALSE);
		else
		  InputErrorWindow (Zq(Z,"Error decoding zEntry number.",
					 "Error decoding rEntry number."),
				    FALSE);
	      }
	      else {
		entryN = -1;
		break;
	      }
	   }
	   PromptWindow (DELETEpw, &PW);
	   break;
	 }
	 case HELPkey_FSI:
	   OnlineHelpWindow (ilhFile, VARSELECThelpID);
	   break;
	 case EXITkey_FSI:
	   entryN = -1;
	   break;
       }
    }
    ItemWindow (DELETEiw, &IW);
    FreeIW (&IW, FatalError);
  }

  /****************************************************************************
  * Return if an entry number wasn't selected/entered.
  ****************************************************************************/

  if (entryN == -1) return TRUE;

  /****************************************************************************
  * Create the entry.
  ****************************************************************************/

  return EditEntry(attrN,entryN,entryType,TRUE,change);
}

/******************************************************************************
* EntryExists.
*     Checks if an entry exists.  Note that FALSE is returned if any error is
* detected (including NO_SUCH_ENTRY) - not just if the entry does not exist.
******************************************************************************/

Logical EntryExists (attrN, entryN, entryType)
long attrN;
long entryN;
int entryType;
{
  CDFstatus status;
  status = CDFlib (SELECT_, ATTR_, attrN,
		   CONFIRM_, E3(entryType,gENTRY_EXISTENCE_,
					  rENTRY_EXISTENCE_,
					  zENTRY_EXISTENCE_), entryN,
		   NULL_);
  if (StatusOK(status)) return TRUE;
  return FALSE;
}

/******************************************************************************
* EditAttrsX.
******************************************************************************/

Logical EditAttrsX (G, CDFname)
Logical G;                      /* If TRUE, global scoped attributes. */
char *CDFname;                  /* Name of the CDF being edited. */
{
   CDFstatus status;
   long nAttrs;                 /* Number of attributes of the proper scope. */
   long attrN;
   long fieldN;
   long *attrNs;
   AOSs2 (header, BLANKSforHEADER, BLANKSforHEADER)
   AOSs2A (trailer,
    "Modify: ________  Create attribute: ________  Toggle scope:     ________",
    "Exit:   ________  Help:             ________  Delete attribute: ________")
   static int exitChars[] = { MODIFYkey_EDIT, EXITkey_FSI, CREATEATTRkey_EDIT,
			      HELPkey_FSI, TOGGLESCOPEkey_EDIT,
			      DELETEATTRorENTRYkey_EDIT, NUL };
   static char label[6+DU_MAX_NAME_LEN+14+1];
   STATIC_ struct ItemWindowStruct IW = {
      0, 0, 80, label, 2, header, 0, NULL, 0, NULL, NULL, NULL, 13, 2, trailer,
      exitChars, REFRESHkey_FSI, TRUE, NSkey_FSI, PSkey_FSI
   };

   /***************************************************************************
   * Build attribute lines and display menu.
   ***************************************************************************/

   EncodeKeyDefinitions (2, trailer, MODIFYkey_EDIT, CREATEATTRkey_EDIT,
			 TOGGLESCOPEkey_EDIT, EXITkey_FSI, HELPkey_FSI,
			 DELETEATTRorENTRYkey_EDIT);
   if (!BuildAttrMenuX(G,CDFname,&nAttrs,&attrNs,&IW)) return FALSE;
   ItemWindow (NEWiw, &IW, 0);

   /***************************************************************************
   * Read/process keystrokes until request to exit menu.
   ***************************************************************************/

   for (;;) {
     ItemWindow (READiw, &IW);
     if (IW.nItems != 0) {
       fieldN = IW.itemN % 2;
       attrN = attrNs[IW.itemN/2];
     }
     switch (IW.key) {
       /***********************************************************************
       * Item selected.
       ***********************************************************************/
       case SELECTkey_EDIT: {
	 /*********************************************************************
	 * Check if any attributes/entries exist.
	 *********************************************************************/

	 if (IW.nItems == 0) {
	   InputErrorWindow (Gq(G,"Nothing selected (no gAttributes exist).",
				  "Nothing selected (no vAttributes exist)."),
			     FALSE);
	   break;
	 }

	 /*********************************************************************
	 * Perform desired function (based on field number).
	 *********************************************************************/

	 switch (fieldN) {
	   /*******************************************************************
	   * Modify attribute name.
	   *******************************************************************/
	   case 0: {
	     static char *header[] = {
	       "Enter new name (with delimiters)...",
	       "Syntax: <delim><char1><char2>...<charN><delim>",
	       "Example: \"FILTERs\""
	     };
	     AOSs1 (trailer,
		    "Enter: ________   Exit: ________  Help: ________")
	     static int exitChars[] = { ENTERkey_EDIT, EXITkey_FSI,
					HELPkey_FSI, NUL };
	     static char value[1+CDF_ATTR_NAME_LEN+1+1];
	     static char label[15+CDF_ATTR_NAME_LEN+1];
	     STATIC_ struct PromptWindowStruct PWt = {
		label, 7, 10, 60, 3, header, CDF_ATTR_NAME_LEN+2, value,
		1, trailer, exitChars, REFRESHkey_FSI, SOLkey_FSI,
		EOLkey_FSI, INSERTorOVERkey_FSI
	     };
	     char attrName[CDF_ATTR_NAME_LEN+1], delim;
	     EncodeKeyDefinitions (1, trailer, ENTERkey_EDIT, EXITkey_FSI,
				   HELPkey_FSI);
	     status = CDFlib (SELECT_, ATTR_, attrN,
			      GET_, ATTR_NAME_, attrName,
			      NULL_);
	     ReportStatus (status);
	     if (StatusBAD(status)) return FALSE;
	     delim = PickDelimiter (attrName, strlen(attrName));
	     sprintf (value, "%c%s%c", delim, attrName, delim);
	     sprintf (label, " %cAttribute %c%s%c ", Gq(G,'g','v'),
		      delim, attrName, delim);
	     PromptWindow (NEWpw, &PWt, (int) (strlen(PWt.value) - 1),
			   (Logical) TRUE);
	     for (;;) {
		if (EnterPW(&PWt,ATTRRENAMEhelpID)) {
		  char attrName[CDF_ATTR_NAME_LEN+1];
		  if (DecodeDelimitedString(PWt.value,attrName)) {
		    status = CDFlib (PUT_, ATTR_NAME_, attrName,
				     NULL_);
		    ReportStatus (status);
		    if (StatusOK(status)) {
		      FreeAttrMenuX (attrNs, &IW);
		      BuildAttrMenuX (G, CDFname, &nAttrs, &attrNs, &IW);
		      ItemWindow (UPDATEiw, &IW, IW.itemN);
		      break;
		    }
		  }
		  else
		    InputErrorWindow ("Illegal name (check delimiters).",
				      FALSE);
		}
		else
		  break;
	     }
	     PromptWindow (DELETEpw, &PWt);
	     break;
	   }

	   /*******************************************************************
	   * Edit entries.
	   *******************************************************************/
	   case 1: {
	     ItemWindow (UNDISPLAYiw, &IW);
	     if (!EditAttrEntriesX(G,attrN)) return FALSE;
	     ItemWindow (REDISPLAYiw, &IW);
	     break;
	   }
	 }
	 break;
       }

       /***********************************************************************
       * Create new attribute.
       ***********************************************************************/
       case CREATEATTRkey_EDIT: {
	 static char *header[] = {
	   "Enter name (with delimiters)...",
	   "Syntax: <delim><char1><char2>...<charN><delim>",
	   "Example: \"Resolution\""
	 };
	 AOSs1 (trailer, "Enter: ________   Exit: ________  Help: ________")
	 static int exitChars[] = { ENTERkey_EDIT, EXITkey_FSI, HELPkey_FSI,
				    NUL };
	 static char value[DELIMed_ATTR_NAME_LEN+1];
	 static char label[16+1];
	 STATIC_ struct PromptWindowStruct PWt = {
	    label, 7, 10, 60, 3, header, CDF_ATTR_NAME_LEN+2, value, 1,
	    trailer, exitChars, REFRESHkey_FSI, SOLkey_FSI, EOLkey_FSI,
	    INSERTorOVERkey_FSI
	 };
	 EncodeKeyDefinitions (1, trailer, ENTERkey_EDIT, EXITkey_FSI,
			       HELPkey_FSI);
	 strcpyX (value, "\"\"", DELIMed_ATTR_NAME_LEN);
	 sprintf (label, " New %cAttribute ", Gq(G,'g','v'));
	 PromptWindow (NEWpw, &PWt, 1, (Logical) TRUE);
	 for (;;) {
	    if (EnterPW(&PWt,ATTRNAMEhelpID)) {
	      char attrName[CDF_ATTR_NAME_LEN+1];
	      long attrNum;
	      if (DecodeDelimitedString(PWt.value,attrName)) {
		status = CDFlib (CREATE_, ATTR_, attrName,
						 Gq(G,GLOBAL_SCOPE,
						      VARIABLE_SCOPE),
						 &attrNum,
				 NULL_);
		ReportStatus (status);
		if (StatusOK(status)) {
		  FreeAttrMenuX (attrNs, &IW);
		  BuildAttrMenuX (G, CDFname, &nAttrs, &attrNs, &IW);
		  ItemWindow (UPDATEiw, &IW, IW.nItems - 2);
		  break;
		}
	      }
	      else
		InputErrorWindow ("Illegal name (check delimiters).", FALSE);
	    }
	    else
	      break;
	 }
	 PromptWindow (DELETEpw, &PWt);
	 break;
       }

       /***********************************************************************
       * Delete attribute.
       ***********************************************************************/
       case DELETEATTRorENTRYkey_EDIT: {
	 char attrName[CDF_ATTR_NAME_LEN+1],
	      question[22+CDF_ATTR_NAME_LEN+1], delim;
	 if (IW.nItems == 0) {
	   InputErrorWindow
	       (Gq(G,"A gAttribute isn't selected (none exist).",
		     "A vAttribute isn't selected (none exist)."),FALSE);
	   break;
	 }
	 status = CDFlib (SELECT_, ATTR_, attrN,
			  GET_, ATTR_NAME_, attrName,
			  NULL_);
	 ReportStatus (status);
	 if (StatusBAD(status)) return FALSE;
	 delim = PickDelimiter (attrName, strlen(attrName));
	 sprintf (question, "Delete %sAttribute %c%s%c ?",
		  BOO(G,"g","v"), delim, attrName, delim);
	 if (ConfirmWindow(7,question,NULL,FALSE,DELETEATTRhelpID)) {
	   status = CDFlib (DELETE_, ATTR_,
			    NULL_);
	   ReportStatus (status);
	   if (StatusOK(status)) {
	     int itemNt = IW.itemN - (IW.itemN % 2);
	     FreeAttrMenuX (attrNs, &IW);
	     BuildAttrMenuX (G, CDFname, &nAttrs, &attrNs, &IW);
	     if (itemNt == IW.nItems) itemNt = MAXIMUM(0,itemNt-2);
	     ItemWindow (UPDATEiw, &IW, itemNt);
	   }
	 }
	 break;
       }

       /***********************************************************************
       * Toggle scope.
       ***********************************************************************/
       case TOGGLESCOPEkey_EDIT: {
	 if (IW.nItems == 0) {
	   InputErrorWindow(Gq(G,"A gAttribute isn't selected (none exist).",
				 "A vAttribute isn't selected (none exist)."),
			    FALSE);
	   break;
	 }
	 status = CDFlib (SELECT_, ATTR_, attrN,
			  PUT_, ATTR_SCOPE_, Gq(G,VARIABLE_SCOPE,GLOBAL_SCOPE),
			  NULL_);
	 ReportStatus (status);
	 if (StatusOK(status)) {
	   int itemNt = IW.itemN - (IW.itemN % 2);
	   FreeAttrMenuX (attrNs, &IW);
	   BuildAttrMenuX (G, CDFname, &nAttrs, &attrNs, &IW);
	   if (itemNt == IW.nItems) itemNt = MAXIMUM(0,itemNt-2);
	   ItemWindow (UPDATEiw, &IW, itemNt);
	 }
	 break;
       }

       /***********************************************************************
       * Display online help.
       ***********************************************************************/
       case HELPkey_FSI:
	 OnlineHelpWindow (ilhFile, Gq(G,GATTRShelpIDx,VATTRShelpIDx));
	 break;

       /***********************************************************************
       * Exit menu.
       ***********************************************************************/
       case EXITkey_FSI:
	 ItemWindow (DELETEiw, &IW);
	 FreeAttrMenuX (attrNs, &IW);
	 return TRUE;
     }
   }
}

/******************************************************************************
* BuildAttrMenuX.
******************************************************************************/

Logical BuildAttrMenuX (G, CDFname, nAttrs, attrNs, IW)
Logical G;
char *CDFname;
long *nAttrs;
long **attrNs;
struct ItemWindowStruct *IW;
{
   CDFstatus status;
   long attrN, nAttrsT; int lineN;

   /***************************************************************************
   * Determine number of attributes (total and of proper scope).
   ***************************************************************************/

   status = CDFlib (GET_, CDF_NUMATTRS_, &nAttrsT,
			  Gq(G,CDF_NUMgATTRS_,CDF_NUMvATTRS_), nAttrs,
		    NULL_);
   ReportStatus (status);
   if (StatusBAD(status)) return FALSE;

   /***************************************************************************
   * Allocate item section control/lines.
   ***************************************************************************/

   *attrNs = (long *) AllocateMemory ((size_t) (*nAttrs * sizeof(long)),
				      FatalError);

   AllocIW (IW, (int) (2 * (*nAttrs)), (int) *nAttrs, 80, FatalError);

   /***************************************************************************
   * Encode label/header.
   ***************************************************************************/

   sprintf (IW->label, " CDF \"%s\" %cAttributes ", CDFname, Gq(G,'g','v'));

   if (G) {
     sprintf (IW->hLines[0], "%ld gAttribute%s", *nAttrs,
	      (*nAttrs == 1 ? "" : "s"));
   }
   else {
     sprintf (IW->hLines[0], "%ld vAttribute%s", *nAttrs,
	      (*nAttrs == 1 ? "" : "s"));
   }
   strcpyX (IW->hLines[1], "AttrName", 0);

   /***************************************************************************
   * Build each attribute/entry line.
   ***************************************************************************/

   for (attrN = 0, lineN = 0; attrN < nAttrsT; attrN++) {
      long scope; int itemN, nChars;
      char attrName[CDF_ATTR_NAME_LEN+1];

      /************************************************************************
      * Inquire attribute.
      ************************************************************************/

      status = CDFlib (SELECT_, ATTR_, attrN,
		       GET_, ATTR_NAME_, attrName,
			     ATTR_SCOPE_, &scope,
		       NULL_);
      ReportStatus (status);
      if (StatusBAD(status)) return FALSE;

      if ((G && scope == GLOBAL_SCOPE) ||
	  (!G && scope == VARIABLE_SCOPE)) {
	/**********************************************************************
	* Save attribute number.
	**********************************************************************/
	(*attrNs)[lineN] = attrN;
	/**********************************************************************
	* Setup attribute name.
	**********************************************************************/
	EncodeString (strlen(attrName), attrName, IW->iLines[lineN], 0,
		      1+CDF_ATTR_NAME_LEN+1);
	nChars = ENTRIES_COLUMNx - strlen(IW->iLines[lineN]);
	if (nChars > 0) CatNcharacters (IW->iLines[lineN], nChars, (int) ' ');
	itemN = 2 * lineN;
	IW->iLineNs[itemN] = lineN;
	IW->iCols[itemN] = 0;
	IW->iLens[itemN] = 1 + strlen(attrName) + 1;
	/**********************************************************************
	* Setup <entries> field.
	**********************************************************************/
	strcatX (IW->iLines[lineN], "<entries>", 0);
	itemN++;
	IW->iLineNs[itemN] = lineN;
	IW->iCols[itemN] = ENTRIES_COLUMNx;
	IW->iLens[itemN] = 9;
	/**********************************************************************
	* Increment to next line.
	**********************************************************************/
	lineN++;
     }
   }

   return TRUE;
}

/******************************************************************************
* FreeAttrMenuX.
******************************************************************************/

void FreeAttrMenuX (attrNs, IW)
long *attrNs;
struct ItemWindowStruct *IW;
{
  if (attrNs != NULL) FreeMemory (attrNs, FatalError);
  FreeIW (IW, FatalError);
  return;
}

/******************************************************************************
* EditAttrEntriesX.
******************************************************************************/

Logical EditAttrEntriesX (G, attrN)
Logical G;                      /* If TRUE, global scoped attribute. */
long attrN;                     /* Number of attribute being edited. */
{
   CDFstatus status;
   long entryN;
   long fieldN;
   AOSs2 (header, BLANKSforHEADER, BLANKSforHEADER)
   AOSs2A (trailer,
"Modify: ________   Create entry: ________   Delete entry: ________",
"Exit:   ________   Help:         ________")
   static int exitChars[] = { MODIFYkey_EDIT, EXITkey_FSI, HELPkey_FSI,
			      CREATEENTRYkey_EDIT, DELETEATTRorENTRYkey_EDIT,
			      NUL };
   static char label[12+1+CDF_ATTR_NAME_LEN+1+9+1];
   STATIC_ struct ItemWindowStruct IW = {
      0, 0, 80, label, 2, header, 0, NULL, 0, NULL, NULL, NULL, 13, 2, trailer,
      exitChars, REFRESHkey_FSI, TRUE, NSkey_FSI, PSkey_FSI
   };
   long nEntries;
   long *entryNs;
   int *entryTypes, entryType;
   Logical change;
   /***************************************************************************
   * Build attribute/entry lines and display menu.
   ***************************************************************************/
   EncodeKeyDefinitions (2, trailer, MODIFYkey_EDIT, CREATEENTRYkey_EDIT,
			 DELETEATTRorENTRYkey_EDIT,EXITkey_FSI,HELPkey_FSI);
   if (!BuildAttrEntriesMenuX(G,attrN,&nEntries,&entryNs,&entryTypes,&IW)) {
     return FALSE;
   }
   ItemWindow (NEWiw, &IW, 0);
   /***************************************************************************
   * Read/process keystrokes until request to exit menu.
   ***************************************************************************/
   for (;;) {
     ItemWindow (READiw, &IW);
     /*********************************************************************
     * Calculate entry and field numbers and entry type.
     *********************************************************************/
     if (IW.nItems != 0) {
       fieldN = IW.itemN % 4;
       entryN = entryNs[IW.itemN/4];
       entryType = entryTypes[IW.itemN/4];
     }
     switch (IW.key) {
       /***********************************************************************
       * Item selected.
       ***********************************************************************/
       case SELECTkey_EDIT: {
	 /*********************************************************************
	 * Check if any attributes/entries exist.
	 *********************************************************************/
	 if (IW.nItems == 0) {
	   InputErrorWindow (Gq(G,"Nothing selected (no gEntries exist).",
				  "Nothing selected (no entries exist)."),
			     FALSE);
	   break;
	 }
	 /*********************************************************************
	 * Perform desired function (based on field number).
	 *********************************************************************/
	 switch (fieldN) {
	   /*******************************************************************
	   * Modify variable name/global entry number.
	   *******************************************************************/
	   case 0: {
	     if (G)
	       InputErrorWindow ("Not supported.", FALSE);
	     else {
	       static char *header[] = {
		 "Enter new name (with delimiters)...",
		 "Syntax: <delim><char1><char2>...<charN><delim>",
		 "Example: \"Humidity\""
	       };
	       AOSs1 (trailer,
		      "Enter: ________   Exit: ________  Help: ________")
	       static int exitChars[] = { ENTERkey_EDIT, EXITkey_FSI,
					  HELPkey_FSI, NUL };
	       static char value[1+CDF_VAR_NAME_LEN+1+1];
	       static char label[14+CDF_VAR_NAME_LEN+1];
	       STATIC_ struct PromptWindowStruct PWt = {
		  label, 7, 10, 60, 3, header, CDF_VAR_NAME_LEN+2, value,
		  1, trailer, exitChars, REFRESHkey_FSI, SOLkey_FSI,
		  EOLkey_FSI, INSERTorOVERkey_FSI
	       };
	       char varName[CDF_VAR_NAME_LEN+1], delim;
	       Logical Z = (entryType == zENTRYt);
	       long nVars;
	       status = CDFlib (GET_, Zq(Z,CDF_NUMzVARS_,
					   CDF_NUMrVARS_), &nVars,
				NULL_);
	       ReportStatus (status);
	       if (StatusBAD(status)) return FALSE;
	       if (entryN >= nVars) {
		 InputErrorWindow (Zq(Z,"No corresponding zVariable.",
					"No corresponding rVariable."), FALSE);
		 break;
	       }
	       EncodeKeyDefinitions (1, trailer, ENTERkey_EDIT, EXITkey_FSI,
				     HELPkey_FSI);
	       status = CDFlib (SELECT_, Zq(Z,zVAR_,rVAR_), entryN,
				GET_, Zq(Z,zVAR_NAME_,
					   rVAR_NAME_), varName,
				NULL_);
	       ReportStatus (status);
	       if (StatusBAD(status)) return FALSE;
	       delim = PickDelimiter (varName, strlen(varName));
	       sprintf (value, "%c%s%c", delim, varName, delim);
	       sprintf (label, " %cVariable %c%s%c ", Zq(Z,'z','r'),
			delim, varName, delim);
	       PromptWindow (NEWpw, &PWt, (int) (strlen(PWt.value) - 1),
			     (Logical) TRUE);
	       for (;;) {
		  if (EnterPW(&PWt,VARRENAMEhelpID)) {
		    char varName[CDF_VAR_NAME_LEN+1];
		    if (DecodeDelimitedString(PWt.value,varName)) {
		      status = CDFlib (PUT_, Zq(Z,zVAR_NAME_,
						  rVAR_NAME_), varName,
				       NULL_);
		      ReportStatus (status);
		      if (StatusOK(status)) {
			FreeAttrEntriesMenuX (entryNs, entryTypes, &IW);
			BuildAttrEntriesMenuX (G, attrN, &nEntries, &entryNs,
					       &entryTypes, &IW);
			ItemWindow (UPDATEiw, &IW, IW.itemN);
			break;
		      }
		    }
		    else
		      InputErrorWindow ("Illegal name (check delimiters).",
					FALSE);
		  }
		  else
		    break;
	       }
	       PromptWindow (DELETEpw, &PWt);
	     }
	     break;
	   }
	   /*******************************************************************
	   * Modify data specification.
	   *******************************************************************/
	   case 1: {
	     long dataType, numElems;
	     static char *header[] = { "Select new data type..." };
	     static char *items[] = { DTiwSYMBOLS };
	     AOSs2 (trailer, "Select: ________   Exit: ________",
			     "Help:   ________")
	     static int iLineNs[] = { DTiwLINEnS };
	     static int iCols[] = { DTiwCOLs };
	     static int iLens[] = { DTiwLENs };
	     static int exitChars[] = { SELECTkey_EDIT, EXITkey_FSI,
					HELPkey_FSI, NUL };
	     static char label[15+CDF_VAR_NAME_LEN+1];
	     STATIC_ struct ItemWindowStruct IWdt = {
		5, 22, 35, label, 1, header, 7, items, 14, iLineNs, iCols,
		iLens, 7, 2, trailer, exitChars, REFRESHkey_FSI, FALSE
	     };
	     EncodeKeyDefinitions (2, trailer, SELECTkey_EDIT, EXITkey_FSI,
				   HELPkey_FSI);
	     if (G)
	       sprintf (label, " gEntry %ld ", entryN + 1);
	     else {
	       Logical Z = (entryType == zENTRYt);
	       char varName[CDF_VAR_NAME_LEN+1], delim;
	       status = CDFlib (SELECT_, Zq(Z,zVAR_,rVAR_), entryN,
				GET_, Zq(Z,zVAR_NAME_,rVAR_NAME_), varName,
				NULL_);
	       switch (status) {
		 case CDF_OK:
		   delim = PickDelimiter (varName, strlen(varName));
		   sprintf (label, " %cEntry for %c%s%c ", Zq(Z,'z','r'),
			    delim, varName, delim);
		   break;
		 case NO_SUCH_VAR:
		   sprintf (label, " %cEntry %ld ", Zq(Z,'z','r'), entryN + 1);
		   break;
		 default:
		   ReportStatus (status);
		   return FALSE;
	       }
	     }
	     status = CDFlib (SELECT_, ATTR_, attrN,
				       E3(entryType,
					  gENTRY_,rENTRY_,zENTRY_), entryN,
			      GET_, E3(entryType,
				       gENTRY_DATATYPE_,
				       rENTRY_DATATYPE_,
				       zENTRY_DATATYPE_), &dataType,
			      NULL_);
	     ReportStatus (status);
	     if (StatusBAD(status)) return FALSE;
	     ItemWindow (NEWiw, &IWdt, DataTypeItemN(dataType));
	     for (;;) {
		if (EnterIW(&IWdt,ENTRYDATATYPEhelpID)) {
		  dataType = WhichDataType(&(items[iLineNs[IWdt.itemN]]
						  [iCols[IWdt.itemN]]));
		  status = CDFlib (GET_, E3(entryType,
					    gENTRY_NUMELEMS_,
					    rENTRY_NUMELEMS_,
					    zENTRY_NUMELEMS_), &numElems,
				   NULL_);
		  ReportStatus (status);
		  if (StatusBAD(status)) return FALSE;
		  status = CDFlib (PUT_, E3(entryType,
					    gENTRY_DATASPEC_,
					    rENTRY_DATASPEC_,
					    zENTRY_DATASPEC_), dataType,
							       numElems,
				   NULL_);
		  ReportStatus (status);
		  if (StatusOK(status)) {
		    FreeAttrEntriesMenuX (entryNs, entryTypes, &IW);
		    BuildAttrEntriesMenuX (G, attrN, &nEntries, &entryNs,
					   &entryTypes, &IW);
		    ItemWindow (UPDATEiw, &IW, IW.itemN);
		    break;
		  }
		}
		else
		  break;
	     }
	     ItemWindow (DELETEiw, &IWdt);
	     break;
	   }
	   /*******************************************************************
	   * Modify data specification/value(s).
	   *******************************************************************/
	   case 2: {
	     if (!EditEntry(attrN,entryN,entryType,TRUE,&change)) return FALSE;
	     if (change) {
	       FreeAttrEntriesMenuX (entryNs, entryTypes, &IW);
	       BuildAttrEntriesMenuX (G, attrN, &nEntries, &entryNs,
				      &entryTypes, &IW);
	       ItemWindow (UPDATEiw, &IW, IW.itemN);
	     }
	     break;
	   }
	   /*******************************************************************
	   * Modify value(s).
	   *******************************************************************/
	   case 3: {
	     if (!EditEntry(attrN,entryN,
			    entryType,FALSE,&change)) return FALSE;
	     if (change) {
	       FreeAttrEntriesMenuX (entryNs, entryTypes, &IW);
	       BuildAttrEntriesMenuX (G, attrN, &nEntries, &entryNs,
				      &entryTypes, &IW);
	       ItemWindow (UPDATEiw, &IW, IW.itemN);
	     }
	     break;
	   }
	 }
	 break;
       }
       /***********************************************************************
       * Create new entry.
       ***********************************************************************/
       case CREATEENTRYkey_EDIT: {
	 if (!CreateEntry(G,attrN,&change)) return FALSE;
	 if (change) {
	   FreeAttrEntriesMenuX (entryNs, entryTypes, &IW);
	   BuildAttrEntriesMenuX (G, attrN, &nEntries, &entryNs, &entryTypes,
				  &IW);
	   ItemWindow (UPDATEiw, &IW, 0);
	 }
	 break;
       }
       /***********************************************************************
       * Delete attribute/entry.
       ***********************************************************************/
       case DELETEATTRorENTRYkey_EDIT: {
	 char attrName[CDF_ATTR_NAME_LEN+1], delim,
	      question1[40+ENTRYNUM_FIELD_LEN+CDF_ATTR_NAME_LEN+1],
	      question2[31+CDF_VAR_NAME_LEN+1];
	 if (IW.nItems == 0) {
	   InputErrorWindow
	       (Gq(G,"A gEntry isn't selected (none exist).",
		     "An entry isn't selected (none exist)."),FALSE);
	   break;
	 }
	 status = CDFlib (SELECT_, ATTR_, attrN,
			  GET_, ATTR_NAME_, attrName,
			  NULL_);
	 ReportStatus (status);
	 if (StatusBAD(status)) return FALSE;
	 delim = PickDelimiter (attrName, strlen(attrName));
	 if (entryType == gENTRYt) {
	   sprintf (question1,
		    "Delete gEntry number %ld of gAttribute %c%s%c ?",
		    entryN + 1, delim, attrName, delim);
	   question2[0] = NUL;
	 }
	 else {
	   char varName[CDF_VAR_NAME_LEN+1], attrName[CDF_ATTR_NAME_LEN+1];
	   status = CDFlib (SELECT_, ATTR_, attrN,
			    GET_, ATTR_NAME_, attrName,
			    SELECT_, BOO(entryType==zENTRYt,zVAR_,
							    rVAR_), entryN,
			    GET_, BOO(entryType==zENTRYt,
				      zVAR_NAME_,rVAR_NAME_), varName,
			    NULL_);
	   switch (status) {
	     case CDF_OK:
	       delim = PickDelimiter (attrName, strlen(attrName));
	       sprintf (question1, "Delete %sEntry of vAttribute %c%s%c",
			BOO(entryType==zENTRYt,"z","r"), delim, attrName,
			delim);
	       delim = PickDelimiter (varName, strlen(varName));
	       sprintf (question2, "corresponding to %sVariable %c%s%c ?",
			BOO(entryType==zENTRYt,"z","r"), delim, varName,delim);
	       break;
	     case NO_SUCH_VAR:
	       sprintf (question1,
			"Delete %sEntry number %ld of vAttribute %c%s%c ?",
			BOO(entryType==zENTRYt,"z","r"), entryN + 1, delim,
			attrName, delim);
	       question2[0] = NUL;
	       break;
	     default:
	       ReportStatus (status);
	       return FALSE;
	   }
	 }
	 if (ConfirmWindow(7,question1,
			   BOO(NULstring(question2),NULL,question2),
			   FALSE,DELETEENTRYhelpID)) {
	   status = CDFlib (SELECT_, E3(entryType,
					gENTRY_,rENTRY_,zENTRY_), entryN,
			    DELETE_, E3(entryType,gENTRY_,rENTRY_,zENTRY_),
			    NULL_);
	   ReportStatus (status);
	   if (StatusOK(status)) {
	     int itemNt = IW.itemN - (IW.itemN % 4);
	     FreeAttrEntriesMenuX (entryNs, entryTypes, &IW);
	     BuildAttrEntriesMenuX (G, attrN, &nEntries, &entryNs, &entryTypes,
				    &IW);
	     if (itemNt == IW.nItems) itemNt = MAXIMUM(0,itemNt-4);
	     ItemWindow (UPDATEiw, &IW, itemNt);
	   }
	 }
	 break;
       }
       /***********************************************************************
       * Display online help.
       ***********************************************************************/
       case HELPkey_FSI:
	 OnlineHelpWindow (ilhFile, Gq(G,GATTRENTRIEShelpIDx,
					 VATTRENTRIEShelpIDx));
	 break;

       /***********************************************************************
       * Exit menu.
       ***********************************************************************/
       case EXITkey_FSI:
	 ItemWindow (DELETEiw, &IW);
	 FreeAttrEntriesMenuX (entryNs, entryTypes, &IW);
	 return TRUE;
     }
   }
}

/******************************************************************************
* BuildAttrEntriesMenuX.
******************************************************************************/

Logical BuildAttrEntriesMenuX (G, attrN, nEntries, entryNs, entryTypes, IW)
Logical G;
long attrN;
long *nEntries;
long **entryNs;
int **entryTypes;
struct ItemWindowStruct *IW;
{
   CDFstatus status;
   int entryX;
   long dataType, numElems, entryN;
   long NgEntries, NrEntries, NzEntries;
   char attrName[CDF_ATTR_NAME_LEN+1];
   int curCol;
   int n;
   int dataSpecCol;
   void *binary;
   char field[80+1];
   int entryType;
   int itemN;

   static char *eLines[] = { "Loading menu..." };
   static int exitChars[] = { NUL };
   STATIC_ struct EditWindowStruct EW = {
     NULL, 10, 30, 17, 0, NULL, 1, 1, eLines, 1, 0, NULL, FALSE, TRUE,
     exitChars, NUL, NUL, NUL, NUL, NUL, NUL, NUL
   };

   /***************************************************************************
   * Make attribute current and get name.
   ***************************************************************************/
   status = CDFlib (SELECT_, ATTR_, attrN,
		    GET_, ATTR_NAME_, attrName,
		    NULL_);
   ReportStatus (status);
   if (StatusBAD(status)) return FALSE;

   /***************************************************************************
   * Determine number of entries.
   ***************************************************************************/

   if (G) {
     status = CDFlib (GET_, ATTR_NUMgENTRIES_, &NgEntries,
		      NULL_);
     ReportStatus (status);
     if (StatusBAD(status)) return FALSE;
     *nEntries = NgEntries;
   }
   else {
     status = CDFlib (GET_, ATTR_NUMrENTRIES_, &NrEntries,
		      ATTR_NUMzENTRIES_, &NzEntries,
		      NULL_);
     ReportStatus (status);
     if (StatusBAD(status)) return FALSE;
     *nEntries = NrEntries + NzEntries;
   }

   if (*nEntries > MANY_ENTRYs) EditWindow (NEWew, &EW, (Logical) TRUE);

   *entryNs = (long *) AllocateMemory ((size_t) ((*nEntries) * sizeof(long)),
				       FatalError);
   *entryTypes = (int *) AllocateMemory ((size_t) ((*nEntries) * sizeof(int)),
					 FatalError);

   if (G) {
     for (entryN = 0, entryX = 0; entryX < NgEntries; entryN++) {
	status = CDFlib (SELECT_, gENTRY_, entryN,
			 CONFIRM_, CURgENTRY_EXISTENCE_,
			 NULL_);
	switch (status) {
	  case CDF_OK:
	    (*entryNs)[entryX] = entryN;
	    (*entryTypes)[entryX] = gENTRYt;
	    entryX++;
	    break;
	  case NO_SUCH_ENTRY:
	    break;
	  default:
	    ReportStatus (status);
	    return FALSE;
	}
     }
   }
   else {
     for (entryN = 0, entryX = 0; entryX < NrEntries; entryN++) {
	status = CDFlib (SELECT_, rENTRY_, entryN,
			 CONFIRM_, CURrENTRY_EXISTENCE_,
			 NULL_);
	switch (status) {
	  case CDF_OK:
	    (*entryNs)[entryX] = entryN;
	    (*entryTypes)[entryX] = rENTRYt;
	    entryX++;
	    break;
	  case NO_SUCH_ENTRY:
	    break;
	  default:
	    ReportStatus (status);
	    return FALSE;
	}
     }
     for (entryN = 0; entryX < *nEntries; entryN++) {
	status = CDFlib (SELECT_, zENTRY_, entryN,
			 CONFIRM_, CURzENTRY_EXISTENCE_,
			 NULL_);
	switch (status) {
	  case CDF_OK:
	    (*entryNs)[entryX] = entryN;
	    (*entryTypes)[entryX] = zENTRYt;
	    entryX++;
	    break;
	  case NO_SUCH_ENTRY:
	    break;
	  default:
	    ReportStatus (status);
	    return FALSE;
	}
     }
   }

   /***************************************************************************
   * Allocate item section control/lines.
   ***************************************************************************/

   AllocIW (IW, (int) (4 * (*nEntries)), (int) *nEntries, 80, FatalError);

   /***************************************************************************
   * Encode label/header.
   ***************************************************************************/

   sprintf (IW->label, " %cAttribute \"%s\" Entries ",
	    Gq(G,'g','v'), attrName);

   if (G) {
     sprintf (IW->hLines[0], "%ld gEntr%s",
	      NgEntries, BOO(NgEntries == 1,"y","ies"));
     strcpyX (IW->hLines[1],
	      "Entry  DataSpec  Value(s)", 0);
   }
   else {
     sprintf (IW->hLines[0], "%ld rEntr%s, %ld zEntr%s",
	      NrEntries, BOO(NrEntries == 1,"y","ies"),
	      NzEntries, BOO(NzEntries == 1,"y","ies"));
     strcpyX (IW->hLines[1],
	      "VarName          DataSpec  Value(s)", 0);
   }

   /***************************************************************************
   * Build each attribute/entry line.
   ***************************************************************************/

   for (entryX = 0, curCol = 0; entryX < *nEntries; entryX++, curCol = 0) {
      entryType = (*entryTypes)[entryX];

      status = CDFlib (SELECT_, E3(entryType,gENTRY_,
					     rENTRY_,
					     zENTRY_), (*entryNs)[entryX],
		       GET_, E3(entryType,gENTRY_DATATYPE_,
					  rENTRY_DATATYPE_,
					  zENTRY_DATATYPE_), &dataType,
			     E3(entryType,gENTRY_NUMELEMS_,
					  rENTRY_NUMELEMS_,
					  zENTRY_NUMELEMS_), &numElems,
		       NULL_);
      ReportStatus (status);
      if (StatusBAD(status)) return FALSE;

      /*******************************************************************
      * Setup entry number field if global scope, variable name if
      * variable scope.
      *******************************************************************/

      if (G) {
	sprintf (field, "%ld", (*entryNs)[entryX] + 1);
	if (strlen(field) > (size_t) ENTRYNUM_FIELD_LENx) {
	  strcpyX (field + ENTRYNUM_FIELD_LENx - 3, "...", 0);
	}
      }
      else {
	char varName[CDF_VAR_NAME_LEN+1];
	status = CDFlib (SELECT_, E3(entryType,
				     0,rVAR_,zVAR_), (*entryNs)[entryX],
			 GET_, E3(entryType,0,rVAR_NAME_,zVAR_NAME_), varName,
			 NULL_);
	switch (status) {
	  case CDF_OK:
	    EncodeString (strlen(varName), varName, field, 0,
			  VARNAME_FIELD_LENx);
	    break;
	  case NO_SUCH_VAR: {
	     Logical Z = E3(entryType,FALSE,FALSE,TRUE);
	     sprintf (field, "<%sEntry.%ld>",
		      Zq(Z,"z","r"), (*entryNs)[entryX] + 1);
	     break;
	  }
	  default:
	    ReportStatus (status);
	    return FALSE;
	}
      }

      strcpyX (IW->iLines[entryX], field, 0);
      if (strlen(field) < (size_t) Gq(G,ENTRYNUM_FIELD_LENx,
					VARNAME_FIELD_LENx)) {
	CatNcharacters (IW->iLines[entryX],
			Gq(G,ENTRYNUM_FIELD_LENx,
			     VARNAME_FIELD_LENx) - strlen(field), (int) ' ');
      }
      CatNcharacters (IW->iLines[entryX],
		      Gq(G,ENTRYNUM_FIELD_BLANKSx,VARNAME_FIELD_BLANKSx),
		      (int) ' ');

      itemN = 4 * entryX;
      IW->iLineNs[itemN] = entryX;
      IW->iCols[itemN] = curCol;
      IW->iLens[itemN] = strlen(field);

      curCol += Gq(G,ENTRYNUM_FIELD_LENx,VARNAME_FIELD_LENx) +
		Gq(G,ENTRYNUM_FIELD_BLANKSx,VARNAME_FIELD_BLANKSx);

      /*******************************************************************
      * Setup data type & number of elements.
      *******************************************************************/

      strcpyX (field, DataTypeToken(dataType), 0);
      catchrX (field, (int) '/', 0);
      sprintf (&field[strlen(field)], "%ld", numElems);
      if (strlen(field) > (size_t) DATASPEC_FIELD_LENx) {
	strcpyX (field + DATASPEC_FIELD_LENx - 3, "...", 0);
      }
      strcatX (IW->iLines[entryX], field, 0);
      if (strlen(field) < (size_t) DATASPEC_FIELD_LENx) {
	CatNcharacters (IW->iLines[entryX],
			DATASPEC_FIELD_LENx - strlen(field), (int) ' ');
      }
      CatNcharacters (IW->iLines[entryX], DATASPEC_FIELD_BLANKSx, (int) ' ');

      itemN = (4 * entryX) + 1;
      IW->iLineNs[itemN] = entryX;
      IW->iCols[itemN] = curCol;
      IW->iLens[itemN] = strlen(field);

      dataSpecCol = curCol;

      curCol += DATASPEC_FIELD_LENx + DATASPEC_FIELD_BLANKSx;

      /*******************************************************************
      * Setup value(s) field.
      *******************************************************************/

      binary = AllocateMemory ((size_t) (numElems * CDFelemSize(dataType)),
			       FatalError);

      status = CDFlib (GET_, E3(entryType,gENTRY_DATA_,
					  rENTRY_DATA_,
					  zENTRY_DATA_), binary,
		       NULL_);
      ReportStatus (status);
      if (StatusBAD(status)) return FALSE;

      n = EncodeValuesFormat (dataType, numElems, binary,
			      &(IW->iLines[entryX][curCol]), NULL, 0,
			      Gq(G,gAttrENTRYVALUE_FIELD_LENx,
				   vAttrENTRYVALUE_FIELD_LENx),
			      EPOCH0_STYLE);
      FreeMemory (binary, FatalError);

      /*******************************************************************
      * Set 'dataSpec/value' item control information.
      *******************************************************************/

      itemN = (4 * entryX) + 2;
      IW->iLineNs[itemN] = entryX;
      IW->iCols[itemN] = dataSpecCol;
      IW->iLens[itemN] = strlen(&(IW->iLines[entryX][dataSpecCol]));

      /*******************************************************************
      * Set `value' item control information.
      *******************************************************************/

      itemN = (4 * entryX) + 3;
      IW->iLineNs[itemN] = entryX;
      IW->iCols[itemN] = curCol;
      IW->iLens[itemN] = n;
   }

   if (*nEntries > MANY_ENTRYs) EditWindow (DELETEew, &EW, TRUE);
   return TRUE;
}

/******************************************************************************
* FreeAttrEntriesMenuX.
******************************************************************************/

void FreeAttrEntriesMenuX (entryNs, entryTypes, IW)
long *entryNs;
int *entryTypes;
struct ItemWindowStruct *IW;
{
  FreeIW (IW, FatalError);
  if (entryTypes != NULL) FreeMemory (entryTypes, FatalError);
  if (entryNs != NULL) FreeMemory (entryNs, FatalError);
  return;
}
