/*static char *SCCSID = "@(#)geaop.c	6.2 91/06/17";*/
/*static char *SCCSID = "@(#)geaop.c	6.2 91/06/17";*/

/************************************************************************
 *
 * GEAOP.C - Pack 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)


/*** PackGEA -- pack GEAList.
 *
 *  preprocessor for aligned data API.
 *
 *  ENTRY:	GEA2List
 *
 *  EXIT:	GEAList
 *
 *  ARGUMENTS:	pGEA2List -- pointer to GEAList to pack.
 *
 *  RETURNS:	0
 *
 *  CALLS:
 *
 *  WARNINGS:	none.
 *
 *  PSEUO-CODE:
 *	src = pGeaList + sizeof( pGeaList->cbList )
 *	dest = pGea2List + sizeof( pGea2List->cbList )
 *	cbList = pGea2List->cbList
 *	while (cbList) {
 *	    size = src->oNextEntry
 *	    cbName = dest->cbName = src->cbName
 *	    cbName++
 *	    while (cbName--)
 *		dest->szName[cbName] = src->szName[cbName];
 *	    cbList -= size
 *	    src += size;
 *	    dest += sizeof( GEA ) + dest->cbName;
 *	}
 */

int PackGEA( PGEA2LIST pGEA2List, PVOID pTMP) {
    ULONG   cbList, cbSize;
    PGEA2   src;
    PGEA    dest;
    BYTE    cbName;

    src = pGEA2List->list;
    dest = ((PGEALIST)pTMP)->list;
    cbList = pGEA2List->cbList;

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

    /* Check to see if GEA2List->list is long word aligned */
    cbSize = 1;
    while ( cbSize )
    {
	      cbSize = src->oNextEntryOffset;
	      (PBYTE)src += cbSize;

          if ( ((ULONG)src % 4) != 0)
             return 11;         /* If not dword aligned return invalid format */

    }

    src = pGEA2List->list;     /* Reset source pointer */
    cbSize = 1;
    while (cbSize) {
	cbSize = src->oNextEntryOffset;
	dest->cbName = cbName = src->cbName;
	memmove( dest->szName, src->szName, cbName+1 );	    /* for the trailing \0 */
	cbList -= cbSize;
	(PBYTE)src += cbSize;
	(PBYTE)dest += sizeof( GEA ) + cbName;
    }
    ((PGEALIST)pTMP)->cbList = (PBYTE)dest - (PBYTE)pTMP;
    return 0;
}

/*** UnpackGEA -- unpack a GEAList
 *
 *  postprocessor for aligned data API.
 *
 *  ENTRY:	GEAList
 *
 *  EXIT:	GEA2List
 *
 *  ARGUMENTS:	pGEAList -- pointer to GEAList to unpack.
 *
 *  RETURNS:	number of GEA2s in the list
 *
 *  CALLS:	memmove()
 *
 *  WARNINGS:	May overflow buffer.  see note.
 *
 *  NOTE:	Since UnpackGEA is called only on buffers that had been
 *		packed with PackGEA, no checking is done to see that the
 *		unpacked data fits into the buffer.
 *
 *  BUGBUG!	Maybe I can save cbTotal from before packing, then not
 *		have to compute it when unpacking.
 *
 *  PSEUO-CODE:
 *	src = pGEAList
 *	cbPrev = src->cbName
 *	src->cbName = 0
 *	src += sizeof( GEA ) + cbPrev
 *	total = ROUNDUP( sizeof( GEA2 ) + cbPrev )
 *	while ( src < pGEA2List + cbList && total < realsize )
 *	    tmp = src->cbName
 *	    src->cbName = cbPrev
 *	    cbPrev = tmp
 *	    src += sizeof( GEA ) + tmp
 *	    total += ROUNDUP( sizeof( GEA2 ) + tmp )
 *
 *	src -= sizeof( GEA ) + tmp
 *	dest = pGEA2List + total
 *	cEAs = 0
 *
 *	// Now src points to the start of the last GEA
 *	// and dest points to the end of the last GEA2.
 *	// cbPrev still has the cbName of the last GEA
 *
 *	do
 *	    dest -= ROUNDUP( sizeof( GEA2 ) + cbPrev )
 *	    tmp = cbPrev
 *	    cbPrev = src->cbName
 *	    dest->cbName = tmp
 *	    memmove( dest->szName, src->szName, tmp )
 *	    src -= sizeof( GEA ) + cbPrev
 *	    ++cEAs
 *	while ( src->cbName )
 */

