/***********************************************************************
 *
 *  ABCTBL2.C
 *
 *  Contents Table - Part 2.
 *
 *
 *  The following routines are implemented in this file.
 *
 *
 *      IVTABC_SeekRow
 *      IVTABC_SeekRowApprox
 *      IVTABC_GetRowCount
 *      IVTABC_QueryPosition
 *      IVTABC_FindRow
 *      IVTABC_Restrict
 *      IVTABC_QueryRows
 *
 *
 *
 *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
 *
 ***********************************************************************/

#include "abp.h"
#include "abctbl.h"
#include "sampabp.rh"

/*************************************************************************
 *
 -  IVTABC_SeekRow
 -
 *
 *  Tries to seek an appropriate number of rows.
 *
 *
 */
STDMETHODIMP 
IVTABC_SeekRow(LPIVTABC lpIVTAbc,
    BOOKMARK bkOrigin,
    LONG lRowCount,
    LONG * lplRowsSought)
{
    LONG lNewPos;
    LONG lMoved;
    LONG lDelta;
    LONG lLast;
    HRESULT hResult = hrSuccess;

    /*
     *  Validate parameters
     */

    IVTABC_ValidateObject(SeekRow, lpIVTAbc);

    Validate_IMAPITable_SeekRow(lpIVTAbc, bkOrigin, lRowCount, lplRowsSought);


    EnterCriticalSection(&lpIVTAbc->cs);

    if (bkOrigin == BOOKMARK_BEGINNING)
    {
        lNewPos = 0;

    }
    else if (bkOrigin == BOOKMARK_CURRENT)
    {
        lNewPos = lpIVTAbc->ulPosition;

    }
    else if (bkOrigin == BOOKMARK_END)
    {
        lNewPos = lpIVTAbc->ulMaxPos;

    }
    else
    {
        ULONG ulBK = (ULONG) bkOrigin - 3;
        LPABCBK lpABCBK = NULL;

        /*
         *  See if it's out of range
         */
        if (ulBK < 0 || ulBK >= MAX_BOOKMARKS)
        {
            /*
             *  bad book mark, it's an error, so...
             */
            hResult = ResultFromScode(E_INVALIDARG);

            goto out;
        }

        if (!(lpABCBK = lpIVTAbc->rglpABCBK[ulBK]))
        {
            /*
             *  bookmark has not been allocated
             */
            hResult = ResultFromScode(MAPI_E_INVALID_BOOKMARK);

            goto out;
        }

        /* Not validating existing bookmark  */
        lNewPos = lpABCBK->ulPosition;
    }

    /*
     *  Figure out what endpoint to use and what direction to go to
     *  get there.
     */
    if (lRowCount < 0)
    {
        lLast = 0;
        lDelta = -1;
    }
    else
    {
        lLast = lpIVTAbc->ulMaxPos;
        lDelta = 1;
    }

    /*
     *  While there's rows to seek ...
     */
    lMoved = 0;
    while (lNewPos != lLast &&
        lMoved != lRowCount)
    {
        lNewPos += lDelta;

        /*
         *  Unrestricted list is easy: just seek.  Also, if the next
         *  'row' is the end of the table, just go there; don't want
         *  to check it against any restriction.
         */
        if (!lpIVTAbc->lpszPartialName ||
            lNewPos == (LONG) lpIVTAbc->ulMaxPos)
        {
            lMoved += lDelta;
        }

        /*
         *  Otherwise, deal with the restricted list:  only count
         *  the row if it's in the restriction.
         */
        else
        {
            if (!FChecked(lpIVTAbc, (ULONG) lNewPos))
            {
                hResult = HrValidateEntry(lpIVTAbc, (ULONG) lNewPos);

                if (HR_FAILED(hResult))
                {
                    goto out;
                }
            }

            if (FMatched(lpIVTAbc, (ULONG) lNewPos))
            {
                lMoved += lDelta;
            }
        }
    }

    if (lplRowsSought)
        *lplRowsSought = lMoved;

    lpIVTAbc->ulPosition = lNewPos;

out:
    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_SeekRow, hResult);
    return hResult;

}

/*************************************************************************
 *
 -  IVTABC_SeekRowApprox
 -
 *  Tries to set the position of the table according to the approximate
 *  position passed in.
 *
 *
 */
