mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1236 lines
29 KiB
1236 lines
29 KiB
/********************************************************************/
|
|
/** 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 );
|
|
|
|
}
|