/*static char *SCCSID = "@(#)easea.c    6.7 92/02/18";*/
/*==============================================================*\
 *								*
 *  EASEA.C - sample PM application				*
 *    (C) Copyright IBM Corporation 1992.			*
 *--------------------------------------------------------------*
 *    This module contains subroutines for eas.c that		*
 *    specifically deal with the manipulation of EAs.		*
 *								*
 *--------------------------------------------------------------*
 *  Procedures in this file:					*
 *   AddEA()	       Handles the Add button press		*
 *   QueryEAs()	       Reads in all the EAs associated with a file
 *   CheckEAIntegrity()Checks an EA buffer to see if it is valid*
 *   Free_FEAList()    Deallocates memory associated with EA list
 *   LookupEAType()    Gets an offset into table for an EA type *
 *   DeleteCurEA()     Deletes the highlighted EA		*
 *   CurEAType()       Returns EA Type given a HoldFEA ptr	*
 *   GetUSHORT()       Returns nth USHORT in ->aValue		*
 *   WriteEAs()	       Updates EAs state on the disk		*
 *   EditEAValue()     Handles editing a given EA ( m-m EAs)	*
 *   EAExists()	       Determines if the given EA Name exists	*
 *   ChangeName()      Handles the change of an EA's name       *
 *   MultiTypeIndex()  Gets a specific field in a m-m structure *
 *   EAValueString()   Returns a representational string for  EA*
 *   MultiAdd()	       Handles addition of a field for m-m	*
\*==============================================================*/
/*--------------------------------------------------------------*\
 *  Include files, macros, defined constants, and externs	*
\*--------------------------------------------------------------*/
#include "eas.h"

extern EADATA ConvTable[EATABLESIZE];

/****************************************************************\
 *								*
 *  Name:    AddEA(hwnd)					*
 *								*
 *  Purpose: This routine handles the addition of a new EA to	*
 *	     the linked list.					*
 *								*
 *  Usage  :							*
 *								*
 *  Method : Routine does NOT do full memory error trapping and *
 *	     the insert message to l-box is not error checked.	*
 *								*
 *  Returns: TRUE if the EA is successfully added.		*
 *								*
\****************************************************************/
BOOL AddEA(HWND hwnd)
{
   HOLDFEA  *pFEA=pHoldFEA;	  /* Points to the beginning of the EA list */
   HOLDFEA  *pNewFEA=NULL;	  /* Used to temporarily hold the new EA    */
   PASSDATA PData;

   if(!FILE_ISOPEN)
   {
      MessageBox(hwnd, IDMSG_NOFILEOPEN, "Error !",
				MB_OK | MB_ERROR, TRUE);
      return(FALSE);
   }
   if(!WinDlgBox(HWND_DESKTOP,			/* get new EA name and type */
		 hwnd,
		 AddEAProc,
		 '\0',
		 IDD_ADDEA,
		 '\0'))
      return(FALSE);					/* they said cancel */

   GetMem((PPVOID)&pNewFEA, sizeof(HOLDFEA));/* Allocate space for new EA struct */

   pNewFEA->cbName = (CHAR) strlen(szEAName);	   /* Fill in new structure */
   pNewFEA->cbValue= 0;
   pNewFEA->fEA	   = 0;					/* Need bit NOT set */
					       /* Name returned in szEAName */
   GetMem((PPVOID)&pNewFEA->szName, pNewFEA->cbName+1);
   strcpy(pNewFEA->szName,strupr(szEAName));
   pNewFEA->aValue = '\0';
   pNewFEA->next   = '\0';

   if(pHoldFEA == '\0')			  /* It's the first EA for the file */
      pHoldFEA = pNewFEA;
   else				    /* Add EA to the end of the linked list */
   {
      while(pFEA->next)
	 pFEA = pFEA->next;
      pFEA->next = pNewFEA;
   }
   PData.Point	       = (CHAR *) pNewFEA;	/* Setup user data for call */
   PData.cbMulti       = 0;			/* to edit of the name and  */
   PData.usMultiOffset = 0;			/* EA Value		    */

   if(!EditEAValue(hwnd,&PData))		  /* They canceled the edit */
   {
      if(pFEA)					    /* It's not the only EA */
	 pFEA->next = '\0';		   /* Disconnect the partial new EA */
      else
	 pHoldFEA = '\0';		   /* The new EA was the first one  */

      FreeMem(pNewFEA->szName);
      FreeMem(pNewFEA);

      return(FALSE);
   }
   WinSendDlgItemMsg(hwnd, IDD_WINLBOX, LM_INSERTITEM,/* Insert name in L-Box */
		     MPFROM2SHORT(LIT_END,0),
		     MPFROMP(pNewFEA->szName));
   return(TRUE);
}

