/*static char *SCCSID = "@(#)feaop.c	6.1 90/11/17";*/
/*static char *SCCSID = "@(#)feaop.c  6.8 90/08/06";*/

/************************************************************************
 *
 * FEAOP.C - Pack and unpack EAOPs
 *
 ************************************************************************/

#define INCL_DOS
#define INCL_32
#define INCL_NOPMAPI
#include <os2.h>
#include <assert.h>
#include "align32.h"

#define ROUNDUP(a)	((a)+3L & ~3L)


/*** PackFEA -- pack FEAList.
 *
 *  preprocessor for aligned data API.
 *
 *  ENTRY:	FEA2List
 *
 *  EXIT:	FEAList
 *
 *  ARGUMENTS:	pFEA2List -- pointer to GEAList to pack.
 *
 *  RETURNS:	0
 *
 *  CALLS:
 *
 *  WARNINGS:	none.
 *
 *  PSEUO-CODE:
 *	if (cbList > 64Kb)
 *	    error
 *	src = pFEA2List + sizeof( pFEA2List->cbList )
 *	dest = pFEAList + sizeof( pFEAList->cbList )
 *	cbList = pFEA2List->cbList
 *	while (cbList) {
 *	    size = src->oNextEntry
 *	    cbEntry = src->cbName + src->cbValue + 1
 *	    memmove( dest->szName, src->szName, cbEntry );
 *	    dest->cbName = src->cbName
 *	    dest->cbValue = src->cbValue
 *	    dest->fEA = src->fEA
 *	    cbList -= size
 *	    src += size;
 *	    dest += sizeof( GEA ) + dest->cbName;
 *	}
 */

int PackFEA( PFEA2LIST pFEA2List ) {
    ULONG   cbList, cbSize;
    PFEA2   src;
    PFEA    dest;
    int	    cbEntry;

    src = pFEA2List->list;
    dest = ((PFEALIST)pFEA2List)->list;
    cbList = pFEA2List->cbList;

    /* If the list is empty, don't pack it. */
    if (cbList == sizeof (pFEA2List->cbList))
	return 0;

    /* If cbList is changed from a USHORT to a ULONG, then the ULONG
     * might not fit into the USHORT while packing.
     */

#if 0
    /* the DCR says that a GEALIST.cbList is a USHORT, but the header
     * says it's a ULONG */
    if (cbList > 65535) {
	return ERROR_BUFFER_OVERFLOW;
    }
#endif
    if (cbList == sizeof(ULONG)) {
	/* Nothing to pack, return */
	return 0;
    }
    cbSize = 1;
    while (cbList > sizeof(ULONG) && cbSize) {
	cbSize = src->oNextEntryOffset;
	cbEntry = src->cbName + src->cbValue + 1;   /* for the trailing \0 */
	dest->fEA = src->fEA;
	dest->cbValue = src->cbValue;
	dest->cbName = src->cbName;
	memmove( ((PBYTE)dest+sizeof(FEA)), src->szName, cbEntry );
	cbList -= cbSize;
	(PBYTE)src += cbSize;
	(PBYTE)dest += sizeof( FEA ) + cbEntry;
    }
    ((PFEALIST)pFEA2List)->cbList = (PBYTE)dest - (PBYTE)pFEA2List;
    return 0;
}


