/*static char *SCCSID = "@(#)ffnwalk.c	6.1 90/11/17";*/
/* STOP_LISTING */

#define INCL_DOS
#define INCL_32
#define INCL_NOPMAPI
#define INCL_ERRORS
#pragma optimize("",off)	 // Depending on the whim of the compilier, this might be required
#include <os2.h>
#include <limits.h>
#include <string.h>
#include <os2defp.h>
#include <pgexport.h>

#include <ffn.h>

/* START_LISTING */

/* Three pseudo-variables.  The source buffer can be three types, but I
 * want one variable to referance them.
 */

#define pffbSrc	    ((MYFFB  *)pvSrc)
#define pffb2Src    ((MYFFB2 *)pvSrc)
#define pffb3Src    ((MYFFB3 *)pvSrc)

/*** FFNWalk	-- Walks a buffer after a call to FindFirst or FindNext,
 *		   preparing it for FFNAlign.
 *
 *  post- processor for aligned data API.
 *
 *  ENTRY:	packed buffer
 *
 *  EXIT:	partly aligned buffer, name of last entry saved
 *		This code walks the buffer, and prepares it to
 *		be unpacked at ring 3
 *
 *  ARGUMENTS:	cbBuf	    -- Size of the buffer to unpack into
 *		ulInfoLevel -- type of data to unpack
 *		pffndata    -- return info for unpacking
 *		pcFileNames -- pointer to count of filenames in buffer
 *
 *  RETURNS:	0
 *
 *  CALLS:	memmove
 *
 *  WARNINGS:	None.
 *
 *  PSEUDO-CODE:
 */

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