ULONG UnpackGEA( PGEALIST pGEAList, PULONG poError ) {
    PGEA pSrc;
    PGEA pEnd;		    /* end of GEA list */
    PGEA2 pDest;
    ULONG cbPrev;	    /* size of previous GEA */
    ULONG cbTmp;
    ULONG cbTotal;	    /* size of the new GEA2List */
    ULONG cEAs;

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

    pSrc = pGEAList->list;
    pEnd = (PGEA) ((PBYTE)pGEAList + pGEAList->cbList);
    cbPrev = pSrc->cbName ;
    pSrc->cbName = 0;	    /* flag first entry in array */
    (PBYTE)pSrc += sizeof( GEA ) + cbPrev;
    cbTotal = ROUNDUP( sizeof( GEA2 ) + cbPrev );

    while ( pSrc < pEnd ) {
	cbTmp = pSrc->cbName;
	pSrc->cbName = (BYTE) cbPrev;
	cbPrev = cbTmp;
	(PBYTE)pSrc += sizeof( GEA ) + cbPrev ;
	cbTotal += ROUNDUP( sizeof( GEA2 ) + cbPrev);
    }
    pDest = (PGEA2) ((PBYTE)pGEAList->list + cbTotal);
    cEAs = 0;

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

    do {
	(PBYTE)pDest -= ROUNDUP( sizeof( GEA2 ) + cbPrev);
	(PBYTE)pSrc -= sizeof( GEA ) + cbPrev ;
	cbTmp = cbPrev;
	cbPrev = pSrc->cbName;
	memmove( pDest->szName, pSrc->szName, cbTmp + 1);
	pDest->cbName = (BYTE)cbTmp;
	// On the last GEA2, oNext is zero.
	pDest->oNextEntryOffset = (cEAs)?ROUNDUP(sizeof( GEA2 ) + cbTmp) : 0;

	if (poError && *poError == (ULONG)((PBYTE)pSrc - (PBYTE)pGEAList)) {
	    *poError = (PBYTE)pDest - (PBYTE)pGEAList;
	    poError = NULL;
	}

	cEAs++;
    } while ( cbPrev );

    ((PGEA2LIST)pGEAList)->cbList = cbTotal + sizeof (((PGEA2LIST)pGEAList)->cbList);
    return cEAs;
}

/*** memmove - move memory
 *
 *
 *  ARGUMENTS:	pDst   -> pointer to destination
 *		pSrc   -> pointer to source
 *		cBytes -> number of bytes to move
 *
 *  RETURNS:	Nothing
 *
 *  NOTES:	Takes care of overlapping memory.
 *
 */
void memmove(PVOID pDst, PVOID pSrc, ULONG cBytes) {

    PBYTE   pDEnd  = (PBYTE)pDst + cBytes;
    PBYTE   pSEnd  = (PBYTE)pSrc + cBytes;
    ULONG   cLWords = cBytes / sizeof(ULONG);
    USHORT  cByte  = (USHORT)(cBytes % sizeof(ULONG));

    if ((pDst > pSrc) && (pDst < pSEnd)) {
	/* Overlap, start from end */

	while (cLWords--)
	    *(--(PULONG)pDEnd) = *(--(PULONG)pSEnd);

	while (cByte--)
	    *(--(PBYTE)pDEnd) = *(--(PBYTE)pSEnd);


    } else {
	/* Normal case, copy from beginning */

	while (cLWords--)
	    *((PULONG)pDst)++ = *((PULONG)pSrc)++;

	while (cByte--)
	    *((PBYTE)pDst)++ = *((PBYTE)pSrc)++;
    }
}