/****************************************************************\
 *								*
 *  Name:    QueryEAs(pszPath)					*
 *								*
 *  Purpose: Call DOS to Query a file's EA names and values.    *
 *								*
 *  Usage:							*
 *								*
 *  Method : Routine does NOT do full memory error trapping.	*
 *	     NOTE:  This routine does NOT prevent other		*
 *	     processes from accessing the file's EAs while it is*
 *	     reading them in, or while the program is editing	*
 *	     them.						*
 *								*
 *  Returns: TRUE if it successfully reads in the EAs. Upon exit*
 *	     , globalp HoldFEA points to a linked list of the	*
 *	     EAs for the current file.				*
 *								*
\****************************************************************/
BOOL QueryEAs(CHAR *pszPath)
{
   CHAR *pAllocc=NULL; /* Holds the FEA struct returned by DosEnumAttribute */
		       /*  used to create the GEA2LIST for DosQueryPathInfo */
   CHAR *pBigAlloc=NULL; /* Temp buffer to hold each EA as it is read in    */
   USHORT cbBigAlloc=0;	 /* Size of buffer				    */

   ULONG ulEntryNum = 1; /* count of current EA to read (1-relative)	    */
   ULONG ulEnumCnt;	 /* Number of EAs for Enum to return, always 1	    */

   HOLDFEA *pLastIn=0;	  /* Points to last EA added, so new EA can link    */
   HOLDFEA *pNewFEA=NULL; /* Struct to build the new EA in		    */

   FEA2 *pFEA;		 /* Used to read from Enum's return buffer          */
   GEA2LIST *pGEAList;/*Ptr used to set up buffer for DosQueryPathInfo call */
   EAOP2  eaopGet;	 /* Used to call DosQueryPathInfo		    */
				   /* Allocate enough room for any GEA List */
   GetMem((PPVOID)&pAllocc, MAX_GEA);
   pFEA = (FEA2 *) pAllocc;		  /* pFEA always uses pAlloc buffer */

   pHoldFEA = '\0';	       /* Reset the pointer for the EA linked list  */

   while(TRUE)		      /* Loop continues until there are no more EAs */
   {
      ulEnumCnt = 1;		       /* Only want to get one EA at a time */
      if(DosEnumAttribute(Ref_ASCIIZ,		 /* Read into pAlloc Buffer */
			  pszPath,		 /* Note that this does not */
			  ulEntryNum,		 /* get the aValue field,   */
			  pAllocc,		 /* so DosQueryPathInfo must*/
			  MAX_GEA,		 /* be called to get it.    */
			  &ulEnumCnt,
			  (LONG) GetInfoLevel1))
	break;				    /* There was some sort of error */

      if(ulEnumCnt != 1)		      /* All the EAs have been read */
	 break;

      ulEntryNum++;

      GetMem((PPVOID)&pNewFEA, sizeof(HOLDFEA));

      if (pNewFEA == '\0')				   /* Out of memory */
      {
	 FreeMem(pAllocc);
	 Free_FEAList(pHoldFEA,pDelList);
	 return (FALSE);
      }

      pNewFEA->cbName = pFEA->cbName;	   /* Fill in the HoldFEA structure */
      pNewFEA->cbValue= pFEA->cbValue;
      pNewFEA->fEA    = pFEA->fEA;
      pNewFEA->next = '\0';

      GetMem((PPVOID)&pNewFEA->szName, pFEA->cbName +1); /* Allocate for 2 arrays */
      GetMem((PPVOID)&pNewFEA->aValue, pFEA->cbValue);

      if (!pNewFEA->szName || !pNewFEA->aValue)		   /* Out of memory */
      {
	 if(pNewFEA->szName)
	    FreeMem(pNewFEA->szName);
	 if(pNewFEA->aValue)
	    FreeMem(pNewFEA->aValue);

	 FreeMem(pAllocc);
	 FreeMem(pNewFEA);

	 Free_FEAList(pHoldFEA,pDelList);
	 return (FALSE);
      }
      strcpy(pNewFEA->szName,pFEA->szName);		 /* Copy in EA Name */

      cbBigAlloc = sizeof(FEA2LIST) + pNewFEA->cbName+1 +
			      pNewFEA->cbValue;
      GetMem((PPVOID)&pBigAlloc, cbBigAlloc);
      if (pBigAlloc == '\0')
      {
	 FreeMem(pNewFEA->szName);
	 FreeMem(pNewFEA->aValue);
	 FreeMem(pAllocc);
	 FreeMem(pNewFEA);
	 Free_FEAList(pHoldFEA,pDelList);
	 return (FALSE);
      }

      pGEAList = (GEA2LIST *) pAllocc;		/* Set up GEAList structure */

      pGEAList->cbList = sizeof(GEA2LIST) + pNewFEA->cbName; /* +1 for NULL */
      pGEAList->list[0].oNextEntryOffset = 0L;
      pGEAList->list[0].cbName = pNewFEA->cbName;
      strcpy(pGEAList->list[0].szName,pNewFEA->szName);

      eaopGet.fpGEA2List = (GEA2LIST FAR *) pAllocc;
      eaopGet.fpFEA2List = (FEA2LIST FAR *) pBigAlloc;

      eaopGet.fpFEA2List->cbList = cbBigAlloc;

      DosQueryPathInfo(pszPath,			    /* Get the complete EA info */
		       FIL_QUERYEASFROMLIST,
		      (PVOID) &eaopGet,
		       sizeof(EAOP2));
      memcpy(pNewFEA->aValue,		       /* Copy the value to HoldFEA */
	     pBigAlloc+sizeof(FEA2LIST)+pNewFEA->cbName,
	     pNewFEA->cbValue);

      FreeMem(pBigAlloc);		    /* Release the temp Enum buffer */

      if(!CheckEAIntegrity(pNewFEA->aValue,pNewFEA->cbValue))	  /* Bad EA */
      {
	 FreeMem(pNewFEA->szName);
	 FreeMem(pNewFEA->aValue);
	 FreeMem(pNewFEA);
	 continue;			/* Don't add this EA to linked list */
      }

      if(pHoldFEA == '\0')		       /* If first EA, set pHoldFEA */
	 pHoldFEA = pNewFEA;
      else				   /* Otherwise, add to end of list */
	 pLastIn->next = pNewFEA;

      pLastIn = pNewFEA;		      /* Update the end of the list */
   }
   FreeMem(pAllocc);			  /* Free up the GEA buf for DosEnum */
   return (TRUE);
}


/****************************************************************\
 *								*
 *  Function name: CheckEAIntegrity(aBuf, cbBuf)		*
 *								*
 *  Returns: TRUE if the buffer is a valid EA structure.	*
 *								*
 *  Purpose: This routine checks the integrity of the passed EA *
 *	     buffer by seeing if there are any non-standard EA	*
 *	     types, or bad data that isn't sized correctly.     *
 *  Usage  :							*
 *  Method : Routine uses MultiTypeIndex() to check m-m type EAs*
 *	     since they are potentially recursive. However, this*
 *	     may not be a good idea if the m-m EA is severly	*
 *	     corrupted because it may cause MTI() to attempt a	*
 *	     read from protected memory. Routine does NOT modify*
 *	     the buffer under any circumstance.			*
 *								*
 *  Returns: TRUE if the buffer is a valid EA structure.	*
 *								*
\****************************************************************/
BOOL CheckEAIntegrity(CHAR *aBuf, ULONG cbBuf)
{
   USHORT *pusPtr = (USHORT *) aBuf;
   USHORT usOffset;
   CHAR	  *aEndPtr;

   usOffset = LookupEAType(*pusPtr);		     /* Get the EA type */

   switch(ConvTable[usOffset].usFldType)
   {
      case IDD_LPDATA:
	 pusPtr++;
	 if(*pusPtr + 2*sizeof(USHORT) == cbBuf)
	    return TRUE;
	 else
	    return FALSE;

      case IDD_MULTILIST:
	 if(*pusPtr == EA_MVMT)
	 {
	    pusPtr += 2;

		/* This checks where the end of the m-m list ends to determine
		   the size of the EA.	This is probably not good if the EA is
		   badly corrupted and it points to protected memory	    */

	    aEndPtr = MultiTypeIndex(aBuf,*pusPtr);
	    if(aEndPtr - aBuf == (CHAR) cbBuf)
	       return TRUE;
	    else
	       return FALSE;
	 }
	 else		 /* Single type, multi-value is not yet implemented */
	 {
	    return TRUE;
	 }
      default:
	 return FALSE;
   }
   return TRUE;
}

