Metropoli BBS
VIEWER: neadea.c MODE: TEXT (ASCII)
/*************************************************************

 This module contains subroutines for nead.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
   EditEAValues()      Handles editing a given EA (also 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 an EA
   MultiAdd()          Handles addition of a field for m-m

**************************************************************/

#include "nead.h"

/************ External GLOBALS *******************************/

extern CHAR szFileName[CCHMAXPATH];
extern CHAR szEAName[MAXEANAME+1];
extern USHORT usRetEAType;
extern BOOL FILE_ISOPEN;
extern BOOL FILE_CHANGED;
extern BOOL COMMAND_LINE_FILE;
extern HHEAP hhp;
extern CHAR *pAlloc,*szEditBuf,*szAscii,*szScratch;
extern HOLDFEA *pHoldFEA;
extern DELETELIST *pDelList;
extern EADATA ConvTable[EATABLESIZE];

/*************************************************************/


/*
 * Function name: AddEA()
 *
 * Parameters:  hwnd which is the current window handle.
 *
 * Returns: TRUE iff the EA is successfully added.
 *
 * Purpose: This routine handles the addition of a new EA to the linked list.
 *
 * Usage/Warnings:  Routine does NOT do full memory error trapping and the
 *                  insert message to the l-box is not error checked.
 *
 * Calls: EditEAValue()
 */

BOOL AddEA(HWND hwnd)
{
   HOLDFEA  *pFEA=pHoldFEA;  /* Points to the beginning of the EA list */
   HOLDFEA  *pNewFEA;        /* Used to temporarily hold the new EA    */
   PASSDATA PData;

   if(!FILE_ISOPEN)
      return(FALSE);

   if(!WinDlgBox(HWND_DESKTOP,         /* get new EA name and type */
                 hwnd,
                 AddEAProc,
                 (HMODULE)NULL,
                 IDD_ADDEA,
                 NULL))
      return(FALSE);                   /* they said cancel */

   GetMem(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 */

   GetMem(pNewFEA->szName,pNewFEA->cbName+1);  /* Name returned in szEAName */
   strcpy(pNewFEA->szName,strupr(szEAName));
   pNewFEA->aValue = NULL;
   pNewFEA->next   = NULL;

   if(pHoldFEA == NULL)           /* 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 = NULL;  /* Disconnect the partial new EA */
      else
         pHoldFEA = NULL;    /* The new EA was the first one  */

      FreeMem(pNewFEA->szName,pNewFEA->cbName+1);
      FreeMem(pNewFEA,sizeof(HOLDFEA));

      return(FALSE);
   }

   WinSendDlgItemMsg(hwnd, IDD_LBOX, LM_INSERTITEM, /* Insert name in L-Box */
                     MPFROM2SHORT(LIT_END,0),
                     MPFROMP(pNewFEA->szName));

   return(TRUE);
}


/*
 * Function name: QueryEAs()
 *
 * Parameters:  hwnd which is the current window handle.
 *              pszPath points to the path of the file to grab EAs from.
 *
 * Returns: TRUE iff it successfully reads in the EAs.  Upon exit, global
 *          pHoldFEA points to a linked list of the EAs for the current file.
 *
 * Purpose: Call DOS to Query a file's EA names and values.
 *
 * Usage/Warnings:  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.
 *
 * Calls: Free_FEAList(), CheckEAIntegrity()
 */