STDMETHODIMP 
IVTABC_SeekRowApprox(LPIVTABC lpIVTAbc,
    ULONG ulNumerator,
    ULONG ulDenominator)
{
    HRESULT hResult = hrSuccess;
    ULONG iByte;
    BYTE bCount;
    ULONG ulPos = 0;
    ULONG ulCount = 0;

    /*
     *  Validate parameters
     */

    IVTABC_ValidateObject(SeekRowApprox, lpIVTAbc);

    Validate_IMAPITable_SeekRowApprox(lpIVTAbc, ulNumerator, ulDenominator);


    EnterCriticalSection(&lpIVTAbc->cs);


    if (ulNumerator >= ulDenominator)
    {
        /*  We're at the end of the list */
        lpIVTAbc->ulPosition = lpIVTAbc->ulMaxPos;

        hResult = hrSuccess;
        goto out;
    }

    /*
     *  Since I'm using muldiv() which takes ints/longs, I should shift right
     *  so that I don't incorrectly call it.
     *  I'm really just checking to see if the sign bit is set...
     */

    if (((long)ulNumerator < 0) || ((long)ulDenominator < 0))
    {
        ulNumerator >>= 1;
        ulDenominator >>= 1;
    }

    if (!lpIVTAbc->lpszPartialName)
    {
        /*
         *  The NON-Restriction method
         */

        lpIVTAbc->ulPosition = MULDIV(lpIVTAbc->ulMaxPos, ulNumerator,
            ulDenominator);

        hResult = hrSuccess;
        goto out;
    }

    /*
     *  Restriction method
     */

    /*  Figure out % of which corresponds with numerator. */
    ulCount = MULDIV(lpIVTAbc->ulRstrDenom, ulNumerator, ulDenominator);

    /*  Count bits in rgMatched until I match numerator */

    for (iByte = 0; iByte < (lpIVTAbc->ulMaxPos / 8); iByte++)
    {
        CBitsB(lpIVTAbc->rgMatched[iByte], bCount); /* <-- MACRO  */
        ulPos += (ULONG) bCount;

        if (ulPos >= ulCount)
        {
            ulPos -= bCount;    /* Go back a byte */
            break;
        }
    }

    /*  My current position is there. */
    lpIVTAbc->ulPosition = iByte * 8;


out:
    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_SeekRowApprox, hResult);
    return hResult;

}

/*************************************************************************
 *
 -  IVTABC_GetRowCount
 -
 *
 *  If there's a restriction applied, I don't necessarily know how many
 *  rows there are...
 */

STDMETHODIMP 
IVTABC_GetRowCount(LPIVTABC lpIVTAbc,
    ULONG ulFlags,
    ULONG * lpulCount)
{
    HRESULT hResult;

    /*
     *  Validate parameters
     */

    IVTABC_ValidateObject(GetRowCount, lpIVTAbc);

    Validate_IMAPITable_GetRowCount(lpIVTAbc, ulFlags, lpulCount);


    EnterCriticalSection(&lpIVTAbc->cs);

    /*
     *  If there's no restriction, you can actually calculate this..
     */
    if (!lpIVTAbc->lpszPartialName)
    {

        /*
         *  Number of actual rows
         */
        *lpulCount = lpIVTAbc->ulMaxPos;
        hResult = hrSuccess;
        goto out;
    }

    /*
     *  There's no way that I can tell how many actual entries there
     *  are without counting them.  That takes way too long, so we give
     *  the client our best guess.
     */

    *lpulCount = lpIVTAbc->ulRstrDenom;

    /*
     *  Then we warn the client that this count may not be accurate
     */
    
    hResult = ResultFromScode(MAPI_W_APPROX_COUNT);

out:
    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_GetRowCount, hResult);
    return hResult;
}

/*************************************************************************
 *
 -  IVTABC_QueryPosition
 -
 *  Figures out the current fractional position
 *
 *
 *
 */