/****************************************************************\
 *								*
 *  Name:    Free_FEAList(pFEA, pDList)				*
 *								*
 *  Purpose: This routine frees up the current list of EAs by	*
 *	     deallocating the space used by the szName and	*
 *	     aValue fields, then deallocating the HoldFEA struct*
 *	     Next, it deletes the EAName space, then the	*
 *	     DeleteList structure.				*
 *  Usage :							*
 *  Method:  Note that EAS always passes in pHoldFEA and	*
 *	     pDelList which is unnecessary since they are global*
 *	     pointers;however, this is done to make the routine *
 *	     more flexible by allowing multiple linked lists to *
 *	     exist.						*
 *								*
 *  Returns: VOID.  The two linked lists passed in are cleaned	*
 *	     out though.					*
 *								*
\****************************************************************/
VOID Free_FEAList(HOLDFEA *pFEA, DELETELIST *pDList)
{
   HOLDFEA *next;	/* Holds the next field since we free the structure */
			/* before reading the current next field	    */
   DELETELIST *Dnext;				   /* Same purpose as *next */

   while(pFEA)
   {
      next = pFEA->next;
      if(pFEA->szName)				  /* Free if non-NULL name  */
	 FreeMem(pFEA->szName);
      if(pFEA->aValue)				  /* Free if non-NULL value */
	 FreeMem(pFEA->aValue);

      FreeMem(pFEA);				     /* Free HoldFEA struct */
      pFEA = next;
   }
   while(pDList)
   {
      Dnext = pDList->next;
      if(pDList->EAName)
	 FreeMem(pDList->EAName);
      FreeMem(pDList);
      pDList = Dnext;
   }
   return;
}

/****************************************************************\
 *								*
 *  Name:    LookupEAType(usType)				*
 *								*
 *  Purpose: This routine takes EA type and returns an offset	*
 *	     into ConvTable which points to an entry that	*
 *	     describes the type passed in.			*
 *								*
 *  Usage:							*
 *  Method:							*
 *								*
 *  Returns: An offset into the ConvTable to the appropriate	*
 *	     entry. If no match is found, the return value	*
 *	     points to the last entry, non-conventional format. *
 *								*
\****************************************************************/
ULONG LookupEAType(ULONG usType)
{
   USHORT cnt;

   for(cnt=0;cnt<EATABLESIZE-1;cnt++)
      if(ConvTable[cnt].usPrefix == usType)
	 return(cnt);
   return(cnt);
}

/****************************************************************\
 *								*
 *  Name:    DeleteCurEA(hwnd)					*
 *								*
 *  Purpose: This routine removes in memory the currently	*
 *	     highlighted EA from the EA list.  It places the	*
 *	     deleted EA in the global pDelList linked list.	*
 *								*
 *  Usage:   This routine is called by MainDlgProc when DELETE	*
 *	     button selected.					*
 *								*
 *  Method:  The memory allocation routines are NOT fully error *
 *	     trapped.						*
 *								*
 *  Returns: VOID.  Removes one item from global pHoldFEA list	*
 *	     and adds one to global pDelList.			*
 *								*
\****************************************************************/
VOID DeleteCurEA(HWND hwnd)
{
   HOLDFEA    *pFEA,*pFEAPrev;
   DELETELIST *pDL=NULL, *pDLcnt;/*Utility ptrs for manipulating the Del list*/
   SHORT      sOffset;
   SHORT      i;

   if(!FILE_ISOPEN)
   {
      MessageBox(hwnd, IDMSG_ERRORSELECT, "Error !",
			       MB_OK | MB_ERROR, TRUE);
      return;
   }
   sOffset = (SHORT) WinSendDlgItemMsg(hwnd, IDD_WINLBOX,
					 LM_QUERYSELECTION,0,0);
   if(sOffset<0)			   /* No item is currently selected */
   {
      MessageBox(hwnd, IDMSG_ERRORSELECT, "Error !",
				     MB_OK | MB_ERROR, TRUE);
      return;
   }
   pFEA = GetCurFEA(hwnd,pHoldFEA);	/* Gets a pointer to item to delete */
   if (pFEA == '\0')
      return;
	       /* These two allocations should be checked for out of memory */
   GetMem((PPVOID)&pDL, sizeof(DELETELIST));	 /* Add Name to Delete List */
   GetMem((PPVOID)&pDL->EAName, pFEA->cbName+1);
   strcpy(pDL->EAName,pFEA->szName);
   pDL->next = '\0';

   if(pDelList == '\0')		      /* The del list was previously empty  */
      pDelList = pDL;
   else				      /* tack name onto the end of the list */
   {
      pDLcnt = pDelList;
      while(pDLcnt->next)
	 pDLcnt = pDLcnt->next;
      pDLcnt->next = pDL;
   }
   WinSendDlgItemMsg(hwnd, IDD_WINLBOX,
		     LM_DELETEITEM,MPFROMSHORT(sOffset),0L);

   if(sOffset<1)			/* Remove pFEA from the linked list */
   {
      pHoldFEA = pFEA->next;
   }
   else
   {
      pFEAPrev = pHoldFEA;

      --sOffset;
      while(sOffset)					/* Find previous EA */
      {
	 pFEAPrev = pFEAPrev->next;
	 --sOffset;
      }
      pFEAPrev->next = pFEA->next;
   }
			   /* Check Type Name is in the list of MVST or not */
   for (i=0; i<MSNUM && MSdata[i].szName!=NULL &&
			strcmp(pFEA->szName,MSdata[i].szName)!=0;)
	  i++;

   if(i<MSNUM && MSdata[i].szName!=NULL)	 /* Found the EA type in MVST list */
   {
      FreeMem(MSdata[i].szName);
      for (; i<MSNUM && MSdata[i+1].szName!=NULL; i++)
      {
	 MSdata[i].szName = MSdata[i+1].szName;
	 MSdata[i].usType = MSdata[i+1].usType;
      }
   }
   FreeMem(pFEA->szName);			      /* Release the memory */
   FreeMem(pFEA->aValue);
   FreeMem(pFEA);

   FILE_CHANGED = TRUE;
   return;
}