BOOL QueryEAs(HWND hwnd,CHAR *pszPath)
{
   CHAR *pAlloc;       /* Holds the FEA struct returned by DosEnumAttribute */
                       /* also used to create the GEALIST for DosQPathInfo  */
   CHAR *pBigAlloc;    /* Temp buffer to hold each EA as it is read in      */
   USHORT cbBigAlloc;  /* 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;     /* Points to last EA added, so new EA can link     */
   HOLDFEA *pNewFEA;     /* Struct to build the new EA in                   */

   FEA *pFEA;            /* Used to read from Enum's return buffer          */
   GEALIST *pGEAList;    /* Ptr used to set up buffer for DosQPathInfo call */
   EAOP eaopGet;         /* Used to call DosQPathInfo                       */

   GetMem((HHEAP) pAlloc,(USHORT) MAX_GEA);     /* Allocate enough room for any GEA List     */
   pFEA = (FEA *) pAlloc;      /* pFEA always uses pAlloc buffer            */

   pHoldFEA = NULL;            /* 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,   */
                          pAlloc,              /* so DosQPathInfo must be */
                          MAX_GEA,             /* called to get it.       */
                          &ulEnumCnt,
                          (LONG) GetInfoLevel1,
                          0L))
        break;                         /* There was some sort of error    */

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

      ulEntryNum++;

      GetMem(pNewFEA,sizeof(HOLDFEA));

      if (pNewFEA == NULL)             /* Out of memory */
      {
         FreeMem(pAlloc,MAX_GEA);
         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 = NULL;

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

      if (!pNewFEA->szName || !pNewFEA->aValue) /* Out of memory */
      {
         if(pNewFEA->szName)
            FreeMem(pNewFEA->szName,pFEA->cbName+1);
         if(pNewFEA->aValue)
            FreeMem(pNewFEA->aValue,pFEA->cbValue);

         FreeMem(pAlloc, MAX_GEA);
         FreeMem(pNewFEA,sizeof(HOLDFEA));

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

      cbBigAlloc = sizeof(FEALIST) + pNewFEA->cbName+1 + pNewFEA->cbValue;
      GetMem(pBigAlloc,cbBigAlloc);
      if (pBigAlloc == NULL)
      {
         FreeMem(pNewFEA->szName,pFEA->cbName+1);
         FreeMem(pNewFEA->aValue,pFEA->cbValue);
         FreeMem(pAlloc, MAX_GEA);
         FreeMem(pNewFEA,sizeof(HOLDFEA));
         Free_FEAList(pHoldFEA,pDelList);
         return (FALSE);
      }

      pGEAList = (GEALIST *) pAlloc;   /* Set up GEAList structure */

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

      eaopGet.fpGEAList = (GEALIST far *) pAlloc;
      eaopGet.fpFEAList = (FEALIST far *) pBigAlloc;

      eaopGet.fpFEAList->cbList = cbBigAlloc;

      DosQPathInfo(pszPath,            /* Get the complete EA info        */
                   GetInfoLevel3,
                   (PVOID) &eaopGet,
                   sizeof(EAOP),
                   0L);
      memcpy(pNewFEA->aValue,          /* Copy the value to HoldFEA       */
             pBigAlloc+sizeof(FEALIST)+pNewFEA->cbName+1,
             pNewFEA->cbValue);

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

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

      if(pHoldFEA == NULL)             /* 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(pAlloc, MAX_GEA);  /* Free up the GEA buf for DosEnum */
   return (TRUE);
}


/*
 * Function name: CheckEAIntegrity()
 *
 * Parameters:  aBuf points to the buffer to check for a valid EA.
 *              cbBuf is the allocated length of aBuf.
 *
 * Returns: TRUE iff 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/Warnings:  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.
 *
 * Calls: MultiTypeIndex()
 */

BOOL CheckEAIntegrity(CHAR *aBuf,USHORT 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 == (SHORT) cbBuf)
               return TRUE;
            else
               return FALSE;
         }
         else /* Single type, multi-value is not yet implemented */
         {
            return TRUE;
         }
      default:
         return FALSE;
   }
   return TRUE;
}