STDMETHODIMP 
IVTABC_QueryPosition(LPIVTABC lpIVTAbc,
    ULONG * lpulRow,
    ULONG * lpulNumerator,
    ULONG * lpulDenominator)
{
    HRESULT hResult = hrSuccess;

    /*
     *  Validate parameters
     */

    IVTABC_ValidateObject(QueryPosition, lpIVTAbc);

    Validate_IMAPITable_QueryPosition(lpIVTAbc, lpulRow, lpulNumerator,
                                        lpulDenominator);

    EnterCriticalSection(&lpIVTAbc->cs);


    /*  ...I don't have a restriction  */
    if (!lpIVTAbc->lpszPartialName)
    {
        *lpulRow = lpIVTAbc->ulPosition;

        *lpulNumerator = lpIVTAbc->ulPosition;
        *lpulDenominator = lpIVTAbc->ulMaxPos;
    }
    else
    {
        BYTE bCount = 0;
        BYTE bFrag;
        ULONG iByte;

        /*
         *  Zero out fraction
         */
        *lpulNumerator = 0;

        /*
         *  Set denominator that we've been keeping track of.
         */
        *lpulDenominator = (lpIVTAbc->ulRstrDenom ? lpIVTAbc->ulRstrDenom : 1);

        /*
         *  Handle corner case - we're at the beginning of the list...
         */
        if (lpIVTAbc->ulPosition == 0)
        {
            *lpulRow = 0;
            goto out;
        }

        /*
         *  Calculate Numerator
         *  We use the rgMatched bit array and count the bits up to
         *  our current position (lpIVTAbc->ulPosition).
         *
         */
        for (iByte = 0; iByte < (lpIVTAbc->ulPosition / 8); iByte++)
        {
            CBitsB(lpIVTAbc->rgMatched[iByte], bCount); /* <-- MACRO  */
            *lpulNumerator += (ULONG) bCount;
        }
        /*  Count the fragment  */
        bFrag = lpIVTAbc->rgMatched[iByte];
        bFrag = bFrag >> (8 - (lpIVTAbc->ulPosition % 8));

        CBitsB(bFrag, bCount);
        *lpulNumerator += (ULONG) bCount;

        /*
         *  Good guess here...
         */
        *lpulRow = *lpulNumerator;

    }

out:

    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_QueryPosition, hResult);
    return hResult;
}

/*************************************************************************
 *
 -  IVTABC_FindRow
 -
 *
 *  Prefix searches to find a row
 *
 *
 */