/*** UnpackFEA -- unpack an FEAList
 *
 *  postprocessor for aligned data API.
 *
 *  ENTRY:	FEAList
 *
 *  EXIT:	FEA2List
 *
 *  ARGUMENTS:	pFEAList  -- pointer to FEAList to unpack.
 *		pFEA2List -- pointer to dest. FEA2List
 *		cbMaxSize -- max. size of GEAList
 *		poError	  -- pointer to offError to fix, NULL if no error.
 *
 *  RETURNS:	TRUE if all EAs fit
 *		FALSE if it truncated EAs
 *
 *  CALLS:	memmove
 *
 *  WARNINGS:	May truncate EAs that don't fit.
 *
 *  NOTE:	assert( pFEAList <= PFEA2List );
 *
 *  PSEUO-CODE:
 *	src = pFEAList
 *	cbPrev = src->cbValue + src->cbName
 *	src->cbValue = 0
 *	src += sizeof( FEA ) + cbPrev
 *	total = ROUNDUP( sizeof( GEA2 ) + cbPrev )
 *	while ( src < pGEA2List + cbList && total < realsize )
 *	    tmp = src->cbValue + src->cbName
 *	    src->cbValue = cbPrev
 *	    cbPrev = tmp
 *	    src += sizeof( FEA ) + tmp
 *	    total += ROUNDUP( sizeof( FEA2 ) + tmp )
 *
 *	src -= sizeof( FEA ) + tmp
 *	dest = pFEA2List + total
 *	cEAs = 0
 *
 *	// Now src points to the start of the last FEA
 *	// and dest points to the end of the last FEA2.
 *	// cbPrev still has the cbValue of the last FEA
 *
 *	do
 *	    dest -= ROUNDUP( sizeof( FEA2 ) + cbPrev )
 *	    tmp = cbPrev
 *	    cbPrev = src->cbValue
 *
 *	    // now copy and unpack
 *	    memmove( dest->szName, src->szName, tmp )
 *	    dest->cbValue = tmp - src->cbName
 *	    dest->cbName = src->cbName
 *	    dest->fEA = 0
 *	    dest->oNext = ROUNDUP( sizeof( FEA2 ) + cbPrev )
 *
 *	    src -= sizeof( FEA ) + cbPrev
 *	    ++cEAs
 *	while ( src->cbValue )
 */

BOOL UnpackFEA( PFEALIST pFEAList, PFEA2LIST pFEA2List, ULONG cbMaxSize, PULONG poError ) {
    PFEA pSrc;
    PFEA pEnd;		    /* end of FEA list */
    PFEA2 pDest;
    ULONG cbPrev;	    /* size of previous FEA */
    ULONG cbTmp;
    ULONG cbTotal;	    /* size of the new FEA2List */
    ULONG cEAs;
    BOOL  fAllFit;	    /* TRUE if all EAs fit */

    fAllFit = TRUE;
    /* If the list is empty, don't unpack it. */
    if (pFEAList->cbList == sizeof (pFEA2List->cbList))
	return TRUE;

    pSrc = pFEAList->list;
    pEnd = (PFEA) ((PBYTE)pFEAList + pFEAList->cbList);
    cbPrev = pSrc->cbName + pSrc->cbValue + 1;
    pSrc->cbValue = 0;	     /* flag first entry in array */
    (PBYTE)pSrc += sizeof( FEA ) + cbPrev;
    cbTotal = ROUNDUP( sizeof( FEA2 ) + cbPrev - 1);

    while ( pSrc < pEnd && cbTotal < cbMaxSize ) {
	cbTmp = pSrc->cbValue + pSrc->cbName + 1;
	pSrc->cbValue = (USHORT) cbPrev;
	cbPrev = cbTmp;
	(PBYTE)pSrc += sizeof( FEA ) + cbPrev;
	cbTotal += ROUNDUP( sizeof( FEA2 ) + cbPrev - 1 );  // less 1 because FEA2 has room for the \0
    }
    pDest = (PFEA2) ((PBYTE)pFEA2List->list + cbTotal);
    cEAs = 0;

    // Now src points to the end of the last FEA
    // and dest points to the end of the last FEA2.
    // cbPrev still has the cbName of the last FEA

    if (pSrc < pEnd) {
	/* EAs will be truncated */
	fAllFit = FALSE;
    }
    do {
	(PBYTE)pDest -= ROUNDUP( sizeof( FEA2 ) + cbPrev - 1);
	(PBYTE)pSrc -= sizeof( FEA ) + cbPrev;
	cbTmp = cbPrev;
	cbPrev = pSrc->cbValue;

	memmove( pDest->szName, ((PBYTE)pSrc+sizeof(FEA)), cbTmp );
	pDest->cbValue = cbTmp - pSrc->cbName - 1;
	pDest->cbName = pSrc->cbName;
	pDest->fEA = pSrc->fEA;
	// On the last FEA2, oNext is zero.
	pDest->oNextEntryOffset = (cEAs)?ROUNDUP(sizeof( FEA2 ) + cbTmp - 1) : 0;

	if (poError && *poError == (PBYTE)pSrc - (PBYTE)pFEAList) {
	    *poError = (PBYTE)pDest - (PBYTE)pFEA2List;
	    poError = NULL;
	}

	cEAs++;
    } while ( cbPrev );

    pFEA2List->cbList = cbTotal + sizeof (pFEA2List->cbList);
    return fAllFit;
}
