/************************************************************* 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;cntEAName,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)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; }