STDMETHODIMP 
IVTABC_FindRow(LPIVTABC lpIVTAbc,
    LPSRestriction lpRestriction,
    BOOKMARK bkOrigin,
    ULONG ulFlags)
{
    HRESULT hResult = hrSuccess;

    ULONG cbRead;
    ABCREC abcrec;
    LONG lpos;
    ULONG fFound = FALSE;
    LPSTR szPrefix;
    int nCmpResult;

    ULONG ulCurMin;
    ULONG ulCurMac;
    ULONG ulPosT;

    /*
     *  Validate parameters
     */

    IVTABC_ValidateObject(FindRow, lpIVTAbc);

    Validate_IMAPITable_FindRow(lpIVTAbc, lpRestriction, bkOrigin, ulFlags);


    /*
     *  I don't go backwards, yet.
     */
    if (ulFlags & DIR_BACKWARD)
    {
        hResult = ResultFromScode(MAPI_E_NO_SUPPORT);

        DebugTraceResult(IVTABC_FindRow, hResult);
        return hResult;
    }


    EnterCriticalSection(&lpIVTAbc->cs);

    /*
     *  Open the file
     */
    hResult = HrOpenFile(lpIVTAbc);
    if (HR_FAILED(hResult))
    {
        goto out;
    }

    /*
     *  initialize
     */
    ulCurMin = lpIVTAbc->ulPosition;
    ulCurMac = lpIVTAbc->ulMaxPos;

    /*
     *  I handle two type of restrictions: 
     *      prefix searching on Display Names
     *      Finding a row based on it's instance key
     */

    /*
     *  The prefix search on display name restriction looks like:
     *
     *  +-----------------
     *  | RES_PROPERTY
     *  +-----------------
     *  | RELOP_GE
     *  +-----------------
     *  | PR_DISPLAY_NAME
     *  +-----------------
     *  | LPSPropVal   -----+
     *  +-----------------  |
     *                      |   +-------------------------
     *                      +-->| PR_DISPLAY_NAME
     *                          +-------------------------
     *                          | 0 <-- for alignment (don't care)
     *                          +-------------------------
     *                          | lpszA <-- prefix for display name
     *                          +-------------------------
     *
     *
     *
     *              -OR-
     *
     *  Find a row based on it's instance key
     *
     *  +-----------------
     *  | RES_PROPERTY
     *  +-----------------
     *  | RELOP_EQ
     *  +-----------------
     *  | PR_INSTANCE_KEY
     *  +-----------------
     *  | LPSPropVal   -----+
     *  +-----------------  |
     *                      |   +-------------------------
     *                      +-->| PR_INSTANCE_KEY
     *                          +-------------------------
     *                          |     | cbInstanceKey
     *                          + bin +-------------------
     *                          |     | lpbInstanceKey
     *                          +-------------------------
     *
     *
     *  If it doesn't look like one of these, return MAPI_E_TOO_COMPLEX.
     */

    /*
     *  Both restrictions require this one
     */
    if (lpRestriction->rt != RES_PROPERTY)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }


    /*
     *  Look for Instance Key first - it's easiest to handle
     */
    if (lpRestriction->res.resProperty.relop == RELOP_EQ)
    {
        LPABCRecInstance lpABCRecInstance;
        
        if (lpRestriction->res.resProperty.ulPropTag != PR_INSTANCE_KEY)
        {
            /*
             *  Clearly something we don't recognize
             */
            hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

            goto out;
        }

        /*
         *  Crack the bin part of this restriction and
         *  see if we can still find our way back to this
         *  record - quickly...
         */
        lpABCRecInstance = (LPABCRecInstance) lpRestriction->res.resProperty.lpProp->Value.bin.lpb;

        /*
         *  First check to see that we're browsing the same file
         */
        if (lstrcmp(lpABCRecInstance->rgchzFileName, lpIVTAbc->lpszFileName))
        {
            /*
             *  Nope, different names, return not found and leave our position alone...
             */
            hResult = ResultFromScode(MAPI_E_NOT_FOUND);
            goto out;
        }

        /*
         *  Ok, so we think we're browsing the same file.  Has it been modified since the
         *  last time we looked?
         */
        if (memcmp(&(lpABCRecInstance->filetime), &(lpIVTAbc->filetime), sizeof (FILETIME)))
        {
            /*
             *  Nope, they're different, so no guarantees here...
             */
            hResult = ResultFromScode(MAPI_E_NOT_FOUND);
            goto out;
        }

        /*
         *  Now, I feel pretty confident about this instance key.  Just set my current position
         *  and go for it.
         */
        lpIVTAbc->ulPosition = lpABCRecInstance->ulRecordPosition;

        /* Done */
        goto out;

    }
            
    /*
     *  Now we're looking for prefix searching on display name
     */
    if (lpRestriction->res.resProperty.relop != RELOP_GE)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }

    if (lpRestriction->res.resProperty.ulPropTag != PR_DISPLAY_NAME_A)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }

    szPrefix = lpRestriction->res.resProperty.lpProp->Value.lpszA;

    if (bkOrigin == BOOKMARK_BEGINNING)
    {
        ulCurMin = 0;

    }
    else if (bkOrigin == BOOKMARK_END)
    {
        ulCurMin = lpIVTAbc->ulMaxPos;

    }
    else if (bkOrigin != BOOKMARK_CURRENT)
    {
        ULONG ulBK = (ULONG) bkOrigin - 3;
        LPABCBK lpABCBK = NULL;

        /*
         *  See if it's out of range
         */
        if (ulBK < 0 || ulBK >= MAX_BOOKMARKS)
        {
            /*
             *  bad book mark, it's an error, so...
             */
            hResult = ResultFromScode(E_INVALIDARG);

            goto out;
        }

        if (!(lpABCBK = lpIVTAbc->rglpABCBK[ulBK]))
        {
            /*
             *  bookmark has not been allocated
             */
            hResult = ResultFromScode(MAPI_E_INVALID_BOOKMARK);

            goto out;
        }

        /*  Not validating existing bookmark  */
        ulCurMin = lpABCBK->ulPosition;
    }

    while (ulCurMin < ulCurMac)
    {
        /*
         *  Look for a row which matches the table restriction (if any).
         */
        ulPosT = (ulCurMin + ulCurMac) / 2;

        lpos = (long)((long)ulPosT * (long)sizeof(ABCREC));

        SetFilePointer(lpIVTAbc->hFile, lpos, NULL, FILE_BEGIN);

        /*  Read in the record at that location  */
        if (!ReadFile(lpIVTAbc->hFile, (LPVOID) &abcrec,
                sizeof(ABCREC), &cbRead, NULL))
        {
            hResult = ResultFromScode(MAPI_E_DISK_ERROR);
            SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_NO_READ);

            goto out;
        }

        /*
         *  I want case insensitive comparisons here...
         */
        nCmpResult = lstrcmpi(szPrefix, abcrec.rgchDisplayName);

        if (nCmpResult > 0)
        {
            ulCurMin = ulPosT + 1;
        }
        else
        {
            ulCurMac = ulPosT;
            if (!lpIVTAbc->lpszPartialName ||
                FNameMatch(lpIVTAbc, abcrec.rgchDisplayName))
            {
                fFound = TRUE;
            }
        }
    }

    /*
     *  If I didn't find a row, return MAPI_E_NOT_FOUND.
     */
    if (!fFound)
    {
        hResult = ResultFromScode(MAPI_E_NOT_FOUND);

        goto out;
    }

    /*
     *  Otherwise, set the current position to the row found.
     */
    lpIVTAbc->ulPosition = ulCurMac;