/*
 * Function name: Free_FEAList()
 *
 * Parameters:  pFEA points to the beginning of the linked list to be freed.
 *              pDList points to the beginning of the deleted linked list.
 *
 * Returns: VOID.  The two linked lists passed in are cleaned out though.
 *
 * 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/Warnings:  Note that NEAD 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.
 *
 * Calls:
 */

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,pFEA->cbName+1);
      if(pFEA->aValue)                         /* Free if non-NULL value */
         FreeMem(pFEA->aValue,pFEA->cbValue);

      FreeMem(pFEA,sizeof(HOLDFEA));           /* Free HoldFEA struct    */
      pFEA = next;
   }

   while(pDList)
   {
      Dnext = pDList->next;
      if(pDList->EAName)
         FreeMem(pDList->EAName,strlen(pDList->EAName)+1);
      FreeMem(pDList,sizeof(DELETELIST));
      pDList = Dnext;
   }
}


/*
 * Function name: LookupEAType()
 *
 * Parameters:  usType is tye EA type to be looked up.
 *
 * 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.
 *
 * Purpose: This routine takes EA type and returns an offset into ConvTable
 *          which points to an entry that describes the type passed in.
 *
 * Usage/Warnings:
 *
 * Calls:
 */

USHORT LookupEAType(USHORT usType)
{
   USHORT cnt;

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


/*
 * Function name: DeleteCurEA()
 *
 * Parameters:  hwnd is the current window handle.
 *
 * Returns: VOID.  Removes one item from global pHoldFEA list and adds one
 *          to global pDelList.
 *
 * 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/Warnings:  The memory allocation routines are NOT fully error trapped.
 *
 * Calls: GetCurFEA()
 */

VOID DeleteCurEA(HWND hwnd)
{
   HOLDFEA *pFEA,*pFEAPrev;
   DELETELIST *pDL,*pDLcnt;  /* Utility ptrs for manipulating the Del list */
   LONG lOffset;

   pFEA = GetCurFEA(hwnd,pHoldFEA);    /* Gets a pointer to item to delete */
   if (pFEA == NULL)
      return;

   /* These two allocations should be checked for out of memory */
   GetMem(pDL,sizeof(DELETELIST));     /* Add Name to Delete List */
   GetMem(pDL->EAName,pFEA->cbName+1);
   strcpy(pDL->EAName,pFEA->szName);
   pDL->next = NULL;

   if(pDelList == NULL)        /* 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;
   }

   lOffset = (LONG) WinSendDlgItemMsg(hwnd, IDD_LBOX,
                                     LM_QUERYSELECTION,0,0);
   WinSendDlgItemMsg(hwnd, IDD_LBOX,
                     LM_DELETEITEM,MPFROMSHORT((SHORT) lOffset),0L);

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

      while(--lOffset)              /* Find previous EA */
         pFEAPrev = pFEAPrev->next;

      pFEAPrev->next = pFEA->next;
   }

   FreeMem(pFEA->szName,pFEA->cbName+1);  /* Release the memory */
   FreeMem(pFEA->aValue,pFEA->cbValue);
   FreeMem(pFEA,sizeof(HOLDFEA));

   FILE_CHANGED = TRUE;
}


/*
 * Function name: CurEAType()
 *
 * Parameters:  pFEA points to the FEA struct to use.
 *
 * Returns: The EA type.
 *
 * 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/Warnings:  Assumes a valid HoldFEA struct.
 *
 * Calls:
 */

USHORT 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);
}


/*
 * Function name: GetUSHORT()
 *
 * Parameters:  pFEA points to the FEA struct to use.
 *              index is an offset of the USHORT to be returned
 *
 * Returns: The appropriate USHORT from the aValue field
 *
 * Purpose: This routine returns the nth USHORT value in the aValue member
 *          of pFEA using index as the offset.
 *
 * Usage/Warnings:  Assumes a valid HoldFEA struct and that index doesn't
 *                  point outside the aValue buffer.
 *
 * Calls:
 */