/****************************************************************\
 *								*
 *  Name:    CurEAType(pFEA)					*
 *								*
 *  Purpose: Given an EA structure, this routine returns the	*
 *	     Type of the EA which resides in the first USHORT of*
 *	     the aValue member. This function is the same as	*
 *	     GetUSHORT(pFEA,0).					*
 *  Usage  :							*
 *  Method : Assumes a valid HoldFEA struct.			*
 *								*
 *  Returns: The EA type.					*
 *								*
\****************************************************************/
ULONG CurEAType(HOLDFEA *pFEA)			 /* Same as GetUSHORT(,0); */
{
   USHORT *pusType;    /* EA Type is stored in first USHORT of aValue field */

   pusType = (USHORT *) pFEA->aValue;
   return(*pusType);
}

/****************************************************************\
 *								*
 *  Name:    GetUSHORT(pFEA, index)				*
 *								*
 *  Purpose: This routine returns the nth USHORT value in the	*
 *	     aValue member of pFEA using index as the offset.	*
 *  Usage  :							*
 *  Method : Assumes a valid HoldFEA struct and that index	*
 *	     doesn't point outside the aValue buffer.           *
 *								*
 *  Returns: The appropriate USHORT from the aValue field	*
 *								*
\****************************************************************/
USHORT GetUSHORT(HOLDFEA *pFEA, ULONG index)
{
   USHORT *pusType;

   pusType = (USHORT *) pFEA->aValue;
   while(index-- > 0)
      pusType++;
   return(*pusType);
}

/****************************************************************\
 *								*
 *  Name:    WriteEAs()						*
 *								*
 *  Purpose: This routine updates the EAs on disk to reflect	*
 *	     their current condition in memory.	 First, all EAs *
 *	     in the delete list are removed from the disk, then *
 *	     all EAs in the pHoldFEA list are written to disk.	*
 *  Usage  :							*
 *  Method : This routine is not bulletproof as it does not get *
 *	     exclusive access to the file EAs, nor does it	*
 *	     handle out of disk space sort of errors. Also,	*
 *	     memory fetches are not fully error trapped.	*
 *								*
 *  Returns: VOID.  But cleans out the pDelList linked list.	*
 *								*
\****************************************************************/
VOID WriteEAs()
{
   DELETELIST *pDL = pDelList,*pDLnext;
   HOLDFEA    *pHFEA= pHoldFEA;
   EAOP2       eaopWrite;
   CHAR	      aBuf[MAX_GEA],*aPtr=NULL;
   FEA2	       *pFEA = (FEA2 *) &aBuf[sizeof(ULONG)];
   USHORT     usMemNeeded, usRet;
   ULONG      *pulPtr=(ULONG *) aBuf;  /* Initally points to top of FEALIST */

   if(!FILE_ISOPEN)
   {
      MessageBox(HWND_DESKTOP, IDMSG_NOFILEOPEN, "Error !",
				MB_OK | MB_ERROR, TRUE);
      return;
   }
   if (!FILE_CHANGED)		       /* Don't write unless it's necessary */
      return;
   eaopWrite.fpFEA2List = (FEA2LIST FAR *) aBuf; /* Setup fields that won't */
   pFEA->fEA	 = 0;				 /* change for the delete   */
   pFEA->cbValue = 0;				 /* calls to DosSetPathInfo */

   while(pDL)			      /* Clean out all the deleted EA names */
   {
      pFEA->cbName = (UCHAR) strlen(pDL->EAName);
      *pulPtr	   = sizeof(FEA2LIST) + pFEA->cbName;
      strcpy(pFEA->szName,pDL->EAName);
      pFEA->oNextEntryOffset = 0L;
					 /* Delete EA's by saying cbValue=0 */
      DosSetPathInfo(szFileName,
		     SetInfoLevel2,
		     (PVOID) &eaopWrite,
		     sizeof(EAOP2),
		     DSPI_WRTTHRU);
      pDLnext = pDL->next;			      /* Temp hold next pDL */
      FreeMem(pDL->EAName);		      /* Free up current Del struct */
      FreeMem(pDL);
      pDL = pDLnext;				  /* Set pDL to saved value */
   }
   pDelList = '\0';				    /* DelList is now empty */

   while(pHFEA)					 /* Go through each HoldFEA */
   {
      usMemNeeded = sizeof(FEA2LIST) + pHFEA->cbName+1 +
				pHFEA->cbValue;
      GetMem((PPVOID)&aPtr, usMemNeeded);

      eaopWrite.fpFEA2List = (FEA2LIST FAR *) aPtr;  /* Fill in eaop struct */
      eaopWrite.fpFEA2List->cbList = usMemNeeded;

      eaopWrite.fpFEA2List->list[0].fEA	    = pHFEA->fEA;
      eaopWrite.fpFEA2List->list[0].cbName  = pHFEA->cbName;
      eaopWrite.fpFEA2List->list[0].cbValue = pHFEA->cbValue;

      strcpy(eaopWrite.fpFEA2List->list[0].szName, pHFEA->szName);
      memcpy(eaopWrite.fpFEA2List->list[0].szName + pHFEA->cbName+1,
	     pHFEA->aValue, pHFEA->cbValue);

      usRet = DosSetPathInfo(szFileName,	 /* Write out the EA */
			     FIL_QUERYEASIZE,
			     (PVOID) &eaopWrite,
			     sizeof(EAOP2),
			     DSPI_WRTTHRU);
      if (usRet)
      {
	  MessageBox(HWND_DESKTOP, IDMSG_WRITEERROR, "Error !",
				  MB_OK | MB_ERROR, TRUE);
	  FreeMem(aPtr);		      /* Free up the FEALIST struct */
	  FILE_CHANGED = FALSE;
	  return;
      }
      FreeMem(aPtr);			      /* Free up the FEALIST struct */

      pHFEA = pHFEA->next;
   }
   FILE_CHANGED = FALSE;
   MessageBox(HWND_DESKTOP, IDMSG_WRITEOK, "Note !", MB_OK, FALSE);
   return;
}