APIRET FFNWalk( FFNDATA *pffndata,
		PVOID	pfindbuf,
		PULONG	pcFileNames ) {
    PVOID   pvSrc;
    ULONG   cbTmp;
    ULONG   cbPrev;
    ULONG   cbTotal;
    ULONG   cbLastTotal;
    ULONG   cFiles;
    ULONG   cbBuf;
    PVOID   pvLastSrc;
    PSZ	    pszLastName;
    USHORT  ulInfoLevel;
    USHORT  rc;


    rc = 0;
    cbBuf = pffndata->cbBuffer;
    if (pffndata->ffnFlags & (USHORT)(FFN_EAS_DIDNT_FIT)) {

	/*  This is a level 3 request, but the packet contains level 2
	 *  structures. We will treat this as a level 2 request with a
	 *  few provisions because of the EAOP structure at the
	 *  beginning of the buffer.
	 */
	ulInfoLevel = 2;
    } else
	ulInfoLevel = pffndata->ulInfoLevel;

    switch ( ulInfoLevel ) {
	case 1: {
	    pvLastSrc = pffbSrc = pfindbuf;

	    // Mark the start of the list

	    cbPrev = pffbSrc->ffb.cchName;
	    pffbSrc->ffb.cchName = 0;

	    // increment pffbSrc to point to the next element in the list

	    // BUGBUG!! There should be a +1 on this, but the compilier doesn't think so
	    (PBYTE)pffbSrc += sizeof( MYFFB ) + cbPrev - CCHMAXPATHCOMP + 1;
	    cbTotal = ROUNDUP( sizeof( FFBU ) + cbPrev - CCHMAXPATHCOMP + 1);

	    // Check to see how well this will fit
	    if (cbBuf < cbTotal)
		return ERROR_BUFFER_OVERFLOW;

	    // now pffbSrc points to the second element in the list, and cbTotal
	    // contains the size of the first element in the unpacked list.
	    // and cbPrev contains the variable-sized portion of this element
	    // for level 1 and 2

	    // walk through the rest of the list, back-linking it.

	    for( cFiles = 1; cFiles < *pcFileNames && cbTotal <= cbBuf; cFiles++ ) {
		pvLastSrc = pffbSrc;
		cbLastTotal = cbTotal;

		cbTmp = cbPrev;
		cbPrev = pffbSrc->ffb.cchName;
		pffbSrc->ffb.cchName = cbTmp;

		// increment pffbSrc to point to the next element in the list

		// BUGBUG!! There should be a +1 on this, but the compilier doesn't think so
		(PBYTE)pffbSrc += sizeof( MYFFB ) + cbPrev - CCHMAXPATHCOMP + 1;
		cbTotal += ROUNDUP( sizeof( FFBU ) + cbPrev - CCHMAXPATHCOMP + 1);
	    }

	    // Now pffbSrc points to the end of the last FFB, and cbTotal
	    // contains the size of the unpacked buffer, and cbPrev
	    // holds the total of the variable sized portion of the
	    // last FFB

	    // If cbTotal > cbBuf, then we need to remove the last entry in
	    // the list.

	    if (cbTotal > cbBuf) {
		cFiles--;
		cbTotal = cbLastTotal;
		pffbSrc = pvLastSrc;

		cbPrev = pffbSrc->ffb.cchName;
		pvLastSrc = (PBYTE)pffbSrc - (sizeof( MYFFB ) + cbPrev - CCHMAXPATHCOMP + 1);
	    }
	    pszLastName = ((MYFFB*)pvLastSrc)->ffb.achName;
	    *pcFileNames = cFiles;
	}
	    break;
	case 2: {

	   if (pffndata->ffnFlags & FFN_EAS_DIDNT_FIT)
		pvLastSrc = pffb2Src = (MYFFB2 *)((PBYTE)pfindbuf + sizeof( EAOP ));
	    else
		pvLastSrc = pffb2Src = pfindbuf;
	    // Mark the start of the list

	    cbPrev = pffb2Src->ffb.cchName;
	    pffb2Src->ffb.cchName = 0;

	    // increment pffb2Src to point to the next element in the list

	    // BUGBUG!! I should have to add 1 to the next expression, but it seems
	    // that the compilier is doing it for me.
	    (PBYTE)pffb2Src += sizeof( MYFFB2 ) + cbPrev - CCHMAXPATHCOMP;
	    cbTotal = ROUNDUP( sizeof( FFB2U ) + cbPrev - CCHMAXPATHCOMP + 1);
	    if (pffndata->ffnFlags & FFN_EAS_DIDNT_FIT)
		cbTotal += sizeof(EAOP2);

	    // Check to see how well this will fit

	    if (cbBuf < cbTotal)
		return ERROR_BUFFER_OVERFLOW;

	    // now pffb2Src points to the second element in the list, and cbTotal
	    // contains the size of the first element in the unpacked list.
	    // and cbPrev contains the variable-sized portion of this element
	    // for level 1 and 2

	    // walk through the rest of the list, back-linking it.

	    for( cFiles = 1; cFiles < *pcFileNames && cbTotal <= cbBuf; cFiles++ ) {
		pvLastSrc = pffb2Src;
		cbLastTotal = cbTotal;

		cbTmp = cbPrev;
		cbPrev = pffb2Src->ffb.cchName;
		pffb2Src->ffb.cchName = cbTmp;

		// increment pffb2Src to point to the next element in the list

		// BUGBUG!! I should have to add 1 to the next expression, but it seems
		// that the compilier is doing it for me.
		(PBYTE)pffb2Src += sizeof( MYFFB2 ) + cbPrev - CCHMAXPATHCOMP;
		cbTotal += ROUNDUP( sizeof( FFB2U ) + cbPrev - CCHMAXPATHCOMP+1);
	    }

	    // Now pffb2Src points to the end of the last FFB, and cbTotal
	    // contains the size of the unpacked buffer, and cbPrev
	    // holds the total of the variable sized portion of the
	    // last FFB

	    // If cbTotal > cbBuf, then we need to remove the last entry in
	    // the list.

	    if (cbTotal > cbBuf) {
		cFiles--;
		cbTotal = cbLastTotal;
		pffb2Src = pvLastSrc;

		cbPrev = pffb2Src->ffb.cchName;
		// BUGBUG!! I should have to add 1 to the next expression, but it seems
		// that the compilier is doing it for me.
		pvLastSrc = (PBYTE)pffb2Src - (sizeof( MYFFB2 ) + cbPrev - CCHMAXPATHCOMP);
	    }
	    pszLastName = ((MYFFB2*)pvLastSrc)->ffb.achName;
	    *pcFileNames = cFiles;
	}
	    break;
    case 3:
	case 4: {
	    ULONG   cbFEASize;

	    pvLastSrc = pffb3Src = (MYFFB3 *)((PBYTE)pfindbuf + sizeof( EAOP ));
	    cbTotal = sizeof( EAOP2 );

	    // Mark the start of the list

	    // for level 3, we double-link the list, since there's bytes to
	    // spare.

	    cbPrev = pffb3Src->ffb.cbList - sizeof (ULONG);
	    pffb3Src->ffb.cbPrevFFB = 0;

	    // increment pffb3Src to point to the next element in the list

	    if (cbPrev) {
		FEA	*pfea;
		ULONG	cbPrev2;

		// Local scope to walk the FEA list.
		// cbPrev is the size of the FEA list

		pfea = pffb3Src->ffb.afea;

		// Here we back-link the FEA list, but instead of storing zero
		// in the first one, we store the size of the last FEA in the
		// first one.

		cbPrev2 = pfea->cbValue + pfea->cbName;
		// The FEA2 has space allocated for the \0, the FEA doesn't
		(PBYTE)pfea += sizeof( FEA ) + cbPrev2 + 1;
		cbFEASize = ROUNDUP( sizeof( FEA2 ) + cbPrev2);
		while ((PBYTE)pfea < (PBYTE)(pffb3Src->ffb.afea) + cbPrev ) {
		    cbTmp = cbPrev2;
		    cbPrev2 = pfea->cbValue + pfea->cbName;
		    pfea->cbValue = cbTmp;
		    (PBYTE)pfea += sizeof( FEA ) + cbPrev2 + 1;
		    cbFEASize += ROUNDUP( sizeof( FEA2 ) + cbPrev2);
		}

		// Now store the sizeof the last on in the first one.

		pffb3Src->ffb.afea[0].cbValue = cbPrev2;

		// now cbFEASize is the size of the dest. FEA2 list, and pfea points
		// past the end of the last FEA, at cchName.

		// set cbPrev to the size of this FFB3.  That's the size of the
		// structure, plus 1 for cchName and one for the null at the end
		// of achName, less the sizeof the one FEA declared.

		cbPrev += (*(PBYTE)pfea) + sizeof( MYFFB3 ) + 2 - sizeof( FEA );
		cbTotal += ROUNDUP( sizeof (FFB3U) + 2 - sizeof (FEA2) + cbFEASize + (*(PBYTE)pfea));
		(PBYTE)pffb3Src += cbPrev;
	    }
	    else {
		// The FEAList is empty.  Don't try to walk it.
		cbPrev = (*(PBYTE)(pffb3Src->ffb.afea)) + sizeof( MYFFB3 ) + 2 - sizeof( FEA );
		cbTotal += ROUNDUP( sizeof (FFB3U) + 2 - sizeof (FEA2) + *(PBYTE)(pffb3Src->ffb.afea));
		(PBYTE)pffb3Src += cbPrev;
	    }


	    // Check to see how well this will fit
	    if (cbBuf < cbTotal) {
		 if (cbBuf < cbTotal - cbFEASize)
		     return ERROR_BUFFER_OVERFLOW;
		 else {
		    PBYTE pName;
		    /* The EAs won't fit, so we must return a structure
		     * similar to that returned by a level 2 request!
		     */
		    pffb3Src = (MYFFB3 *)((PBYTE)pfindbuf + sizeof( EAOP ));
		    pName = (PBYTE)(pffb3Src->ffb.afea) + pffb3Src->ffb.cbList;
		    memmove( pffb3Src->ffb.afea, pName, (ULONG)((UCHAR)(*pName)));

		    pffndata->ffnFlags |= FFN_EAS_DIDNT_FIT;
		    pvLastSrc = pffb2Src = (MYFFB2 *)((PBYTE)pfindbuf + sizeof( EAOP ));
		    cbPrev = pffb2Src->ffb.cchName;
		    pffb2Src->ffb.cchName = 0;
		    (PBYTE)pffb2Src += sizeof( MYFFB2 ) + cbPrev - CCHMAXPATHCOMP;
		    cbTotal = ROUNDUP( sizeof( FFB2U ) + cbPrev - CCHMAXPATHCOMP + 1);
		    cbTotal += sizeof(EAOP2);
		    pszLastName = ((MYFFB2*)pvLastSrc)->ffb.achName;
		    *pcFileNames = 1;
		    rc = ERROR_EAS_DIDNT_FIT;
		    break;

		 }
	    }

	    // now pffb3Src points to the second element in the list, and cbTotal
	    // contains the size of the first element in the unpacked list.
	    // and cbPrev contains the total size for level 3.

	    // walk through the rest of the list, back-linking it.

	    for( cFiles = 1; cFiles < *pcFileNames && cbTotal <= cbBuf; cFiles++ ) {
		pvLastSrc = pffb3Src;
		cbLastTotal = cbTotal;

		// for level 3, we double-link the list, since there's bytes to
		// spare.

		pffb3Src->ffb.cbPrevFFB = cbPrev;
		cbPrev = pffb3Src->ffb.cbList - 4;

		// increment pffb3Src to point to the next element in the list

		if (cbPrev) {
		    FEA	    *pfea;
		    ULONG   cbPrev2;

		    // Local scope to walk the FEA list.
		    // cbPrev is the size of the FEA list

		    pfea = pffb3Src->ffb.afea;

		    // Here we back-link the FEA list, but instead of storing zero
		    // in the first one, we store the size of the last FEA in the
		    // first one.

		    cbPrev2 = pfea->cbValue + pfea->cbName;
		    // The FEA2 has space allocated for the \0, the FEA doesn't
		    (PBYTE)pfea += sizeof( FEA ) + cbPrev2 + 1;
		    cbFEASize = ROUNDUP( sizeof( FEA2 ) + cbPrev2 );
		    while ((PBYTE)pfea < (PBYTE)(pffb3Src->ffb.afea) + cbPrev ) {
			cbTmp = cbPrev2;
			cbPrev2 = pfea->cbValue + pfea->cbName;
			pfea->cbValue = cbTmp;
			(PBYTE)pfea += sizeof( FEA ) + cbPrev2 + 1;
			cbFEASize += ROUNDUP( sizeof( FEA2 ) + cbPrev2 );
		    }

		    // Now store the sizeof the last on in the first one.

		    pffb3Src->ffb.afea[0].cbValue = cbPrev2;

		    // now cbFEASize is the size of the dest. FEA2 list, and pfea points
		    // past the end of the last FEA, at cchName.

		    // set cbPrev to the size of this FFB3.  That's the size of the
		    // structure, plus 1 for cchName and one for the null at the end
		    // of achName, less the sizeof the one FEA declared.

		    cbPrev += (*(PBYTE)pfea) + sizeof( MYFFB3 ) + 2 - sizeof( FEA );
		    cbTmp   = sizeof (FFB3U) + cbFEASize + 2 - sizeof (FEA2);
		    cbTmp  += (*(PBYTE)pfea);
		    cbTotal += ROUNDUP(cbTmp);
		    (PBYTE)pffb3Src += cbPrev;
		}
		else {
		    // The FEAList is empty.  Don't try to walk it.
		    cbPrev = (*(PBYTE)(pffb3Src->ffb.afea)) + sizeof( MYFFB3 ) + 2 - sizeof( FEA );
		    cbTotal += ROUNDUP(sizeof (FFB3U) + 2 - sizeof (FEA2) + *(PBYTE)(pffb3Src->ffb.afea));
		    (PBYTE)pffb3Src += cbPrev;
		}

	    }

	    // Now pffb3Src points to the end of the last FFB, and cbTotal
	    // contains the size of the unpacked buffer, and cbPrev
	    // holds the total of the variable sized portion of the
	    // last FFB

	    // If cbTotal > cbBuf, then we need to remove the last entry in
	    // the list.

	    if (cbTotal > cbBuf) {
		cFiles--;
		cbTotal = cbLastTotal;
		pffb3Src = pvLastSrc;

		cbPrev = pffb3Src->ffb.cbPrevFFB;
		pvLastSrc = (PBYTE)pffb3Src - cbPrev;
	    }
	    pszLastName = (PBYTE)(((MYFFB3 *)pvLastSrc)->ffb.afea);
	    pszLastName += ((MYFFB3 *)pvLastSrc)->ffb.cbList + 1 - sizeof (ULONG);
	    *pcFileNames = cFiles;
	}
    }

    // Now pvSrc points to the end of the last FILEFINDBUF
    // and cbTotal points to the end of the last FILEFINDBUF2.
    // cbPrev still has the variable part of the last FILEFINDBUF.

    // pass cbSrc, cbDest, and cbPrev up to FindFirst.
    // save the info needed for a FindFromName from pvLastSrc
    pffndata->cbSrc = (PBYTE)pvSrc - (PBYTE)pfindbuf;
    pffndata->cbDest = cbTotal;
    pffndata->cbPrev = cbPrev;
    pffndata->ulPosition = ((MYFFB *)pvLastSrc)->ulPosition;
    pffndata->oName = pszLastName - (PBYTE)pfindbuf;

    return rc;
}