USHORT GetUSHORT(HOLDFEA *pFEA,USHORT index)
{
   USHORT *pusType;

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


/*
 * Function name: WriteEAs()
 *
 * Parameters:  hwnd is the current window handle used only by memory
 *              allocation error handling.
 *
 * Returns: VOID.  But cleans out the pDelList linked list.
 *
 * 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 out to disk.
 *
 * Usage/Warnings:  NOTE:  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.
 *
 * Calls:
 */

VOID WriteEAs(HWND hwnd)
{
   DELETELIST *pDL = pDelList,*pDLnext;
   HOLDFEA    *pHFEA= pHoldFEA;
   EAOP       eaopWrite;
   CHAR       aBuf[MAX_GEA],*aPtr;
   FEA        *pFEA = (FEA *) &aBuf[sizeof(ULONG)];
   USHORT     usRet,usMemNeeded;
   ULONG      *pulPtr=(ULONG *) aBuf; /* Initally points to top of FEALIST */

   if(!FILE_ISOPEN || !FILE_CHANGED) /* Don't write unless it's necessary */
      return;

   eaopWrite.fpFEAList = (FEALIST 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(FEALIST) + pFEA->cbName+1; /* +1 for NULL */
      strcpy(aBuf+sizeof(FEALIST),pDL->EAName);

      usRet=DosSetPathInfo(szFileName,    /* Delete EA's by saying cbValue=0 */
                           SetInfoLevel2,
                           (PVOID) &eaopWrite,
                           (USHORT) sizeof(EAOP),
                           DSPI_WRTTHRU,
                           0L);

      pDLnext = pDL->next;                   /* Temp hold next pDL         */
      FreeMem(pDL->EAName, pFEA->cbName+1);  /* Free up current Del struct */
      FreeMem(pDL, sizeof(DELETELIST));
      pDL = pDLnext;                         /* Set pDL to saved value     */
   }
   pDelList = NULL;                          /* DelList is now empty       */

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

      eaopWrite.fpFEAList = (FEALIST far *) aPtr; /* Fill in eaop struct */
      eaopWrite.fpFEAList->cbList = usMemNeeded;

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

      strcpy(aPtr + sizeof(FEALIST),  pHFEA->szName);
      memcpy(aPtr + sizeof(FEALIST) + pHFEA->cbName+1,
             pHFEA->aValue, pHFEA->cbValue);

      usRet=DosSetPathInfo(szFileName,             /* Write out the EA */
                           SetInfoLevel2,
                           (PVOID) &eaopWrite,
                           (USHORT) sizeof(EAOP),
                           DSPI_WRTTHRU,0L);

      FreeMem(aPtr,usMemNeeded);       /* Free up the FEALIST struct */

      pHFEA = pHFEA->next;
   }

   FILE_CHANGED = FALSE;
}


/*
 * Function name: EditEAValue()
 *
 * Parameters:  hwnd is the current window handle.
 *              pPDat is a pointer to PassData which contains Edit EA info.
 *
 * Returns: TRUE iff the edit was successful.
 *
 * 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/Warnings:  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 in
 *                  to 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.
 *
 * Calls: MultiTypeIndex(), ChangeName(), AsciiEditProc() (thru PM),
 *        MultiTypeProc() (thru PM).
 */