/****************************************************************\
 *								*
 *  Function name: EditEAValue(hwnd, pPDat)			*
 *								*
 *  Purpose: This routine allows the entry/edit of an EA value	*
 *	     condition in memory.  First, all EAs in the delete *
 *	     list are removed from the disk, then all EAs in the*
 *	     pHoldFEA list are written out to disk.		*
 *  Usage  :							*
 *  Method : Expects the PassData structure to tell it the	*
 *	     HoldFEA to edit, and if it is a subfield of a multi*
 *	     -multi EA, the rest of the PassData structure will *
 *	     be filled into indicated which m-m is being edited.*
 *	     Note that if this is a new edit, usRetEAType is	*
 *	     expected to be set to the proper EA type upon entry*
 *    NOTE:  memory sizing  requests are not fully error trapped*
 *								*
 *  Returns: TRUE if the edit was successful.			*
 *								*
\****************************************************************/
BOOL EditEAValue(HWND hwnd, PPASSDATA pPDat)
{
   USHORT usEAType;		       /* Holds the field type to be edited */
   USHORT *pusPtr;
   USHORT usSize;  /* Holds the delta difference of the old and new buffers */
   CHAR	  *szNew=NULL,*szTrash;				   /* Temporary pointers */
   PASSDATA PDat;
   HOLDFEA *pFEA = (HOLDFEA *) pPDat->Point;	     /* The EA to be edited */

			    /* Determine the type of EA that will be edited */
   if(pPDat->cbMulti)				  /* It's a multi-type job  */
   {
      pusPtr = (USHORT *) MultiTypeIndex(pFEA->aValue+pPDat->usMultiOffset,
					 pPDat->usIndex);
      usEAType = *pusPtr;
   }
   else if(pFEA->cbValue)			  /* It isn't a new EA name */
   {
      pusPtr   = (USHORT *)pFEA->aValue;
      usEAType = *pusPtr;
   }
   else							   /* It's a new EA */
      usEAType = ConvTable[usRetEAType].usPrefix;

   PDat.Point	= pFEA->szName;		 /* General setup for AsciiEditProc */
						  /* =1 if there is a multi */
   PDat.usIndex =(USHORT)( pPDat->cbMulti ? 1 : 0);
   PDat.fFlag	= (BYTE) ((pFEA->fEA & 0x80) ? TRUE : FALSE);

   switch(usEAType)
   {
      case EA_ASCIIZ:
      case EA_ASCIIZFN:
      case EA_ASCIIZEA:
      case EA_ASN1:
	 if(pPDat->cbMulti)			/* It is a multi-type field */
	    szAscii=MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
				   pPDat->usIndex)
		    +sizeof(USHORT);
	 else if(pFEA->cbValue)			/* There is a current value */
	    szAscii=pFEA->aValue+sizeof(USHORT);
	 else						   /* It's a new EA */
	    szAscii='\0';

	 if(!WinDlgBox(HWND_DESKTOP,		   /* Do an ascii text edit */
		       hwnd,
		       AsciiEditProc,
		       '\0',
		       IDD_ASCIIEDIT,
		       &PDat))
	    return(FALSE);				/* They said cancel */

	 if(PDat.fFlag)				/* Handle the need/nice bit */
	    PDat.fFlag = 0x80;
	 if(PDat.fFlag != (BYTE)(PDat.fFlag & 0x80))
	    FILE_CHANGED = TRUE;
	 pFEA->fEA =(BYTE)( (pFEA->fEA & 0x7f) | PDat.fFlag );

	 if(stricmp(strupr(szEAName),pFEA->szName))	/* The name changed */
	    ChangeName(pFEA,szEAName);

	 if(pFEA->cbValue)			/* There is a current value */
	 {
	    if(!strcmp(szAscii,szScratch))	       /* It hasn't changed */
	       return(TRUE);

	    if(pPDat->cbMulti)		  /* Do the whole thing here if m-m */
	    {						  /* Change in size */
	       usSize = (strlen(szScratch)-strlen(szAscii));

	       if(usSize > 0)			/* The new string is longer */
	       {
		  ResizeMem((PPVOID)&pFEA->aValue,	     /* Enlarge the EA size */
			    pFEA->cbValue,
			    pFEA->cbValue+usSize);
		  szTrash=MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
					       (pPDat->usIndex+1));
		  memmove(szTrash+usSize,    /* Move end of EA to make room */
			  szTrash,
			  pFEA->cbValue-(szTrash-pFEA->aValue));
	       }
	       else
	       {
		  szTrash= MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
					       (pPDat->usIndex+1));
		  memmove(szTrash+usSize,    /* Move back the end of the EA */
			  szTrash,
			  pFEA->cbValue-(szTrash-pFEA->aValue));
		  DosSetMem(pFEA->aValue, pFEA->cbValue+usSize, fPERM|PAG_COMMIT);
	       }
	       szTrash=MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
				      pPDat->usIndex);
	       strcpy(szTrash+sizeof(USHORT),szScratch); /* Copy in new val */
	       pFEA->cbValue+=usSize;		   /* Change buffer count   */

	       FILE_CHANGED = TRUE;
	       return(FILE_CHANGED);		      /* Done with m-m edit */
	    }
	    else
	    {
	       FreeMem(pFEA->aValue);		   /* Release old Value mem */
	    }
	 }
	 GetMem((PPVOID)&szNew, strlen(szScratch)+3); /* +3 for Type & NULL */
	 pusPtr = (USHORT *) szNew;
	 *pusPtr= usEAType;			  /* Set type in new buffer */
	 strcpy(szNew+2,szScratch);		   /* Copy in the new value */
	 pFEA->aValue = szNew;			    /* Fix up the structure */
	 pFEA->cbValue= (USHORT)(strlen(szScratch)+3);

	 FILE_CHANGED = TRUE;
	 return(FILE_CHANGED);

      case EA_LPBITMAP:
      case EA_LPICON:
      case EA_LPBINARY:
      case EA_LPASCII:
      case EA_LPMETAFILE:
	 if (usEAType==EA_LPBITMAP || usEAType==EA_LPICON)
	 {
	    WinDlgBox(HWND_DESKTOP,
		      hwnd,
		      IconDlgProc,
		      '\0',
		      IDD_ABOUTBOX,
		      NULL);
	 }
	 if(pPDat->cbMulti)			/* It is a multi-type field */
	 {   /* szTrash points to field to edit, pusPtr to the field length */
	    szTrash=MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
				   pPDat->usIndex);
	    pusPtr = (USHORT *) ((CHAR *) szTrash + sizeof(USHORT));
	    usSize = *pusPtr;
	    if(usSize)				       /* It isn't a new EA */
	    {				    /* Set up inital value for edit */
	       GetMem((PPVOID)&szAscii, usSize+1);
	       memcpy(szAscii,szTrash+2*sizeof(USHORT),usSize);
	       *( ( CHAR *)szAscii + usSize ) ='\0';
	    }
	    else					 /* No inital value */
	       szAscii = '\0';
	 }
	 else if(pFEA->cbValue)	   /* Single type && there is current value */
	 {
	    usSize=GetUSHORT(pFEA,1);	   /* Get size and set inital value */
	    if(usSize)
	    {
	       GetMem((PPVOID)&szTrash, usSize+1);	     /* +1 for null */
	       memcpy(szTrash,pFEA->aValue+4,usSize);
	       szTrash[usSize]='\0';
	       szAscii=szTrash;
	    }
	    else
	       szAscii = '\0';
	 }
	 else
	    szAscii = '\0';

	 if(!WinDlgBox(HWND_DESKTOP,		   /* Do an ascii text edit */
		       hwnd,
		       AsciiEditProc,
		       '\0',
		       IDD_ASCIIEDIT,
		       &PDat))
	 {     /* Cancel, but check if memory needs to be freed before exit */
	    if(pPDat->cbMulti || pFEA->cbValue)
	       if(szAscii)				   /* It's not NULL */
		  FreeMem(szAscii);			     /* +1 for NULL */
	    return(FALSE);
	 }
	 if(PDat.fFlag)				/* Handle the need/nice bit */
	    PDat.fFlag = 0x80;
	 if(PDat.fFlag != (BYTE)(PDat.fFlag & 0x80))
	    FILE_CHANGED = TRUE;
	 pFEA->fEA = (BYTE)((pFEA->fEA & 0x7f) | PDat.fFlag);

	 if(stricmp(strupr(szEAName),pFEA->szName))	/* The name changed */
	    ChangeName(pFEA,szEAName);

	 if(pFEA->cbValue)			/* There is a current value */
	 {
	    if(szAscii && !strcmp(szAscii,szScratch))  /* It hasn't changed */
	    {
	       if(szAscii)
		  FreeMem(szAscii);
	       return(TRUE);
	    }
	    if(szAscii)			       /* Free default value buffer */
	       FreeMem(szAscii);

	    if(pPDat->cbMulti)	   /* Do the whole thing here is multi-type */
	    {						   /* Change in len */
	       USHORT usDelta = (USHORT)(strlen(szScratch) - usSize);
	       if(usDelta > 0)	  /* The new string is longer, resize first */
	       {
		  ResizeMem((PPVOID)&pFEA->aValue, pFEA->cbValue, pFEA->cbValue+usDelta);
		  szTrash= MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
					 (pPDat->usIndex+1));
		  memmove(szTrash+usDelta,szTrash,
			  pFEA->cbValue-(szTrash-pFEA->aValue));
	       }
	       else
	       {
		  szTrash= MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
					 (pPDat->usIndex+1));
		  memmove(szTrash+usDelta,szTrash,
			  pFEA->cbValue-(szTrash-pFEA->aValue));
		  ResizeMem((PPVOID)&pFEA->aValue, pFEA->cbValue+usDelta, fPERM|PAG_COMMIT);
	       }
	       szTrash=MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
				      pPDat->usIndex);
	       memmove(szTrash+2*sizeof(USHORT),szScratch,strlen(szScratch));
	       pusPtr = (USHORT *) ((CHAR *)szTrash + sizeof(USHORT));
	       *pusPtr= strlen(szScratch); /* Set the length field */

	       pFEA->cbValue += usDelta;	 /* Adjust struct len field */

	       FILE_CHANGED = TRUE;
	       return(FILE_CHANGED);
	    }
	    FreeMem(pFEA->aValue);		       /* Free up old value */
	 }
	 GetMem((PPVOID)&szNew, strlen(szScratch)+4); /* Get space for new value */
	 pusPtr = (USHORT *) szNew;
	 *pusPtr= usEAType;				  /* Set type field */
	 pusPtr++;
	 *pusPtr= strlen(szScratch);		/* Set length field */
	 memcpy(szNew+4,szScratch,*pusPtr);	       /* Copy in new value */
	 pFEA->aValue = szNew;				 /* Adjust pointers */
	 pFEA->cbValue= strlen(szScratch)+4;/* +4 for type and LP cnt */

	 FILE_CHANGED = TRUE;
	 return(FILE_CHANGED);

      case EA_MVST:
      case EA_MVMT:			     /* It's multi-value multi-type */
	 if(pFEA->cbValue == 0)				   /* It's a new EA */
	 {					   /* Allocate empty m-m EA */
	    GetMem((PPVOID)&pFEA->aValue, 3*sizeof(USHORT));
	    pFEA->cbValue = 3*sizeof(USHORT);
	    pusPtr	= (USHORT *) pFEA->aValue;
	    if(usEAType == EA_MVMT)
	       *pusPtr = 0xffdf;		 /* Multi-value, multi-type */
	    else
	       *pusPtr = 0xffde;		/* Multi-value, single-type */
	    pusPtr+=2;				     /* Skip type, codepage */
	    *pusPtr = 0;		 /* No fields initially	    */
	    FILE_CHANGED = TRUE;
	 }
						   /* Set up passed in data */
	 if(pPDat->cbMulti)			  /* It's a multi-type job  */
	 {
	    szNew   = MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
				     pPDat->usIndex);
	    szTrash = MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
					   (pPDat->usIndex+1));
	    PDat.usMultiOffset = (USHORT)(szNew - pFEA->aValue);
	    PDat.cbMulti       = (USHORT)(szTrash - szNew);
	 }
	 else
	 {
	    PDat.usMultiOffset = 0;
	    PDat.cbMulti       = pFEA->cbValue;
	 }
	 PDat.Point = (CHAR *) pFEA;

	 WinDlgBox(HWND_DESKTOP,		  /* Do the Multi-type edit */
		   hwnd,
		   MultiTypeProc,
		   '\0',
		   IDD_MULTIBOX,
		   &PDat);
	 return(TRUE);

      default:
	 return(FALSE);
   }
   return(FALSE);
}

