ABCTBL2.C

/*********************************************************************** 
*
* 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;

}