1237 lines
29 KiB
C
1237 lines
29 KiB
C
/********************************************************************/
|
||
/** Copyright(c) 1989 Microsoft Corporation. **/
|
||
/********************************************************************/
|
||
|
||
//***
|
||
//
|
||
// Filename: buffer.c
|
||
//
|
||
// Description: This module contains routines to manipulate cached
|
||
// information. ie volume info, server properties and
|
||
// ETC mappings info.
|
||
//
|
||
// History:
|
||
// May 11,1992. NarenG Created original version.
|
||
//
|
||
#include "afpsvcp.h"
|
||
|
||
// This should be more than the size (in bytes) all the value names
|
||
// each AfpMultSzInfo structure. It will be used to calculate the amount
|
||
// of memory needed to create a multi-sz.
|
||
//
|
||
#define AFP_CUMULATIVE_VALNAME_SIZE 150
|
||
|
||
// This data structure will be used by AfpBufParseMultiSz and
|
||
// AfpBufMakeMultiSz.
|
||
//
|
||
typedef struct _AfpMultiSzInfo {
|
||
|
||
DWORD dwType; // Type of data, string or DWORD
|
||
DWORD dwOffset; // Offset of this field from the start
|
||
LPWSTR lpwsValueName; // Value name for this field.
|
||
// If this is NULL then it does not
|
||
// have a value name. It is the
|
||
// value name for this MULT_SZ.
|
||
DWORD fIsInPlace; // If string, is it a pointer or a
|
||
// buffer.
|
||
DWORD cch; // If fIsInPlace is TRUE, then how
|
||
// big (in UNICODE chars.) is the
|
||
// buffer.
|
||
|
||
} AFP_MULTISZ_INFO, *PAFP_MULTISZ_INFO;
|
||
|
||
static AFP_MULTISZ_INFO AfpVolumeMultiSz[] = {
|
||
|
||
REG_SZ,
|
||
AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_name ),
|
||
NULL,
|
||
FALSE,
|
||
0,
|
||
|
||
REG_SZ,
|
||
AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_password ),
|
||
AFPREG_VALNAME_PASSWORD,
|
||
FALSE,
|
||
0,
|
||
|
||
REG_DWORD,
|
||
AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_max_uses ),
|
||
AFPREG_VALNAME_MAXUSES,
|
||
FALSE,
|
||
0,
|
||
|
||
REG_DWORD,
|
||
AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_props_mask ),
|
||
AFPREG_VALNAME_PROPS,
|
||
FALSE,
|
||
0,
|
||
|
||
REG_SZ,
|
||
AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_path ),
|
||
AFPREG_VALNAME_PATH,
|
||
FALSE,
|
||
0,
|
||
|
||
REG_NONE, 0, 0, 0, 0
|
||
};
|
||
|
||
static AFP_MULTISZ_INFO AfpExtensionMultiSz[] = {
|
||
|
||
REG_SZ,
|
||
AFP_FIELD_OFFSET( AFP_EXTENSION, afpe_extension[0] ),
|
||
NULL,
|
||
TRUE,
|
||
AFP_FIELD_SIZE( AFP_EXTENSION, afpe_extension ),
|
||
|
||
REG_DWORD,
|
||
AFP_FIELD_OFFSET( AFP_EXTENSION, afpe_tcid ),
|
||
AFPREG_VALNAME_ID,
|
||
FALSE,
|
||
0,
|
||
|
||
REG_NONE, 0, 0, 0, 0
|
||
};
|
||
|
||
|
||
static AFP_MULTISZ_INFO AfpTypeCreatorMultiSz[] = {
|
||
|
||
REG_SZ,
|
||
AFP_FIELD_OFFSET(AFP_TYPE_CREATOR, afptc_creator[0] ),
|
||
AFPREG_VALNAME_CREATOR,
|
||
TRUE,
|
||
AFP_FIELD_SIZE(AFP_TYPE_CREATOR, afptc_creator ),
|
||
|
||
REG_SZ,
|
||
AFP_FIELD_OFFSET( AFP_TYPE_CREATOR, afptc_type[0] ),
|
||
AFPREG_VALNAME_TYPE,
|
||
TRUE,
|
||
AFP_FIELD_SIZE( AFP_TYPE_CREATOR, afptc_type ),
|
||
|
||
REG_SZ,
|
||
AFP_FIELD_OFFSET( AFP_TYPE_CREATOR, afptc_comment[0] ),
|
||
AFPREG_VALNAME_COMMENT,
|
||
TRUE,
|
||
AFP_FIELD_SIZE( AFP_TYPE_CREATOR, afptc_comment ),
|
||
|
||
REG_DWORD,
|
||
AFP_FIELD_OFFSET( AFP_TYPE_CREATOR, afptc_id ),
|
||
NULL,
|
||
FALSE,
|
||
0,
|
||
|
||
REG_NONE, 0, 0, 0, 0
|
||
};
|
||
|
||
static AFP_MULTISZ_INFO AfpIconMultiSz[] = {
|
||
|
||
REG_SZ,
|
||
AFP_FIELD_OFFSET( AFP_ICON_INFO, afpicon_type[0] ),
|
||
AFPREG_VALNAME_TYPE,
|
||
TRUE,
|
||
AFP_FIELD_SIZE( AFP_ICON_INFO, afpicon_type ),
|
||
|
||
REG_SZ,
|
||
AFP_FIELD_OFFSET( AFP_ICON_INFO, afpicon_creator[0] ),
|
||
AFPREG_VALNAME_CREATOR,
|
||
TRUE,
|
||
AFP_FIELD_SIZE( AFP_ICON_INFO, afpicon_creator ),
|
||
|
||
REG_DWORD,
|
||
AFP_FIELD_OFFSET( AFP_ICON_INFO, afpicon_icontype ),
|
||
AFPREG_VALNAME_ICONTYPE,
|
||
FALSE,
|
||
0,
|
||
|
||
REG_DWORD,
|
||
AFP_FIELD_OFFSET( AFP_ICON_INFO, afpicon_length ),
|
||
AFPREG_VALNAME_LENGTH,
|
||
FALSE,
|
||
0,
|
||
|
||
REG_SZ,
|
||
AFP_FIELD_OFFSET( AFP_ICON_INFO, afpicon_data ),
|
||
AFPREG_VALNAME_DATA,
|
||
FALSE,
|
||
0,
|
||
|
||
REG_NONE, 0, 0, 0, 0
|
||
};
|
||
|
||
// These arrays represents the byte offsets, from the beginning of the
|
||
// structure, of the LPWSTR fields.
|
||
//
|
||
static BYTE ServerOffsetTable[] = {
|
||
AFP_FIELD_OFFSET( AFP_SERVER_INFO, afpsrv_name ),
|
||
AFP_FIELD_OFFSET( AFP_SERVER_INFO, afpsrv_login_msg ),
|
||
AFP_FIELD_OFFSET( AFP_SERVER_INFO, afpsrv_codepage ),
|
||
0xFF
|
||
};
|
||
|
||
static BYTE VolumeOffsetTable[] = {
|
||
AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_name ),
|
||
AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_password ),
|
||
AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_path ),
|
||
0xFF
|
||
};
|
||
|
||
static BYTE DirOffsetTable[] = {
|
||
AFP_FIELD_OFFSET( AFP_DIRECTORY_INFO, afpdir_path ),
|
||
AFP_FIELD_OFFSET( AFP_DIRECTORY_INFO, afpdir_owner ),
|
||
AFP_FIELD_OFFSET( AFP_DIRECTORY_INFO, afpdir_group ),
|
||
0xFF
|
||
};
|
||
|
||
static BYTE SessionOffsetTable[] = {
|
||
AFP_FIELD_OFFSET( AFP_SESSION_INFO, afpsess_ws_name ),
|
||
AFP_FIELD_OFFSET( AFP_SESSION_INFO, afpsess_username ),
|
||
0xFF
|
||
};
|
||
|
||
static BYTE FileOffsetTable[] = {
|
||
AFP_FIELD_OFFSET( AFP_FILE_INFO, afpfile_path ),
|
||
AFP_FIELD_OFFSET( AFP_FILE_INFO, afpfile_username ),
|
||
0xFF
|
||
};
|
||
|
||
static BYTE ConnectionOffsetTable[] = {
|
||
AFP_FIELD_OFFSET( AFP_CONNECTION_INFO, afpconn_username ),
|
||
AFP_FIELD_OFFSET( AFP_CONNECTION_INFO, afpconn_volumename ),
|
||
0xFF
|
||
};
|
||
|
||
static BYTE MessageOffsetTable[] = {
|
||
AFP_FIELD_OFFSET( AFP_MESSAGE_INFO, afpmsg_text ),
|
||
0xFF
|
||
};
|
||
|
||
static BYTE FinderOffsetTable[] = {
|
||
AFP_FIELD_OFFSET( AFP_FINDER_INFO, afpfd_path ),
|
||
0xFF
|
||
};
|
||
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBufStructureSize
|
||
//
|
||
// Returns: The size (in bytes) of the data withing the structure.
|
||
//
|
||
// Description: It will calculate the size of all the variable data and
|
||
// add that to the fixed size of the structure.
|
||
//
|
||
DWORD
|
||
AfpBufStructureSize(
|
||
IN AFP_STRUCTURE_TYPE dwStructureType,
|
||
IN LPBYTE lpbStructure
|
||
)
|
||
{
|
||
DWORD cbStructureSize;
|
||
DWORD dwIndex;
|
||
DWORD cbBufSize;
|
||
LPWSTR* plpwsStringField;
|
||
PBYTE OffsetTable;
|
||
|
||
switch( dwStructureType ) {
|
||
|
||
case AFP_VOLUME_STRUCT:
|
||
OffsetTable = VolumeOffsetTable;
|
||
cbStructureSize = sizeof( AFP_VOLUME_INFO );
|
||
break;
|
||
|
||
case AFP_SERVER_STRUCT:
|
||
OffsetTable = ServerOffsetTable;
|
||
cbStructureSize = sizeof( AFP_SERVER_INFO );
|
||
break;
|
||
|
||
case AFP_DIRECTORY_STRUCT:
|
||
OffsetTable = DirOffsetTable;
|
||
cbStructureSize = sizeof( AFP_DIRECTORY_INFO );
|
||
break;
|
||
|
||
case AFP_EXTENSION_STRUCT:
|
||
return( sizeof(AFP_EXTENSION) );
|
||
break;
|
||
|
||
case AFP_TYPECREATOR_STRUCT:
|
||
return( sizeof(AFP_TYPE_CREATOR) );
|
||
break;
|
||
|
||
case AFP_MESSAGE_STRUCT:
|
||
OffsetTable = MessageOffsetTable;
|
||
cbStructureSize = sizeof( AFP_MESSAGE_INFO );
|
||
break;
|
||
|
||
case AFP_ICON_STRUCT:
|
||
return( sizeof(AFP_ICON_INFO) +
|
||
((PAFP_ICON_INFO)lpbStructure)->afpicon_length );
|
||
break;
|
||
|
||
case AFP_FINDER_STRUCT:
|
||
OffsetTable = FinderOffsetTable;
|
||
cbStructureSize = sizeof( AFP_FINDER_INFO );
|
||
break;
|
||
|
||
default:
|
||
return( 0 );
|
||
}
|
||
|
||
// First calculate the amount of memory that will be needed to
|
||
// store all the string information.
|
||
//
|
||
for( dwIndex = 0, cbBufSize = 0;
|
||
|
||
OffsetTable[dwIndex] != 0xFF;
|
||
|
||
dwIndex++
|
||
|
||
) {
|
||
|
||
plpwsStringField=(LPWSTR*)((ULONG_PTR)lpbStructure + OffsetTable[dwIndex]);
|
||
|
||
cbBufSize += ( ( *plpwsStringField == NULL ) ? 0 :
|
||
STRLEN( *plpwsStringField ) + 1 );
|
||
}
|
||
|
||
// Convert to UNICODE size
|
||
//
|
||
cbBufSize *= sizeof( WCHAR );
|
||
|
||
// Add size of fixed part of the structure
|
||
//
|
||
cbBufSize += cbStructureSize;
|
||
|
||
return( cbBufSize );
|
||
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBufMakeFSDRequest
|
||
//
|
||
// Returns: NO_ERROR
|
||
// ERROR_NOT_ENOUGH_MEMORY
|
||
//
|
||
// Description: This routine is called by the worker routines for the client
|
||
// API calls. The purpose of this routine is to convert a
|
||
// AFP_XXX_INFO structure passed by the client API into a
|
||
// contiguous self-relative buffer. This has to be done because
|
||
// the FSD cannot reference pointers to user space.
|
||
//
|
||
// This routine will allocate the required amount of memory to
|
||
// store all the information in self relative form. It is
|
||
// the reponsibility of the caller to free this memory.
|
||
//
|
||
// All pointer fields will be converted to offsets from the
|
||
// beginning of the structure.
|
||
//
|
||
// The cbReqPktSize parameter specifies how many bytes of space
|
||
// should be allocated before the self relative data structure.
|
||
// i.e.
|
||
// |------------|
|
||
// |cbReqPktSize|
|
||
// | bytes |
|
||
// |------------|
|
||
// | Self |
|
||
// | relative |
|
||
// | structure |
|
||
// |------------|
|
||
//
|
||
DWORD
|
||
AfpBufMakeFSDRequest(
|
||
|
||
// Buffer as received by the client API
|
||
//
|
||
IN LPBYTE pBuffer,
|
||
|
||
// Size of FSD request packet.
|
||
//
|
||
IN DWORD cbReqPktSize,
|
||
|
||
IN AFP_STRUCTURE_TYPE dwStructureType,
|
||
|
||
// Self-relative form of I/P buf
|
||
//
|
||
OUT LPBYTE *ppSelfRelativeBuf,
|
||
|
||
// Size of self relative buf
|
||
//
|
||
OUT LPDWORD lpdwSelfRelativeBufSize
|
||
)
|
||
{
|
||
LPBYTE lpbSelfRelBuf;
|
||
DWORD cbSRBufSize;
|
||
DWORD dwIndex;
|
||
LPWSTR lpwsVariableData;
|
||
LPWSTR * plpwsStringField;
|
||
LPWSTR * plpwsStringFieldSR;
|
||
PBYTE OffsetTable;
|
||
DWORD cbStructureSize;
|
||
|
||
|
||
// Initialize the offset table and the structure size values
|
||
//
|
||
switch( dwStructureType ) {
|
||
|
||
case AFP_VOLUME_STRUCT:
|
||
OffsetTable = VolumeOffsetTable;
|
||
cbStructureSize = sizeof( AFP_VOLUME_INFO );
|
||
break;
|
||
|
||
case AFP_SERVER_STRUCT:
|
||
OffsetTable = ServerOffsetTable;
|
||
cbStructureSize = sizeof( AFP_SERVER_INFO );
|
||
break;
|
||
|
||
case AFP_DIRECTORY_STRUCT:
|
||
OffsetTable = DirOffsetTable;
|
||
cbStructureSize = sizeof( AFP_DIRECTORY_INFO );
|
||
break;
|
||
|
||
case AFP_MESSAGE_STRUCT:
|
||
OffsetTable = MessageOffsetTable;
|
||
cbStructureSize = sizeof( AFP_MESSAGE_INFO );
|
||
break;
|
||
|
||
case AFP_FINDER_STRUCT:
|
||
OffsetTable = FinderOffsetTable;
|
||
cbStructureSize = sizeof( AFP_FINDER_INFO );
|
||
break;
|
||
|
||
default:
|
||
return( ERROR_INVALID_PARAMETER );
|
||
}
|
||
|
||
cbSRBufSize = cbReqPktSize + AfpBufStructureSize(dwStructureType, pBuffer);
|
||
|
||
// Allocate space for self relative buffer
|
||
//
|
||
if ( ( lpbSelfRelBuf = (LPBYTE)LocalAlloc( LPTR, cbSRBufSize ) ) == NULL )
|
||
return( ERROR_NOT_ENOUGH_MEMORY );
|
||
|
||
*ppSelfRelativeBuf = lpbSelfRelBuf;
|
||
*lpdwSelfRelativeBufSize = cbSRBufSize;
|
||
|
||
// Advance this pointer beyond the request packet
|
||
//
|
||
lpbSelfRelBuf += cbReqPktSize;
|
||
|
||
// memcpy to fill in the non-string data
|
||
//
|
||
CopyMemory( lpbSelfRelBuf, pBuffer, cbStructureSize );
|
||
|
||
// Now copy all the strings
|
||
//
|
||
for( dwIndex = 0,
|
||
lpwsVariableData = (LPWSTR)((ULONG_PTR)lpbSelfRelBuf + cbStructureSize);
|
||
|
||
OffsetTable[dwIndex] != 0xFF;
|
||
|
||
dwIndex++ ) {
|
||
|
||
|
||
// This will point to a string pointer field in the non self-relative
|
||
// structure.
|
||
//
|
||
plpwsStringField = (LPWSTR*)((ULONG_PTR)pBuffer + OffsetTable[dwIndex]);
|
||
|
||
// This will point to the corresponding string pointer field in the
|
||
// self-relative structure
|
||
//
|
||
plpwsStringFieldSR=(LPWSTR*)((ULONG_PTR)lpbSelfRelBuf+OffsetTable[dwIndex]);
|
||
|
||
// If there is no string to be copied, then just set to NULL
|
||
//
|
||
if ( *plpwsStringField == NULL )
|
||
*plpwsStringFieldSR = NULL;
|
||
else {
|
||
|
||
// There is a string so copy it
|
||
//
|
||
STRCPY( lpwsVariableData, *plpwsStringField );
|
||
|
||
// Store the pointer value
|
||
//
|
||
*plpwsStringFieldSR = lpwsVariableData;
|
||
|
||
// Convert the pointer to this data to an offset
|
||
//
|
||
POINTER_TO_OFFSET( *plpwsStringFieldSR, lpbSelfRelBuf );
|
||
|
||
// Update the pointer to where the next variable length data
|
||
// will be stored.
|
||
//
|
||
lpwsVariableData += ( STRLEN( *plpwsStringField ) + 1 );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return( NO_ERROR );
|
||
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBufOffsetToPointer
|
||
//
|
||
// Returns: none.
|
||
//
|
||
// Description: Will walk a list of structures, converting all offsets
|
||
// within each structure to pointers.
|
||
//
|
||
VOID
|
||
AfpBufOffsetToPointer(
|
||
IN OUT LPBYTE pBuffer,
|
||
IN DWORD dwNumEntries,
|
||
IN AFP_STRUCTURE_TYPE dwStructureType
|
||
)
|
||
{
|
||
PBYTE OffsetTable;
|
||
DWORD cbStructureSize;
|
||
LPWSTR *plpwsStringField;
|
||
DWORD dwIndex;
|
||
|
||
|
||
// Initialize the offset table and the structure size values
|
||
//
|
||
switch( dwStructureType ) {
|
||
|
||
case AFP_VOLUME_STRUCT:
|
||
OffsetTable = VolumeOffsetTable;
|
||
cbStructureSize = sizeof( AFP_VOLUME_INFO );
|
||
break;
|
||
|
||
case AFP_SESSION_STRUCT:
|
||
OffsetTable = SessionOffsetTable;
|
||
cbStructureSize = sizeof( AFP_SESSION_INFO );
|
||
break;
|
||
|
||
case AFP_CONNECTION_STRUCT:
|
||
OffsetTable = ConnectionOffsetTable;
|
||
cbStructureSize = sizeof( AFP_CONNECTION_INFO );
|
||
break;
|
||
|
||
case AFP_FILE_STRUCT:
|
||
OffsetTable = FileOffsetTable;
|
||
cbStructureSize = sizeof( AFP_FILE_INFO );
|
||
break;
|
||
|
||
case AFP_DIRECTORY_STRUCT:
|
||
OffsetTable = DirOffsetTable;
|
||
cbStructureSize = sizeof( AFP_DIRECTORY_INFO );
|
||
break;
|
||
|
||
case AFP_MESSAGE_STRUCT:
|
||
OffsetTable = MessageOffsetTable;
|
||
cbStructureSize = sizeof( AFP_MESSAGE_INFO );
|
||
break;
|
||
|
||
case AFP_SERVER_STRUCT:
|
||
OffsetTable = ServerOffsetTable;
|
||
cbStructureSize = sizeof( AFP_SERVER_INFO );
|
||
break;
|
||
|
||
default:
|
||
return;
|
||
}
|
||
|
||
// Walk the list and convert each structure.
|
||
//
|
||
while( dwNumEntries-- ) {
|
||
|
||
// Convert every LPWSTR from an offset to a pointer
|
||
//
|
||
for( dwIndex = 0; OffsetTable[dwIndex] != 0xFF; dwIndex++ ) {
|
||
|
||
plpwsStringField = (LPWSTR*)( (ULONG_PTR)pBuffer
|
||
+ (DWORD)OffsetTable[dwIndex] );
|
||
|
||
OFFSET_TO_POINTER( *plpwsStringField, pBuffer );
|
||
|
||
}
|
||
|
||
pBuffer += cbStructureSize;
|
||
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBufMakeMultiSz
|
||
//
|
||
// Returns: NO_ERROR - success
|
||
// ERROR_NOT_ENOUGH_MEMORY
|
||
//
|
||
// Description: This routine will take a give structure and create a
|
||
// REG_MULTI_SZ from it. This can then be set directly into the
|
||
// registry. It is the caller's responsibility to free
|
||
// the memory allocated for *ppbMultiSz.
|
||
//
|
||
DWORD
|
||
AfpBufMakeMultiSz(
|
||
IN AFP_STRUCTURE_TYPE dwStructureType,
|
||
IN LPBYTE lpbStructure,
|
||
OUT LPBYTE * ppbMultiSz,
|
||
OUT LPDWORD lpdwMultiSzSize
|
||
)
|
||
{
|
||
PAFP_MULTISZ_INFO pAfpMultiSz;
|
||
PWCHAR lpwchWalker;
|
||
PVOID pData;
|
||
DWORD dwIndex;
|
||
DWORD cbStructureSize;
|
||
|
||
switch( dwStructureType ) {
|
||
|
||
case AFP_VOLUME_STRUCT:
|
||
pAfpMultiSz = AfpVolumeMultiSz;
|
||
break;
|
||
|
||
case AFP_EXTENSION_STRUCT:
|
||
pAfpMultiSz = AfpExtensionMultiSz;
|
||
break;
|
||
|
||
case AFP_TYPECREATOR_STRUCT:
|
||
pAfpMultiSz = AfpTypeCreatorMultiSz;
|
||
break;
|
||
|
||
case AFP_ICON_STRUCT:
|
||
pAfpMultiSz = AfpIconMultiSz;
|
||
break;
|
||
|
||
default:
|
||
return( ERROR_INVALID_PARAMETER );
|
||
}
|
||
|
||
// Allocate enough memory to create the multi-sz.
|
||
// AFP_CUMULATIVE_VALNAME_SIZE should be greater than the sum of all the
|
||
// value names of all the structures.
|
||
//
|
||
cbStructureSize = AfpBufStructureSize( dwStructureType, lpbStructure )
|
||
+ AFP_CUMULATIVE_VALNAME_SIZE;
|
||
|
||
if ( ( *ppbMultiSz = (LPBYTE)LocalAlloc( LPTR, cbStructureSize ) ) == NULL )
|
||
return( ERROR_NOT_ENOUGH_MEMORY );
|
||
|
||
ZeroMemory( *ppbMultiSz, cbStructureSize );
|
||
|
||
// For every field, we create a string
|
||
//
|
||
for ( dwIndex = 0,
|
||
lpwchWalker = (PWCHAR)*ppbMultiSz;
|
||
|
||
pAfpMultiSz[dwIndex].dwType != REG_NONE;
|
||
|
||
dwIndex++
|
||
|
||
){
|
||
|
||
// This is the value name so do not put it in the buffer.
|
||
//
|
||
if ( pAfpMultiSz[dwIndex].lpwsValueName == NULL )
|
||
continue;
|
||
|
||
STRCPY( lpwchWalker, pAfpMultiSz[dwIndex].lpwsValueName );
|
||
STRCAT( lpwchWalker, TEXT("="));
|
||
|
||
lpwchWalker += STRLEN( lpwchWalker );
|
||
|
||
pData = lpbStructure + pAfpMultiSz[dwIndex].dwOffset;
|
||
|
||
// Convert to string and concatenate
|
||
//
|
||
if ( pAfpMultiSz[dwIndex].dwType == REG_DWORD ) {
|
||
|
||
UCHAR chAnsiBuf[12];
|
||
|
||
_itoa( *((LPDWORD)pData), chAnsiBuf, 10 );
|
||
|
||
mbstowcs( lpwchWalker, chAnsiBuf, sizeof(chAnsiBuf) );
|
||
}
|
||
|
||
if ( pAfpMultiSz[dwIndex].dwType == REG_SZ ) {
|
||
|
||
// Check if this is a pointer or an in-place buffer
|
||
//
|
||
if ( pAfpMultiSz[dwIndex].fIsInPlace )
|
||
STRCPY( lpwchWalker, (LPWSTR)pData );
|
||
else {
|
||
|
||
if ( *(LPWSTR*)pData != NULL )
|
||
STRCPY( lpwchWalker, *((LPWSTR*)pData) );
|
||
}
|
||
}
|
||
|
||
lpwchWalker += ( STRLEN( lpwchWalker ) + 1 );
|
||
|
||
}
|
||
|
||
*lpdwMultiSzSize = (DWORD)((ULONG_PTR)lpwchWalker - (ULONG_PTR)(*ppbMultiSz) ) + sizeof(WCHAR);
|
||
|
||
return( NO_ERROR );
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBufParseMultiSz
|
||
//
|
||
// Returns: NO_ERROR - success
|
||
// ERROR_INVALID_PARAMETER
|
||
//
|
||
// Description: This routine will parse a REG_MULTI_SZ and fill in the
|
||
// appropriate data structure. All pointers will point to
|
||
// the pbMultiSz input parameter.
|
||
//
|
||
DWORD
|
||
AfpBufParseMultiSz(
|
||
IN AFP_STRUCTURE_TYPE dwStructureType,
|
||
IN LPBYTE pbMultiSz,
|
||
OUT LPBYTE pbStructure
|
||
)
|
||
{
|
||
PAFP_MULTISZ_INFO pAfpMultiSz;
|
||
DWORD dwIndex;
|
||
DWORD cbStructSize;
|
||
LPWSTR lpwchWalker;
|
||
PVOID pData;
|
||
UCHAR chAnsiBuf[12];
|
||
DWORD dwDisableCatsearch=0;
|
||
|
||
switch( dwStructureType ) {
|
||
|
||
case AFP_VOLUME_STRUCT:
|
||
pAfpMultiSz = AfpVolumeMultiSz;
|
||
cbStructSize = sizeof( AFP_VOLUME_INFO );
|
||
|
||
//
|
||
// The following "quick fix" is for Disabling CatSearch support. Read in the
|
||
// DisableCatsearch parameter if it's put in. In most cases, this parm won't
|
||
// be there. If it is, the server disables CatSearch
|
||
//
|
||
for ( (lpwchWalker = (LPWSTR)pbMultiSz);
|
||
(*lpwchWalker != TEXT('\0') );
|
||
(lpwchWalker += ( STRLEN( lpwchWalker ) + 1 ) ))
|
||
{
|
||
if ( STRNICMP( AFPREG_VALNAME_CATSEARCH,
|
||
lpwchWalker,
|
||
STRLEN( AFPREG_VALNAME_CATSEARCH ) ) == 0 )
|
||
{
|
||
lpwchWalker += ( STRLEN( AFPREG_VALNAME_CATSEARCH ) + 1 );
|
||
wcstombs( chAnsiBuf, lpwchWalker, sizeof(chAnsiBuf) );
|
||
dwDisableCatsearch = atoi( chAnsiBuf );
|
||
break;
|
||
}
|
||
}
|
||
|
||
break;
|
||
|
||
case AFP_EXTENSION_STRUCT:
|
||
pAfpMultiSz = AfpExtensionMultiSz;
|
||
cbStructSize = sizeof( AFP_EXTENSION );
|
||
break;
|
||
|
||
case AFP_TYPECREATOR_STRUCT:
|
||
pAfpMultiSz = AfpTypeCreatorMultiSz;
|
||
cbStructSize = sizeof( AFP_TYPE_CREATOR );
|
||
break;
|
||
|
||
case AFP_ICON_STRUCT:
|
||
pAfpMultiSz = AfpIconMultiSz;
|
||
cbStructSize = sizeof( AFP_ICON_INFO );
|
||
break;
|
||
|
||
default:
|
||
return( ERROR_INVALID_PARAMETER );
|
||
}
|
||
|
||
ZeroMemory( pbStructure, cbStructSize );
|
||
|
||
// For every field in the structure
|
||
//
|
||
for ( dwIndex = 0; pAfpMultiSz[dwIndex].dwType != REG_NONE; dwIndex++ ){
|
||
|
||
// This is the value name so do not try to retrieve it from the
|
||
// buffer.
|
||
//
|
||
if ( pAfpMultiSz[dwIndex].lpwsValueName == NULL )
|
||
continue;
|
||
|
||
// Search for valuename for this field
|
||
//
|
||
for ( lpwchWalker = (LPWSTR)pbMultiSz;
|
||
|
||
( *lpwchWalker != TEXT('\0') )
|
||
&&
|
||
( STRNICMP( pAfpMultiSz[dwIndex].lpwsValueName,
|
||
lpwchWalker,
|
||
STRLEN(pAfpMultiSz[dwIndex].lpwsValueName) ) != 0 );
|
||
|
||
lpwchWalker += ( STRLEN( lpwchWalker ) + 1 ) );
|
||
|
||
// Could not find parameter
|
||
//
|
||
if ( *lpwchWalker == TEXT('\0') )
|
||
return( ERROR_INVALID_PARAMETER );
|
||
|
||
// Otherwise we found it so get the value
|
||
//
|
||
lpwchWalker += ( STRLEN( pAfpMultiSz[dwIndex].lpwsValueName ) + 1 );
|
||
|
||
pData = pbStructure + pAfpMultiSz[dwIndex].dwOffset;
|
||
|
||
// If there is no value after the value name then ignore this field
|
||
// It defaults to zero.
|
||
//
|
||
if ( *lpwchWalker != TEXT( '\0' ) ) {
|
||
|
||
// Convert to integer
|
||
//
|
||
if ( pAfpMultiSz[dwIndex].dwType == REG_DWORD ) {
|
||
|
||
wcstombs( chAnsiBuf, lpwchWalker, sizeof(chAnsiBuf) );
|
||
|
||
*((LPDWORD)pData) = atoi( chAnsiBuf );
|
||
|
||
}
|
||
|
||
//
|
||
// CatSearch hack continued: if we are looking at the volume mask
|
||
// parameter, see if we must turn the bit off.
|
||
//
|
||
if( dwStructureType == AFP_VOLUME_STRUCT && dwDisableCatsearch )
|
||
{
|
||
if ( STRNICMP( pAfpMultiSz[dwIndex].lpwsValueName,
|
||
AFPREG_VALNAME_PROPS,
|
||
STRLEN(pAfpMultiSz[dwIndex].lpwsValueName) ) == 0 )
|
||
{
|
||
*((LPDWORD)pData) |= AFP_VOLUME_DISALLOW_CATSRCH;
|
||
}
|
||
}
|
||
|
||
if ( pAfpMultiSz[dwIndex].dwType == REG_SZ ) {
|
||
|
||
// Check if this is a pointer or an in-place buffer
|
||
//
|
||
if ( pAfpMultiSz[dwIndex].fIsInPlace ) {
|
||
|
||
if ( STRLEN( lpwchWalker ) > pAfpMultiSz[dwIndex].cch )
|
||
return( ERROR_INVALID_PARAMETER );
|
||
|
||
STRCPY( (LPWSTR)pData, lpwchWalker );
|
||
}
|
||
else
|
||
*((LPWSTR*)pData) = lpwchWalker;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return( NO_ERROR );
|
||
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBufMakeFSDETCMappings
|
||
//
|
||
// Returns: NO_ERROR
|
||
// ERROR_NOT_ENOUGH_MEMORY
|
||
//
|
||
// Description: This routine will convert all the mappings in the
|
||
// form stored in AfpGlobals.AfpETCMapInfo to the form
|
||
// required by the FSD, ie. the ETCMAPINFO structure.
|
||
// It is the responsibility for the caller to free
|
||
// allocated memory.
|
||
//
|
||
DWORD
|
||
AfpBufMakeFSDETCMappings(
|
||
OUT PSRVETCPKT *ppSrvSetEtc,
|
||
OUT LPDWORD lpdwSrvSetEtcBufSize
|
||
)
|
||
{
|
||
DWORD dwIndex;
|
||
PETCMAPINFO2 pETCMapInfo;
|
||
PAFP_EXTENSION pExtensionWalker;
|
||
PAFP_TYPE_CREATOR pTypeCreator;
|
||
AFP_TYPE_CREATOR AfpTypeCreatorKey;
|
||
DWORD dwNumTypeCreators;
|
||
|
||
|
||
// Allocate space to hold the ETCMaps in the form required by the FSD.
|
||
//
|
||
*ppSrvSetEtc = (PSRVETCPKT)LocalAlloc( LPTR,
|
||
AFP_FIELD_SIZE( SRVETCPKT, retc_NumEtcMaps ) +
|
||
(AfpGlobals.AfpETCMapInfo.afpetc_num_extensions*sizeof(ETCMAPINFO2)));
|
||
|
||
if ( *ppSrvSetEtc == NULL )
|
||
return( ERROR_NOT_ENOUGH_MEMORY );
|
||
|
||
// Walk through the extension list
|
||
//
|
||
for( dwIndex = 0,
|
||
pETCMapInfo = (*ppSrvSetEtc)->retc_EtcMaps,
|
||
pExtensionWalker = AfpGlobals.AfpETCMapInfo.afpetc_extension,
|
||
pTypeCreator = AfpGlobals.AfpETCMapInfo.afpetc_type_creator,
|
||
dwNumTypeCreators = AfpGlobals.AfpETCMapInfo.afpetc_num_type_creators,
|
||
(*ppSrvSetEtc)->retc_NumEtcMaps = 0;
|
||
|
||
dwIndex < AfpGlobals.AfpETCMapInfo.afpetc_num_extensions;
|
||
|
||
dwIndex++,
|
||
dwNumTypeCreators = AfpGlobals.AfpETCMapInfo.afpetc_num_type_creators,
|
||
pExtensionWalker++
|
||
|
||
) {
|
||
|
||
|
||
// Ignore any extensions that are associated with the default
|
||
// type/creator. They shouldnt be in the registry to begin with.
|
||
//
|
||
if ( pExtensionWalker->afpe_tcid == AFP_DEF_TCID )
|
||
continue;
|
||
|
||
// Find the type/creator associated with this extension.
|
||
//
|
||
AfpTypeCreatorKey.afptc_id = pExtensionWalker->afpe_tcid;
|
||
|
||
pTypeCreator = _lfind( &AfpTypeCreatorKey,
|
||
AfpGlobals.AfpETCMapInfo.afpetc_type_creator,
|
||
(unsigned int *)&dwNumTypeCreators,
|
||
sizeof(AFP_TYPE_CREATOR),
|
||
AfpLCompareTypeCreator );
|
||
|
||
|
||
// If there is a type/creator associated with this extension
|
||
//
|
||
if ( pTypeCreator != NULL ) {
|
||
|
||
AfpBufCopyFSDETCMapInfo( pTypeCreator,
|
||
pExtensionWalker,
|
||
pETCMapInfo );
|
||
|
||
pETCMapInfo++;
|
||
(*ppSrvSetEtc)->retc_NumEtcMaps++;
|
||
}
|
||
|
||
}
|
||
|
||
*lpdwSrvSetEtcBufSize = AFP_FIELD_SIZE( SRVETCPKT, retc_NumEtcMaps ) +
|
||
((*ppSrvSetEtc)->retc_NumEtcMaps * sizeof(ETCMAPINFO2));
|
||
|
||
return( NO_ERROR );
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBufMakeFSDIcon
|
||
//
|
||
// Returns: none.
|
||
//
|
||
// Description: This routine will copy the icon information from the
|
||
// AFP_ICON_INFO data structure to an SRVICONINFO data
|
||
// structure viz. the form that the FSD needs.
|
||
//
|
||
VOID
|
||
AfpBufMakeFSDIcon(
|
||
IN PAFP_ICON_INFO pIconInfo,
|
||
OUT LPBYTE lpbFSDIcon,
|
||
OUT LPDWORD lpcbFSDIconSize
|
||
)
|
||
{
|
||
UCHAR chBuffer[sizeof(AFP_ICON_INFO)]; // Need enough space to translate
|
||
|
||
// Blank out the whole structure so that type and creator will
|
||
// be padded with blanks
|
||
//
|
||
memset( lpbFSDIcon, ' ', sizeof(SRVICONINFO) );
|
||
|
||
// Convert to ANSI and copy type
|
||
//
|
||
wcstombs(chBuffer,pIconInfo->afpicon_type,sizeof(chBuffer));
|
||
|
||
CopyMemory( ((PSRVICONINFO)lpbFSDIcon)->icon_type,
|
||
chBuffer,
|
||
STRLEN(pIconInfo->afpicon_type));
|
||
|
||
// Convert to ANSI copy creator
|
||
//
|
||
wcstombs(chBuffer,pIconInfo->afpicon_creator,sizeof(chBuffer));
|
||
|
||
CopyMemory( ((PSRVICONINFO)lpbFSDIcon)->icon_creator,
|
||
chBuffer,
|
||
STRLEN(pIconInfo->afpicon_creator));
|
||
|
||
// Set icon type
|
||
//
|
||
((PSRVICONINFO)lpbFSDIcon)->icon_icontype = pIconInfo->afpicon_icontype;
|
||
|
||
// Set icon data length
|
||
//
|
||
((PSRVICONINFO)lpbFSDIcon)->icon_length = pIconInfo->afpicon_length;
|
||
|
||
CopyMemory( lpbFSDIcon + sizeof(SRVICONINFO),
|
||
pIconInfo->afpicon_data,
|
||
((PSRVICONINFO)lpbFSDIcon)->icon_length );
|
||
|
||
*lpcbFSDIconSize = sizeof(SRVICONINFO) + pIconInfo->afpicon_length;
|
||
|
||
return;
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBufCopyFSDETCMapInfo
|
||
//
|
||
// Returns: none
|
||
//
|
||
// Description: This routine will copu information from the AFP_TYPE_CREATOR
|
||
// and AFP_EXTENSION data structures into a ETCMAPINFO data
|
||
// structure viz. in the form as required by the FSD.
|
||
//
|
||
VOID
|
||
AfpBufCopyFSDETCMapInfo(
|
||
IN PAFP_TYPE_CREATOR pAfpTypeCreator,
|
||
IN PAFP_EXTENSION pAfpExtension,
|
||
OUT PETCMAPINFO2 pFSDETCMapInfo
|
||
)
|
||
{
|
||
CHAR Buffer[sizeof(AFP_TYPE_CREATOR)];
|
||
|
||
|
||
// Insert blanks which will be used to pad type/creators less
|
||
// than their max. lengths.
|
||
//
|
||
memset( (LPBYTE)pFSDETCMapInfo, ' ', sizeof(ETCMAPINFO2) );
|
||
ZeroMemory( (LPBYTE)(pFSDETCMapInfo->etc_extension),
|
||
AFP_FIELD_SIZE( ETCMAPINFO2, etc_extension ) );
|
||
|
||
CopyMemory( pFSDETCMapInfo->etc_extension,
|
||
pAfpExtension->afpe_extension,
|
||
wcslen(pAfpExtension->afpe_extension) * sizeof(WCHAR));
|
||
|
||
wcstombs( Buffer, pAfpTypeCreator->afptc_type, sizeof(Buffer) );
|
||
CopyMemory( pFSDETCMapInfo->etc_type,
|
||
Buffer,
|
||
STRLEN(pAfpTypeCreator->afptc_type));
|
||
|
||
wcstombs( Buffer, pAfpTypeCreator->afptc_creator, sizeof(Buffer) );
|
||
CopyMemory( pFSDETCMapInfo->etc_creator,
|
||
Buffer,
|
||
STRLEN(pAfpTypeCreator->afptc_creator));
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBufUnicodeToNibble
|
||
//
|
||
// Returns: NO_ERROR
|
||
// ERROR_INVALID_PARAMETER
|
||
//
|
||
// Description: This routine will take a pointer to a UNCODE string and
|
||
// convert each UNICODE char to a the corresponding nibble.
|
||
// it char. 'A' will be converted to a nibble having value 0xA
|
||
// This conversion is done in-place.
|
||
//
|
||
DWORD
|
||
AfpBufUnicodeToNibble(
|
||
IN OUT LPWSTR lpwsData
|
||
)
|
||
{
|
||
DWORD dwIndex;
|
||
BYTE bData;
|
||
LPBYTE lpbData = (LPBYTE)lpwsData;
|
||
|
||
// Convert each UNICODE character to nibble. (in place)
|
||
//
|
||
for ( dwIndex = 0; *lpwsData != TEXT('\0'); dwIndex++, lpwsData++ ) {
|
||
|
||
if ( iswalpha( *lpwsData ) ) {
|
||
|
||
if ( iswupper( *lpwsData ) )
|
||
bData = *lpwsData - TEXT('A');
|
||
else
|
||
bData = *lpwsData - TEXT('a');
|
||
|
||
bData += 10;
|
||
|
||
if ( bData > 0x0F )
|
||
return( ERROR_INVALID_PARAMETER );
|
||
}
|
||
else if ( iswdigit( *lpwsData ) )
|
||
bData = *lpwsData - TEXT('0');
|
||
else
|
||
return( ERROR_INVALID_PARAMETER );
|
||
|
||
// Multipy so that data is in the most significant nibble.
|
||
// Do this every other time.
|
||
//
|
||
if ( ( dwIndex % 2 ) == 0 )
|
||
*lpbData = bData * 16;
|
||
else {
|
||
*lpbData += bData;
|
||
lpbData++;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
return( NO_ERROR );
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBCompareTypeCreator
|
||
//
|
||
// Returns: < 0 if pAfpTypeCreator1 comes before pAfpTypeCreator2
|
||
// > 0 if pAfpTypeCreator1 comes before pAfpTypeCreator2
|
||
// == 0 if pAfpTypeCreator1 is equal to pAfpTypeCreator2
|
||
//
|
||
// Description: This routine is called by qsort to sort the list of
|
||
// type creators in the cache. The list is sorted in
|
||
// ascending alphabetical order of the concatenation of the
|
||
// creator and type. This list is sorted to facilitate quick
|
||
// lookup (binary search). This routine is also called by
|
||
// bsearch to do a binary search on the list.
|
||
//
|
||
int
|
||
_cdecl
|
||
AfpBCompareTypeCreator(
|
||
IN const void * pAfpTypeCreator1,
|
||
IN const void * pAfpTypeCreator2
|
||
)
|
||
{
|
||
WCHAR wchTypeCreator1[ sizeof( AFP_TYPE_CREATOR )];
|
||
WCHAR wchTypeCreator2[ sizeof( AFP_TYPE_CREATOR )];
|
||
|
||
|
||
STRCPY(wchTypeCreator1,
|
||
((PAFP_TYPE_CREATOR)pAfpTypeCreator1)->afptc_creator);
|
||
|
||
if (STRLEN(((PAFP_TYPE_CREATOR)pAfpTypeCreator1)->afptc_creator) == 0)
|
||
wchTypeCreator1[0]=L'\0';
|
||
|
||
STRCAT(wchTypeCreator1,((PAFP_TYPE_CREATOR)pAfpTypeCreator1)->afptc_type );
|
||
|
||
STRCPY(wchTypeCreator2,
|
||
((PAFP_TYPE_CREATOR)pAfpTypeCreator2)->afptc_creator);
|
||
|
||
STRCAT(wchTypeCreator2,((PAFP_TYPE_CREATOR)pAfpTypeCreator2)->afptc_type );
|
||
|
||
return( STRCMP( wchTypeCreator1, wchTypeCreator2 ) );
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpLCompareTypeCreator
|
||
//
|
||
// Returns: < 0 if pAfpTypeCreator1 comes before pAfpTypeCreator2
|
||
// > 0 if pAfpTypeCreator1 comes before pAfpTypeCreator2
|
||
// == 0 if pAfpTypeCreator1 is equal to pAfpTypeCreator2
|
||
//
|
||
// Description: This routine is called by lfind to do a linear search of
|
||
// the type/creator list.
|
||
//
|
||
int
|
||
_cdecl
|
||
AfpLCompareTypeCreator(
|
||
IN const void * pAfpTypeCreator1,
|
||
IN const void * pAfpTypeCreator2
|
||
)
|
||
{
|
||
|
||
return( ( ((PAFP_TYPE_CREATOR)pAfpTypeCreator1)->afptc_id ==
|
||
((PAFP_TYPE_CREATOR)pAfpTypeCreator2)->afptc_id ) ? 0 : 1 );
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBCompareExtension
|
||
//
|
||
// Returns: < 0 if pAfpExtension1 comes before pAfpExtension2
|
||
// > 0 if pAfpExtension1 comes before pAfpExtension2
|
||
// == 0 if pAfpExtension1 is equal to pAfpExtension2
|
||
//
|
||
// Description: This is called by qsort to sort the list of extensions in the
|
||
// cache. The list is sorted by ID. This routine is also called
|
||
// by bserach to do a binary lookup of this list.
|
||
//
|
||
int
|
||
_cdecl
|
||
AfpBCompareExtension(
|
||
IN const void * pAfpExtension1,
|
||
IN const void * pAfpExtension2
|
||
)
|
||
{
|
||
return((((PAFP_EXTENSION)pAfpExtension1)->afpe_tcid ==
|
||
((PAFP_EXTENSION)pAfpExtension2)->afpe_tcid ) ? 0 :
|
||
((((PAFP_EXTENSION)pAfpExtension1)->afpe_tcid <
|
||
((PAFP_EXTENSION)pAfpExtension2)->afpe_tcid ) ? -1 : 1 ));
|
||
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpLCompareExtension
|
||
//
|
||
// Returns: < 0 if pAfpExtension1 comes before pAfpExtension2
|
||
// > 0 if pAfpExtension1 comes before pAfpExtension2
|
||
// == 0 if pAfpExtension1 is equal to pAfpExtension2
|
||
//
|
||
// Description: This routine is called by lfind to do a linear lookup of the
|
||
// list of extensions in the cache.
|
||
//
|
||
int
|
||
_cdecl
|
||
AfpLCompareExtension(
|
||
IN const void * pAfpExtension1,
|
||
IN const void * pAfpExtension2
|
||
)
|
||
{
|
||
return( STRICMP( ((PAFP_EXTENSION)pAfpExtension1)->afpe_extension,
|
||
((PAFP_EXTENSION)pAfpExtension2)->afpe_extension ) );
|
||
}
|
||
|
||
//**
|
||
//
|
||
// Call: AfpBinarySearch
|
||
//
|
||
// Returns: Pointer to first occurance of element that matches pKey.
|
||
//
|
||
// Description: This is a wrapper around bsearch. Since bsearch does not
|
||
// return the first occurance of an element within the array,
|
||
// this routine will back up to point to the first occurance
|
||
// of a record with a particular key is reached.
|
||
//
|
||
void *
|
||
AfpBinarySearch(
|
||
IN const void * pKey,
|
||
IN const void * pBase,
|
||
IN size_t num,
|
||
IN size_t width,
|
||
IN int (_cdecl *compare)(const void * pElem1, const void * pElem2 )
|
||
)
|
||
{
|
||
void * pCurrElem = bsearch( pKey, pBase, num, width, compare);
|
||
|
||
|
||
if ( pCurrElem == NULL )
|
||
return( NULL );
|
||
|
||
// Backup until first occurance is reached
|
||
//
|
||
while ( ( (ULONG_PTR)pCurrElem > (ULONG_PTR)pBase )
|
||
&&
|
||
( (*compare)( pKey, (void*)((ULONG_PTR)pCurrElem - width) ) == 0 ) )
|
||
|
||
pCurrElem = (void *)((ULONG_PTR)pCurrElem - width);
|
||
|
||
return( pCurrElem );
|
||
|
||
}
|