/****************************************************************\
 *								*
 *  Name:    EAExists(szEAName)					*
 *								*
 *  Purpose: This routine goes through the linked list pointed	*
 *	     to by global pHoldFEA and determines whether or not*
 *	     an EA othe passed name already exists.		*
 *  Usage:							*
 *  Method : The comparison is NOT case sensitive.		*
 *								*
 *  Returns: TRUE if an EA with a name matching szEAName exists.*
 *								*
\****************************************************************/
BOOL EAExists(CHAR *szEAName)
{
   HOLDFEA *phFEA=pHoldFEA;

   while(phFEA)
   {
      if(!stricmp(szEAName,phFEA->szName))
	 return(TRUE);
      phFEA=phFEA->next;
   }
   return(FALSE);
}

/****************************************************************\
 *								*
 *  Name:    ChangeName(pFEA, szName)				*
 *								*
 *  Purpose: This routine copies the current EA Name to the	*
 *	     delete list, then allocates a new space, copies the*
 *	     new name into it, and sets the FEA pointer to it.	*
 *  Usage :							*
 *  Method : Not all the memory allocations are fully error	*
 *	     trapped.						*
 *								*
 *  Returns: VOID.  Fixes up the pFEA structure and global	*
 *	     pDelList.						*
 *								*
\****************************************************************/
VOID ChangeName(HOLDFEA *pFEA, CHAR *szName)
{
   CHAR *szTemp=NULL;
   DELETELIST *pDL=NULL;

   GetMem((PPVOID)&szTemp, strlen(szName+1));/* Allocate space for new name */
   if(!szTemp)
      return;
					    /* Allocate a new delete struct */
   GetMem((PPVOID)&pDL,	 sizeof(DELETELIST));
   pDL->EAName = pFEA->szName;		       /* Fill in DeleteList struct */
   pDL->next   = pDelList;
   pDelList    = pDL;

   strcpy(szTemp,szName);		   /* Copy name to permanent buffer */
   pFEA->szName = szTemp;		   /* Fix up struct		    */
   pFEA->cbName = (CHAR) strlen(szName);

   FILE_CHANGED = TRUE;
   return;
}