out:
    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_FindRow, hResult);
    return hResult;
}

/*************************************************************************
 *
 -  IVTABC_Restrict
 -
 *
 *      Should just support ANR type restrictions...
 */
STDMETHODIMP 
IVTABC_Restrict(LPIVTABC lpIVTAbc,
    LPSRestriction lpRestriction,
    ULONG ulFlags)
{
    LPSTR szT = NULL;
    LPSTR lpszTNew = NULL;
    ULONG cbTB = 0;
    BYTE bFilter = 0;
    SCODE scode;
    HRESULT hResult = hrSuccess;

    /*
     *  Validate parameters
     */

    IVTABC_ValidateObject(Restrict, lpIVTAbc);

    Validate_IMAPITable_Restrict(lpIVTAbc, lpRestriction, ulFlags);


    /*
     *  I only handle ANR type restrictions
     */

    /*
     *  Check to see if they're resetting the restrictions
     */
    if (!lpRestriction)
    {
        EnterCriticalSection(&lpIVTAbc->cs);
        
        if (lpIVTAbc->lpszPartialName)
            (*(lpIVTAbc->lpFreeBuff)) (lpIVTAbc->lpszPartialName);
        lpIVTAbc->lpszPartialName = NULL;

        FreeANRBitmaps(lpIVTAbc);

        LeaveCriticalSection(&lpIVTAbc->cs);
        
        return hrSuccess;
    }

    /*
     *  The restriction must look like:
     *
     *
     *  +--------------
     *  | RES_PROPERTY
     *  +--------------
     *  | RELOP_EQ
     *  +--------------
     *  | PR_ANR
     *  +--------------
     *  | LPSPropVal ---+
     *  +-------------- |
     *                  |   +-------------------------
     *                  +-->| PR_ANR
     *                      +-------------------------
     *                      | 0 <-- for alignment (don't care)
     *                      +-------------------------
     *                      | lpszA <-- string to ANR on
     *                      +-------------------------
     *
     *
     *
     *  If it doesn't look like this, return MAPI_E_TOO_COMPLEX.
     *
     */

    if (lpRestriction->rt != RES_PROPERTY)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }

    if (lpRestriction->res.resProperty.relop != RELOP_EQ)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }

    if (lpRestriction->res.resProperty.ulPropTag != PR_ANR)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }

    /*
     *  NULL string is not defined - it's a bad restriction
     */
    if (!lpRestriction->res.resProperty.lpProp->Value.lpszA)
    {
        hResult = ResultFromScode(E_INVALIDARG);
        
        goto out;
    }


    szT = lpRestriction->res.resProperty.lpProp->Value.lpszA;

    /*
     *  Skip over leading spaces
     */

    while (*szT == ' ')
        szT++;

    /*
     *  Empty string is not defined - it's a bad restriction
     */
    if (*szT == '\0')
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }


    /*
     *  Copy the string for the partial name
     */

    scode = lpIVTAbc->lpAllocBuff(lstrlenA(szT) + 1, (LPVOID *) &lpszTNew);
    if (FAILED(scode))
    {
        /*
         *  Memory error
         */

        hResult = ResultFromScode(scode);
        goto out;
    }
    lstrcpyA(lpszTNew, szT);


    EnterCriticalSection(&lpIVTAbc->cs);


    /*
     *  Clear up any old restriction
     */

    if (lpIVTAbc->lpszPartialName)
        lpIVTAbc->lpFreeBuff(lpIVTAbc->lpszPartialName);
    
    lpIVTAbc->lpszPartialName = lpszTNew;

    FreeANRBitmaps(lpIVTAbc);


    /*
     *  Allocate enough bits for the checked&matched arrays
     */
    cbTB = (lpIVTAbc->ulMaxPos) / 8 + 1;    /* Number of bytes in both arrays */

    lpIVTAbc->rgChecked = lpIVTAbc->lpMalloc->lpVtbl->Alloc(
        lpIVTAbc->lpMalloc,
        cbTB);

    if (lpIVTAbc->rgChecked == NULL)
    {
        lpIVTAbc->lpFreeBuff(lpIVTAbc->lpszPartialName);
        lpIVTAbc->lpszPartialName = NULL;

        hResult = ResultFromScode(scode);

        LeaveCriticalSection(&lpIVTAbc->cs);
        goto out;
    }

    lpIVTAbc->rgMatched = lpIVTAbc->lpMalloc->lpVtbl->Alloc(
        lpIVTAbc->lpMalloc,
        cbTB);

    if (lpIVTAbc->rgMatched == NULL)
    {
        (*(lpIVTAbc->lpFreeBuff)) (lpIVTAbc->lpszPartialName);
        lpIVTAbc->lpszPartialName = NULL;

        lpIVTAbc->lpMalloc->lpVtbl->Free(lpIVTAbc->lpMalloc, lpIVTAbc->rgChecked);
        lpIVTAbc->rgChecked = NULL;

        hResult = ResultFromScode(scode);

        LeaveCriticalSection(&lpIVTAbc->cs);
        goto out;
    }

    /*
     *  Initialize the checked array with 0's
     */

    FillMemory(lpIVTAbc->rgChecked, (UINT) cbTB, 0x00);

    /*
     *  Initialize the matched array with 1's
     *  [we assume that if you haven't checked it, it matches]
     */

    FillMemory(lpIVTAbc->rgMatched, (UINT) cbTB, 0xFF);

    /*
     *  Fill in the end bits so we don't have to worry about them
     *  later
     */
    bFilter = (0xFF >> (lpIVTAbc->ulMaxPos % 8));

    /*  Checked end bits should be 1 */
    lpIVTAbc->rgChecked[cbTB - 1] = bFilter;

    /*  Matched end bits should be 0 */
    lpIVTAbc->rgMatched[cbTB - 1] = ~bFilter;

    /*
     *  Set the currenly known total number of rows
     *  that match the restriction.
     */

    lpIVTAbc->ulRstrDenom = lpIVTAbc->ulMaxPos;

    LeaveCriticalSection(&lpIVTAbc->cs);