BOOL EditEAValue(HWND hwnd, PASSDATA *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,*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 */
   PDat.usIndex = pPDat->cbMulti ? 1 : 0;   /* =1 if there is a multi  */
   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=NULL;


         if(!WinDlgBox(HWND_DESKTOP,        /* Do an ascii text edit */
                       hwnd,
                       AsciiEditProc,
                       (HMODULE) NULL,
                       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(hwnd,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 */
            {
               usSize = strlen(szScratch)-strlen(szAscii); /* Change in size */

               if(usSize > 0) /* The new string is longer */
               {
                  ResizeMem(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 /* The new string is shorter */
               {
                  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));
                  ResizeMem(pFEA->aValue,  /* Shrink the EA buffer */
                            pFEA->cbValue,
                            pFEA->cbValue+usSize);
               }
               szTrash=MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
                                      pPDat->usIndex);
               strcpy(szTrash+sizeof(USHORT),szScratch); /* Copy in new val */
               pFEA->cbValue+=usSize;              /* Change buffer count   */

               return(FILE_CHANGED = TRUE);  /* Done with m-m edit */
            }
            else
            {
               FreeMem(pFEA->aValue,pFEA->cbValue); /* Release old Value mem */
            }
         }
         GetMem(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= strlen(szScratch)+3;

         return(FILE_CHANGED = TRUE);

      case EA_LPBINARY:
      case EA_LPASCII:
      case EA_LPMETAFILE:
         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 */
            {
               GetMem(szAscii,usSize+1); /* Set up inital value for edit */
               memcpy(szAscii,szTrash+2*sizeof(USHORT),usSize);
               szAscii[usSize]=0;
            }
            else                       /* No inital value */
               szAscii = NULL;
         }
         else if(pFEA->cbValue)
         {
            usSize=GetUSHORT(pFEA,1);  /* Get size and set inital value */
            if(usSize)
            {
               GetMem(szTrash,usSize+1); /* +1 for null */
               memcpy(szTrash,pFEA->aValue+4,usSize);
               szTrash[usSize]=0;
               szAscii=szTrash;
            }
            else
               szAscii = NULL;
         }
         else
            szAscii = NULL;

         if(!WinDlgBox(HWND_DESKTOP,        /* Do an ascii text edit */
                       hwnd,
                       AsciiEditProc,
                       (HMODULE) NULL,
                       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,strlen(szAscii)+1); /* +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(hwnd,pFEA,szEAName);

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

            if(pPDat->cbMulti)   /* Do the whole thing here is multi-type */
            {
               USHORT usDelta = strlen(szScratch) - usSize; /* Change in len */
               if(usDelta > 0) /* The new string is longer, resize first */
               {
                  ResizeMem(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  /* move first, resize afterwards */
               {
                  szTrash=MultiTypeIndex(pFEA->aValue + pPDat->usMultiOffset,
                                         pPDat->usIndex+1);
                  memmove(szTrash+usDelta,szTrash,
                          pFEA->cbValue-(szTrash-pFEA->aValue));
                  ResizeMem(pFEA->aValue,pFEA->cbValue,pFEA->cbValue+usDelta);
               }
               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 */

              return(FILE_CHANGED = TRUE);
            }

            FreeMem(pFEA->aValue,pFEA->cbValue); /* Free up old value */
         }
         GetMem(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 */

         return(FILE_CHANGED = TRUE);

      case EA_MVMT:                    /* It's multi-value multi-type */
         if(pFEA->cbValue == 0) /* It's a new EA */
         {
            GetMem(pFEA->aValue,3*sizeof(USHORT)); /* Allocate empty m-m EA */
            pFEA->cbValue = 3*sizeof(USHORT);
            pusPtr      = (USHORT *) pFEA->aValue;
            *pusPtr = 0xffdf;                 /* Multi-value, multi-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 = szNew - pFEA->aValue;
            PDat.cbMulti       = szTrash - szNew;
         }
         else
         {
            PDat.usMultiOffset = 0;
            PDat.cbMulti       = pFEA->cbValue;
         }
         PDat.Point         = (CHAR *) pFEA;

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

   }
}


/*
 * Function name: EAExists()
 *
 * Parameters:  szEAName points to the EA Name to check for.
 *
 * Returns: TRUE iff an EA with a name matching szEAName exists.
 *
 * Purpose: This routine goes through the linked list pointed to by global
 *          pHoldFEA and determines whether or not an EA of the passed name
 *          already exists.
 *
 * Usage/Warnings:  The comparison is NOT case sensitive.
 *
 * Calls:
 *
 */

BOOL EAExists(CHAR *szEAName)
{
   HOLDFEA *phFEA=pHoldFEA;

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


/*
 * Function name: ChangeName()
 *
 * Parameters:  hwnd is the current window used for error messages.
 *              pFEA points to the current FEA.
 *              szName points to the new EA name.
 *
 * Returns: VOID.  Fixes up the pFEA structure and global pDelList.
 *
 * 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/Warnings:  NOTE:  Not all the memory allocations are fully error
 *                         trapped.
 *
 * Calls:
 *
 */

VOID ChangeName(HWND hwnd,HOLDFEA *pFEA,CHAR *szName)
{
   CHAR *szTemp;
   DELETELIST *pDL;

   GetMem(szTemp,strlen(szName+1));  /* Allocate space for new name */
   if(!szTemp)
      return;

   GetMem(pDL,(USHORT) sizeof(DELETELIST)); /* Allocate a new delete struct  */
   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;
}


/*
 * Function name: MultiTypeIndex()
 *
 * Parameters:  pMulti points to the current m-m field.
 *              usIndex is the field the caller is interested in.
 *
 * Returns: a pointer to the field specified by the usIndex param.
 *
 * 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/Warnings:  NOTE:  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.
 *
 * Calls: LookupEAType, MultiTypeIndex()
 *
 */

CHAR *MultiTypeIndex(CHAR *pMulti, USHORT usIndex)
{
   USHORT *pusPtr;
   USHORT usOffset;

   pMulti += 3*sizeof(USHORT);  /* skip over 0xffdf, codepage, and field cnt */

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

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

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

         case IDD_LPDATA:
            pusPtr = (USHORT *) pMulti;                /* Get the length */
            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;
            }
            /* Not yet implemented for Multi-valued single-type stuff... */
            break;
      }
   }
   return(pMulti);
}


/*
 * Function name: EAValueString()
 *
 * Parameters:  hwnd is the current window handle.
 *              aEAVal is a pointer to an EA value field.
 *
 * Returns: a pointer to an ASCII description of the field value.
 *
 * Purpose: This routine takes a pointer to an EA Value (i.e. starting with
 *          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/Warnings:  NOTE:  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.
 *
 * Calls:
 *
 */

CHAR *EAValueString(HWND hwnd,CHAR *aEAVal)
{
   USHORT *pusPtr= (USHORT *) aEAVal;  /* Points to EA Type     */
   CHAR *szRet,*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(szRet,strlen(aEAVal)+1);
            strcpy(szRet,aEAVal);
         }
         else
         {
            GetMem(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(szRet,*pusPtr +1);
            strncpy(szRet,aEAVal,*pusPtr);
            szRet[*pusPtr]=0;
         }
         else
         {
            GetMem(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(szRet,strlen(szTemp)+1); /* Copy string from static to dynamic */
   strcpy(szRet,szTemp);
   return(szRet);
}


/*
 * Function name: MultiAdd()
 *
 * Parameters:  hwnd is the current window handle.
 *              pFEA points to the current FEA.
 *              pPDat gives the current m-m data.
 *
 * Returns: VOID.  Modifies the current pFEA.
 *
 * Purpose: This routine is called by MultiTypeProc and handles the addition
 *          of a subvalue to a multi-value, multi-type EA.
 *
 * Usage/Warnings:  NOTE:  Not all GetMem's are totally error trapped.
 *                  It is also possible that the add to the listbox could fail.
 *
 * Calls: AddEAProc() (thru PM), MultiTypeIndex()
 *
 */

VOID MultiAdd(HWND hwnd, HOLDFEA *pFEA,PASSDATA FAR *pPDat)
{
   USHORT   usSize;
   USHORT   *pusPtr;
   CHAR     aUtility[6];      /* Used to hold the header for all EA types */
   CHAR     *pInsert,*pValue;
   PASSDATA PDat;

   PDat.Point = pFEA->szName;

   if(!WinDlgBox(HWND_DESKTOP,        /* Get the name and type */
                 hwnd,
                 AddEAProc,
                 (HMODULE) NULL,
                 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(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));
      ResizeMem(pFEA->aValue,pFEA->cbValue,pFEA->cbValue-usSize);
      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(hwnd,pInsert);   /* Add new field to the list box */

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

   FILE_CHANGED = TRUE;
}


[ RETURN TO DIRECTORY ]