/****************************************************************\
 *								*
 *  Name:    MultiTypeIndex(pMulti, usIndex)			*
 *								*
 *  Purpose: This routine takes a pointer to a Multi-Multi data *
 *	     field and returns a pointer to the nth data field	*
 *	     in this buffer.					*
 *  Usage:							*
 *  Method:  Memory bounds are not checked and a corrupt EA field
 *	     could cause unspecified results. Recursively calls *
 *	     itself to handle nesting. Does not support multi-	*
 *	     value single type fields.				*
 *								*
 *  Returns: a pointer to the field specified by the usIndex	*
 *	     parameters.					*
 *								*
 *  Calls: LookupEAType, MultiTypeIndex()			*
 *								*
\****************************************************************/
CHAR *MultiTypeIndex(CHAR *pMulti, ULONG usIndex)
{
   USHORT *pusPtr;
   USHORT usOffset;
			       /* skip over 0xffdf, codepage, and field cnt */
   pMulti += 3*sizeof(USHORT);

   while(usIndex--)		    /* loop to skip over correct # of flds  */
   {
      pusPtr   = (USHORT *) pMulti;   /* The address of EA data type stored */
      usOffset = LookupEAType(*pusPtr);		/* Get offset of field type */

      pMulti += sizeof(USHORT);	  /* Skip over the type field to first data */

      switch(ConvTable[usOffset].usFldType)
      {
	 case IDD_ASCIIZ:
	    while(*pMulti)		   /* Increment to point after NULL */
		pMulti++;
	    pMulti++;
	    break;

	 case IDD_LPDATA:
	    pusPtr = (USHORT *) pMulti;		/* Get the length of a data */
	    pMulti += *pusPtr + sizeof(USHORT);		     /* skip to end */
	    break;

	 case IDD_MULTILIST:
	    if(*pusPtr == EA_MVMT)  /* m-m, do a recursive call to skip fld */
	    {
	       pusPtr = (USHORT *) pMulti;	     /* points to field cnt */
	       pMulti = MultiTypeIndex(pMulti-sizeof(USHORT),*pusPtr);
	       break;
	    }
	       /* implemented for Multi-valued single-type stuff... */
	    pusPtr = (USHORT *) pMulti;		/* Get the length of a data */
	    pMulti += *pusPtr + sizeof(USHORT);		     /* skip to end */
	    break;
      }
   }
   return(pMulti);
}

/****************************************************************\
 *								*
 *  Name:    EAValueString(aEAVal)				*
 *								*
 *  Purpose: This routine takes a pointer to an EA Value (i.e.	*
 *	     starting with $ffxx) and returns a pointer to a	*
 *	     string representing the value of the EA. This string
 *	     must be Freed by the user when finished with it.	*			   *
 *  Usage:							*
 *  Method : Not all GetMem's are totally error trapped. The    *
 *	     string returned is allocated in this procedure, but*
 *	     it is the caller's responsibility to free the buffer
 *								*
 *  Returns: a pointer to an ASCII description of the field value
 *								*
\****************************************************************/
CHAR *EAValueString(CHAR *aEAVal)
{
   USHORT *pusPtr= (USHORT *) aEAVal;		   /* Points to EA Type	    */
   CHAR *szRet=NULL, *szTemp;			 /* szRet points to return string */

   switch(*pusPtr)
   {
      case EA_ASCIIZ:	  /* For asciiz strings, return MAXSHOWSIZE-1 chars */
      case EA_ASCIIZFN:
      case EA_ASCIIZEA:
      case EA_ASN1:
	 aEAVal += sizeof(USHORT);
	 if(strlen(aEAVal)<MAXSHOWSIZE)
	 {
	    GetMem((PPVOID)&szRet, strlen(aEAVal)+1);
	    strcpy(szRet,aEAVal);
	 }
	 else
	 {
	    GetMem((PPVOID)&szRet, MAXSHOWSIZE);
	    strncpy(szRet,aEAVal,MAXSHOWSIZE-4);
	    strcpy (szRet+MAXSHOWSIZE-4,"...");
	    szRet[MAXSHOWSIZE-1]='\0';
	 }
	 return(szRet);

      case EA_LPASCII:		 /* Display up to first MAXSHOWSIZE-1 chars */
      case EA_LPMETAFILE:
	 pusPtr++;
	 aEAVal += 2*sizeof(USHORT);
	 if(*pusPtr < MAXSHOWSIZE)
	 {
	    GetMem((PPVOID)&szRet, *pusPtr +1);
	    strncpy(szRet,aEAVal,*pusPtr);
	    szRet[*pusPtr]='\0';
	 }
	 else
	 {
	    GetMem((PPVOID)&szRet, MAXSHOWSIZE);
	    strncpy(szRet,aEAVal,MAXSHOWSIZE-4);
	    strcpy (szRet+MAXSHOWSIZE-4,"...");
	    szRet[MAXSHOWSIZE-1]='\0';
	 }
	 return(szRet);
		 /* For the rest of the types, just display the field type */
      case EA_LPBINARY:
	 szTemp = "*** LP Binary ***";
	 break;

      case EA_LPBITMAP:
	 szTemp = "*** LP Bitmap ***";
	 break;

      case EA_LPICON:
	 szTemp = "*** LP Icon ***";
	 break;

      case EA_MVMT:
	 szTemp = "*** Multi-value Multi-type ***";
	 break;

      case EA_MVST:
	 szTemp = "*** Multi-value Single-type ***";
	 break;

      default:
	 szTemp = "*** Unknown EA type ***";
	 break;

   }
   GetMem((PPVOID)&szRet, strlen(szTemp)+1); /* Copy string from static to dynamic */
   strcpy(szRet,szTemp);
   return(szRet);
}

