Leaked source code of windows server 2003
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

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