out:

    DebugTraceResult(IVTABC_Restrict, hResult);
    return hResult;

}



/*************************************************************************
 *
 -  IVTABC_QueryRows
 -
 *  Attempts to retrieve ulRowCount rows from the .SAB file.  Even in the
 *  restricted case.
 *
 *
 */
STDMETHODIMP 
IVTABC_QueryRows(LPIVTABC lpIVTAbc,
    LONG lRowCount,
    ULONG ulFlags,
    LPSRowSet * lppRows)
{
    SCODE scode;
    HRESULT hResult = hrSuccess;
    LPSRowSet lpRowSet = NULL;
    LPSPropValue lpRow = NULL;
    int cbSizeOfRow;
    int cRows, iRow;
    int cCols;
    DWORD cbRead;
    ABCREC abcrec;
    ULONG ulOrigPosition;

    /*
     *  Validate parameters
     */

    IVTABC_ValidateObject(QueryRows, lpIVTAbc);

    Validate_IMAPITable_QueryRows(lpIVTAbc, lRowCount, ulFlags, lppRows);


    if (lRowCount < 0)
    {
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_QueryRows, hResult);
        return hResult;
    }

    /*
     *  Check the flags
     */
    if (ulFlags & ~TBL_NOADVANCE)
    {
        hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS);

        DebugTraceResult(IVTABC_QueryRows, hResult);
        return hResult;
    }


    EnterCriticalSection(&lpIVTAbc->cs);

    /*
     *  Open the file
     */
    hResult = HrOpenFile(lpIVTAbc);
    if (HR_FAILED(hResult))
    {
        goto out;
    }

    ulOrigPosition = lpIVTAbc->ulPosition;

    /*
     *  Calculate # of rows that will be read.
     */
    cRows = (int)lpIVTAbc->ulMaxPos - (int)lpIVTAbc->ulPosition;
    cRows = (cRows < (int)lRowCount ? cRows : (int)lRowCount);

    /*
     *  Allocate array for SRowSet
     */

    scode = lpIVTAbc->lpAllocBuff(sizeof(ULONG) + cRows * sizeof(SRow),
                                        (LPVOID *) &lpRowSet);
    if (FAILED(scode))
    {
        hResult = ResultFromScode(scode);

        goto out;
    }

    /*
     *  Initialize - so we can clean up later if we have to...
     */
    lpRowSet->cRows = cRows;

    for (iRow = 0; iRow < cRows; iRow++)
        lpRowSet->aRow[iRow].lpProps = NULL;



    /*
     *  Seek to correct position in file
     */
    (void) SetFilePointer(lpIVTAbc->hFile, lpIVTAbc->ulPosition * sizeof(ABCREC),
                        NULL, FILE_BEGIN);

    /*
     *  Read each row from the file
     */
    for (iRow = 0; iRow < cRows; iRow++)
    {

        /*  The only properties available are:
         *
         *  PR_DISPLAY_NAME, PR_ENTRYID, PR_ADDRTYPE, PR_EMAIL_ADDRESS,
         *  PR_OBJECT_TYPE, PR_DISPLAY_TYPE
         */

        /*
         *  Handle restricted lists
         */
        if (lpIVTAbc->lpszPartialName)
        {
            ULONG ulPos = lpIVTAbc->ulPosition;

next:
            if (ulPos == lpIVTAbc->ulMaxPos)
            {
                break;
            }

            if (!FChecked(lpIVTAbc, ulPos))
            {
                hResult = HrValidateEntry(lpIVTAbc, ulPos);
                if (HR_FAILED(hResult))
                {
                    goto err;
                }
            }

            if (!FMatched(lpIVTAbc, ulPos))
            {
                ulPos++;
                goto next;
            }

            lpIVTAbc->ulPosition = ulPos;
            (void) SetFilePointer(lpIVTAbc->hFile, lpIVTAbc->ulPosition * sizeof(ABCREC),
                                    NULL, FILE_BEGIN);

        }

        lpIVTAbc->ulPosition++;

        /*
         *  Read in the record from the file
         */
        if (!ReadFile(lpIVTAbc->hFile, (LPVOID) &abcrec,
                sizeof(ABCREC), &cbRead, NULL))
        {
            hResult = ResultFromScode(MAPI_E_DISK_ERROR);
            SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_NO_READ);

            goto err;
        }
        /*  Second check  */
        if ((UINT) cbRead != sizeof(ABCREC))
        {
            /*
             *  Should never get here.
             */
            hResult = ResultFromScode(MAPI_E_DISK_ERROR);
            SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_NO_READ);

            goto err;
        }

        /*  Allocate memory to start a row.
         */
        cbSizeOfRow = sizeof(ULONG) +
            (int)lpIVTAbc->lpPTAColSet->cValues * sizeof(SPropValue);

        scode = lpIVTAbc->lpAllocBuff(cbSizeOfRow, (LPVOID *) &lpRow);
        if (FAILED(scode))
        {
            hResult = ResultFromScode(scode);

            goto err;
        }

        /*
         *  Get all the data
         */
        for (cCols = 0; cCols < (int)lpIVTAbc->lpPTAColSet->cValues; cCols++)
        {
            switch (lpIVTAbc->lpPTAColSet->aulPropTag[cCols])
            {
            case PR_DISPLAY_NAME_A:
                {

                    lpRow[cCols].ulPropTag = PR_DISPLAY_NAME_A;
                    scode = lpIVTAbc->lpAllocMore (lstrlenA(abcrec.rgchDisplayName) + 1,
                                                lpRow,
                                                (LPVOID *) &(lpRow[cCols].Value.lpszA));

                    if (FAILED(scode))
                    {
                        lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR,
                            PROP_ID(PR_DISPLAY_NAME_A));
                        lpRow[cCols].Value.err = scode;
                    }
                    else
                    {

                        lstrcpyA(lpRow[cCols].Value.lpszA,
                            abcrec.rgchDisplayName);
                    }

                }
                break;

            case PR_EMAIL_ADDRESS_A:
                {

                    lpRow[cCols].ulPropTag = PR_EMAIL_ADDRESS_A;
                    scode = lpIVTAbc->lpAllocMore (
                                lstrlenA(abcrec.rgchEmailAddress) + 1,
                                lpRow, (LPVOID *) &(lpRow[cCols].Value.lpszA));

                    if (FAILED(scode))
                    {
                        lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR,
                            PROP_ID(PR_EMAIL_ADDRESS_A));
                        lpRow[cCols].Value.err = scode;
                    }
                    else
                    {

                        lstrcpyA(lpRow[cCols].Value.lpszA,
                            abcrec.rgchEmailAddress);
                    }

                }
                break;

            case PR_ADDRTYPE:
                {
                    /*
                     *  AddrType is always "MSPEER" for the SAB
                     */

                    lpRow[cCols].ulPropTag = PR_ADDRTYPE_A;
                    scode = lpIVTAbc->lpAllocMore (lstrlenA(lpszEMT) + 1,
                                lpRow, (LPVOID *) &(lpRow[cCols].Value.lpszA));

                    if (FAILED(scode))
                    {
                        lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR,
                            PROP_ID(PR_ADDRTYPE_A));
                        lpRow[cCols].Value.err = scode;
                    }
                    else
                    {

                        lstrcpyA(lpRow[cCols].Value.lpszA, lpszEMT);
                    }

                }
                break;

            case PR_ENTRYID:
                {
                    /*
                     *  Fixed sized entryid.  Basically just the .SAB file record
                     */
                    LPUSR_ENTRYID lpUsrEid;

                    lpRow[cCols].ulPropTag = PR_ENTRYID;
                    scode = lpIVTAbc->lpAllocMore (sizeof(USR_ENTRYID), lpRow,
                                (LPVOID *) &(lpRow[cCols].Value.bin.lpb));

                    if (FAILED(scode))
                    {
                        lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR,
                            PROP_ID(PR_ENTRYID));
                        lpRow[cCols].Value.err = scode;
                    }
                    else
                    {
                        lpUsrEid = (LPUSR_ENTRYID) lpRow[cCols].Value.bin.lpb;

                        ZeroMemory(lpUsrEid, sizeof(USR_ENTRYID));

                        /*  Size of entryid */
                        lpRow[cCols].Value.bin.cb = sizeof(USR_ENTRYID);

                        lpUsrEid->abFlags[0] = 0;   /*  long-term, recipient */
                        lpUsrEid->abFlags[1] = 0;
                        lpUsrEid->abFlags[2] = 0;
                        lpUsrEid->abFlags[3] = 0;
                        lpUsrEid->muid = muidABSample;
                        lpUsrEid->ulVersion = SAMP_VERSION;
                        lpUsrEid->ulType = SAMP_USER;
                        lpUsrEid->abcrec = abcrec;
                    }

                }
                break;

            case PR_OBJECT_TYPE:
                {
                    /*
                     *  MailUser
                     */

                    lpRow[cCols].ulPropTag = PR_OBJECT_TYPE;
                    lpRow[cCols].Value.ul = MAPI_MAILUSER;
                }
                break;

            case PR_DISPLAY_TYPE:
                {
                    /*
                     *  MailUser
                     */

                    lpRow[cCols].ulPropTag = PR_DISPLAY_TYPE;
                    lpRow[cCols].Value.ul = DT_MAILUSER;

                }
                break;

            case PR_INSTANCE_KEY:
                {
                    LPABCRecInstance lpABCRecInstance;
                    UINT cbRecInstance;
                    /*
                     *  Instance keys are made up of:
                     *      ulRecordPosition - current position in the file
                     *      filetime         - current date and time stamp of file
                     *      lpszFileName     - current file that we're browsing
                     */
                    lpRow[cCols].ulPropTag = PR_INSTANCE_KEY;
                    
                    cbRecInstance = sizeof(ABCRecInstance)+lstrlen(lpIVTAbc->lpszFileName)+1;
                    scode = lpIVTAbc->lpAllocMore(cbRecInstance,
                                                lpRow,
                                                (LPVOID) &(lpRow[cCols].Value.bin.lpb));

                    if (FAILED(scode))
                    {
                        lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR,
                            PROP_ID(PR_INSTANCE_KEY));
                        lpRow[cCols].Value.err = scode;
                    }
                    else
                    {
                        lpABCRecInstance = (LPABCRecInstance) lpRow[cCols].Value.bin.lpb;

                        ZeroMemory(lpABCRecInstance, cbRecInstance);

                        lpRow[cCols].Value.bin.cb = (ULONG) cbRecInstance;

                        lpABCRecInstance->ulRecordPosition = lpIVTAbc->ulPosition;
                        lpABCRecInstance->filetime = lpIVTAbc->filetime;
                        lstrcpy(lpABCRecInstance->rgchzFileName, lpIVTAbc->lpszFileName);

                    }
                }
                break;                      
                        

            default:
                {

                    lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR,
                        PROP_ID(lpIVTAbc->lpPTAColSet->aulPropTag[cCols]));
                    lpRow[cCols].Value.err = MAPI_E_NOT_FOUND;
                }
                break;
            }
        }

        /*  # of columns  */
        lpRowSet->aRow[iRow].cValues = lpIVTAbc->lpPTAColSet->cValues;

        /*  Actual row of data */
        lpRowSet->aRow[iRow].lpProps = lpRow;

        lpRow = NULL;

    }

    /*
     *  it's always iRow.
     */
    lpRowSet->cRows = iRow;

    /*
     *  Handle Seeked position stuff
     */
    if (ulFlags & TBL_NOADVANCE)
    {
        /*
         *  Set it back to it's original position
         */
        lpIVTAbc->ulPosition = ulOrigPosition;
    }


    *lppRows = lpRowSet;

out:
    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_QueryRows, hResult);
    return hResult;

err:
    /*
     *  Clean up memory...
     */

    /*  Free the row  */
    lpIVTAbc->lpFreeBuff(lpRow);

    /*  Clean up the rest of the rows  */
    FreeProws(lpRowSet);

    *lppRows = NULL;

    goto out;

}

