1128 lines
35 KiB
C++
1128 lines
35 KiB
C++
//
|
|
// The developer of the original code and/or files is Tripwire, Inc.
|
|
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
|
|
// Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights
|
|
// reserved.
|
|
//
|
|
// This program is free software. The contents of this file are subject
|
|
// to the terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 2 of the License, or (at your
|
|
// option) any later version. You may redistribute it and/or modify it
|
|
// only in compliance with the GNU General Public License.
|
|
//
|
|
// This program is distributed in the hope that it will be useful.
|
|
// However, this program is distributed AS-IS WITHOUT ANY
|
|
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
|
|
// FOR A PARTICULAR PURPOSE. Please see the GNU General Public License
|
|
// for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
// USA.
|
|
//
|
|
// Nothing in the GNU General Public License or any other license to use
|
|
// the code or files shall permit you to use Tripwire's trademarks,
|
|
// service marks, or other intellectual property without Tripwire's
|
|
// prior written consent.
|
|
//
|
|
// If you have any questions, please contact Tripwire, Inc. at either
|
|
// info@tripwire.org or www.tripwire.org.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// parserhelper.cpp -- helper classes that are called by yacc parser
|
|
//
|
|
|
|
//=========================================================================
|
|
// OTHER DIRECTIVES
|
|
//=========================================================================
|
|
|
|
#include "stdtwparser.h"
|
|
|
|
//=========================================================================
|
|
// INCLUDES
|
|
//=========================================================================
|
|
|
|
#include "parserhelper.h"
|
|
#include "core/fsservices.h"
|
|
#include "fco/fcopropvector.h"
|
|
#include "fco/parsergenreutil.h"
|
|
#include "fco/genreswitcher.h"
|
|
#include "fco/twfactory.h"
|
|
#include "fco/fcospechelper.h"
|
|
#include "core/twlimits.h"
|
|
#include "core/stringutil.h"
|
|
|
|
//=========================================================================
|
|
// UTIL FUNCTION PROTOTYES
|
|
//=========================================================================
|
|
|
|
static int util_ConvertHex( const char* psz, int* const pnCharsRead );
|
|
static int util_ConvertUnicode( const char* psz, int* const pnCharsRead );
|
|
static int util_ConvertOctal( const char* psz, int* const pnCharsRead );
|
|
static bool util_IsOctal( const char ch );
|
|
static int util_GetEscapeValueOfChar( char ch );
|
|
static int util_GetRecurseDepth( const cParseNamedAttrList* pList ); //throw( eParserHelper )
|
|
static void util_EatAllSpaces( TSTRING& str );
|
|
static void util_LoseSurroundingWS( TSTRING& str );
|
|
#ifdef _DEBUG
|
|
static bool util_AsciiCharsActLikeTheyShould();
|
|
#endif
|
|
|
|
// finds first '&', '+', '-' on or after str[i]
|
|
static TSTRING::size_type util_FindNextDelim( const TSTRING& str, TSTRING::size_type i );
|
|
|
|
|
|
//=========================================================================
|
|
// STATIC DATA MEMBERS
|
|
//=========================================================================
|
|
|
|
cErrorBucket* cParserHelper::mpError;
|
|
int cParserHelper::miLineNum;
|
|
cPreprocessor::AcceptStack cPreprocessor::mStateStack;
|
|
bool cPreprocessor::mfIgnoreSection;
|
|
|
|
cParserHelper::ScopedAttrContainer cParserHelper::mScopedAttrs;
|
|
cParserHelper::GenreContainer cParserHelper::mAph;
|
|
cGenreParseInfo* cParserHelper::pCurrentGenreInfo;
|
|
cSymbolTable cParserHelper::mGlobalVarTable;
|
|
bool cParserHelper::mfParseOnly;
|
|
|
|
|
|
//=========================================================================
|
|
// METHOD CODE
|
|
//=========================================================================
|
|
|
|
eParserHelper::eParserHelper( const TSTRING& strMsg, int nLine /*= CURRENT_LINE */)
|
|
{
|
|
TOSTRINGSTREAM strErr;
|
|
|
|
strErr << strMsg;
|
|
|
|
if( NO_LINE != nLine )
|
|
{
|
|
// separate the message from the line number
|
|
if( ! strMsg.empty() )
|
|
strErr << _T(": ");
|
|
|
|
// output the line number
|
|
strErr << TSS_GetString( cTWParser, twparser::STR_LINE_NUMBER );
|
|
|
|
if( CURRENT_LINE == nLine )
|
|
strErr << cParserHelper::GetLineNumber();
|
|
else
|
|
strErr << nLine;
|
|
}
|
|
|
|
mMsg = strErr.str();
|
|
}
|
|
|
|
|
|
void cParserHelper::Init( cErrorBucket* pE )
|
|
{
|
|
// start off in default genre
|
|
cGenreSwitcher::GetInstance()->SelectGenre( cGenreSwitcher::GetInstance()->GetDefaultGenre() );
|
|
|
|
mpError = pE;
|
|
cPreprocessor::PushState( cPreprocessor::STATE_ACCEPT );
|
|
miLineNum = 1;
|
|
cPreprocessor::ReadSection();
|
|
pCurrentGenreInfo = NULL;
|
|
mfParseOnly = false;
|
|
}
|
|
|
|
void cParserHelper::Finit( cGenreSpecListVector* pPolicy )
|
|
{
|
|
//
|
|
// don't build spec list if just checking syntax
|
|
//
|
|
if( ! pPolicy )
|
|
{
|
|
CleanUp();
|
|
return;
|
|
}
|
|
|
|
int nRulesInPolicy = 0;
|
|
|
|
GenreContainer::iterator i;
|
|
for( i = mAph.begin(); i != mAph.end(); i++ )
|
|
{
|
|
cGenreSpecListPair slp;
|
|
cGenre::Genre g = i->first;
|
|
cGenreParseInfo* pgpi = i->second;
|
|
|
|
//
|
|
// create the specs from rules
|
|
//
|
|
cParserUtil::CreateFCOSpecs( g, pgpi, slp.GetSpecList() );
|
|
slp.SetGenre( g );
|
|
|
|
//
|
|
// get rule count for section
|
|
//
|
|
int nRulesInSection = slp.GetSpecList().Size();
|
|
if( 0 == nRulesInSection )
|
|
{
|
|
TSTRING str = TSS_GetString( cTWParser, twparser::STR_ERR2_PARSER_NO_RULES_IN_SECTION);
|
|
str.append( cGenreSwitcher::GetInstance()->GenreToString( slp.GetGenre(), true ) );
|
|
|
|
eParserNoRulesInSection e( str );
|
|
e.SetFatality( false );
|
|
cParserHelper::GetErrorBucket()->AddError( e );
|
|
}
|
|
|
|
//
|
|
// increment rule count for policy file
|
|
//
|
|
nRulesInPolicy += slp.GetSpecList().Size();
|
|
|
|
//
|
|
// add to policy
|
|
//
|
|
pPolicy->push_back( slp );
|
|
}
|
|
|
|
CleanUp();
|
|
|
|
//
|
|
// check that we have some rules
|
|
//
|
|
if( 0 == nRulesInPolicy )
|
|
throw eParserNoRules( _T(""), eParserHelper::NO_LINE );
|
|
}
|
|
|
|
void cParserHelper::CleanUp()
|
|
{
|
|
GenreContainer::iterator i;
|
|
for( i = mAph.begin(); i != mAph.end(); i++ )
|
|
delete i->second;
|
|
while( ! cPreprocessor::Empty() )
|
|
cPreprocessor::PopState();
|
|
while( ! cParserHelper::ScopeEmpty() )
|
|
cParserHelper::PopScope();
|
|
}
|
|
|
|
cPreprocessor::cPreprocessor()
|
|
{
|
|
// start in the accept state
|
|
mStateStack.push( STATE_ACCEPT );
|
|
}
|
|
|
|
void cPreprocessor::PushState( AcceptState state )
|
|
{
|
|
mStateStack.push( state );
|
|
|
|
cDebug d("cPreprocessor::PushState");
|
|
|
|
if( state == STATE_ACCEPT )
|
|
d.TraceDebug(_T("State == STATE_ACCEPT\n"));
|
|
else
|
|
d.TraceDebug(_T("State == STATE_IGNORE\n"));
|
|
}
|
|
|
|
void cPreprocessor::PopState()
|
|
{
|
|
cDebug d("cPreprocessor::PopState");
|
|
|
|
ASSERT( ! mStateStack.empty() );
|
|
mStateStack.pop();
|
|
|
|
#ifdef _DEBUG
|
|
if( !Empty() && TopState() == STATE_ACCEPT )
|
|
d.TraceDebug(_T("State == STATE_ACCEPT\n"));
|
|
else
|
|
d.TraceDebug(_T("State == STATE_IGNORE\n"));
|
|
#endif
|
|
}
|
|
|
|
cPreprocessor::AcceptState cPreprocessor::GetState()
|
|
{
|
|
if( STATE_ACCEPT == TopState() && false == mfIgnoreSection )
|
|
return STATE_ACCEPT;
|
|
else
|
|
return STATE_IGNORE;
|
|
}
|
|
|
|
cPreprocessor::AcceptState cPreprocessor::TopState()
|
|
{
|
|
ASSERT( ! mStateStack.empty() );
|
|
|
|
return mStateStack.top();
|
|
}
|
|
|
|
cPreprocessor::AcceptState cPreprocessor::UnderneathTopState()
|
|
{
|
|
ASSERT( mStateStack.size() > 1 );
|
|
|
|
AcceptState stateRet, stateSave;
|
|
|
|
stateSave = mStateStack.top();
|
|
mStateStack.pop();
|
|
stateRet = mStateStack.top();
|
|
mStateStack.push( stateSave );
|
|
|
|
return( stateRet );
|
|
}
|
|
|
|
void cPreprocessor::ToggleTopState()
|
|
{
|
|
ASSERT( ! mStateStack.empty() );
|
|
cDebug d("cPreprocessor::ToggleState");
|
|
|
|
AcceptState state = TopState();
|
|
|
|
mStateStack.pop();
|
|
|
|
// toggle state
|
|
mStateStack.push( GetOppositeState( state ) );
|
|
|
|
if( GetOppositeState( state ) == STATE_ACCEPT )
|
|
d.TraceDebug(_T("State == STATE_ACCEPT\n"));
|
|
else
|
|
d.TraceDebug(_T("State == STATE_IGNORE\n"));
|
|
}
|
|
|
|
|
|
cPreprocessor::AcceptState cPreprocessor::GetOppositeState( AcceptState state )
|
|
{
|
|
if( state == STATE_IGNORE )
|
|
return STATE_ACCEPT;
|
|
else
|
|
return STATE_IGNORE;
|
|
}
|
|
|
|
|
|
bool cPreprocessor::AtTopLevel()
|
|
{
|
|
ASSERT( ! mStateStack.empty() );
|
|
|
|
bool fAtTop;
|
|
AcceptState stateSave;
|
|
|
|
// save and pop current state
|
|
stateSave = mStateStack.top();
|
|
mStateStack.pop();
|
|
|
|
// we're at top level if there was only one item in stack
|
|
fAtTop = mStateStack.empty();
|
|
|
|
// return item to stack
|
|
mStateStack.push( stateSave );
|
|
|
|
return( fAtTop );
|
|
}
|
|
|
|
// pushes cPreprocessor::STATE_IGNORE onto the state stack if
|
|
// strSection is not an acceptable genre for this OS
|
|
|
|
void cParserHelper::SetSection( TSTRING& strSection ) // throw( eParserHelper )
|
|
{
|
|
//
|
|
// do we recognize the section string?
|
|
//
|
|
cGenre::Genre g = cGenreSwitcher::GetInstance()->StringToGenre( strSection.c_str() );
|
|
if( cGenre::GENRE_INVALID != g && cGenreSwitcher::GetInstance()->IsGenreAppropriate( g ) )
|
|
{
|
|
cGenreSwitcher::GetInstance()->SelectGenre( g );
|
|
|
|
// set current genre info pointer
|
|
GenreContainer::iterator i = mAph.find( cGenreSwitcher::GetInstance()->CurrentGenre() );
|
|
if( mAph.end() == i )
|
|
{
|
|
pCurrentGenreInfo = new cGenreParseInfo;
|
|
mAph.insert( GenreContainer::value_type( cGenreSwitcher::GetInstance()->CurrentGenre(), pCurrentGenreInfo ) );
|
|
|
|
cPreprocessor::ReadSection();
|
|
}
|
|
else
|
|
{
|
|
throw eParserSectionAlreadyDefined( strSection );
|
|
}
|
|
}
|
|
else // ignore
|
|
{
|
|
if( ! ParseOnly() )
|
|
{
|
|
eParserIgnoringSection e( strSection );
|
|
e.SetFatality( false );
|
|
if( mpError ) mpError->AddError( e );
|
|
}
|
|
|
|
cPreprocessor::IgnoreSection();
|
|
}
|
|
}
|
|
|
|
// A function for inserting a global variable into the global variable table. Throws
|
|
// an exception if they try to redefine one of the predefined variables. Returns
|
|
// the return value of the hash table insert.
|
|
bool cParserHelper::InsertGlobalVariable( const TSTRING& var, const TSTRING& val )
|
|
{
|
|
TSTRING dummy;
|
|
/* if( cParserHelper::GetGenreInfo()->GetPredefVarTable().Lookup(var, dummy) )
|
|
throw eParserRedefineVar( var );
|
|
*/
|
|
// TODO : Verify that there is no feasible way to search the predefines without
|
|
// messing everything up, given our current architecture.
|
|
|
|
return mGlobalVarTable.Insert( var, val );
|
|
}
|
|
|
|
cGenreParseInfo* cParserHelper::GetGenreInfo()
|
|
{
|
|
if( ! pCurrentGenreInfo )
|
|
{
|
|
pCurrentGenreInfo = new cGenreParseInfo;
|
|
mAph.insert( GenreContainer::value_type( cGenreSwitcher::GetInstance()->CurrentGenre(), pCurrentGenreInfo ) );
|
|
}
|
|
|
|
return pCurrentGenreInfo;
|
|
}
|
|
|
|
bool cParserUtil::AnyOfTheseHostsExists( cParseStringList* pList )
|
|
{
|
|
TSTRING strHostName;
|
|
cDebug d("cPreprocessor::AnyOfTheseHostsExists");
|
|
|
|
// this throws an exception. let it go on up, since we can't do any ifhosting
|
|
// if we don't know what machine we're on
|
|
iFSServices::GetInstance()->GetMachineName( strHostName );
|
|
|
|
// want to do a case-insensitive compare
|
|
std::transform( strHostName.begin(), strHostName.end(), strHostName.begin(), _totlower );
|
|
|
|
// if the host name matches any in the list, return true
|
|
for( std::list<TSTRING>::iterator iter = pList->begin(); iter != pList->end(); iter++ )
|
|
{
|
|
// want to do case-insensitive compare
|
|
std::transform( (*iter).begin(), (*iter).end(), (*iter).begin(), _totlower );
|
|
|
|
if( 0 == strHostName.compare( (*iter) ) )
|
|
{
|
|
d.TraceDebug(_T("host exists\n"));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
d.TraceDebug(_T("host doesn't exist\n"));
|
|
return false;
|
|
}
|
|
|
|
// converts escaped character sequences in strEscapedString into their escape characters in strInterpretedString
|
|
//( e.g. turns "Hello World!\n" (literal backslash) into "Hello World!<return char>"
|
|
// all C++ escape sequences are recognized:
|
|
// (1) octal numbers \012 (1, 2, or 3 octal digits),
|
|
// (2) hex numbers \x2A ( 'x' followed by one or two hex digits ),
|
|
// (3) unicode characters \uABCD ( 'u' followed by exactly 4 hex digits )
|
|
// (4) chars: \t, \v, \b, \r, \f, \a, \\, \?, \', and \"
|
|
// (5) all other escaped chars are treated as if not escaped
|
|
void cParserUtil::InterpretEscapedString( const std::string& strEscapedString, TSTRING& strInterpretedString )
|
|
{
|
|
cDebug d("cParserHelper::InterpretEscapedString");
|
|
|
|
ASSERT( (void*)&strEscapedString != (void*)&strInterpretedString ); // don't let us read and write to same string
|
|
|
|
// The source string may contain literal multibyte characters, escaped MB characters, and escaped Unicode characters.
|
|
// On Unix TCHAR == char always, therefore literal and esacped mb chars can be stored in strInterpretedString
|
|
// directly, and everythign will be hunky dory (escased Unicode will cause an error).
|
|
// But on Windows we need to build an intermediate narrow string because it needs to be passed through
|
|
// cStringUtil::StrToTstr() to convert literal and escaped mb chars to TCHARs (which are wchar_t in this case).
|
|
// Escaped Unicode chars are included in this intermediate string specially encoded so that StrToTstr()
|
|
// leaves it alone. After we have the wide string, we convert these encoded Unicode chars into true 16 bit
|
|
// Unicode chars.
|
|
|
|
|
|
// for unix we cheat a bit and do the initial interpretation
|
|
// directly to the final string.
|
|
typedef TCHAR INTERCHAR;
|
|
TSTRING& strIntermediateString = strInterpretedString;
|
|
|
|
const char* pchCur = strEscapedString.c_str();
|
|
|
|
for( strIntermediateString.erase();
|
|
*pchCur != 0;
|
|
pchCur++ ) // only single byte in this part of the policy file
|
|
{
|
|
if( *pchCur != '\\' ) // just regular char
|
|
{
|
|
strIntermediateString += *pchCur;
|
|
}
|
|
else // deal with escaped character sequence
|
|
{
|
|
// make sure the '\' isn't the end of the string
|
|
// ( if it is, the "for" will ++ pchCur, see it's 0, then break )
|
|
if( *(pchCur + 1 ) )
|
|
{
|
|
int nCharsRead;
|
|
|
|
pchCur++; // go to char past '\'
|
|
|
|
if (*pchCur == 'x' && std::isxdigit<TCHAR>(*(pchCur+1), std::locale() ) ) // deal with \xXXXX where 'x' is the character 'x', and 'X' is a hex number
|
|
{
|
|
pchCur++; // go to char past 'x'
|
|
char cEscapedChar = static_cast<char>( util_ConvertHex( pchCur, &nCharsRead ) );
|
|
pchCur += ( nCharsRead == 0 ) ? 0 : nCharsRead - 1; //increment pointer to last char we read
|
|
|
|
if (cEscapedChar == 0) // null characters are not allowed
|
|
{
|
|
throw eParserBadHex( cStringUtil::StrToTstr( pchCur - (( nCharsRead == 0 ) ? 0 : nCharsRead - 1) ) );
|
|
}
|
|
else
|
|
{
|
|
strIntermediateString += static_cast<INTERCHAR>(cEscapedChar);
|
|
}
|
|
}
|
|
else if (*pchCur == 'u') // unicode escape
|
|
{
|
|
pchCur++; // go to char past 'x'
|
|
wchar_t wcEscapedChar = static_cast<wchar_t>( util_ConvertUnicode( pchCur, &nCharsRead ) );
|
|
pchCur += ( nCharsRead == 0 ) ? 0 : nCharsRead - 1; //increment pointer to last char we read
|
|
|
|
if (wcEscapedChar == 0) // null characters are not allowed
|
|
{
|
|
throw eParserBadHex( cStringUtil::StrToTstr( pchCur - (( nCharsRead == 0 ) ? 0 : nCharsRead - 1) ) );
|
|
}
|
|
else if (wcEscapedChar > 0xff)
|
|
{
|
|
throw eParserBadHex( cStringUtil::StrToTstr( pchCur - (( nCharsRead == 0 ) ? 0 : nCharsRead - 1) ) );
|
|
}
|
|
else
|
|
{
|
|
strIntermediateString += static_cast<INTERCHAR>(wcEscapedChar);
|
|
}
|
|
}
|
|
else if( util_IsOctal( *pchCur ) ) // deal with \xxx where 'x' is an octal digit
|
|
{
|
|
strIntermediateString += static_cast<INTERCHAR>( util_ConvertOctal( pchCur, &nCharsRead ) );
|
|
pchCur += ( nCharsRead == 0 ) ? 0 : nCharsRead - 1; //increment pointer to last char we read
|
|
}
|
|
else // deal with \x where 'x' is any other character
|
|
{
|
|
strIntermediateString += static_cast<INTERCHAR>( util_GetEscapeValueOfChar( *pchCur ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
std::string str;
|
|
|
|
str = "before: <";
|
|
str += strEscapedString;
|
|
str += ">\n";
|
|
d.TraceDebug( str.c_str() );
|
|
|
|
TSTRING tstr;
|
|
tstr = _T("after: <");
|
|
tstr += strInterpretedString;
|
|
tstr += _T(">\n");
|
|
d.TraceDebug( tstr.c_str() );
|
|
#endif
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CreateFCOSpecs -- foreach rule, create an FCOSpec. then attach the stop
|
|
// points.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void cParserUtil::CreateFCOSpecs( cGenre::Genre g, cGenreParseInfo* pgpi, cFCOSpecList &fcospeclist) //throw( eParserHelper )
|
|
{
|
|
cGenreSwitcher::GetInstance()->SelectGenre( g );
|
|
iParserGenreUtil* pHelper = iTWFactory::GetInstance()->CreateParserGenreUtil();
|
|
|
|
cDebug d("cGenreParseInfo::CreateFCOSpecs");
|
|
|
|
// foreach rule
|
|
std::list<const cParseRule *>::iterator rule;
|
|
for (rule = pgpi->GetRules()->begin(); rule != pgpi->GetRules()->end(); rule++)
|
|
{
|
|
//
|
|
// create the spec with its the last element of its start point as its name.
|
|
// if the attribute 'rulename' is specified, we'll set it as the spec name later
|
|
// Need to set the start point after the helper is set
|
|
//
|
|
cFCOName startpoint = (*rule)->GetName();
|
|
iFCOSpec *pfcospec = iTWFactory::GetInstance()->CreateSpec( startpoint.GetShortName() ); // we'll set the helper below
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// look to see if no recurse was specified. if it is, set the helper to a cFCOSpecNoChildren,
|
|
// otherwise set it to a cFCOSpecStopPointSet
|
|
int depth = util_GetRecurseDepth( (*rule)->GetAttrList() );
|
|
if( 0 == depth )
|
|
{
|
|
// don't use stop point set. say don't recurse
|
|
cFCOSpecNoChildren* pNoKids = new cFCOSpecNoChildren;
|
|
pfcospec->SetHelper( pNoKids );
|
|
}
|
|
else
|
|
{
|
|
cFCOSpecStopPointSet* pStopPtSet = new cFCOSpecStopPointSet;
|
|
|
|
// set stop points
|
|
cGenreParseInfo::StopListType::iterator stop;
|
|
for (stop = pgpi->GetStopList()->begin(); stop != pgpi->GetStopList()->end(); stop++)
|
|
{
|
|
// add stop point if below start point
|
|
cFCOName::Relationship relate = startpoint.GetRelationship(*stop);
|
|
|
|
if (relate == cFCOName::REL_ABOVE)
|
|
{
|
|
pStopPtSet->Add( *stop );
|
|
}
|
|
}
|
|
|
|
pStopPtSet->SetRecurseDepth( -1 == depth ? cFCOSpecStopPointSet::RECURSE_INFINITE : depth );
|
|
pfcospec->SetHelper( pStopPtSet );
|
|
}
|
|
//
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// set start point
|
|
//
|
|
pfcospec->SetStartPoint( startpoint );
|
|
|
|
//
|
|
// set default mask
|
|
//
|
|
const iFCOSpecMask *pdefspecmask = iFCOSpecMask::GetDefaultMask();
|
|
ASSERT(pdefspecmask != 0);
|
|
|
|
//
|
|
// build property vector
|
|
//
|
|
cFCOPropVector v = (*rule)->GetDefSpecMask().GetPropVector();
|
|
pHelper->AddSubTypeProps( v );
|
|
pfcospec->SetPropVector( pdefspecmask, v);
|
|
|
|
//
|
|
// add attributes
|
|
//
|
|
TSTRING strAttrVal;
|
|
cParseNamedAttrList* pal = (*rule)->GetAttrList();
|
|
cFCOSpecAttr* pAttr = new cFCOSpecAttr;
|
|
if( pal != NULL )
|
|
{
|
|
// TODO: make storage place for these keywords
|
|
// attribute names are stored in lowercase
|
|
const cParseNamedAttr* pa;
|
|
|
|
//
|
|
// get rulename
|
|
//
|
|
pa = pal->Lookup( TSS_GetString( cTWParser, twparser::STR_PARSER_RULENAME ) );
|
|
if( pa )
|
|
{
|
|
// TODO -- at some future date, remove the name in the spec
|
|
// and only set it in pAttr
|
|
pfcospec->SetName( pa->GetValue() );
|
|
pAttr->SetName( pa->GetValue() );
|
|
}
|
|
|
|
//
|
|
// get severity
|
|
//
|
|
pa = pal->Lookup( TSS_GetString( cTWParser, twparser::STR_PARSER_SEVERITY ) );
|
|
if( pa )
|
|
{
|
|
int iSev;
|
|
cSeverityLimits sl;
|
|
if( ! sl.InterpretInt( pa->GetValue(), &iSev ) )
|
|
throw eParserBadSevVal( pa->GetValue(), pa->GetLineNum() );
|
|
|
|
pAttr->SetSeverity( iSev );
|
|
}
|
|
|
|
//
|
|
// get emailto
|
|
//
|
|
pa = pal->Lookup( TSS_GetString( cTWParser, twparser::STR_PARSER_EMAILTO ) ) ;
|
|
if( pa )
|
|
{
|
|
TSTRING strAddressee;
|
|
int index = 0;
|
|
while( cParserUtil::GetNextMailName( pa->GetValue(), strAddressee, index++ ) )
|
|
{
|
|
d.TraceDebug( _T("email to recip: <%s>\n"), strAddressee.c_str() );
|
|
pAttr->AddEmail( strAddressee );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// store rule in the spec list
|
|
//
|
|
fcospeclist.Add( pfcospec, pAttr );
|
|
pfcospec->Release();
|
|
pAttr->Release();
|
|
}
|
|
|
|
delete pHelper;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CreatePropVector -- given the string representation of the attribute vector,
|
|
// create an FCOPropVector
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void cParserUtil::CreatePropVector( const TSTRING& strPropListC, class cFCOPropVector& v, iParserGenreUtil* pHelper )
|
|
{
|
|
ASSERT( pHelper != NULL );
|
|
|
|
// state: turning modes on or off
|
|
enum Mode { MO_TURNON, MO_TURNOFF };
|
|
Mode mode = MO_TURNON;
|
|
|
|
// clear out all spaces in the string
|
|
TSTRING strPropList = strPropListC;
|
|
util_EatAllSpaces( strPropList );
|
|
|
|
// zero it out
|
|
v.Clear();
|
|
|
|
TSTRING::const_iterator iter;
|
|
TSTRING::size_type i; // index into string
|
|
for ( iter = strPropList.begin(), i = 0; iter != strPropList.end(); iter++, i++ )
|
|
{
|
|
int propIndex = -1; // index into propvector
|
|
|
|
switch (*iter)
|
|
{
|
|
/////////////////////////////////////////////////////////////
|
|
// parsing modes
|
|
case '+':
|
|
mode = MO_TURNON;
|
|
continue;
|
|
case '-':
|
|
mode = MO_TURNOFF;
|
|
continue;
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
// long attribute names
|
|
case '&':
|
|
{
|
|
//
|
|
// collect string
|
|
//
|
|
|
|
// find next '&', '+', or '-'
|
|
TSTRING::size_type next = util_FindNextDelim( strPropList, i+1 );
|
|
|
|
// get attribute name
|
|
TSTRING strProp;
|
|
if( next == TSTRING::npos )
|
|
strProp = strPropList.substr( i+1 );
|
|
else
|
|
strProp = strPropList.substr( i+1, next - i - 1 );
|
|
|
|
// increment past string
|
|
iter += strProp.length();
|
|
i += strProp.length();
|
|
|
|
// map attribute
|
|
if( ! pHelper->MapStringToProperty( strProp, propIndex ) )
|
|
throw eParserPropChar( strProp );
|
|
}
|
|
break;
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
// not '+' or '-' or '&' so map the char to a property
|
|
default:
|
|
if( ! pHelper->MapStringToProperty( TSTRING( 1, *iter ), propIndex ) )
|
|
throw eParserPropChar( TSTRING( 1, *iter ) );
|
|
break;
|
|
//
|
|
/////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
// now turn on or turn off bit, according to mode
|
|
ASSERT( propIndex != -1 );
|
|
if (mode == MO_TURNON)
|
|
v.AddItemAndGrow( propIndex );
|
|
else
|
|
v.RemoveItem( propIndex );
|
|
}
|
|
|
|
/* for 1.5, allow no properties (just track file existence)
|
|
// if v is empty, error
|
|
cFCOPropVector emptyPropVector;
|
|
emptyPropVector.Clear();
|
|
if( v == emptyPropVector )
|
|
{
|
|
d.TraceError("CreatePropVector failed!!\n");
|
|
throw eError( ERR_BAD_PROP_STRING, strPropV.c_str() );
|
|
}
|
|
*/
|
|
|
|
return;
|
|
}
|
|
|
|
// gets semicolon-delimited words in a string. index starts at 0.
|
|
// strNameRet has surronding WS discarded
|
|
bool cParserUtil::GetNextMailName( const TSTRING& strNameList, TSTRING& strNameRet, int index )
|
|
{
|
|
ASSERT( index >= 0 );
|
|
|
|
// go to nth name
|
|
TSTRING::size_type nextSC, lastSC = (TSTRING::size_type)-1; // algorithm starts searching for next ';'
|
|
// at lastSC+1
|
|
for( ; index >= 0; index-- )
|
|
{
|
|
nextSC = strNameList.find( _T(';'), ( lastSC + 1 ) );
|
|
|
|
if( 0 == index ) // if the name # we're looking for
|
|
{
|
|
strNameRet = strNameList.substr( ( lastSC + 1 ), TSTRING::npos == nextSC ? TSTRING::npos : nextSC - ( lastSC + 1 ) );
|
|
util_LoseSurroundingWS( strNameRet );
|
|
return true;
|
|
}
|
|
else if( TSTRING::npos == nextSC ) // didn't get to index'th naem: no names left
|
|
{
|
|
return false;
|
|
}
|
|
|
|
lastSC = nextSC;
|
|
}
|
|
|
|
// unreachable code
|
|
ASSERT( false );
|
|
return false; // just to placate the compiler
|
|
}
|
|
|
|
void cParserUtil::MergeMailLists( TSTRING& tstrMergeIntoMe, const TSTRING& tstrRHS )
|
|
{
|
|
// foreach word in rhs
|
|
TSTRING tstrWordToAdd;
|
|
int index = 0;
|
|
while( cParserUtil::GetNextMailName( tstrRHS, tstrWordToAdd, index++ ) )
|
|
{
|
|
// if it isn't in lhs, add it
|
|
bool fAlreadyInList = false;
|
|
int index2 = 0;
|
|
TSTRING tstrWordInLHS;
|
|
while( !fAlreadyInList && cParserUtil::GetNextMailName( tstrMergeIntoMe, tstrWordInLHS, index2++ ) )
|
|
{
|
|
if( 0 == tstrWordInLHS.compare( tstrWordToAdd ) )
|
|
fAlreadyInList = true;
|
|
}
|
|
|
|
if( ! fAlreadyInList )
|
|
{
|
|
tstrMergeIntoMe += _T(";");
|
|
tstrMergeIntoMe += tstrWordToAdd;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool cParserHelper::AtTopScope()
|
|
{
|
|
return( mScopedAttrs.empty() );
|
|
}
|
|
|
|
void cParserHelper::PopScope()
|
|
{
|
|
ASSERT( ! mScopedAttrs.empty() );
|
|
|
|
delete mScopedAttrs.back();
|
|
mScopedAttrs.pop_back();
|
|
}
|
|
|
|
cParseNamedAttrList* cParserHelper::GetGlobalAttrList()
|
|
{
|
|
if( ! mScopedAttrs.empty() )
|
|
return mScopedAttrs.back()->GetAttrList();
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void cParserHelper::IncrementScopeStatementCount()
|
|
{
|
|
// must add count to ALL previous scopes.
|
|
for( ScopedAttrContainer::iterator i = mScopedAttrs.begin(); i != mScopedAttrs.end(); i++ )
|
|
(*i)->IncrementStatementCount();
|
|
}
|
|
|
|
int cParserHelper::GetScopeStatementCount()
|
|
{
|
|
if( ! mScopedAttrs.empty() )
|
|
{
|
|
return mScopedAttrs.back()->GetStatementCount();
|
|
}
|
|
else
|
|
{
|
|
// should never ask for this when we aren't in a scope
|
|
ASSERT( false );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
void cParserHelper::PushScope( cAttrScopeInfo* pSI )
|
|
{
|
|
// add previous scope's info to this one
|
|
if( ! mScopedAttrs.empty() )
|
|
{
|
|
pSI->GetAttrList()->MergeNoOverwrite( mScopedAttrs.back()->GetAttrList() );
|
|
}
|
|
|
|
mScopedAttrs.push_back( pSI );
|
|
}
|
|
|
|
|
|
//=========================================================================
|
|
// UTIL FUNCTION CODE
|
|
//=========================================================================
|
|
|
|
int util_ConvertHex(const char* const cpsz, int* const pnCharsRead)
|
|
{
|
|
ASSERT( util_AsciiCharsActLikeTheyShould() );
|
|
ASSERT( cpsz && pnCharsRead );
|
|
|
|
if (*cpsz == 0 || !std::isxdigit<TCHAR>( *cpsz, std::locale() ))
|
|
throw eParserBadHex( cStringUtil::StrToTstr( cpsz ) );
|
|
|
|
int iValue;
|
|
const char* psz = cpsz;
|
|
for(
|
|
*pnCharsRead = 0, iValue = 0;
|
|
*psz && std::isxdigit<TCHAR>( *psz, std::locale() ) && (*pnCharsRead < 2);
|
|
psz++, (*pnCharsRead)++
|
|
)
|
|
{
|
|
iValue *= 0x10;
|
|
|
|
if( std::isdigit<TCHAR>( *psz, std::locale() ) )
|
|
{
|
|
iValue += ( *psz - '0' );
|
|
}
|
|
else
|
|
{
|
|
if( std::islower<TCHAR>( *psz, std::locale() ) )
|
|
iValue += ( *psz - 'a' + 10 ); // so that A=10, B=11, ..., F=15
|
|
else // is uppercase
|
|
iValue += ( *psz - 'A' + 10 ); // so that a=10, a=11, ..., f=15
|
|
}
|
|
}
|
|
|
|
return iValue;
|
|
}
|
|
|
|
int util_ConvertUnicode(const char* const cpsz, int* const pnCharsRead)
|
|
{
|
|
ASSERT( util_AsciiCharsActLikeTheyShould() );
|
|
ASSERT( cpsz && pnCharsRead );
|
|
|
|
if (*cpsz == 0 || !std::isxdigit<TCHAR>( *cpsz, std::locale() ))
|
|
throw eParserBadHex( cStringUtil::StrToTstr( cpsz ) );
|
|
|
|
int iValue;
|
|
const char* psz = cpsz;
|
|
for(
|
|
*pnCharsRead = 0, iValue = 0;
|
|
*pnCharsRead < 4;
|
|
psz++, (*pnCharsRead)++
|
|
)
|
|
{
|
|
// we require 4 chars for unicode escapes
|
|
if (*psz == 0 || !std::isxdigit<TCHAR>( *psz, std::locale() ))
|
|
throw eParserBadHex( cStringUtil::StrToTstr( cpsz ) );
|
|
|
|
iValue *= 0x10;
|
|
|
|
if( std::isdigit<TCHAR>( *psz, std::locale() ) )
|
|
{
|
|
iValue += ( *psz - '0' );
|
|
}
|
|
else
|
|
{
|
|
if( std::islower<TCHAR>( *psz, std::locale() ) )
|
|
iValue += ( *psz - 'a' + 10 ); // so that A=10, B=11, ..., F=15
|
|
else // is uppercase
|
|
iValue += ( *psz - 'A' + 10 ); // so that a=10, a=11, ..., f=15
|
|
}
|
|
}
|
|
|
|
return iValue;
|
|
}
|
|
|
|
// only 3 octal chars allowed
|
|
int util_ConvertOctal( const char* psz, int* const pnCharsRead )
|
|
{
|
|
ASSERT( util_AsciiCharsActLikeTheyShould() );
|
|
ASSERT( psz && pnCharsRead );
|
|
ASSERT( util_IsOctal( *psz ) ); // at least one oct char
|
|
|
|
int iValue;
|
|
for(
|
|
iValue = 0, *pnCharsRead = 0;
|
|
*psz && util_IsOctal( *psz ) && *pnCharsRead < 3; // limit of 3 octal chars
|
|
psz++, (*pnCharsRead)++
|
|
)
|
|
{
|
|
iValue *= 010;
|
|
iValue += ( *psz - '0' );
|
|
}
|
|
|
|
return( iValue );
|
|
}
|
|
|
|
bool util_IsOctal( const char ch )
|
|
{
|
|
ASSERT( util_AsciiCharsActLikeTheyShould() );
|
|
|
|
return( ch >= '0' && ch <= '7' );
|
|
}
|
|
|
|
int util_GetEscapeValueOfChar( char ch )
|
|
{
|
|
int iValue = -1;
|
|
|
|
switch( ch )
|
|
{
|
|
case 'n':
|
|
iValue = 10;
|
|
break;
|
|
case 't':
|
|
iValue = 9;
|
|
break;
|
|
case 'v':
|
|
iValue = 11;
|
|
break;
|
|
case 'b':
|
|
iValue = 8;
|
|
break;
|
|
case 'r':
|
|
iValue = 13;
|
|
break;
|
|
case 'f':
|
|
iValue = 12;
|
|
break;
|
|
case 'a':
|
|
iValue = 7;
|
|
break;
|
|
case '\\':
|
|
iValue = 92;
|
|
break;
|
|
case '\?':
|
|
iValue = 63;
|
|
break;
|
|
case '\'':
|
|
iValue = 39;
|
|
break;
|
|
case '\"':
|
|
iValue = 34;
|
|
break;
|
|
default:
|
|
{
|
|
// unrecognized escape char: just return character
|
|
iValue = static_cast<int>( ch );
|
|
}
|
|
break;
|
|
}
|
|
|
|
ASSERT( iValue != -1 ); //should have *some* value at this point
|
|
|
|
return( iValue );
|
|
}
|
|
|
|
|
|
// -1 means recurse all levels
|
|
int util_GetRecurseDepth( const cParseNamedAttrList* pList ) //throw( eParserHelper )
|
|
{
|
|
if( pList != NULL )
|
|
{
|
|
const cParseNamedAttr* pa = pList->Lookup( TSS_GetString( cTWParser, twparser::STR_PARSER_RECURSE ) );
|
|
if( pa )
|
|
{
|
|
TSTRING str = pa->GetValue();
|
|
|
|
std::transform( str.begin(), str.end(), str.begin(), _totlower );
|
|
|
|
if( 0 == str.compare( TSS_GetString( cTWParser, twparser::STR_PARSER_FALSE ) ) )
|
|
{
|
|
return 0;
|
|
}
|
|
else if( 0 == str.compare( TSS_GetString( cTWParser, twparser::STR_PARSER_TRUE ) ) )
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
// must be number
|
|
int i;
|
|
cRecurseDepthLimits rdl;
|
|
if( ! rdl.InterpretInt( str, &i ) )
|
|
throw eParserUnrecognizedAttrVal( str, pa->GetLineNum() );
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return( -1 ); // defaults to recurse all levels
|
|
}
|
|
|
|
// finds first '&', '+', '-' on or after str[i]
|
|
TSTRING::size_type util_FindNextDelim( const TSTRING& str, TSTRING::size_type i )
|
|
{
|
|
TSTRING::size_type min = TSTRING::npos;
|
|
TSTRING::size_type amp = str.find( '&', i );
|
|
TSTRING::size_type plus = str.find( '+', i );
|
|
TSTRING::size_type minus = str.find( '-', i );
|
|
|
|
// now find minimum of the three
|
|
if( amp != TSTRING::npos )
|
|
min = amp;
|
|
if( min != TSTRING::npos && plus != TSTRING::npos && plus < min )
|
|
min = plus;
|
|
if( min != TSTRING::npos && minus != TSTRING::npos && minus < min )
|
|
min = minus;
|
|
|
|
return min;
|
|
}
|
|
|
|
// deletes each space in the string
|
|
void util_EatAllSpaces( TSTRING& str )
|
|
{
|
|
for( TSTRING::iterator i = str.begin(); i != str.end(); i++ )
|
|
{
|
|
if( *i == _T(' ') )
|
|
{
|
|
str.erase( i );
|
|
}
|
|
}
|
|
}
|
|
|
|
void util_LoseSurroundingWS( TSTRING& str )
|
|
{
|
|
TSTRING::size_type nonWSStart = str.find_first_not_of( _T(" \t"), 0 );
|
|
TSTRING::size_type nonWSEnd = str.find_last_not_of( _T(" \t") );
|
|
|
|
if( TSTRING::npos == nonWSStart ) // if only whitespace
|
|
{
|
|
ASSERT( TSTRING::npos == nonWSEnd );
|
|
str = str.erase();
|
|
}
|
|
else // has non WS chars
|
|
{
|
|
ASSERT( TSTRING::npos != nonWSEnd );
|
|
str = str.substr( nonWSStart, nonWSEnd - nonWSStart + 1 );
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef _DEBUG
|
|
bool util_AsciiCharsActLikeTheyShould()
|
|
{
|
|
// we need numbers whose character
|
|
// representation increase by one
|
|
return(
|
|
( '0' < '1' ) &&
|
|
( '0' + 1 == '1' ) &&
|
|
( 'a' < 'b' ) &&
|
|
( 'a' + 1 == 'b' ) &&
|
|
( 'A' < 'B' ) &&
|
|
( 'A' + 1 == 'B' )
|
|
);
|
|
}
|
|
#endif
|
|
|