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.
1147 lines
24 KiB
1147 lines
24 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
parmlist.cxx
|
|
|
|
Abstract:
|
|
|
|
Simple class for parsing and storing parameter list pairs
|
|
|
|
Author:
|
|
|
|
John Ludeman (johnl) 22-Feb-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
//
|
|
// System include files.
|
|
//
|
|
|
|
#include "precomp.hxx"
|
|
|
|
inline
|
|
HRESULT
|
|
UnescapeStr(
|
|
STRA * pstr
|
|
)
|
|
{
|
|
CHAR * pch;
|
|
|
|
pch = pstr->QueryStr();
|
|
pch = strchr( pch, '+' );
|
|
while ( pch )
|
|
{
|
|
*pch = ' ';
|
|
pch = strchr( pch, '+' );
|
|
}
|
|
|
|
return pstr->Unescape();
|
|
}
|
|
|
|
PARAM_LIST::~PARAM_LIST(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Param list destructor
|
|
|
|
--*/
|
|
{
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
|
|
while ( !IsListEmpty( &_FieldListHead ) )
|
|
{
|
|
pFVP = CONTAINING_RECORD( _FieldListHead.Flink,
|
|
FIELD_VALUE_PAIR,
|
|
ListEntry );
|
|
|
|
DBG_ASSERT( pFVP != NULL );
|
|
|
|
RemoveEntryList( &pFVP->ListEntry );
|
|
|
|
delete( pFVP );
|
|
pFVP = NULL;
|
|
}
|
|
|
|
while ( !IsListEmpty( &_FreeHead ))
|
|
{
|
|
pFVP = CONTAINING_RECORD( _FreeHead.Flink,
|
|
FIELD_VALUE_PAIR,
|
|
ListEntry );
|
|
|
|
DBG_ASSERT( pFVP != NULL );
|
|
|
|
RemoveEntryList( &pFVP->ListEntry );
|
|
|
|
delete( pFVP );
|
|
pFVP = NULL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PARAM_LIST::Reset(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resets the parameter list back to its initially constructed state
|
|
|
|
--*/
|
|
{
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
|
|
while ( !IsListEmpty( &_FieldListHead ) )
|
|
{
|
|
pFVP = CONTAINING_RECORD( _FieldListHead.Flink,
|
|
FIELD_VALUE_PAIR,
|
|
ListEntry );
|
|
|
|
DBG_ASSERT( pFVP != NULL );
|
|
|
|
RemoveEntryList( &pFVP->ListEntry );
|
|
|
|
//
|
|
// Put the removed item on the end so the same entry will
|
|
// tend to be used for the same purpose on the next use
|
|
//
|
|
|
|
InsertTailList( &_FreeHead, &pFVP->ListEntry );
|
|
}
|
|
|
|
_fCanonicalized = FALSE;
|
|
}
|
|
|
|
HRESULT
|
|
PARAM_LIST::ParsePairs(
|
|
const CHAR * pszList,
|
|
BOOL fDefaultParams,
|
|
BOOL fAddBlankValues,
|
|
BOOL fCommaIsDelim
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Puts the text list into a linked list of field/value pairs
|
|
|
|
This can be used to parse lists in the form of:
|
|
|
|
"a=b,c=d,e=f" (with fCommaIsDelim = TRUE)
|
|
"name=Joe, Billy\nSearch=tom, sue, avery"(with fCommaIsDelim = FALSE)
|
|
|
|
Duplicates will be appended and tab separated
|
|
|
|
Arguments:
|
|
|
|
pszList - list of comma separated field/value pairs
|
|
fDefaultParams - If TRUE, means these parameters are only defaults
|
|
and shouldn't be added to the list if the field
|
|
name is already in the list and the value is
|
|
non-empty.
|
|
fAddBlankValues - if TRUE, allow fields with empty values to be
|
|
added to the list, else ignore empty values.
|
|
fCommaIsDelim - if TRUE, a comma acts as a separator between two
|
|
sets of fields values, otherwise the comma is
|
|
ignored
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
CHAR * pch;
|
|
DWORD cParams = 0;
|
|
DWORD i;
|
|
STRA strParams;
|
|
STRA strField;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Make a copy we can muck with
|
|
//
|
|
|
|
hr = strParams.Copy( pszList );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error copying field/value pairs list, hr = 0x%x.\n",
|
|
hr ));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Replace all of the equal signs and commas with '\n's for
|
|
// easier parsing
|
|
//
|
|
|
|
pch = strParams.QueryStr();
|
|
pch = strchr( pch, '=' );
|
|
while ( pch )
|
|
{
|
|
*pch = '\n';
|
|
cParams++;
|
|
pch = strchr( pch, '=' );
|
|
}
|
|
|
|
if ( fCommaIsDelim )
|
|
{
|
|
pch = strParams.QueryStr();
|
|
pch = strchr( pch, ',' );
|
|
while ( pch )
|
|
{
|
|
*pch = '\n';
|
|
cParams++;
|
|
pch = strchr( pch, ',' );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Pick out the fields and values and build the associative list
|
|
//
|
|
|
|
ODBC_PARSER Parser( strParams.QueryStr() );
|
|
|
|
for ( i = 0; i < cParams; i++ )
|
|
{
|
|
hr = strField.Copy( Parser.QueryToken() );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error copying token, hr = 0x%x.\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
Parser.NextLine();
|
|
|
|
pch = Parser.QueryToken();
|
|
|
|
//
|
|
// If we aren't supposed to add blanks, then just go to the next
|
|
// line
|
|
//
|
|
|
|
if ( !fAddBlankValues && !*pch )
|
|
{
|
|
Parser.NextLine();
|
|
continue;
|
|
}
|
|
|
|
if ( !fDefaultParams )
|
|
{
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
LIST_ENTRY * ple;
|
|
|
|
//
|
|
// Look for an existing field with this name and append
|
|
// the value there if we find it, otherwise add a new entry
|
|
//
|
|
|
|
for ( ple = _FieldListHead.Flink;
|
|
ple != &_FieldListHead;
|
|
ple = ple->Flink )
|
|
{
|
|
pFVP = CONTAINING_RECORD( ple,
|
|
FIELD_VALUE_PAIR,
|
|
ListEntry );
|
|
|
|
if ( !_stricmp( pFVP->QueryField(),
|
|
strField.QueryStr() ))
|
|
{
|
|
//
|
|
// CODEWORK - Remove this allocation
|
|
//
|
|
|
|
STRA strtmp;
|
|
|
|
hr = strtmp.Copy( pch );
|
|
if( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error copying, hr = 0x%x.\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Found this header, append the new value
|
|
//
|
|
|
|
pFVP->_cValues++;
|
|
|
|
hr = UnescapeStr( &strtmp );
|
|
if( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error unescaping string, hr = 0x%x.\n",
|
|
hr ));
|
|
|
|
return hr;
|
|
}
|
|
|
|
hr = pFVP->_strValue.Append( "\t" );
|
|
if( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error appending '\t', hr = 0x%x.\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
hr = pFVP->_strValue.Append( strtmp );
|
|
|
|
goto Found;
|
|
}
|
|
}
|
|
|
|
hr = AddEntry( strField.QueryStr(),
|
|
pch,
|
|
TRUE );
|
|
Found:
|
|
;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't add if already in list
|
|
//
|
|
|
|
hr = AddParam( strField.QueryStr(),
|
|
pch );
|
|
}
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error parsing pairs list, hr = 0x%x.\n",
|
|
hr ));
|
|
|
|
return hr;
|
|
}
|
|
|
|
Parser.NextLine();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
PARAM_LIST::AddEntryUsingConcat(
|
|
const CHAR * pszField,
|
|
const CHAR * pszValue,
|
|
BOOL fPossibleFastMap
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Concatenate value with existing entry of same name
|
|
or call AddEntry if none exists
|
|
|
|
Arguments:
|
|
|
|
pszField - Field to add
|
|
pszValue - Value to add
|
|
fPossibleFastMap - TRUE if entry is not known not to be
|
|
in the fast map
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Look for an existing field with this name
|
|
// and add the value there
|
|
//
|
|
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
LIST_ENTRY * ple;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Find the field
|
|
//
|
|
|
|
for ( ple = _FieldListHead.Flink;
|
|
ple != &_FieldListHead;
|
|
ple = ple->Flink )
|
|
{
|
|
pFVP = CONTAINING_RECORD( ple, FIELD_VALUE_PAIR, ListEntry );
|
|
|
|
if ( !_stricmp( pFVP->QueryField(),
|
|
pszField ))
|
|
{
|
|
//
|
|
// Found this header, append the new value
|
|
//
|
|
|
|
pFVP->_cValues++;
|
|
|
|
hr = pFVP->_strValue.Append( "," );
|
|
if( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error appending ',', hr = 0x%x.\n",
|
|
hr ));
|
|
|
|
return hr;
|
|
}
|
|
|
|
hr = pFVP->_strValue.Append( pszValue );
|
|
|
|
goto Found;
|
|
}
|
|
}
|
|
|
|
hr = AddEntry( pszField,
|
|
pszValue,
|
|
FALSE,
|
|
fPossibleFastMap );
|
|
|
|
Found:
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
PARAM_LIST::ParseSimpleList(
|
|
const CHAR * pszList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Puts the comma separated list into a linked list of field/value pairs
|
|
where the value is always NULL
|
|
|
|
Arguments:
|
|
|
|
pszList - list of comma separated fields
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
CHAR * pch;
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Pick out the fields and values and build the associative list
|
|
//
|
|
|
|
ODBC_PARSER Parser( (CHAR *) pszList );
|
|
|
|
Parser.SetListMode( TRUE );
|
|
|
|
while ( *(pch = Parser.QueryToken()) )
|
|
{
|
|
hr = AddEntry( pch,
|
|
NULL,
|
|
TRUE );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error AddEntry, hr = 0x%x.\n",
|
|
hr ));
|
|
|
|
return hr;
|
|
}
|
|
|
|
Parser.NextItem();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
CHAR *
|
|
PARAM_LIST::FindValue(
|
|
const CHAR * pszField,
|
|
BOOL * pfIsMultiValue OPTIONAL,
|
|
DWORD * pcbValue OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the value associated with pszField or NULL of no value was
|
|
found
|
|
|
|
Arguments:
|
|
|
|
pszField - field to search for value for
|
|
pfIsMultiValue - Set to TRUE if this value is a composite of multiple fields
|
|
pcbValue - Set to size of value (excluding nul terminator)
|
|
|
|
|
|
Return Value:
|
|
|
|
Pointer to value or NULL if not found
|
|
|
|
--*/
|
|
{
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
LIST_ENTRY * ple;
|
|
|
|
//
|
|
// Do we need to canon?
|
|
//
|
|
|
|
if ( !IsCanonicalized() )
|
|
{
|
|
CanonList( );
|
|
}
|
|
|
|
//
|
|
// Find the field
|
|
//
|
|
|
|
for ( ple = _FieldListHead.Flink;
|
|
ple != &_FieldListHead;
|
|
ple = ple->Flink )
|
|
{
|
|
pFVP = CONTAINING_RECORD( ple, FIELD_VALUE_PAIR, ListEntry );
|
|
|
|
if ( !_stricmp( pFVP->QueryField(),
|
|
pszField ))
|
|
{
|
|
if ( pfIsMultiValue )
|
|
{
|
|
*pfIsMultiValue = pFVP->IsMultiValued();
|
|
}
|
|
|
|
if ( pcbValue )
|
|
{
|
|
*pcbValue = pFVP->_strValue.QueryCB();
|
|
}
|
|
|
|
return pFVP->QueryValue();
|
|
}
|
|
}
|
|
|
|
|
|
return NULL;
|
|
}
|
|
|
|
HRESULT
|
|
PARAM_LIST::AddEntry(
|
|
const CHAR * pszField,
|
|
const CHAR * pszValue,
|
|
BOOL fUnescape,
|
|
BOOL fPossibleFastMap
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unconditionally adds the field/value pair to the end of the list
|
|
|
|
Arguments:
|
|
|
|
pszField - Field to add
|
|
pszValue - Value to add
|
|
fPossibleFastMap - TRUE if entry is not known not to be
|
|
in the fast map
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
HRESULT hr;
|
|
|
|
if ( !IsListEmpty( &_FreeHead ) )
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
|
|
pEntry = _FreeHead.Flink;
|
|
|
|
RemoveEntryList( _FreeHead.Flink );
|
|
|
|
pFVP = CONTAINING_RECORD( pEntry, FIELD_VALUE_PAIR, ListEntry );
|
|
|
|
pFVP->_strField.Reset();
|
|
pFVP->_strValue.Reset();
|
|
}
|
|
else
|
|
{
|
|
pFVP = new FIELD_VALUE_PAIR;
|
|
|
|
if ( !pFVP )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error creating new FIELD_VALUE_PAIR, hr = 0x%x.\n",
|
|
hr ));
|
|
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
pFVP->_cValues = 1;
|
|
|
|
//
|
|
// Add it to the list now so we don't have to worry about deleting it
|
|
// if one of the below copies fail
|
|
//
|
|
|
|
InsertTailList( &_FieldListHead, &pFVP->ListEntry );
|
|
|
|
hr = pFVP->_strField.Copy( pszField );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error copying field, hr = 0x%x.\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
hr = pFVP->_strValue.Copy( pszValue );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error copying value, hr = 0x%x.\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
if ( fUnescape )
|
|
{
|
|
//
|
|
// Normalize the fields and values (unescape and replace
|
|
// '+' with ' ')
|
|
//
|
|
|
|
hr = UnescapeStr( &pFVP->_strField );
|
|
if( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error unescaping field, hr = 0x%x.\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
hr = UnescapeStr( &pFVP->_strValue );
|
|
if( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error unescaping value, hr = 0x%x.\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
PARAM_LIST::AddEntry(
|
|
const CHAR * pszField,
|
|
DWORD cbField,
|
|
const CHAR * pszValue,
|
|
DWORD cbValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unconditionally adds the field/value pair to the end of the list
|
|
|
|
The fast map is not used and the fields are not unescaped
|
|
|
|
Arguments:
|
|
|
|
pszField - Field to add
|
|
cbField - Number of bytes in pszField
|
|
pszValue - Value to add
|
|
cbValue - Number of bytes in pszValue
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
PLIST_ENTRY listEntry;
|
|
HRESULT hr;
|
|
|
|
listEntry = RemoveHeadList( &_FreeHead );
|
|
|
|
if ( listEntry != &_FreeHead )
|
|
{
|
|
pFVP = CONTAINING_RECORD(
|
|
listEntry,
|
|
FIELD_VALUE_PAIR,
|
|
ListEntry );
|
|
|
|
pFVP->_strField.Reset();
|
|
pFVP->_strValue.Reset();
|
|
}
|
|
else
|
|
{
|
|
pFVP = new FIELD_VALUE_PAIR;
|
|
|
|
if ( !pFVP )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error creating new FIELD_VALUE_PAIR, hr = 0x%x.\n",
|
|
hr ));
|
|
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
pFVP->_cValues = 1;
|
|
|
|
//
|
|
// Add it to the list now so we don't have to worry about deleting it
|
|
// if one of the below copies fail
|
|
//
|
|
|
|
InsertTailList( &_FieldListHead, &pFVP->ListEntry );
|
|
|
|
hr = pFVP->_strField.Copy( pszField, cbField );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error copying field, hr = 0x%x.\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
hr = pFVP->_strValue.Copy( pszValue, cbValue );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error copying value, hr = 0x%x.\n",
|
|
hr ));
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
PARAM_LIST::AddParam(
|
|
const CHAR * pszField,
|
|
const CHAR * pszValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a field/value pair to the list if the field isn't already in the list
|
|
or the value is empty
|
|
|
|
The fields added through this method will be escaped
|
|
|
|
Arguments:
|
|
|
|
pszField - Field to add
|
|
pszValue - Value to add
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
LIST_ENTRY * ple;
|
|
|
|
//
|
|
// Find the field
|
|
//
|
|
|
|
for ( ple = _FieldListHead.Flink;
|
|
ple != &_FieldListHead;
|
|
ple = ple->Flink )
|
|
{
|
|
pFVP = CONTAINING_RECORD( ple, FIELD_VALUE_PAIR, ListEntry );
|
|
|
|
if ( !_stricmp( pFVP->QueryField(),
|
|
pszField ) )
|
|
{
|
|
//
|
|
// We found the field, replace the value if it is empty
|
|
//
|
|
|
|
if ( !*pFVP->QueryValue() )
|
|
{
|
|
return pFVP->_strValue.Copy( pszValue );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The field name wasn't found, add it
|
|
//
|
|
|
|
return AddEntry( pszField,
|
|
pszValue,
|
|
TRUE );
|
|
}
|
|
|
|
BOOL
|
|
PARAM_LIST::RemoveEntry(
|
|
const CHAR * pszFieldName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes all occurrences of the specified fieldname from the list
|
|
|
|
Arguments:
|
|
|
|
pszField - Field to remove
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
LIST_ENTRY * ple;
|
|
LIST_ENTRY * pleNext;
|
|
BOOL fFound = FALSE;
|
|
|
|
//
|
|
// Do we need to canon?
|
|
//
|
|
|
|
if ( !IsCanonicalized() )
|
|
{
|
|
CanonList( );
|
|
}
|
|
|
|
//
|
|
// Find the field
|
|
//
|
|
|
|
for ( ple = _FieldListHead.Flink;
|
|
ple != &_FieldListHead;
|
|
ple = pleNext )
|
|
{
|
|
pleNext = ple->Flink;
|
|
|
|
pFVP = CONTAINING_RECORD( ple, FIELD_VALUE_PAIR, ListEntry );
|
|
|
|
if ( !_stricmp( pFVP->QueryField(),
|
|
pszFieldName ))
|
|
{
|
|
//
|
|
// We found a matching field, remove it
|
|
//
|
|
|
|
RemoveEntryList( ple );
|
|
|
|
InsertHeadList( &_FreeHead, ple );
|
|
|
|
fFound = TRUE;
|
|
}
|
|
}
|
|
|
|
return (fFound);
|
|
}
|
|
|
|
|
|
VOID *
|
|
PARAM_LIST::NextPair(
|
|
VOID * pCookie,
|
|
CHAR * * ppszField,
|
|
CHAR * * ppszValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerates the field and values in this parameter list
|
|
|
|
Arguments:
|
|
|
|
pCookie - Stores location in enumeration, set to NULL for new enumeration
|
|
ppszField - Receives field
|
|
ppszValue - Receives corresponding value
|
|
|
|
Return Value:
|
|
|
|
NULL when the enumeration is complete
|
|
|
|
--*/
|
|
{
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
|
|
//
|
|
// Do we need to canon?
|
|
//
|
|
|
|
if ( !IsCanonicalized() )
|
|
{
|
|
CanonList( );
|
|
}
|
|
|
|
//
|
|
// pCookie points to the ListEntry in the FIELD_VALUE_PAIR class
|
|
//
|
|
if ( pCookie == NULL )
|
|
{
|
|
|
|
if ( IsListEmpty( &_FieldListHead ))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Start a new enumeration
|
|
//
|
|
|
|
pCookie = (VOID *) _FieldListHead.Flink;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Have we finished the current enumeration?
|
|
//
|
|
|
|
if ( pCookie == (VOID *) &_FieldListHead )
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
pFVP = CONTAINING_RECORD( pCookie, FIELD_VALUE_PAIR, ListEntry );
|
|
|
|
*ppszField = pFVP->QueryField();
|
|
*ppszValue = pFVP->QueryValue();
|
|
|
|
pCookie = pFVP->ListEntry.Flink;
|
|
|
|
return pCookie;
|
|
}
|
|
|
|
VOID *
|
|
PARAM_LIST::NextPair(
|
|
VOID * pCookie,
|
|
CHAR * * ppszField,
|
|
DWORD * pcbField,
|
|
CHAR * * ppszValue,
|
|
DWORD * pcbValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerates the field and values in this parameter list
|
|
|
|
Arguments:
|
|
|
|
pCookie - Stores location in enumeration, set to NULL for new enumeration
|
|
ppszField - Receives field
|
|
pcbField - Receives pointer to length of field
|
|
ppszValue - Receives corresponding value
|
|
pcbValue - Receives pointer to length of value
|
|
|
|
Return Value:
|
|
|
|
NULL when the enumeration is complete
|
|
|
|
--*/
|
|
{
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
|
|
//
|
|
// Do we need to canon?
|
|
//
|
|
|
|
if ( !IsCanonicalized() )
|
|
{
|
|
CanonList( );
|
|
}
|
|
|
|
//
|
|
// pCookie points to the ListEntry in the FIELD_VALUE_PAIR class
|
|
//
|
|
|
|
if ( pCookie == NULL )
|
|
{
|
|
|
|
if ( IsListEmpty( &_FieldListHead ))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Start a new enumeration
|
|
//
|
|
|
|
pCookie = (VOID *) _FieldListHead.Flink;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Have we finished the current enumeration?
|
|
//
|
|
|
|
if ( pCookie == (VOID *) &_FieldListHead )
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
pFVP = CONTAINING_RECORD( pCookie, FIELD_VALUE_PAIR, ListEntry );
|
|
|
|
*ppszField = pFVP->QueryField();
|
|
*pcbField = pFVP->_strField.QueryCB();
|
|
|
|
*ppszValue = pFVP->QueryValue();
|
|
*pcbValue = pFVP->_strValue.QueryCB();
|
|
|
|
pCookie = pFVP->ListEntry.Flink;
|
|
|
|
return pCookie;
|
|
}
|
|
|
|
DWORD
|
|
PARAM_LIST::GetCount(
|
|
VOID
|
|
)
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
DWORD cParams = 0;
|
|
|
|
//
|
|
// Do we need to canon?
|
|
//
|
|
|
|
if ( !IsCanonicalized() )
|
|
{
|
|
CanonList( );
|
|
}
|
|
|
|
for ( pEntry = _FieldListHead.Flink;
|
|
pEntry != &_FieldListHead;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
cParams++;
|
|
}
|
|
|
|
return cParams;
|
|
}
|
|
|
|
|
|
VOID
|
|
PARAM_LIST::CanonList(
|
|
VOID
|
|
)
|
|
{
|
|
FIELD_VALUE_PAIR * pFVP;
|
|
PLIST_ENTRY listEntry;
|
|
PLIST_ENTRY nextEntry;
|
|
PLIST_ENTRY tmpEntry;
|
|
|
|
DBG_ASSERT(!_fCanonicalized);
|
|
|
|
//
|
|
// Go through the list and make sure that there are no dups.
|
|
// if there are, convert them into comma separated lists.
|
|
//
|
|
|
|
for ( listEntry = _FieldListHead.Flink;
|
|
listEntry != &_FieldListHead;
|
|
listEntry = nextEntry
|
|
)
|
|
{
|
|
DWORD fieldLen;
|
|
PCHAR fieldName;
|
|
|
|
nextEntry = listEntry->Flink;
|
|
|
|
pFVP = CONTAINING_RECORD( listEntry, FIELD_VALUE_PAIR, ListEntry );
|
|
|
|
|
|
//
|
|
// if field or value is empty, zap it
|
|
//
|
|
|
|
if ( (*pFVP->QueryField() == '\0') ||
|
|
(*pFVP->QueryValue() == '\0') )
|
|
{
|
|
|
|
RemoveEntryList( listEntry );
|
|
InsertHeadList( &_FreeHead, listEntry );
|
|
continue;
|
|
}
|
|
|
|
fieldName = pFVP->QueryField();
|
|
fieldLen = pFVP->_strField.QueryCB();
|
|
|
|
//
|
|
// Walk the rest of the list and look for dup fields
|
|
//
|
|
|
|
tmpEntry = nextEntry;
|
|
|
|
while ( tmpEntry != &_FieldListHead )
|
|
{
|
|
|
|
FIELD_VALUE_PAIR * pTmpFVP;
|
|
pTmpFVP = CONTAINING_RECORD(
|
|
tmpEntry,
|
|
FIELD_VALUE_PAIR,
|
|
ListEntry );
|
|
|
|
//
|
|
// combine the two fields
|
|
//
|
|
|
|
if ( (pTmpFVP->_strField.QueryCB() == fieldLen) &&
|
|
(_stricmp(pTmpFVP->QueryField(), fieldName) == 0) &&
|
|
(*pTmpFVP->QueryValue() != '\0') )
|
|
{
|
|
|
|
pFVP->_cValues++;
|
|
|
|
pFVP->_strValue.Append( "," );
|
|
pFVP->_strValue.Append( pTmpFVP->QueryValue() );
|
|
|
|
pTmpFVP->_strField.Reset();
|
|
}
|
|
|
|
tmpEntry = tmpEntry->Flink;
|
|
}
|
|
}
|
|
|
|
_fCanonicalized = TRUE;
|
|
return;
|
|
|
|
} // PARAM_LIST::CanonList
|
|
|
|
|