/****************************************************************\
 *								*
 *  Function name: MultiAdd(hwnd, pFEA, pPDat)			*
 *								*
 *  Purpose: This routine is called by MultiTypeProc and handles*
 *	     the addition of a subvalue to a multi-value, multi-*
 *	     type EA.						*
 *  Usage :							*
 *  Method : Not all GetMem's are totally error trapped.  It is *
 *	     possible that the add to the listbox could fail.	*
 *								*
 *  Returns: VOID.  Modifies the current pFEA.			*
 *								*
\****************************************************************/
VOID MultiAdd(HWND hwnd, HOLDFEA *pFEA, PPASSDATA pPDat)
{
   USHORT   usSize, i;
   USHORT   *pusPtr;
   CHAR	    aUtility[6];	/* Used to hold the header for all EA types */
   CHAR	    *pInsert,*pValue;
   PASSDATA PDat;

   PDat.Point = pFEA->szName;
			   /* Check Type Name is in the list of MVST or not */
   for (i=0; i<MSNUM && MSdata[i].szName!=NULL &&
			     strcmp(PDat.Point,MSdata[i].szName)!=0;)
	 i++;

   if(ConvTable[usRetEAType].usPrefix != EA_MVST && i==MSNUM)
   {
     if(!WinDlgBox(HWND_DESKTOP,		   /* Get the name and type */
		   hwnd,
		   AddEAProc,
		   '\0',
		   IDD_ADDEA,
		   &PDat))
	return;						/* They said cancel */
   }
   else			   /* MVST */
   {
     if (i < MSNUM && MSdata[i].szName!=NULL && MSdata[i].usType<10)
	 usRetEAType = MSdata[i].usType;	     /* EAname & type exist */
     else
	if(!WinDlgBox(HWND_DESKTOP,	 /* EAname is new, get the new type */
		      hwnd,
		      AddEAProc,
		      '\0',
		      IDD_ADDEA,
		      &PDat))
	   return;					/* They said cancel */
   }
   pusPtr = (USHORT *) aUtility;
   *pusPtr= ConvTable[usRetEAType].usPrefix;  /* Set the type in header buf */

   switch(ConvTable[usRetEAType].usFldType)
   {
      case IDD_ASCIIZ:		 /* make buffer look like: xx FF 00, size 3 */
	 usSize = 3;
	 aUtility[2]=0;
	 break;

      case IDD_LPDATA:	  /* make the buffer look like: xx FF 00 00, size 4 */
	 usSize = 4;
	 pusPtr = (USHORT *) &aUtility[2];
	 *pusPtr= 0;
	 break;

      case IDD_MULTILIST:
	 usSize = 6;
	 pusPtr = (USHORT *) &aUtility[2];
	 *pusPtr= 0;				       /* Zero out codepage */
	 pusPtr++;
	 *pusPtr= 0;					/* Zero out fld cnt */
	 break;
   }
			       /* Increase EA size to accomodate the header */
   ResizeMem((PPVOID)&pFEA->aValue, pFEA->cbValue, pFEA->cbValue+usSize);

   pusPtr  = (USHORT *) ((CHAR *) pFEA->aValue + pPDat->usMultiOffset);
   pusPtr+=2;		       /* Point to the current number of m-m fields */

	/* Get ptr to beginning of current EA, scoot the rest down and insert
	 the 3-4 byte header at the end of the list.			    */
   pInsert = MultiTypeIndex(pFEA->aValue+pPDat->usMultiOffset, *pusPtr);
   memmove(pInsert+usSize,pInsert, pFEA->cbValue-(pInsert-pFEA->aValue));
   memcpy(pInsert,aUtility,usSize);

   pFEA->cbValue += usSize;			       /* Fix up the counts */
   pPDat->cbMulti+= usSize;
						   /* Set the PDat for call */
   PDat.Point	      = (CHAR *) pFEA;
   PDat.cbMulti	      = pPDat->cbMulti;
   PDat.usMultiOffset = pPDat->usMultiOffset;
   PDat.usIndex	      = *pusPtr;

   if(!EditEAValue(hwnd,&PDat))			  /* They canceled the edit */
   {	      /* Move the EA's back to effectively kill the inserted header */
      memmove(pInsert,pInsert+usSize,pFEA->cbValue-(pInsert-pFEA->aValue));
      DosSetMem(pFEA->aValue, pFEA->cbValue-usSize, fPERM|PAG_COMMIT);
      pFEA->cbValue -= usSize;				 /* Adjust counters */
      pPDat->cbMulti-= usSize;

      return;
   }
	/* Reset pusPtr since EditEAValue could have moved the base address */
   pusPtr  = (USHORT *) ((CHAR *) pFEA->aValue + pPDat->usMultiOffset);
   pusPtr+=2;

   pInsert = MultiTypeIndex(pFEA->aValue+pPDat->usMultiOffset, *pusPtr);

   *pusPtr += 1;		/* Field cnt incremented AFTER call to Edit */

   pValue = EAValueString(pInsert);	   /* Add new field to the list box */

   WinSendDlgItemMsg(hwnd, IDD_LBOX, LM_INSERTITEM,
		     MPFROM2SHORT(LIT_END,0),
		     MPFROMP(pValue));
   FreeMem(pValue);

   FILE_CHANGED = TRUE;
   return;
}
